From b86a0d4898bd1ad4967d81153a099c9e4c31cdf3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:40:22 +0000 Subject: [PATCH 1/7] Initial plan From 01871bb24ff7a5e3bd6f30fd9ff6d561af12eacb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:50:38 +0000 Subject: [PATCH 2/7] Fix VSC extension Launch and Debug failing when AppxManifest at root Co-authored-by: chiaramooney <34109996+chiaramooney@users.noreply.github.com> --- src/winapp-VSC/.vscode/launch.json | 22 + src/winapp-VSC/.vscode/tasks.json | 20 + src/winapp-VSC/.vscodeignore | 13 + src/winapp-VSC/esbuild.mjs | 51 + src/winapp-VSC/eslint.config.mjs | 27 + src/winapp-VSC/images/icon.png | Bin 0 -> 10142 bytes src/winapp-VSC/package-lock.json | 3668 ++++++++++++++++++++++++ src/winapp-VSC/package.json | 211 ++ src/winapp-VSC/src/extension.ts | 674 +++++ src/winapp-VSC/src/winapp-cli-utils.ts | 26 + src/winapp-VSC/tsconfig.json | 19 + 11 files changed, 4731 insertions(+) create mode 100644 src/winapp-VSC/.vscode/launch.json create mode 100644 src/winapp-VSC/.vscode/tasks.json create mode 100644 src/winapp-VSC/.vscodeignore create mode 100644 src/winapp-VSC/esbuild.mjs create mode 100644 src/winapp-VSC/eslint.config.mjs create mode 100644 src/winapp-VSC/images/icon.png create mode 100644 src/winapp-VSC/package-lock.json create mode 100644 src/winapp-VSC/package.json create mode 100644 src/winapp-VSC/src/extension.ts create mode 100644 src/winapp-VSC/src/winapp-cli-utils.ts create mode 100644 src/winapp-VSC/tsconfig.json diff --git a/src/winapp-VSC/.vscode/launch.json b/src/winapp-VSC/.vscode/launch.json new file mode 100644 index 00000000..879eed83 --- /dev/null +++ b/src/winapp-VSC/.vscode/launch.json @@ -0,0 +1,22 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "sourceMaps": true, + "preLaunchTask": "npm: watch" + } + ] +} diff --git a/src/winapp-VSC/.vscode/tasks.json b/src/winapp-VSC/.vscode/tasks.json new file mode 100644 index 00000000..afa128e2 --- /dev/null +++ b/src/winapp-VSC/.vscode/tasks.json @@ -0,0 +1,20 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "group": "build", + "isBackground": true, + "label": "npm: watch", + "problemMatcher": [] + }, + { + "type": "npm", + "script": "compile", + "group": "build", + "problemMatcher": "$esbuild", + "label": "npm: compile" + } + ] +} diff --git a/src/winapp-VSC/.vscodeignore b/src/winapp-VSC/.vscodeignore new file mode 100644 index 00000000..9de3ca54 --- /dev/null +++ b/src/winapp-VSC/.vscodeignore @@ -0,0 +1,13 @@ +.vscode/** +.vscode-test/** +src/** +!bin/**/*.exe +out/** +node_modules/** +.gitignore +eslint.config.mjs +esbuild.mjs +tsconfig.json +**/*.ts +**/*.map +**/*.backup diff --git a/src/winapp-VSC/esbuild.mjs b/src/winapp-VSC/esbuild.mjs new file mode 100644 index 00000000..ea1dbf40 --- /dev/null +++ b/src/winapp-VSC/esbuild.mjs @@ -0,0 +1,51 @@ +import * as esbuild from 'esbuild'; + +const production = process.argv.includes('--production'); +const watch = process.argv.includes('--watch'); + +/** @type {import('esbuild').BuildOptions} */ +const buildOptions = { + entryPoints: ['src/extension.ts'], + bundle: true, + outfile: 'dist/extension.js', + external: ['vscode'], + format: 'cjs', + platform: 'node', + target: 'ES2022', + sourcemap: !production, + minify: production, + sourcesContent: false, +}; + +/** + * Plugin that logs build start/end so the VS Code problem matcher + * can detect when the background watch task is ready. + */ +const watchPlugin = { + name: 'watch-plugin', + setup(build) { + build.onStart(() => { + console.log('[esbuild] watching for changes...'); + }); + build.onEnd((result) => { + if (result.errors.length === 0) { + console.log('[esbuild] build finished'); + } + }); + }, +}; + +async function main() { + if (watch) { + const ctx = await esbuild.context({ ...buildOptions, plugins: [watchPlugin] }); + await ctx.watch(); + } else { + await esbuild.build(buildOptions); + console.log('[esbuild] build complete'); + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/src/winapp-VSC/eslint.config.mjs b/src/winapp-VSC/eslint.config.mjs new file mode 100644 index 00000000..7c51b0c0 --- /dev/null +++ b/src/winapp-VSC/eslint.config.mjs @@ -0,0 +1,27 @@ +import typescriptEslint from "typescript-eslint"; + +export default [{ + files: ["**/*.ts"], +}, { + plugins: { + "@typescript-eslint": typescriptEslint.plugin, + }, + + languageOptions: { + parser: typescriptEslint.parser, + ecmaVersion: 2022, + sourceType: "module", + }, + + rules: { + "@typescript-eslint/naming-convention": ["warn", { + selector: "import", + format: ["camelCase", "PascalCase"], + }], + + curly: "warn", + eqeqeq: "warn", + "no-throw-literal": "warn", + semi: "warn", + }, +}]; \ No newline at end of file diff --git a/src/winapp-VSC/images/icon.png b/src/winapp-VSC/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..390638dbe8614112f730840a1c70787da71a1a37 GIT binary patch literal 10142 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 literal 0 HcmV?d00001 diff --git a/src/winapp-VSC/package-lock.json b/src/winapp-VSC/package-lock.json new file mode 100644 index 00000000..d0235bd3 --- /dev/null +++ b/src/winapp-VSC/package-lock.json @@ -0,0 +1,3668 @@ +{ + "name": "winapp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "winapp", + "version": "0.1.0", + "dependencies": { + "glob": "^13.0.6" + }, + "devDependencies": { + "@types/mocha": "^10.0.10", + "@types/node": "22.x", + "@types/vscode": "^1.109.0", + "@vscode/test-cli": "^0.0.12", + "@vscode/test-electron": "^2.5.2", + "esbuild": "^0.27.3", + "eslint": "^10.0.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.56.1" + }, + "engines": { + "vscode": "^1.109.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz", + "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.2", + "debug": "^4.3.1", + "minimatch": "^10.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", + "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", + "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", + "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", + "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/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/@isaacs/cliui/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/@isaacs/cliui/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/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", + "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/vscode": { + "version": "1.109.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.109.0.tgz", + "integrity": "sha512-0Pf95rnwEIwDbmXGC08r0B4TQhAbsHQ5UyTIgVgoieDe4cOnf92usuR5dEczb6bTKEp7ziZH4TV1TRGPPCExtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.12.tgz", + "integrity": "sha512-iYN0fDg29+a2Xelle/Y56Xvv7Nc8Thzq4VwpzAF/SIE6918rDicqfsQxV6w1ttr2+SOm+10laGuY9FG2ptEKsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mocha": "^10.0.10", + "c8": "^10.1.3", + "chokidar": "^3.6.0", + "enhanced-resolve": "^5.18.3", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^11.7.4", + "supports-color": "^10.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-cli/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "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/@vscode/test-cli/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/@vscode/test-cli/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/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "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/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "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": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "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/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "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/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/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/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==", + "dev": true, + "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==", + "dev": true, + "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==", + "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/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/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "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.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "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-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "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": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "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/eslint": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz", + "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.2", + "@eslint/config-helpers": "^0.5.2", + "@eslint/core": "^1.1.0", + "@eslint/plugin-kit": "^0.6.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.1", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.1.1", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.1", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", + "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz", + "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "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/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-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==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.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/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "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/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "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/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/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/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "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/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "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/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-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "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/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/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "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/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "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/mocha/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/mocha/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/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/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/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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "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/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "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/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/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==", + "dev": true, + "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/ora/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "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": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "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/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "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/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/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "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==", + "dev": true, + "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/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.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "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": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "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/test-exclude/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/test-exclude/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/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", + "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.56.1", + "@typescript-eslint/parser": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.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==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" + }, + "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/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-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/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/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/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/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/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/src/winapp-VSC/package.json b/src/winapp-VSC/package.json new file mode 100644 index 00000000..bc35554a --- /dev/null +++ b/src/winapp-VSC/package.json @@ -0,0 +1,211 @@ +{ + "name": "winapp", + "displayName": "WinApp", + "description": "WinApp extension for Visual Studio Code to assist in building, debugging, and packaging Windows applications using the WinAppCLI.", + "icon": "images/icon.png", + "version": "0.1.0", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/WinAppCli.git" + }, + "engines": { + "vscode": "^1.109.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onDebug", + "onCommand:winapp.init", + "onCommand:winapp.restore", + "onCommand:winapp.update", + "onCommand:winapp.pack", + "onCommand:winapp.run", + "onCommand:winapp.createDebugIdentity", + "onCommand:winapp.manifestGenerate", + "onCommand:winapp.manifestUpdateAssets", + "onCommand:winapp.certGenerate", + "onCommand:winapp.certInstall", + "onCommand:winapp.sign", + "onCommand:winapp.tool", + "onCommand:winapp.getWinappPath" + ], + "main": "./dist/extension.js", + "contributes": { + "commands": [ + { + "command": "winapp.init", + "title": "WinApp: Initialize Project", + "category": "WinApp" + }, + { + "command": "winapp.restore", + "title": "WinApp: Restore Packages", + "category": "WinApp" + }, + { + "command": "winapp.update", + "title": "WinApp: Update Packages", + "category": "WinApp" + }, + { + "command": "winapp.pack", + "title": "WinApp: Create MSIX Package", + "category": "WinApp" + }, + { + "command": "winapp.run", + "title": "WinApp: Run Application", + "category": "WinApp" + }, + { + "command": "winapp.createDebugIdentity", + "title": "WinApp: Create Debug Identity", + "category": "WinApp" + }, + { + "command": "winapp.manifestGenerate", + "title": "WinApp: Generate Manifest", + "category": "WinApp" + }, + { + "command": "winapp.manifestUpdateAssets", + "title": "WinApp: Update Manifest Assets", + "category": "WinApp" + }, + { + "command": "winapp.certGenerate", + "title": "WinApp: Generate Certificate", + "category": "WinApp" + }, + { + "command": "winapp.certInstall", + "title": "WinApp: Install Certificate", + "category": "WinApp" + }, + { + "command": "winapp.sign", + "title": "WinApp: Sign Package", + "category": "WinApp" + }, + { + "command": "winapp.tool", + "title": "WinApp: Run SDK Tool", + "category": "WinApp" + }, + { + "command": "winapp.getWinappPath", + "title": "WinApp: Get WinApp Path", + "category": "WinApp" + } + ], + "debuggers": [ + { + "type": "winapp", + "label": "WinApp", + "configurationAttributes": { + "launch": { + "properties": { + "debuggerType": { + "type": "string", + "description": "The underlying debugger to use (e.g., 'coreclr', 'cppvsdbg')", + "default": "coreclr" + }, + "manifest": { + "type": "string", + "description": "Explicit path to AppxManifest.xml. When set, 'buildOutputManifest' is ignored. Use this when the manifest is at the workspace root or in a fixed location." + }, + "inputFolder": { + "type": "string", + "description": "Directory containing the app binaries passed to 'winapp run'. Defaults to the workspace root. Set this when the manifest is not co-located with the build output (e.g. manifest at root, binaries in bin/Debug/)." + }, + "workingDirectory": { + "type": "string", + "description": "Working directory for the application" + }, + "buildOutputManifest": { + "type": "string", + "description": "Glob pattern to locate AppxManifest.xml in build output. Ignored when 'manifest' is set. Matches at any depth including the workspace root.", + "default": "**/AppxManifest.xml" + }, + "args": { + "type": "string", + "description": "Command-line arguments to pass to the application" + }, + "outputAppxDirectory": { + "type": "string", + "description": "Output directory for the loose layout package. If not specified, a directory named AppX inside the manifest's directory will be used." + } + } + } + }, + "initialConfigurations": [ + { + "type": "winapp", + "request": "launch", + "name": "WinApp: Launch and Attach" + } + ], + "configurationSnippets": [ + { + "label": "WinApp: Launch and Attach", + "description": "Launch a WinApp package and attach the debugger", + "body": { + "type": "winapp", + "request": "launch", + "name": "WinApp: Launch and Attach", + "debuggerType": "coreclr" + } + }, + { + "label": "WinApp: Launch and Attach (manifest at root)", + "description": "Launch a WinApp package where AppxManifest.xml is at the workspace root and binaries are in a build output folder", + "body": { + "type": "winapp", + "request": "launch", + "name": "WinApp: Launch and Attach", + "debuggerType": "coreclr", + "manifest": "${workspaceFolder}/AppxManifest.xml", + "inputFolder": "${workspaceFolder}/bin/Debug" + } + } + ] + } + ] + }, + "scripts": { + "vscode:prepublish": "npm run package", + "compile": "node esbuild.mjs", + "watch": "node esbuild.mjs --watch", + "package": "node esbuild.mjs --production", + "compile-tsc": "tsc -p ./", + "pretest": "npm run compile-tsc && npm run lint", + "lint": "eslint src", + "test": "vscode-test", + "clean": "node -e \"require('fs').rmSync('bin', {recursive: true, force: true}); require('fs').rmSync('out', {recursive: true, force: true}); require('fs').rmSync('dist', {recursive: true, force: true});\"", + "build-cli": "npm run build-cli-x64 && npm run build-cli-arm64", + "build-cli-x64": "dotnet publish ../winapp-CLI/WinApp.Cli/WinApp.Cli.csproj -c Release -r win-x64 --self-contained -o bin/win-x64", + "build-cli-arm64": "dotnet publish ../winapp-CLI/WinApp.Cli/WinApp.Cli.csproj -c Release -r win-arm64 --self-contained -o bin/win-arm64", + "build-copy-only": "npm run copy-cli-x64 && npm run copy-cli-arm64", + "copy-cli-x64": "node -e \"const fs = require('fs'); const src = '../winapp-CLI/WinApp.Cli/bin/Release/net10.0-windows/win-x64/publish'; const dest = 'bin/win-x64'; fs.mkdirSync(dest, {recursive: true}); fs.cpSync(src, dest, {recursive: true});\"", + "copy-cli-arm64": "node -e \"const fs = require('fs'); const src = '../winapp-CLI/WinApp.Cli/bin/Release/net10.0-windows/win-arm64/publish'; const dest = 'bin/win-arm64'; fs.mkdirSync(dest, {recursive: true}); fs.cpSync(src, dest, {recursive: true});\"" + }, + "dependencies": { + "glob": "^13.0.6" + }, + "devDependencies": { + "@types/mocha": "^10.0.10", + "@types/node": "22.x", + "@types/vscode": "^1.109.0", + "@vscode/test-cli": "^0.0.12", + "@vscode/test-electron": "^2.5.2", + "esbuild": "^0.27.3", + "eslint": "^10.0.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.56.1" + }, + "overrides": { + "minimatch": "^10.2.1", + "diff": "^8.0.3" + } +} diff --git a/src/winapp-VSC/src/extension.ts b/src/winapp-VSC/src/extension.ts new file mode 100644 index 00000000..fdf6b6e4 --- /dev/null +++ b/src/winapp-VSC/src/extension.ts @@ -0,0 +1,674 @@ +import * as vscode from 'vscode'; +import * as path from 'path'; +import { spawn } from 'child_process'; +import { getWinappCliPath, WINAPP_CLI_CALLER_VALUE } from './winapp-cli-utils'; +import { glob } from 'glob'; + +const WINAPP_DEBUG_TYPE = 'winapp'; + +/** + * Execute a winapp CLI command and show output in the terminal + */ +async function runWinappCommand(extensionPath: string, command: string, cwd: string, showTerminal: boolean = true): Promise { + const cliPath = getWinappCliPath(extensionPath); + const terminal = vscode.window.createTerminal({ + name: 'WinApp CLI', + cwd: cwd, + env: { WINAPP_CLI_CALLER: WINAPP_CLI_CALLER_VALUE } + }); + + if (showTerminal) { + terminal.show(); + } + + terminal.sendText(`& "${cliPath}" ${command}`); + return ''; +} + +/** + * Get the current workspace folder path + */ +function getWorkspacePath(): string | undefined { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + vscode.window.showErrorMessage('No workspace folder open'); + return undefined; + } + return workspaceFolders[0].uri.fsPath; +} + +/** + * Prompt user to select a file + */ +async function selectFile(title: string, filters?: { [name: string]: string[] }): Promise { + const result = await vscode.window.showOpenDialog({ + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: false, + title: title, + filters: filters + }); + + return result?.[0]?.fsPath; +} + +/** + * Prompt user to select a folder + */ +async function selectFolder(title: string): Promise { + const result = await vscode.window.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + title: title + }); + + return result?.[0]?.fsPath; +} + +class WinAppDebugConfigurationProvider implements vscode.DebugConfigurationProvider { + private extensionPath: string; + + constructor(extensionPath: string) { + this.extensionPath = extensionPath; + } + + async resolveDebugConfiguration( + folder: vscode.WorkspaceFolder | undefined, + config: vscode.DebugConfiguration, + _token?: vscode.CancellationToken + ): Promise { + // If no configuration, create a default one + if (!config.type && !config.request && !config.name) { + config.type = WINAPP_DEBUG_TYPE; + config.name = 'WinApp: Launch and Attach'; + config.request = 'launch'; + } + + return config; + } + + async resolveDebugConfigurationWithSubstitutedVariables( + folder: vscode.WorkspaceFolder | undefined, + config: vscode.DebugConfiguration, + _token?: vscode.CancellationToken + ): Promise { + if (!folder) { + vscode.window.showErrorMessage('No workspace folder open'); + return undefined; + } + + return config; + } +} + +class WinAppDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory { + private extensionPath: string; + + constructor(extensionPath: string) { + this.extensionPath = extensionPath; + } + + async createDebugAdapterDescriptor( + session: vscode.DebugSession, + _executable: vscode.DebugAdapterExecutable | undefined + ): Promise { + const config = session.configuration; + const folder = session.workspaceFolder; + + if (!folder) { + throw new Error('No workspace folder open'); + } + + try { + // Resolve the manifest path: use an explicit path if provided, otherwise search via glob + let manifest: string; + if (config.manifest) { + // Exact manifest path provided — use it directly + manifest = config.manifest; + } else { + // Search for AppxManifest.xml in build output using glob (bypasses .gitignore). + // The default pattern matches at any depth, including the workspace root. + const searchPattern = config.buildOutputManifest || '**/AppxManifest.xml'; + const allMatches = await glob(searchPattern, { + cwd: folder.uri.fsPath, + absolute: true, + nocase: true + }); + + // Filter out manifests inside AppX or .winapp folders (created by winapp run/debug) + const matches = allMatches.filter(m => { + const parts = m.split(path.sep); + return !parts.includes('AppX') && !parts.includes('.winapp'); + }); + + if (matches.length === 0) { + throw new Error(`No manifest found matching "${searchPattern}". Build your project first, set "manifest" to an explicit path, or update "buildOutputManifest" in launch.json.`); + } else if (matches.length === 1) { + manifest = matches[0]; + } else { + // Multiple manifests found — let the user pick + const items = matches.map(m => ({ + label: path.relative(folder.uri.fsPath, m), + fsPath: m + })); + const picked = await vscode.window.showQuickPick(items, { + placeHolder: 'Multiple AppxManifest.xml files found — select one' + }); + if (!picked) { + throw new Error('No manifest selected, cancelling debug session.'); + } + manifest = picked.fsPath; + } + } + + // Build the command with mapped arguments. + // inputFolder is the directory containing the app binaries passed to winapp run. + // When the manifest lives outside the build output (e.g. at the project root), + // set "inputFolder" in launch.json to point at the build output directory. + const inputFolder = config.inputFolder || folder.uri.fsPath; + const cmdParts: string[] = [getWinappCliPath(this.extensionPath), 'run', `"${inputFolder}"`]; + cmdParts.push('--manifest', `"${manifest}"`); + + if (config.outputAppxDirectory) { + cmdParts.push('--output-appx-directory', `"${config.outputAppxDirectory}"`); + } + + // Determine the debugger type based on config or default to coreclr + const debuggerType = config.debuggerType || 'coreclr'; + + let args = config.args || ''; + if (debuggerType === 'node') { + args = '--inspect' + (config.port ? `=${config.port}` : '') + ' ' + args; + } + + if (args.trim()) { + cmdParts.push('--args', `"${args.trim()}"`); + } + + cmdParts.push('--json'); + const command = cmdParts.join(' '); + + // Spawn winapp run --json. The process stays alive while the app runs, + // so we stream stdout to parse the JSON with the PID before waiting for exit. + const { processId, runProcess } = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: 'Launching package...', + cancellable: false + }, async (progress) => { + progress.report({ message: 'Running winapp run...' }); + + let cwd = folder.uri.fsPath; + if (config.workingDirectory) { + cwd = config.workingDirectory; + } + + return new Promise<{ processId: number; runProcess: ReturnType }>((resolve, reject) => { + const child = spawn(command, { + cwd, + env: { ...process.env, WINAPP_CLI_CALLER: WINAPP_CLI_CALLER_VALUE }, + shell: true + }); + + let stdout = ''; + let stderr = ''; + let resolved = false; + + child.stdout!.on('data', (data: Buffer) => { + stdout += data.toString(); + if (resolved) { return; } + + const pid = parseProcessIdFromJson(stdout); + if (pid) { + resolved = true; + resolve({ processId: pid, runProcess: child }); + } + }); + + child.stderr!.on('data', (data: Buffer) => { + stderr += data.toString(); + console.warn('winapp run stderr:', data.toString()); + }); + + child.on('error', (err) => { + if (!resolved) { + reject(new Error(`Failed to start winapp run: ${err.message}`)); + } + }); + + child.on('close', (code) => { + if (!resolved) { + if (code !== 0) { + reject(new Error(`winapp run exited with code ${code}. stderr: ${stderr}\nstdout: ${stdout}`)); + } else { + reject(new Error(`winapp run exited before returning a process ID. stdout: ${stdout}`)); + } + } + }); + }); + }); + + // Build the attach debug configuration + const debugConfiguration: vscode.DebugConfiguration = { + type: debuggerType, + name: config.name || 'Attach to WinApp Package', + request: 'attach' + }; + + if (debuggerType === 'node') { + debugConfiguration.port = config.port || 9229; + } else { + debugConfiguration.processId = processId; + } + + // Start the real debug session as a child of the winapp session + await vscode.debug.startDebugging(folder, debugConfiguration, { parentSession: session }); + + // When the child debug session ends, kill the winapp run process and stop the parent session + const parentSession = session; + const disposable = vscode.debug.onDidTerminateDebugSession((ended) => { + if (ended.parentSession === parentSession) { + disposable.dispose(); + runProcess.kill(); + vscode.debug.stopDebugging(parentSession); + } + }); + + // When the winapp run process exits (app closed), stop the debug session + runProcess.on('close', () => { + vscode.debug.stopDebugging(parentSession); + }); + + // Return an inline no-op adapter — the real debugging happens in the child session above + return new vscode.DebugAdapterInlineImplementation(new NoOpDebugAdapter()); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + vscode.window.showErrorMessage(`Failed to launch and attach: ${message}`); + throw error; + } + } +} + +/** + * A minimal no-op debug adapter. The winapp debug type doesn't need a real adapter + * since we delegate to a child debug session (coreclr/node). + */ +class NoOpDebugAdapter implements vscode.DebugAdapter { + private sendMessageEmitter = new vscode.EventEmitter(); + readonly onDidSendMessage: vscode.Event = this.sendMessageEmitter.event; + + handleMessage(message: vscode.DebugProtocolMessage): void { + // Respond to the initialize request so VS Code doesn't hang + const msg = message as any; + if (msg.type === 'request' && msg.command === 'initialize') { + this.sendMessageEmitter.fire({ + type: 'response', + request_seq: msg.seq, + success: true, + command: msg.command, + seq: 0 + } as any); + } else if (msg.type === 'request' && msg.command === 'disconnect') { + this.sendMessageEmitter.fire({ + type: 'response', + request_seq: msg.seq, + success: true, + command: msg.command, + seq: 0 + } as any); + } + } + + dispose(): void { + this.sendMessageEmitter.dispose(); + } +} + +export function activate(context: vscode.ExtensionContext) { + const extensionPath = context.extensionPath; + const provider = new WinAppDebugConfigurationProvider(extensionPath); + + context.subscriptions.push( + vscode.debug.registerDebugConfigurationProvider(WINAPP_DEBUG_TYPE, provider) + ); + + const factory = new WinAppDebugAdapterFactory(extensionPath); + context.subscriptions.push( + vscode.debug.registerDebugAdapterDescriptorFactory(WINAPP_DEBUG_TYPE, factory) + ); + + // Register winapp.init command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.init', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const sdkMode = await vscode.window.showQuickPick( + ['stable', 'preview', 'experimental', 'none'], + { placeHolder: 'Select SDK installation mode' } + ); + + let command = 'init --use-defaults'; + if (sdkMode && sdkMode !== 'stable') { + command += ` --setup-sdks ${sdkMode}`; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.restore command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.restore', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + await runWinappCommand(extensionPath, 'restore', workspacePath); + }) + ); + + // Register winapp.update command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.update', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const sdkMode = await vscode.window.showQuickPick( + ['stable', 'preview', 'experimental'], + { placeHolder: 'Select SDK installation mode (optional)' } + ); + + let command = 'update'; + if (sdkMode && sdkMode !== 'stable') { + command += ` --setup-sdks ${sdkMode}`; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.pack command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.pack', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const inputFolder = await selectFolder('Select input folder to package'); + if (!inputFolder) { + return; + } + + const generateCert = await vscode.window.showQuickPick( + ['Yes', 'No'], + { placeHolder: 'Generate and install a development certificate?' } + ); + + const selfContained = await vscode.window.showQuickPick( + ['Yes', 'No'], + { placeHolder: 'Bundle Windows App SDK runtime (self-contained)?' } + ); + + let command = `pack "${inputFolder}"`; + if (generateCert === 'Yes') { + command += ' --generate-cert --install-cert'; + } + if (selfContained === 'Yes') { + command += ' --self-contained'; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.run command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.run', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const inputFolder = await selectFolder('Select input folder containing the app to run'); + if (!inputFolder) { + return; + } + + await runWinappCommand(extensionPath, `run "${inputFolder}"`, workspacePath); + }) + ); + + // Register winapp.createDebugIdentity command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.createDebugIdentity', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + const entrypoint = await selectFile('Select executable', { + 'Executables': ['exe'], + 'All files': ['*'] + }); + + let command = 'create-debug-identity'; + if (entrypoint) { + command += ` "${entrypoint}"`; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.manifestGenerate command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.manifestGenerate', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const template = await vscode.window.showQuickPick( + ['packaged', 'sparse'], + { placeHolder: 'Select manifest template type' } + ); + + let command = 'manifest generate'; + if (template) { + command += ` --template ${template}`; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.manifestUpdateAssets command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.manifestUpdateAssets', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const imagePath = await selectFile('Select source image for assets', { + 'Images': ['png', 'jpg', 'jpeg', 'gif', 'bmp'] + }); + + if (!imagePath) { + vscode.window.showErrorMessage('An image file is required'); + return; + } + + await runWinappCommand(extensionPath, `manifest update-assets "${imagePath}"`, workspacePath); + }) + ); + + // Register winapp.certGenerate command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.certGenerate', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const install = await vscode.window.showQuickPick( + ['Yes', 'No'], + { placeHolder: 'Install certificate after generation?' } + ); + + let command = 'cert generate'; + if (install === 'Yes') { + command += ' --install'; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.certInstall command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.certInstall', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const certPath = await selectFile('Select certificate to install', { + 'Certificates': ['pfx', 'cer'] + }); + + if (!certPath) { + vscode.window.showErrorMessage('A certificate file is required'); + return; + } + + await runWinappCommand(extensionPath, `cert install "${certPath}"`, workspacePath); + }) + ); + + // Register winapp.sign command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.sign', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const filePath = await selectFile('Select file to sign', { + 'MSIX Packages': ['msix', 'appx'], + 'Executables': ['exe', 'dll'], + 'All files': ['*'] + }); + + if (!filePath) { + vscode.window.showErrorMessage('A file to sign is required'); + return; + } + + const certPath = await selectFile('Select signing certificate', { + 'Certificates': ['pfx'] + }); + + if (!certPath) { + vscode.window.showErrorMessage('A certificate file is required'); + return; + } + + await runWinappCommand(extensionPath, `sign "${filePath}" --cert "${certPath}"`, workspacePath); + }) + ); + + // Register winapp.tool command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.tool', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const toolSelection = await vscode.window.showQuickPick( + ['makeappx', 'signtool', 'mt', 'makepri', 'other'], + { placeHolder: 'Select Windows SDK tool' } + ); + + if (!toolSelection) { + return; + } + + let toolName: string; + if (toolSelection === 'other') { + const customTool = await vscode.window.showInputBox({ + prompt: 'Enter the Windows SDK tool name', + placeHolder: 'e.g., custom-tool' + }); + + if (!customTool) { + return; + } + toolName = customTool; + } else { + toolName = toolSelection; + } + + const args = await vscode.window.showInputBox({ + prompt: `Enter arguments for ${toolName}`, + placeHolder: 'e.g., --help' + }); + + let command = `tool ${toolName}`; + if (args) { + command += ` ${args}`; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); + + // Register winapp.getWinappPath command + context.subscriptions.push( + vscode.commands.registerCommand('winapp.getWinappPath', async () => { + const workspacePath = getWorkspacePath(); + if (!workspacePath) { + return; + } + + const global = await vscode.window.showQuickPick( + ['Local (.winapp in workspace)', 'Global (shared cache)'], + { placeHolder: 'Which path to retrieve?' } + ); + + let command = 'get-winapp-path'; + if (global === 'Global (shared cache)') { + command += ' --global'; + } + + await runWinappCommand(extensionPath, command, workspacePath); + }) + ); +} + +/** + * Parse the process ID from the winapp run --json output. + * Expects a JSON object with a processId (or pid) field. + */ +function parseProcessIdFromJson(output: string): number | undefined { + try { + const json = JSON.parse(output.trim()); + const pid = json.processId ?? json.pid ?? json.ProcessId ?? json.PID; + if (typeof pid === 'number' && pid > 0) { + return pid; + } + } catch { + // JSON not complete yet or invalid + } + return undefined; +} + +export function deactivate() { +} diff --git a/src/winapp-VSC/src/winapp-cli-utils.ts b/src/winapp-VSC/src/winapp-cli-utils.ts new file mode 100644 index 00000000..2f59633e --- /dev/null +++ b/src/winapp-VSC/src/winapp-cli-utils.ts @@ -0,0 +1,26 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; + +export const WINAPP_CLI_CALLER_VALUE = 'vscode-extension'; + +/** + * Get the path to the bundled winapp CLI executable. + * Looks in the extension's bin/ directory first (by architecture), + * then falls back to development paths and the system PATH. + */ +export function getWinappCliPath(extensionPath: string): string { + const arch = os.arch() === 'arm64' ? 'win-arm64' : 'win-x64'; + + const possiblePaths = [ + // Bundled in extension (production) + path.join(extensionPath, 'bin', arch, 'winapp.exe'), + // Development builds (when running from source) + path.join(extensionPath, '..', 'winapp-CLI', 'WinApp.Cli', 'bin', 'Debug', 'net10.0-windows', arch, 'winapp.exe'), + path.join(extensionPath, '..', 'winapp-CLI', 'WinApp.Cli', 'bin', 'Release', 'net10.0-windows', arch, 'winapp.exe'), + // Fallback to system PATH + 'winapp', + ]; + + return possiblePaths.find((p) => fs.existsSync(p)) || possiblePaths[0]; +} diff --git a/src/winapp-VSC/tsconfig.json b/src/winapp-VSC/tsconfig.json new file mode 100644 index 00000000..36b75c56 --- /dev/null +++ b/src/winapp-VSC/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "Node16", + "target": "ES2022", + "outDir": "out", + "lib": [ + "ES2022", + "DOM" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true, /* enable all strict type-checking options */ + "types": ["node"] + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} From 8a185d9f0921f8e18842ba17a465e818e1aa2666 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:04:01 +0000 Subject: [PATCH 3/7] Fix VSC extension Launch and Debug failing when AppxManifest at root Co-authored-by: chiaramooney <34109996+chiaramooney@users.noreply.github.com> --- .gitattributes | 4 +- .github/actions/collect-metrics/action.yml | 11 +- .github/actions/report-metrics/action.yml | 24 +- .github/plugin/agents/winapp.agent.md | 18 +- .github/plugin/plugin.json | 4 +- .../skills/winapp-cli/frameworks/SKILL.md | 36 +- .../skills/winapp-cli/identity/SKILL.md | 51 +- .../skills/winapp-cli/manifest/SKILL.md | 28 +- .../plugin/skills/winapp-cli/package/SKILL.md | 2 +- .../plugin/skills/winapp-cli/setup/SKILL.md | 28 +- .../plugin/skills/winapp-cli/signing/SKILL.md | 2 +- .../skills/winapp-cli/troubleshoot/SKILL.md | 21 +- .github/workflows/build-package.yml | 6 - .gitignore | 2 + .pipelines/templates/build.yaml | 2 +- .vscode/settings.json | 10 +- AGENTS.md | 28 - README.md | 7 +- docs/cli-schema.json | 118 +- docs/debugging.md | 141 - docs/dotnet-run-support.md | 45 +- .../fragments/skills/winapp-cli/frameworks.md | 34 +- docs/fragments/skills/winapp-cli/identity.md | 49 +- docs/fragments/skills/winapp-cli/manifest.md | 21 +- docs/fragments/skills/winapp-cli/setup.md | 24 - .../skills/winapp-cli/troubleshoot.md | 19 +- docs/getting-started.md | 208 ++ docs/gui-usage.md | 64 + docs/guides/cpp.md | 52 +- docs/guides/dotnet.md | 2 - docs/guides/flutter.md | 15 +- docs/guides/rust.md | 3 - docs/guides/tauri.md | 39 +- docs/npm-usage.md | 121 +- docs/usage.md | 121 +- llms.txt | 1 - plugin.json | 67 - samples/cpp-app-winui/.gitignore | 8 - .../cpp-app-winui/Assets/LockScreenLogo.png | Bin 229 -> 0 bytes .../Assets/LockScreenLogo.scale-200.png | Bin 432 -> 0 bytes samples/cpp-app-winui/Assets/SplashScreen.png | Bin 1579 -> 0 bytes .../Assets/SplashScreen.scale-200.png | Bin 5372 -> 0 bytes samples/cpp-app-winui/CMakeLists.txt | 100 - samples/cpp-app-winui/README.md | 53 - samples/cpp-app-winui/appxmanifest.xml | 54 - samples/cpp-app-winui/main.cpp | 134 - samples/cpp-app-winui/winapp.yaml | 15 - samples/cpp-app/CMakeLists.txt | 10 + samples/cpp-app/README.md | 26 +- samples/cpp-app/appxmanifest.xml | 10 +- samples/cpp-app/winapp.yaml | 12 +- samples/dotnet-app/dotnet-app.csproj | 2 - samples/electron-winml/package-lock.json | 20 +- samples/electron-winml/package.json | 2 +- samples/electron/Assets/AppList.png | Bin 1774 -> 0 bytes samples/electron/Assets/AppList.scale-125.png | Bin 2238 -> 0 bytes samples/electron/Assets/AppList.scale-150.png | Bin 2462 -> 0 bytes samples/electron/Assets/AppList.scale-200.png | Bin 3466 -> 0 bytes samples/electron/Assets/AppList.scale-400.png | Bin 8370 -> 0 bytes .../electron/Assets/AppList.targetsize-16.png | Bin 721 -> 0 bytes ...AppList.targetsize-16_altform-unplated.png | Bin 721 -> 0 bytes .../electron/Assets/AppList.targetsize-20.png | Bin 910 -> 0 bytes ...AppList.targetsize-20_altform-unplated.png | Bin 910 -> 0 bytes .../electron/Assets/AppList.targetsize-24.png | Bin 1083 -> 0 bytes ...AppList.targetsize-24_altform-unplated.png | Bin 1083 -> 0 bytes .../Assets/AppList.targetsize-256.png | Bin 13555 -> 0 bytes ...ppList.targetsize-256_altform-unplated.png | Bin 13555 -> 0 bytes .../electron/Assets/AppList.targetsize-30.png | Bin 1306 -> 0 bytes ...AppList.targetsize-30_altform-unplated.png | Bin 1306 -> 0 bytes .../electron/Assets/AppList.targetsize-32.png | Bin 1378 -> 0 bytes ...AppList.targetsize-32_altform-unplated.png | Bin 1378 -> 0 bytes .../electron/Assets/AppList.targetsize-36.png | Bin 1613 -> 0 bytes ...AppList.targetsize-36_altform-unplated.png | Bin 1613 -> 0 bytes .../electron/Assets/AppList.targetsize-40.png | Bin 1683 -> 0 bytes ...AppList.targetsize-40_altform-unplated.png | Bin 1683 -> 0 bytes .../electron/Assets/AppList.targetsize-48.png | Bin 1922 -> 0 bytes ...AppList.targetsize-48_altform-unplated.png | Bin 1922 -> 0 bytes .../electron/Assets/AppList.targetsize-60.png | Bin 2067 -> 0 bytes ...AppList.targetsize-60_altform-unplated.png | Bin 2067 -> 0 bytes .../electron/Assets/AppList.targetsize-64.png | Bin 2210 -> 0 bytes ...AppList.targetsize-64_altform-unplated.png | Bin 2210 -> 0 bytes .../electron/Assets/AppList.targetsize-72.png | Bin 2767 -> 0 bytes ...AppList.targetsize-72_altform-unplated.png | Bin 2767 -> 0 bytes .../electron/Assets/AppList.targetsize-80.png | Bin 2851 -> 0 bytes ...AppList.targetsize-80_altform-unplated.png | Bin 2851 -> 0 bytes .../electron/Assets/AppList.targetsize-96.png | Bin 3269 -> 0 bytes ...AppList.targetsize-96_altform-unplated.png | Bin 3269 -> 0 bytes samples/electron/Assets/LockScreenLogo.png | Bin 0 -> 1192 bytes .../Assets/LockScreenLogo.scale-200.png | Bin 0 -> 3886 bytes samples/electron/Assets/MedTile.png | Bin 3912 -> 0 bytes samples/electron/Assets/MedTile.scale-125.png | Bin 8537 -> 0 bytes samples/electron/Assets/MedTile.scale-150.png | Bin 12278 -> 0 bytes samples/electron/Assets/MedTile.scale-400.png | Bin 46433 -> 0 bytes samples/electron/Assets/SplashScreen.png | Bin 0 -> 13947 bytes .../Assets/SplashScreen.scale-200.png | Bin 0 -> 100434 bytes samples/electron/Assets/Square150x150Logo.png | Bin 0 -> 1696 bytes ...00.png => Square150x150Logo.scale-200.png} | Bin samples/electron/Assets/Square44x44Logo.png | Bin 0 -> 727 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 0 -> 1238 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 480 bytes samples/electron/Assets/StoreLogo.png | Bin 1809 -> 803 bytes .../electron/Assets/StoreLogo.scale-125.png | Bin 2337 -> 0 bytes .../electron/Assets/StoreLogo.scale-150.png | Bin 2416 -> 0 bytes .../electron/Assets/StoreLogo.scale-200.png | Bin 2875 -> 0 bytes .../electron/Assets/StoreLogo.scale-400.png | Bin 7659 -> 0 bytes samples/electron/Assets/Wide310x150Logo.png | Bin 0 -> 3278 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 13947 bytes samples/electron/Assets/WideTile.png | Bin 4541 -> 0 bytes .../electron/Assets/WideTile.scale-125.png | Bin 9760 -> 0 bytes .../electron/Assets/WideTile.scale-150.png | Bin 14010 -> 0 bytes .../electron/Assets/WideTile.scale-200.png | Bin 6344 -> 0 bytes .../electron/Assets/WideTile.scale-400.png | Bin 55873 -> 0 bytes samples/electron/Assets/app.ico | Bin 18745 -> 0 bytes samples/electron/appxmanifest.xml | 20 +- samples/electron/package-lock.json | 44 +- samples/electron/package.json | 4 +- samples/electron/winapp.yaml | 12 +- samples/flutter-app/README.md | 7 +- samples/flutter-app/lib/main.dart | 27 +- samples/flutter-app/pubspec.lock | 28 +- samples/flutter-app/winapp.yaml | 10 +- .../windows/runner/winapp_sdk_plugin.cpp | 2 +- samples/nuget.config | 7 + samples/rust-app/Cargo.lock | 130 +- samples/rust-app/Cargo.toml | 2 +- samples/tauri-app/README.md | 69 +- samples/tauri-app/package.json | 3 +- samples/tauri-app/src-tauri/Cargo.toml | 2 +- samples/wpf-app/wpf-app.csproj | 4 +- scripts/build-cli.ps1 | 204 +- scripts/generate-llm-docs.ps1 | 2 +- scripts/package-msix.ps1 | 11 +- scripts/package-nuget.ps1 | 55 +- scripts/package-vsc.ps1 | 237 ++ scripts/setup-winapprun.ps1 | 32 +- scripts/test-e2e-electron.ps1 | 2 +- src/winapp-CLI/Directory.Packages.props | 11 +- .../AppLauncherServiceTests.cs | 76 - .../AppxManifestDocumentTests.cs | 780 ---- .../WinApp.Cli.Tests/EndToEndTests.cs | 1 + .../FakeAppLauncherService.cs | 12 - .../FakeDebugOutputService.cs | 21 - .../FakePackageRegistrationService.cs | 74 - .../WinApp.Cli.Tests/FakePowerShellService.cs | 15 + .../IncrementalCopyHelperTests.cs | 280 -- .../WinApp.Cli.Tests/InitCommandTests.cs | 3 +- .../WinApp.Cli.Tests/ManifestCommandTests.cs | 76 +- .../ManifestUpdateAssetsCommandTests.cs | 300 +- .../WinApp.Cli.Tests/MrtAssetHelperTests.cs | 395 -- .../WinApp.Cli.Tests/MsixServiceTests.cs | 243 +- .../WinApp.Cli.Tests/PackageCommandTests.cs | 272 -- src/winapp-CLI/WinApp.Cli.Tests/PngHelper.cs | 10 - .../PowerShellServiceTests.cs | 48 + .../WinApp.Cli.Tests/RunCommandTests.cs | 188 - .../UnregisterCommandTests.cs | 201 - .../WinApp.Cli.Tests/WinApp.Cli.Tests.csproj | 2 +- .../WorkspaceSetupServiceTests.cs | 4 +- .../Assets/msix_default_assets/AppList.png | Bin 326 -> 0 bytes .../msix_default_assets/AppList.scale-200.png | Bin 637 -> 0 bytes ...AppList.targetsize-24_altform-unplated.png | Bin 283 -> 0 bytes .../Assets/msix_default_assets/MedTile.png | Bin 733 -> 0 bytes .../msix_default_assets/MedTile.scale-200.png | Bin 1755 -> 0 bytes .../Square150x150Logo.png | Bin .../Square150x150Logo.scale-200.png | Bin .../msix_default_assets}/Square44x44Logo.png | Bin .../Square44x44Logo.scale-200.png | Bin ...x44Logo.targetsize-24_altform-unplated.png | Bin .../msix_default_assets}/Wide310x150Logo.png | Bin .../Wide310x150Logo.scale-200.png | Bin .../Assets/msix_default_assets/WideTile.png | Bin 806 -> 0 bytes .../WideTile.scale-200.png | Bin 2097 -> 0 bytes .../Commands/CreateDebugIdentityCommand.cs | 8 +- .../Commands/ManifestUpdateAssetsCommand.cs | 14 +- .../WinApp.Cli/Commands/RunCommand.cs | 165 +- .../WinApp.Cli/Commands/UnregisterCommand.cs | 174 - .../WinApp.Cli/Commands/WinAppRootCommand.cs | 12 +- .../WinApp.Cli/Helpers/CliSchema.cs | 4 +- .../Helpers/HostBuilderExtensions.cs | 5 +- .../WinApp.Cli/Helpers/ManifestHelper.cs | 32 - src/winapp-CLI/WinApp.Cli/NativeMethods.txt | 14 - src/winapp-CLI/WinApp.Cli/Program.cs | 7 - .../WinApp.Cli/Services/AppLauncherService.cs | 61 +- .../Services/AppxManifestDocument.cs | 626 ---- .../WinApp.Cli/Services/DebugOutputService.cs | 214 -- .../Services/IAppLauncherService.cs | 17 - .../Services/IDebugOutputService.cs | 24 - .../WinApp.Cli/Services/IImageAssetService.cs | 24 +- .../WinApp.Cli/Services/IManifestService.cs | 1 - .../Services/IPackageRegistrationService.cs | 71 - .../WinApp.Cli/Services/IPowerShellService.cs | 16 + .../WinApp.Cli/Services/IPriService.cs | 29 - .../WinApp.Cli/Services/ImageAssetService.cs | 535 +-- .../Services/IncrementalCopyHelper.cs | 107 - .../WinApp.Cli/Services/ManifestService.cs | 132 +- .../WinApp.Cli/Services/MrtAssetHelper.cs | 281 -- .../Services/MsixService.Identity.cs | 771 ---- .../Services/MsixService.Runtime.cs | 881 ----- .../WinApp.Cli/Services/MsixService.cs | 3253 ++++++++++++++--- .../Services/PackageRegistrationService.cs | 206 -- .../WinApp.Cli/Services/PeHelper.cs | 82 - .../WinApp.Cli/Services/PowerShellService.cs | 245 ++ .../WinApp.Cli/Services/PriService.cs | 240 -- .../Services/WorkspaceSetupService.cs | 166 +- .../Templates/appxmanifest.packaged.xml | 6 +- .../Templates/appxmanifest.sparse.xml | 4 +- src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj | 5 +- src/winapp-GUI/winapp-GUI.sln | 43 + src/winapp-GUI/winapp-GUI/App.xaml | 17 + src/winapp-GUI/winapp-GUI/App.xaml.cs | 50 + src/winapp-GUI/winapp-GUI/Assets/AppIcon.ico | Bin 0 -> 204629 bytes .../winapp-GUI/Assets/BadgeLogo.scale-100.png | Bin 0 -> 186 bytes .../winapp-GUI/Assets/BadgeLogo.scale-125.png | Bin 0 -> 217 bytes .../winapp-GUI/Assets/BadgeLogo.scale-150.png | Bin 0 -> 270 bytes .../winapp-GUI/Assets/BadgeLogo.scale-200.png | Bin 0 -> 339 bytes .../winapp-GUI/Assets/BadgeLogo.scale-400.png | Bin 0 -> 650 bytes .../winapp-GUI/Assets/LargeTile.scale-100.png | Bin 0 -> 8082 bytes .../winapp-GUI/Assets/LargeTile.scale-125.png | Bin 0 -> 12323 bytes .../winapp-GUI/Assets/LargeTile.scale-150.png | Bin 0 -> 18042 bytes .../winapp-GUI/Assets/LargeTile.scale-200.png | Bin 0 -> 34652 bytes .../winapp-GUI/Assets/LargeTile.scale-400.png | Bin 0 -> 167896 bytes .../Assets/LockScreenLogo.scale-200.png | Bin 0 -> 4280 bytes .../winapp-GUI/Assets/SmallTile.scale-100.png | Bin 0 -> 1654 bytes .../winapp-GUI/Assets/SmallTile.scale-125.png | Bin 0 -> 2252 bytes .../winapp-GUI/Assets/SmallTile.scale-150.png | Bin 0 -> 2658 bytes .../winapp-GUI/Assets/SmallTile.scale-200.png | Bin 0 -> 3927 bytes .../winapp-GUI/Assets/SmallTile.scale-400.png | Bin 0 -> 14068 bytes .../winapp-GUI/Assets/SplashScreen.png | Bin 0 -> 4280 bytes .../Assets/SplashScreen.scale-100.png | Bin 0 -> 8307 bytes .../Assets/SplashScreen.scale-125.png | Bin 0 -> 12048 bytes .../Assets/SplashScreen.scale-150.png | Bin 0 -> 17952 bytes .../Assets/SplashScreen.scale-200.png | Bin 0 -> 34736 bytes .../Assets/SplashScreen.scale-400.png | Bin 0 -> 165742 bytes .../Assets/Square150x150Logo.scale-100.png | Bin 0 -> 2746 bytes .../Assets/Square150x150Logo.scale-125.png | Bin 0 -> 3505 bytes .../Assets/Square150x150Logo.scale-150.png | Bin 0 -> 4817 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 0 -> 7693 bytes .../Assets/Square150x150Logo.scale-400.png | Bin 0 -> 31941 bytes ...go.altform-lightunplated_targetsize-16.png | Bin 0 -> 671 bytes ...go.altform-lightunplated_targetsize-24.png | Bin 0 -> 972 bytes ...o.altform-lightunplated_targetsize-256.png | Bin 0 -> 47900 bytes ...go.altform-lightunplated_targetsize-32.png | Bin 0 -> 1328 bytes ...go.altform-lightunplated_targetsize-48.png | Bin 0 -> 2023 bytes ...x44Logo.altform-unplated_targetsize-16.png | Bin 0 -> 671 bytes ...44Logo.altform-unplated_targetsize-256.png | Bin 0 -> 47900 bytes ...x44Logo.altform-unplated_targetsize-32.png | Bin 0 -> 1328 bytes ...x44Logo.altform-unplated_targetsize-48.png | Bin 0 -> 2023 bytes .../Assets/Square44x44Logo.scale-100.png | Bin 0 -> 1423 bytes .../Assets/Square44x44Logo.scale-125.png | Bin 0 -> 1843 bytes .../Assets/Square44x44Logo.scale-150.png | Bin 0 -> 2315 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 0 -> 3342 bytes .../Assets/Square44x44Logo.scale-400.png | Bin 0 -> 10828 bytes .../Assets/Square44x44Logo.targetsize-16.png | Bin 0 -> 536 bytes .../Assets/Square44x44Logo.targetsize-24.png | Bin 0 -> 814 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 972 bytes .../Assets/Square44x44Logo.targetsize-256.png | Bin 0 -> 25664 bytes .../Assets/Square44x44Logo.targetsize-32.png | Bin 0 -> 1044 bytes .../Assets/Square44x44Logo.targetsize-48.png | Bin 0 -> 1622 bytes .../winapp-GUI/Assets/StoreLogo.backup.png | Bin 0 -> 4280 bytes .../winapp-GUI}/Assets/StoreLogo.png | Bin .../winapp-GUI/Assets/StoreLogo.scale-100.png | Bin 0 -> 2148 bytes .../winapp-GUI/Assets/StoreLogo.scale-125.png | Bin 0 -> 2812 bytes .../winapp-GUI/Assets/StoreLogo.scale-150.png | Bin 0 -> 3724 bytes .../winapp-GUI/Assets/StoreLogo.scale-200.png | Bin 0 -> 5961 bytes .../winapp-GUI/Assets/StoreLogo.scale-400.png | Bin 0 -> 26548 bytes src/winapp-GUI/winapp-GUI/Assets/Upload.png | Bin 0 -> 594 bytes .../Assets/Wide310x150Logo.scale-100.png | Bin 0 -> 2919 bytes .../Assets/Wide310x150Logo.scale-125.png | Bin 0 -> 3815 bytes .../Assets/Wide310x150Logo.scale-150.png | Bin 0 -> 5153 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 8307 bytes .../Assets/Wide310x150Logo.scale-400.png | Bin 0 -> 34736 bytes .../Controls/CertificatePickerControl.xaml | 23 + .../Controls/CertificatePickerControl.xaml.cs | 38 + .../Controls/FileNamePanelControl.xaml | 16 + .../Controls/FileNamePanelControl.xaml.cs | 26 + .../Controls/ProgressPanelControl.xaml | 32 + .../Controls/ProgressPanelControl.xaml.cs | 21 + src/winapp-GUI/winapp-GUI/MainWindow.xaml | 257 ++ src/winapp-GUI/winapp-GUI/MainWindow.xaml.cs | 577 +++ .../winapp-GUI/Package.appxmanifest | 51 + src/winapp-GUI/winapp-GUI/ProgressCard.cs | 88 + .../PublishProfiles/win-arm64.pubxml | 14 + .../Properties/PublishProfiles/win-x64.pubxml | 14 + .../Properties/PublishProfiles/win-x86.pubxml | 14 + .../winapp-GUI/Properties/launchSettings.json | 10 + .../winapp-GUI/Services/CliService.cs | 106 + .../winapp-GUI/Services/DropResults.cs | 25 + .../winapp-GUI/Services/ManifestService.cs | 44 + .../winapp-GUI/Services/MsixService.cs | 158 + .../winapp-GUI/Services/NetAppService.cs | 82 + .../Services/PackageOptionsService.cs | 94 + .../winapp-GUI/Services/PythonAppService.cs | 86 + src/winapp-GUI/winapp-GUI/app.manifest | 19 + src/winapp-GUI/winapp-GUI/winapp-GUI.csproj | 116 + src/winapp-NuGet/README.md | 24 - ...rosoft.Windows.SDK.BuildTools.WinApp.props | 8 - ...soft.Windows.SDK.BuildTools.WinApp.targets | 9 +- .../Microsoft.WindowsAppSDK.Templates.csproj | 42 + src/winapp-Templates/README.md | 49 + .../winui/.template.config/template.json | 110 + src/winapp-Templates/templates/winui/App.xaml | 16 + .../templates/winui/App.xaml.cs | 33 + .../winui/Assets/LockScreenLogo.scale-200.png | Bin 0 -> 338 bytes .../winui/Assets/SplashScreen.scale-200.png | Bin 0 -> 3029 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 0 -> 1478 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 0 -> 451 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 272 bytes .../templates/winui/Assets/StoreLogo.png | Bin 0 -> 346 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 1486 bytes .../templates/winui/MainWindow.xaml | 19 + .../templates/winui/MainWindow.xaml.cs | 17 + .../templates/winui/Package.appxmanifest | 50 + .../PublishProfiles/win-arm64.pubxml | 14 + .../Properties/PublishProfiles/win-x64.pubxml | 14 + .../Properties/PublishProfiles/win-x86.pubxml | 14 + .../templates/winui/WinUIApp1.csproj | 64 + .../templates/winui}/app.manifest | 8 +- src/winapp-npm/package-lock.json | 232 +- src/winapp-npm/package.json | 13 +- src/winapp-npm/scripts/generate-commands.mjs | 4 +- src/winapp-npm/src/winapp-cli-utils.ts | 7 +- src/winapp-npm/src/winapp-commands.ts | 135 +- version.json | 2 +- 322 files changed, 7189 insertions(+), 9948 deletions(-) delete mode 100644 docs/debugging.md create mode 100644 docs/getting-started.md create mode 100644 docs/gui-usage.md delete mode 100644 plugin.json delete mode 100644 samples/cpp-app-winui/.gitignore delete mode 100644 samples/cpp-app-winui/Assets/LockScreenLogo.png delete mode 100644 samples/cpp-app-winui/Assets/LockScreenLogo.scale-200.png delete mode 100644 samples/cpp-app-winui/Assets/SplashScreen.png delete mode 100644 samples/cpp-app-winui/Assets/SplashScreen.scale-200.png delete mode 100644 samples/cpp-app-winui/CMakeLists.txt delete mode 100644 samples/cpp-app-winui/README.md delete mode 100644 samples/cpp-app-winui/appxmanifest.xml delete mode 100644 samples/cpp-app-winui/main.cpp delete mode 100644 samples/cpp-app-winui/winapp.yaml delete mode 100644 samples/electron/Assets/AppList.png delete mode 100644 samples/electron/Assets/AppList.scale-125.png delete mode 100644 samples/electron/Assets/AppList.scale-150.png delete mode 100644 samples/electron/Assets/AppList.scale-200.png delete mode 100644 samples/electron/Assets/AppList.scale-400.png delete mode 100644 samples/electron/Assets/AppList.targetsize-16.png delete mode 100644 samples/electron/Assets/AppList.targetsize-16_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-20.png delete mode 100644 samples/electron/Assets/AppList.targetsize-20_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-24.png delete mode 100644 samples/electron/Assets/AppList.targetsize-24_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-256.png delete mode 100644 samples/electron/Assets/AppList.targetsize-256_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-30.png delete mode 100644 samples/electron/Assets/AppList.targetsize-30_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-32.png delete mode 100644 samples/electron/Assets/AppList.targetsize-32_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-36.png delete mode 100644 samples/electron/Assets/AppList.targetsize-36_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-40.png delete mode 100644 samples/electron/Assets/AppList.targetsize-40_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-48.png delete mode 100644 samples/electron/Assets/AppList.targetsize-48_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-60.png delete mode 100644 samples/electron/Assets/AppList.targetsize-60_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-64.png delete mode 100644 samples/electron/Assets/AppList.targetsize-64_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-72.png delete mode 100644 samples/electron/Assets/AppList.targetsize-72_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-80.png delete mode 100644 samples/electron/Assets/AppList.targetsize-80_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-96.png delete mode 100644 samples/electron/Assets/AppList.targetsize-96_altform-unplated.png create mode 100644 samples/electron/Assets/LockScreenLogo.png create mode 100644 samples/electron/Assets/LockScreenLogo.scale-200.png delete mode 100644 samples/electron/Assets/MedTile.png delete mode 100644 samples/electron/Assets/MedTile.scale-125.png delete mode 100644 samples/electron/Assets/MedTile.scale-150.png delete mode 100644 samples/electron/Assets/MedTile.scale-400.png create mode 100644 samples/electron/Assets/SplashScreen.png create mode 100644 samples/electron/Assets/SplashScreen.scale-200.png create mode 100644 samples/electron/Assets/Square150x150Logo.png rename samples/electron/Assets/{MedTile.scale-200.png => Square150x150Logo.scale-200.png} (100%) create mode 100644 samples/electron/Assets/Square44x44Logo.png create mode 100644 samples/electron/Assets/Square44x44Logo.scale-200.png create mode 100644 samples/electron/Assets/Square44x44Logo.targetsize-24_altform-unplated.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-125.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-150.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-200.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-400.png create mode 100644 samples/electron/Assets/Wide310x150Logo.png create mode 100644 samples/electron/Assets/Wide310x150Logo.scale-200.png delete mode 100644 samples/electron/Assets/WideTile.png delete mode 100644 samples/electron/Assets/WideTile.scale-125.png delete mode 100644 samples/electron/Assets/WideTile.scale-150.png delete mode 100644 samples/electron/Assets/WideTile.scale-200.png delete mode 100644 samples/electron/Assets/WideTile.scale-400.png delete mode 100644 samples/electron/Assets/app.ico create mode 100644 samples/nuget.config create mode 100644 scripts/package-vsc.ps1 delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/AppLauncherServiceTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs create mode 100644 src/winapp-CLI/WinApp.Cli.Tests/FakePowerShellService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs create mode 100644 src/winapp-CLI/WinApp.Cli.Tests/PowerShellServiceTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.scale-200.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.targetsize-24_altform-unplated.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.scale-200.png rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Square150x150Logo.png (100%) rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Square150x150Logo.scale-200.png (100%) rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Square44x44Logo.png (100%) rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Square44x44Logo.scale-200.png (100%) rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Square44x44Logo.targetsize-24_altform-unplated.png (100%) rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Wide310x150Logo.png (100%) rename {samples/cpp-app-winui/Assets => src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets}/Wide310x150Logo.scale-200.png (100%) delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.scale-200.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/IPowerShellService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IPriService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/PowerShellService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/PriService.cs create mode 100644 src/winapp-GUI/winapp-GUI.sln create mode 100644 src/winapp-GUI/winapp-GUI/App.xaml create mode 100644 src/winapp-GUI/winapp-GUI/App.xaml.cs create mode 100644 src/winapp-GUI/winapp-GUI/Assets/AppIcon.ico create mode 100644 src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/LockScreenLogo.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SplashScreen.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-16.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-256.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-32.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-48.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-16.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-24.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-256.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-32.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-48.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/StoreLogo.backup.png rename {samples/cpp-app-winui => src/winapp-GUI/winapp-GUI}/Assets/StoreLogo.png (100%) create mode 100644 src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Upload.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-100.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-125.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-150.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-200.png create mode 100644 src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-400.png create mode 100644 src/winapp-GUI/winapp-GUI/Controls/CertificatePickerControl.xaml create mode 100644 src/winapp-GUI/winapp-GUI/Controls/CertificatePickerControl.xaml.cs create mode 100644 src/winapp-GUI/winapp-GUI/Controls/FileNamePanelControl.xaml create mode 100644 src/winapp-GUI/winapp-GUI/Controls/FileNamePanelControl.xaml.cs create mode 100644 src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml create mode 100644 src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml.cs create mode 100644 src/winapp-GUI/winapp-GUI/MainWindow.xaml create mode 100644 src/winapp-GUI/winapp-GUI/MainWindow.xaml.cs create mode 100644 src/winapp-GUI/winapp-GUI/Package.appxmanifest create mode 100644 src/winapp-GUI/winapp-GUI/ProgressCard.cs create mode 100644 src/winapp-GUI/winapp-GUI/Properties/PublishProfiles/win-arm64.pubxml create mode 100644 src/winapp-GUI/winapp-GUI/Properties/PublishProfiles/win-x64.pubxml create mode 100644 src/winapp-GUI/winapp-GUI/Properties/PublishProfiles/win-x86.pubxml create mode 100644 src/winapp-GUI/winapp-GUI/Properties/launchSettings.json create mode 100644 src/winapp-GUI/winapp-GUI/Services/CliService.cs create mode 100644 src/winapp-GUI/winapp-GUI/Services/DropResults.cs create mode 100644 src/winapp-GUI/winapp-GUI/Services/ManifestService.cs create mode 100644 src/winapp-GUI/winapp-GUI/Services/MsixService.cs create mode 100644 src/winapp-GUI/winapp-GUI/Services/NetAppService.cs create mode 100644 src/winapp-GUI/winapp-GUI/Services/PackageOptionsService.cs create mode 100644 src/winapp-GUI/winapp-GUI/Services/PythonAppService.cs create mode 100644 src/winapp-GUI/winapp-GUI/app.manifest create mode 100644 src/winapp-GUI/winapp-GUI/winapp-GUI.csproj create mode 100644 src/winapp-Templates/Microsoft.WindowsAppSDK.Templates.csproj create mode 100644 src/winapp-Templates/README.md create mode 100644 src/winapp-Templates/templates/winui/.template.config/template.json create mode 100644 src/winapp-Templates/templates/winui/App.xaml create mode 100644 src/winapp-Templates/templates/winui/App.xaml.cs create mode 100644 src/winapp-Templates/templates/winui/Assets/LockScreenLogo.scale-200.png create mode 100644 src/winapp-Templates/templates/winui/Assets/SplashScreen.scale-200.png create mode 100644 src/winapp-Templates/templates/winui/Assets/Square150x150Logo.scale-200.png create mode 100644 src/winapp-Templates/templates/winui/Assets/Square44x44Logo.scale-200.png create mode 100644 src/winapp-Templates/templates/winui/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 src/winapp-Templates/templates/winui/Assets/StoreLogo.png create mode 100644 src/winapp-Templates/templates/winui/Assets/Wide310x150Logo.scale-200.png create mode 100644 src/winapp-Templates/templates/winui/MainWindow.xaml create mode 100644 src/winapp-Templates/templates/winui/MainWindow.xaml.cs create mode 100644 src/winapp-Templates/templates/winui/Package.appxmanifest create mode 100644 src/winapp-Templates/templates/winui/Properties/PublishProfiles/win-arm64.pubxml create mode 100644 src/winapp-Templates/templates/winui/Properties/PublishProfiles/win-x64.pubxml create mode 100644 src/winapp-Templates/templates/winui/Properties/PublishProfiles/win-x86.pubxml create mode 100644 src/winapp-Templates/templates/winui/WinUIApp1.csproj rename {samples/cpp-app-winui => src/winapp-Templates/templates/winui}/app.manifest (53%) diff --git a/.gitattributes b/.gitattributes index 05a66486..6403f04d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,12 +1,10 @@ # Auto detect text files and perform LF normalization * text=auto -# Generated files - always use LF to match what generate-llm-docs.ps1 writes +# Generated LLM documentation - always use LF to match what generate-llm-docs.ps1 writes docs/cli-schema.json text eol=lf .github/plugin/plugin.json text eol=lf .github/plugin/skills/** text eol=lf -docs/npm-usage.md text eol=lf -src/winapp-npm/src/winapp-commands.ts text eol=lf # Shell scripts should use LF *.sh text eol=lf diff --git a/.github/actions/collect-metrics/action.yml b/.github/actions/collect-metrics/action.yml index 27be437b..f4f4a044 100644 --- a/.github/actions/collect-metrics/action.yml +++ b/.github/actions/collect-metrics/action.yml @@ -18,20 +18,17 @@ runs: $artifacts = "${{ inputs.artifacts-path }}" $sizes = @{} - # Sum all files excluding PDBs to get total CLI distribution size - $x64Files = Get-ChildItem "$artifacts/cli/win-x64" -File -ErrorAction SilentlyContinue | Where-Object { $_.Extension -ne '.pdb' } - $arm64Files = Get-ChildItem "$artifacts/cli/win-arm64" -File -ErrorAction SilentlyContinue | Where-Object { $_.Extension -ne '.pdb' } + $x64Exe = Get-Item "$artifacts/cli/win-x64/winapp.exe" -ErrorAction SilentlyContinue + $arm64Exe = Get-Item "$artifacts/cli/win-arm64/winapp.exe" -ErrorAction SilentlyContinue $npmPkg = Get-ChildItem "$artifacts/*.tgz" -ErrorAction SilentlyContinue | Select-Object -First 1 $msixX64 = Get-ChildItem "$artifacts/msix-packages/*x64*.msix" -ErrorAction SilentlyContinue | Select-Object -First 1 $msixArm64 = Get-ChildItem "$artifacts/msix-packages/*arm64*.msix" -ErrorAction SilentlyContinue | Select-Object -First 1 - $nugetPkg = Get-ChildItem "$artifacts/nuget/*.nupkg" -ErrorAction SilentlyContinue | Select-Object -First 1 - if ($x64Files) { $sizes["cli-win-x64"] = ($x64Files | Measure-Object -Property Length -Sum).Sum } - if ($arm64Files) { $sizes["cli-win-arm64"] = ($arm64Files | Measure-Object -Property Length -Sum).Sum } + if ($x64Exe) { $sizes["cli-win-x64"] = $x64Exe.Length } + if ($arm64Exe) { $sizes["cli-win-arm64"] = $arm64Exe.Length } if ($npmPkg) { $sizes["npm-package"] = $npmPkg.Length } if ($msixX64) { $sizes["msix-x64"] = $msixX64.Length } if ($msixArm64) { $sizes["msix-arm64"] = $msixArm64.Length } - if ($nugetPkg) { $sizes["nuget-package"] = $nugetPkg.Length } $sizes | ConvertTo-Json | Set-Content "$artifacts/binary-sizes.json" diff --git a/.github/actions/report-metrics/action.yml b/.github/actions/report-metrics/action.yml index 89cfb86b..64ed8c5a 100644 --- a/.github/actions/report-metrics/action.yml +++ b/.github/actions/report-metrics/action.yml @@ -19,9 +19,8 @@ runs: uses: actions/cache/restore@v4 with: path: metrics-baseline - key: build-metrics-baseline - restore-keys: | - build-metrics-main- + key: build-metrics-baseline-no-exact-match + restore-keys: build-metrics-main- # On main, save current metrics as the baseline for future PRs - name: Save metrics baseline @@ -34,27 +33,12 @@ runs: Copy-Item "${{ inputs.artifacts-path }}/startup-time.json" "metrics-baseline/startup.json" -ErrorAction SilentlyContinue Copy-Item "${{ inputs.artifacts-path }}/coverage-summary.json" "metrics-baseline/coverage.json" -ErrorAction SilentlyContinue - # GitHub Actions caches are immutable — delete the old entry so we can save an updated one - - name: Delete old metrics baseline cache - if: github.ref == 'refs/heads/main' - shell: pwsh - env: - GH_TOKEN: ${{ inputs.github-token }} - run: | - $result = gh cache delete "build-metrics-baseline" --repo $env:GITHUB_REPOSITORY 2>&1 - if ($LASTEXITCODE -eq 0) { - Write-Host "Old baseline cache deleted" - } else { - Write-Host "No existing baseline cache to delete (expected on first run)" - } - exit 0 - - name: Cache metrics baseline if: github.ref == 'refs/heads/main' uses: actions/cache/save@v4 with: path: metrics-baseline - key: build-metrics-baseline + key: build-metrics-main-${{ github.sha }} # On PRs, compare against baseline and post a comment - name: Post PR metrics comment @@ -108,7 +92,6 @@ runs: 'npm-package': 'npm-package', 'msix-x64': 'msix-packages', 'msix-arm64': 'msix-packages', - 'nuget-package': 'nuget-packages', }; const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; for (const [sizeKey, artifactName] of Object.entries(artifactNameMap)) { @@ -170,7 +153,6 @@ runs: 'npm-package': 'NPM Package', 'msix-x64': 'MSIX (x64)', 'msix-arm64': 'MSIX (ARM64)', - 'nuget-package': 'NuGet Package', }; let body = '## Build Metrics Report\n\n'; diff --git a/.github/plugin/agents/winapp.agent.md b/.github/plugin/agents/winapp.agent.md index bf599783..264d1ea6 100644 --- a/.github/plugin/agents/winapp.agent.md +++ b/.github/plugin/agents/winapp.agent.md @@ -1,6 +1,6 @@ --- name: winapp -description: Expert in Windows app development, packaging, distribution, and platform integration for non-WinUI frameworks. Activate for ANY task involving packaging apps for Windows, creating Windows installers (MSIX), code signing Windows apps, Windows SDK setup, Windows App SDK, Windows API access (push notifications, background tasks, share target, startup tasks), creating or editing appxmanifest.xml, generating certificates for Windows apps, distributing apps through the Microsoft Store, adding execution aliases or file type associations, or adding MSIX packaging to build scripts or CI/CD pipelines. Covers all app frameworks including Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Uses the winapp CLI tool. +description: Expert in Windows app development, packaging, and distribution. Activate for ANY task involving packaging apps for Windows, creating Windows installers (MSIX), code signing Windows apps, Windows SDK setup, Windows App SDK, Windows API access (push notifications, background tasks, share target, startup tasks), creating or editing appxmanifest.xml, generating certificates for Windows apps, distributing apps through the Microsoft Store, adding execution aliases or file type associations, or adding MSIX packaging to build scripts or CI/CD pipelines. Covers all app frameworks including Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Uses the winapp CLI tool. infer: true --- @@ -118,8 +118,6 @@ Does the project already have an appxmanifest.xml? - `--manifest ` — path to `appxmanifest.xml` (default: auto-detect) - `--args ` — command-line arguments to pass to the app - `--no-launch` — register the package without launching -- `--with-alias` — launch via execution alias (console apps run in current terminal) -- `--debug-output` — capture `OutputDebugString` messages and first-chance exceptions (prevents other debuggers like VS/VS Code from attaching) - `--output-appx-directory ` — custom output directory for loose layout **Requires:** Built app output directory + `appxmanifest.xml` @@ -158,9 +156,9 @@ Does the project already have an appxmanifest.xml? - `--logo-path` — source image for asset generation - `--if-exists error|skip|overwrite` -### `winapp manifest update-assets [--light-image ]` -**Purpose:** Regenerate all required icon sizes, scale variants, and app.ico from a single source image (PNG, SVG, ICO, etc.). -**When to use:** When updating your app icon. Source image should be at least 400×400 pixels. SVG recommended for best quality. Use `--light-image` for light theme variants. +### `winapp manifest update-assets ` +**Purpose:** Regenerate all required icon sizes from a single source image. +**When to use:** When updating your app icon. Source image should be at least 400×400 pixels. ### `winapp tool [args...]` (alias: `winapp run-buildtool`) **Purpose:** Run Windows SDK tools directly (makeappx, signtool, makepri, etc.). @@ -186,13 +184,12 @@ Does the project already have an appxmanifest.xml? - **Package:** Build with your packager (e.g., Electron Forge), then `winapp package --cert .\devcert.pfx` - Use `winapp node create-addon` to create native C#/C++ addons for Windows APIs - Use `winapp node add-electron-debug-identity` / `clear-electron-debug-identity` for identity management -- **⚠️ Always run `npx winapp node add-electron-debug-identity` before testing any Windows API that requires package identity** — without this, APIs will fail at runtime - Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/electron/setup.md ### .NET (WPF, WinForms, Console) -- **Setup:** `winapp init --use-defaults` — but if you already have a `Package.appxmanifest` (e.g., WinUI 3 apps), you likely **don't need `winapp init`**. Just ensure your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package and has the right properties for packaged builds. -- **Run with identity:** Build with `dotnet build -c Debug -p:Platform=x64`, then `winapp run bin\x64\Debug\\win-x64\`. Replace `` with your target framework (e.g., `net10.0-windows10.0.26100.0`) and adjust architecture as needed. -- **Package:** `dotnet build -c Release -p:Platform=x64`, then `winapp package bin\x64\Release\\win-x64\ --cert devcert.pfx` +- **Setup:** `winapp init --use-defaults` +- **Run with identity:** `winapp init` auto-adds the `Microsoft.Windows.SDK.BuildTools.WinApp` NuGet package, so just `dotnet run` registers a loose layout package and launches with identity. Without the NuGet package, use `dotnet build` then `winapp run ./bin/Debug`. +- **Package:** `dotnet build -c Release`, then `winapp package bin\Release\net10.0-windows --cert devcert.pfx` - No native addons needed — .NET has direct Windows API access via `Microsoft.Windows.SDK.NET.Ref` - Guide: https://github.com/microsoft/WinAppCli/blob/main/docs/guides/dotnet.md @@ -274,7 +271,6 @@ When the user encounters an error, check these common causes: | "Package installation failed" | Stale registration or untrusted cert | Run `Get-AppxPackage \| Remove-AppxPackage`, ensure cert is trusted | | "Certificate not trusted" | Dev cert not installed | Run `winapp cert install ./devcert.pfx` as admin | | "Build tools not found" | First run, tools not downloaded | winapp auto-downloads tools; ensure internet access | -| Windows APIs fail at runtime | Debug identity not registered | Register debug identity after build and before launching: `winapp create-debug-identity ` (or `npx winapp node add-electron-debug-identity` for Electron) — this is **mandatory** for any app using identity-requiring APIs | ## Key files and concepts diff --git a/.github/plugin/plugin.json b/.github/plugin/plugin.json index 72f7f7e8..9682e329 100644 --- a/.github/plugin/plugin.json +++ b/.github/plugin/plugin.json @@ -1,7 +1,7 @@ { - "name": "winappcli", + "name": "winapp-cli", "description": "Windows app development, packaging, and distribution. Helps with creating Windows installers (MSIX), code signing, certificates, Windows SDK and Windows App SDK setup, package identity for Windows APIs (push notifications, background tasks, share target), appxmanifest authoring, and Microsoft Store distribution. Supports Electron, .NET, C++, Rust, Flutter, and Tauri apps.", - "version": "0.2.2", + "version": "0.2.1", "author": { "name": "Microsoft", "url": "https://github.com/microsoft/WinAppCli" diff --git a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md index fcd830f6..2423f1b8 100644 --- a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md +++ b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-frameworks description: Framework-specific Windows development guidance for Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Use when packaging or adding Windows features to an Electron app, .NET desktop app, Flutter app, Tauri app, Rust app, or C++ app. -version: 0.2.2 +version: 0.2.1 --- ## When to use @@ -50,20 +50,19 @@ Additional Electron guides: - Projects with NuGet references to `Microsoft.Windows.SDK.BuildTools` or `Microsoft.WindowsAppSDK` **don't need `winapp.yaml`** — winapp auto-detects SDK versions from the `.csproj` - The key prerequisite is `appxmanifest.xml`, not `winapp.yaml` - No native addon step needed — unlike Electron, .NET can call Windows APIs directly - -**If you already have a `Package.appxmanifest`** (e.g., WinUI 3 apps or projects with an existing packaging setup), you likely **don't need `winapp init`** — your project is already configured for packaged builds. Just make sure: -- Your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package (WinUI 3 apps already have this) -- The project properties are set up for packaged builds (e.g., `MSIX` or equivalent) -- WinUI 3 apps created from Visual Studio templates are typically already fully configured +- `winapp init` automatically adds the `Microsoft.Windows.SDK.BuildTools.WinApp` NuGet package, enabling `dotnet run` with automatic identity registration Quick start: ```powershell winapp init --use-defaults -dotnet build -c Debug -p:Platform=x64 -winapp run bin\x64\Debug\\win-x64\ +dotnet run ``` -Replace `` with your target framework (e.g., `net10.0-windows10.0.26100.0`), and adjust `x64` to match your target architecture. +If not using the NuGet package, build and run manually: +```powershell +dotnet build +winapp run ./bin/Debug +``` ### C++ (CMake, MSBuild) C++ projects use winapp primarily for SDK projections (CppWinRT headers) and packaging: @@ -87,25 +86,6 @@ C++ projects use winapp primarily for SDK projections (CppWinRT headers) and pac - Use winapp specifically for **MSIX distribution** and package identity features - winapp adds capabilities beyond what Tauri's built-in bundler provides (identity, sparse packages, Windows API access) -## Debugging by framework - -| Framework | Recommended command | Notes | -|-----------|-------------------|-------| -| **.NET** | `winapp run .\bin\x64\Debug\\win-x64\` | Build with `dotnet build -c Debug -p:Platform=x64` first; GUI apps launch directly; console apps need `--with-alias` | -| **C++** | `winapp run .\build\Debug --with-alias` | Console apps need `--with-alias` + `uap5:ExecutionAlias` in manifest | -| **Rust** | `winapp run .\target\debug --with-alias` | Console apps need `--with-alias` + `uap5:ExecutionAlias` in manifest | -| **Flutter** | `winapp run .\build\windows\x64\runner\Debug` | GUI app — plain `winapp run` works | -| **Tauri** | `winapp run .\dist` | Stage exe to `dist/` first (avoids copying entire `target/` tree); GUI app | -| **Electron** | `npx winapp node add-electron-debug-identity` | Uses Electron-specific identity registration; `winapp run` is **not** recommended for Electron | - -**Key rules:** -- **GUI apps** (Flutter, Tauri, WPF): use `winapp run ` — launches via AUMID activation -- **Console apps** (C++, Rust, .NET console): use `winapp run --with-alias` — launches via execution alias to preserve stdin/stdout. Requires `uap5:ExecutionAlias` in `appxmanifest.xml` -- **Electron**: different mechanism — uses `npx winapp node add-electron-debug-identity` because `electron.exe` is in `node_modules/`, not your build output -- **Startup debugging (any framework)**: use `winapp create-debug-identity ` so your IDE can F5-launch the exe with identity from the first instruction - -For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Related skills - **Setup**: `winapp-setup` — initial project setup with `winapp init` - **Manifest**: `winapp-manifest` — creating and customizing `appxmanifest.xml` diff --git a/.github/plugin/skills/winapp-cli/identity/SKILL.md b/.github/plugin/skills/winapp-cli/identity/SKILL.md index b94456d5..af0b0b63 100644 --- a/.github/plugin/skills/winapp-cli/identity/SKILL.md +++ b/.github/plugin/skills/winapp-cli/identity/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-identity description: Enable Windows package identity for desktop apps to access Windows APIs like push notifications, background tasks, share target, and startup tasks. Use when adding Windows notifications, background tasks, or other identity-requiring Windows features to a desktop app. -version: 0.2.2 +version: 0.2.1 --- ## When to use @@ -83,53 +83,6 @@ After running, launch your exe normally — Windows will recognize it as having - If you have both a debug identity and an installed MSIX, they may conflict — use `--keep-identity` carefully - For Electron apps, use `npx winapp node add-electron-debug-identity` instead (handles Electron-specific paths) -## Debugging: `winapp run` vs `create-debug-identity` - -| | `winapp run` | `create-debug-identity` | -|---|---|---| -| **What it registers** | Full loose layout package (entire folder) | Sparse package (single exe) | -| **How the app launches** | Launched by winapp (AUMID activation or execution alias) | You launch the exe yourself (command line, IDE, etc.) | -| **Simulates MSIX install** | Yes — closest to production behavior | No — sparse identity only | -| **Files stay in place** | Copied to an AppX layout directory | Yes — exe stays at its original path | -| **Debugger-friendly** | Attach to PID after launch, or use `--no-launch` then launch via alias | Launch directly from your IDE's debugger — the exe has identity regardless | -| **Console app support** | `--with-alias` keeps stdin/stdout in terminal | Run exe directly in terminal | -| **Best for** | Most frameworks (.NET, C++, Rust, Flutter, Tauri) | Electron, or when you need full IDE debugger control (F5 startup debugging) | - -### When to use which - -**Default to `winapp run`** for most development — it simulates a real MSIX install with full identity, capabilities, and file associations: - -```powershell -winapp run .\build\output # GUI apps -winapp run .\build\output --with-alias # console apps (preserves stdin/stdout) -``` - -**Use `create-debug-identity` when:** -- **Debugging startup code** — your IDE launches + debugs the exe directly; identity is attached from the first instruction -- **Exe is separate from build output** — e.g., Electron where `electron.exe` is in `node_modules/` -- **Testing sparse package behavior** — `AllowExternalContent`, `TrustedLaunch` - -```powershell -winapp create-debug-identity .\bin\Debug\myapp.exe -# Now launch any way you like — F5, terminal, script — the exe has identity -``` - -### Common debugging scenarios - -| Scenario | Command | Notes | -|----------|---------|-------| -| **Just run with identity** | `winapp run .\build\Debug` | Simplest workflow; add `--with-alias` for console apps | -| **Attach debugger to running app** | `winapp run .\build\Debug`, then attach to PID | Misses startup code | -| **Register identity, launch via AUMID** | `winapp run .\build\Debug --no-launch` | Launch with `start shell:AppsFolder\` or the execution alias (not the exe directly) | -| **F5 startup debugging** | `winapp create-debug-identity .\bin\myapp.exe` | IDE controls process from first instruction; best for debugging activation/startup code | -| **Capture debug output** | `winapp run .\build\Debug --debug-output` | Captures `OutputDebugString`; **blocks other debuggers** (one debugger per process) | -| **Run and auto-clean** | `winapp run .\build\Debug --unregister-on-exit` | Unregisters the dev package after the app exits | -| **Clean up stale registration** | `winapp unregister` | Removes dev packages for the current project (auto-detects from manifest) | - -> **Using Visual Studio with a packaging project?** VS already handles identity, AUMID activation, and debugger attachment from F5. These workflows are most useful for VS Code, terminal-based development, and frameworks VS doesn't natively package (Rust, Flutter, Tauri, Electron, C++). - -For full details including IDE setup examples, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Related skills - Need a manifest? See `winapp-manifest` to generate `appxmanifest.xml` - Need a certificate? See `winapp-signing` — a trusted cert is required for identity registration @@ -140,7 +93,7 @@ For full details including IDE setup examples, see the [Debugging Guide](https:/ | Error | Cause | Solution | |-------|-------|----------| | "appxmanifest.xml not found" | No manifest in current directory | Run `winapp init` or `winapp manifest generate`, or pass `--manifest` | -| "Failed to add package identity" | Previous registration stale or cert untrusted | Run `winapp unregister` to remove stale packages, then `winapp cert install ./devcert.pfx` (admin) | +| "Failed to add package identity" | Previous registration stale or cert untrusted | `Get-AppxPackage *yourapp* \| Remove-AppxPackage`, then `winapp cert install ./devcert.pfx` (admin) | | "Access denied" | Cert not trusted or permission issue | Run `winapp cert install ./devcert.pfx` as admin | | APIs still fail after registration | App launched before registration completed | Close app, re-run `create-debug-identity`, then relaunch | diff --git a/.github/plugin/skills/winapp-cli/manifest/SKILL.md b/.github/plugin/skills/winapp-cli/manifest/SKILL.md index 4c2a3ffb..88e42f7e 100644 --- a/.github/plugin/skills/winapp-cli/manifest/SKILL.md +++ b/.github/plugin/skills/winapp-cli/manifest/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-manifest -description: Create and edit Windows app manifest files (appxmanifest.xml) that define app identity, capabilities, and visual assets, or generate new assets from existing images. Use when creating a Windows app manifest for any app type (GUI, console, CLI tool, service), adding Windows capabilities, generating new app icons and assets, or adding execution aliases, file associations, protocol handlers, or other app extensions. -version: 0.2.2 +description: Create and edit Windows app manifest files (appxmanifest.xml) that define app identity, capabilities, and visual assets. Use when creating a Windows app manifest for any app type (GUI, console, CLI tool, service), adding Windows capabilities, updating app icons and assets, or adding execution aliases, file associations, protocol handlers, or other app extensions. +version: 0.2.1 --- ## When to use @@ -13,7 +13,7 @@ Use this skill when: ## Prerequisites - winapp CLI installed -- Optional: a source image (PNG or SVG, at least 400x400 pixels) for custom app icons +- Optional: a source image (PNG, at least 400x400 pixels) for custom app icons ## Key concepts @@ -64,24 +64,11 @@ Output: # Generate all required icon sizes from one source image winapp manifest update-assets ./my-logo.png -# SVG source images produce the best quality at all sizes -winapp manifest update-assets ./my-logo.svg - # Specify manifest location (if not in current directory) winapp manifest update-assets ./my-logo.png --manifest ./path/to/appxmanifest.xml - -# Generate light theme variants from a separate image -winapp manifest update-assets ./my-logo.png --light-image ./my-logo-light.png - -# Use the same image for both (generates all MRT light theme qualifiers) -winapp manifest update-assets ./my-logo.png --light-image ./my-logo.png ``` -The source image should be at least 400x400 pixels (PNG or SVG recommended). The command reads the manifest to determine which asset sizes are needed and generates: -- **5 scale variants** per asset (100%, 125%, 150%, 200%, 400%) -- **14 plated + 14 unplated targetsize variants** for the app icon (44x44) -- **app.ico** — multi-resolution ICO file for shell integration. If an existing `.ico` file is present in the assets directory, it is replaced in-place (preserving the original filename) -- With `--light-image`: light theme variants using the correct MRT qualifiers per asset type +The source image should be at least 400x400 pixels (PNG recommended). The command reads the manifest to determine which asset sizes are needed and generates them all. ### Add an execution alias @@ -145,7 +132,7 @@ Key fields to edit: - The `sparse` template adds `uap10:AllowExternalContent="true"` for apps that need identity but run outside the MSIX container - You can manually edit `appxmanifest.xml` after generation — it's a standard XML file - Image assets must match the paths referenced in the manifest — `update-assets` handles this automatically -- For logos, transparent PNGs or SVGs work best. SVG source images are rendered as vectors directly at each target size, producing pixel-perfect results. Use a square image for best results across all sizes. +- For logos, transparent PNGs work best. Use a square image for best results across all sizes. - **`$targetnametoken$` placeholder:** When `winapp manifest generate` creates `appxmanifest.xml`, it sets `Application.Executable` to `$targetnametoken$.exe` by default. This is a valid placeholder that gets automatically resolved by `winapp package --executable ` at packaging time — you rarely need to override it during manifest generation. If `--executable` is provided to `winapp manifest generate`, winapp reads `FileVersionInfo` from the actual exe to auto-fill package name, description, publisher, and extract an icon, so the exe must already exist on disk. ## Related skills @@ -157,7 +144,7 @@ Key fields to edit: | Error | Cause | Solution | |-------|-------|----------| | "Manifest already exists" | `appxmanifest.xml` present | Use `--if-exists overwrite` to replace, or edit existing file directly | -| "Invalid source image" | Image too small or wrong format | Use PNG or SVG, at least 400x400 pixels | +| "Invalid source image" | Image too small or wrong format | Use PNG, at least 400x400 pixels | | "Publisher mismatch" during packaging | Manifest publisher ≠ cert publisher | Edit `Identity.Publisher` in manifest, or regenerate cert with `--manifest` | @@ -194,13 +181,12 @@ Generate new assets for images referenced in an appxmanifest.xml from a single s | Argument | Required | Description | |----------|----------|-------------| -| `` | Yes | Path to source image file (SVG, PNG, ICO, JPG, BMP, GIF) | +| `` | Yes | Path to source image file | #### Options | Option | Description | Default | |--------|-------------|---------| -| `--light-image` | Path to source image for light theme variants (SVG, PNG, ICO, JPG, BMP, GIF) | (none) | | `--manifest` | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | (none) | ### `winapp manifest add-alias` diff --git a/.github/plugin/skills/winapp-cli/package/SKILL.md b/.github/plugin/skills/winapp-cli/package/SKILL.md index 89080b8b..a8f70dca 100644 --- a/.github/plugin/skills/winapp-cli/package/SKILL.md +++ b/.github/plugin/skills/winapp-cli/package/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-package description: Package a Windows app as an MSIX installer for distribution or testing. Use when creating a Windows installer, packaging an Electron/Flutter/.NET/Rust/C++/Tauri app for Windows, building an MSIX, distributing a desktop app, packaging a console app or CLI tool, or adding MSIX packaging to a build script or CI/CD pipeline. -version: 0.2.2 +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md index 4b329c9a..c0bd6aaf 100644 --- a/.github/plugin/skills/winapp-cli/setup/SKILL.md +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-setup description: Set up a Windows app project for MSIX packaging, Windows SDK access, or Windows API usage. Use when adding Windows support to an Electron, .NET, C++, Rust, Flutter, or Tauri project, or restoring SDK packages after cloning. -version: 0.2.2 +version: 0.2.1 --- ## When to use @@ -24,8 +24,6 @@ npm install --save-dev @microsoft/winappcli You need an **existing app project** — `winapp init` does **not** create new projects, it adds Windows platform files to your existing codebase. -> **Already have a `Package.appxmanifest`?** .NET projects that already have a packaging manifest (e.g., WinUI 3 apps or projects with an existing MSIX packaging setup) likely **don't need `winapp init`**. Ensure your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package and has the right properties for packaged builds (e.g., `MSIX`). WinUI 3 apps created from Visual Studio templates are typically already fully configured — you can go straight to building and using `winapp run` or `winapp package`. - ## Key concepts **`appxmanifest.xml`** is the most important file winapp creates — it declares your app's identity, capabilities, and visual assets. Most winapp commands require it (`package`, `run`, `cert generate --manifest`). @@ -94,32 +92,10 @@ winapp run ./dist --manifest ./out/AppxManifest.xml --args "--my-flag value" # Register identity without launching (useful for attaching a debugger manually) winapp run ./bin/Debug --no-launch - -# Launch and capture OutputDebugString messages and first-chance exceptions -# Note: prevents other debuggers (VS, VS Code) from attaching — use --no-launch if you need those instead -winapp run ./bin/Debug --debug-output ``` Use `winapp run` during iterative development — it creates a loose layout package, registers a debug identity, and launches the app in one step. For identity-only registration without loose layout, use `winapp create-debug-identity` instead. -#### Choosing between `run` and `create-debug-identity` - -| | `winapp run` | `create-debug-identity` | -|---|---|---| -| **Registers** | Full loose layout package (entire folder) | Sparse package (single exe) | -| **App launch** | Winapp launches via AUMID or alias | You launch the exe yourself | -| **Simulates MSIX** | Yes — closest to production | No — identity only | -| **Files** | Copied to AppX layout dir | Exe stays in place | -| **Best for** | Most frameworks (.NET, C++, Rust, Flutter, Tauri) | Electron, or F5 startup debugging | - -**Default to `winapp run`.** Use `create-debug-identity` when you need your IDE to launch and debug the exe directly (startup debugging), or when the exe is separate from your source (Electron). - -For console apps, add `--with-alias` to preserve stdin/stdout in the current terminal. - -> **`--debug-output` caveat:** Captures `OutputDebugString` but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. - -For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Recommended workflow 1. **Initialize** — `winapp init --use-defaults` in your existing project @@ -214,10 +190,8 @@ Creates packaged layout, registers the Application, and launches the packaged ap | Option | Description | Default | |--------|-------------|---------| | `--args` | Command-line arguments to pass to the application | (none) | -| `--debug-output` | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | (none) | | `--json` | Format output as JSON | (none) | | `--manifest` | Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) | (none) | | `--no-launch` | Only create the debug identity and register the package without launching the application | (none) | | `--output-appx-directory` | Output directory for the loose layout package. If not specified, a directory named AppX inside the input-folder directory will be used. | (none) | -| `--unregister-on-exit` | Unregister the development package after the application exits. Only removes packages registered in development mode. | (none) | | `--with-alias` | Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a uap5:ExecutionAlias in the manifest. Use "winapp manifest add-alias" to add an execution alias to the manifest. | (none) | diff --git a/.github/plugin/skills/winapp-cli/signing/SKILL.md b/.github/plugin/skills/winapp-cli/signing/SKILL.md index e51330e3..c3e7f03a 100644 --- a/.github/plugin/skills/winapp-cli/signing/SKILL.md +++ b/.github/plugin/skills/winapp-cli/signing/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-signing description: Create and manage code signing certificates for Windows apps and MSIX packages. Use when generating a certificate, signing a Windows app or installer, or fixing certificate trust issues. -version: 0.2.2 +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md index e379da42..74ca21a7 100644 --- a/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md +++ b/.github/plugin/skills/winapp-cli/troubleshoot/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-troubleshoot description: Diagnose and fix common Windows app packaging, signing, identity, and SDK errors. Use when encountering errors with MSIX packaging, certificate signing, Windows SDK setup, or app installation. -version: 0.2.2 +version: 0.2.1 --- ## When to use @@ -39,7 +39,7 @@ Does the project have an appxmanifest.xml? │ └─ winapp update ├─ Need a dev certificate? │ └─ winapp cert generate (then winapp cert install for trust) - ├─ Need package identity for debugging? (see [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md)) + ├─ Need package identity for debugging? │ ├─ Exe is in your build output folder? (most frameworks) │ │ └─ winapp run │ └─ Exe is separate from app code? (Electron, sparse testing) @@ -65,22 +65,6 @@ Does the project have an appxmanifest.xml? - Projects with NuGet package references (e.g., `.csproj` referencing `Microsoft.Windows.SDK.BuildTools`) can use winapp commands without `winapp.yaml` - For Electron projects, use the npm package (`npm install --save-dev @microsoft/winappcli`) which includes Node.js-specific commands under `npx winapp node` -## Debugging approach quick reference - -| Goal | Command | Key detail | -|------|---------|------------| -| Run with identity (most common) | `winapp run .\build\Debug` | Registers loose layout + launches; add `--with-alias` for console apps | -| Attach debugger to running app | `winapp run .\build\Debug` → attach to PID | Misses startup code | -| Register identity, launch manually | `winapp run .\build\Debug --no-launch` | Launch via `start shell:AppsFolder\` or execution alias — **not** the exe directly | -| F5 startup debugging (IDE launches exe) | `winapp create-debug-identity .\bin\myapp.exe` | Exe has identity regardless of how it's launched; best for debugging activation/startup code | -| Capture OutputDebugString | `winapp run .\build\Debug --debug-output` | **Blocks other debuggers** — use `--no-launch` if you need VS Code/WinDbg | -| Run and auto-clean | `winapp run .\build\Debug --unregister-on-exit` | Unregisters the dev package after the app exits | -| Clean up stale registration | `winapp unregister` | Removes dev-mode packages for the current project | - -> **Visual Studio users:** If you have a packaging project, VS already handles identity and debugging from F5 — you likely don't need winapp for debugging. These workflows are for VS Code, terminal, and frameworks VS doesn't natively package. - -For full details, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Prerequisites & state matrix | Command | Requires | Creates/Modifies | @@ -94,7 +78,6 @@ For full details, see the [Debugging Guide](https://github.com/microsoft/WinAppC | `cert install` | Certificate file + admin | Machine certificate store | | `create-debug-identity` | `appxmanifest.xml` + exe + trusted cert | Registers sparse package with Windows | | `run` | Build output folder + `appxmanifest.xml` | Registers loose layout package, launches app | -| `unregister` | `appxmanifest.xml` (auto-detect or `--manifest`) | Removes dev-mode package registrations | | `package` | Build output + `appxmanifest.xml` | `.msix` file | | `sign` | File + certificate | Signed file (in-place) | | `create-external-catalog` | Directory with executables | `CodeIntegrityExternal.cat` | diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index e369d1e4..84a1ad6a 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -10,7 +10,6 @@ on: permissions: contents: write # Required for creating releases pull-requests: write # Required for binary size report comments - actions: write # Required for deleting old metrics baseline cache jobs: # Fast docs validation - runs in parallel with the main build to fail PRs early on doc drift @@ -180,11 +179,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v5 - - - name: Enable Windows Developer Mode - run: | - # Registry key to enable Developer Mode - reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /v "AllowDevelopmentWithoutDevLicense" /d 1 /f - name: Setup Node.js uses: actions/setup-node@v5 diff --git a/.gitignore b/.gitignore index 8b5bedba..d962b820 100644 --- a/.gitignore +++ b/.gitignore @@ -155,6 +155,8 @@ obj/ *.sln.docstates # VS Code .vscode/ +# Allow .vscode config for the VS Code extension project +!src/winapp-VSC/.vscode/ # Visual Studio .vs/ # NuGet packages diff --git a/.pipelines/templates/build.yaml b/.pipelines/templates/build.yaml index d0c2ba09..906462bd 100644 --- a/.pipelines/templates/build.yaml +++ b/.pipelines/templates/build.yaml @@ -15,7 +15,7 @@ steps: inputs: pwsh: true filePath: $(System.DefaultWorkingDirectory)\scripts\build-cli.ps1 - arguments: "-Stable:$${{ parameters.stable }} -SkipMsix" + arguments: "-Stable:$${{ parameters.stable }} -SkipNpm -SkipMsix" - ${{ if eq(parameters['DoEsrp'], 'true') }}: - task: EsrpCodeSigning@5 displayName: Code Sign ESRP - CLI diff --git a/.vscode/settings.json b/.vscode/settings.json index 68ad7fa8..34ebea58 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -77,5 +77,13 @@ "xtr1common": "cpp", "xtree": "cpp", "xutility": "cpp" - } + }, + "files.exclude": { + "src\\winapp-VSC\\out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "src\\winapp-VSC\\out": true // set this to false to include "out" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" } \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 8f2b0a89..269b536a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -89,31 +89,3 @@ The following files are auto-generated from `cli-schema.json` via `scripts/gener **To edit skill content**, modify the hand-written templates in `docs/fragments/skills/winapp-cli/`. Each template file (e.g., `package.md`, `manifest.md`) contains the workflow docs, examples, and troubleshooting content. The auto-generation script appends command reference tables from the CLI schema. Running `scripts/build-cli.ps1` triggers regeneration automatically. Skill descriptions (used for Copilot skill matching) are defined in the `$SkillDescriptions` hashtable in `scripts/generate-llm-docs.ps1`. - -## C# service architecture guidelines - -### File size limits -- **Target**: ≤500 lines per file -- **Soft limit**: ~800 lines — if approaching this, look for extraction opportunities -- **Hard limit**: Do not let any single file exceed ~1,000 lines. Split into partial classes or extract services. - -### Service patterns -Use the appropriate pattern for new code: - -| Pattern | When to use | Example | -|---------|------------|---------| -| **Interface + DI service** | Stateful logic, needs dependencies | `IPriService` / `PriService` | -| **Static helper** | Pure functions, no DI needed | `PeHelper`, `MrtAssetHelper` | -| **Data document** | Wraps a file/data format with typed access | `AppxManifestDocument` | -| **Partial class** | Splitting a large service with tight internal coupling | `MsixService.Runtime.cs` | - -### Separation of concerns -- One responsibility per service/helper file -- Extract shared logic into helpers rather than duplicating across services -- If a method group only uses 1-2 of a service's 10+ dependencies, it's a candidate for extraction - -### XML handling -- **Use `XDocument` / `XElement`** (System.Xml.Linq) for structured XML manipulation — never regex -- Regex is acceptable ONLY for: pre-parse placeholder replacement (`$targetnametoken$`), raw text scanning before XML is valid -- Use `AppxManifestDocument` for AppxManifest.xml operations — it provides typed access and namespace-aware queries -- When adding new manifest manipulation, add methods to `AppxManifestDocument` rather than writing regex in consuming code diff --git a/README.md b/README.md index 479f29ba..97b7d4e6 100644 --- a/README.md +++ b/README.md @@ -168,14 +168,11 @@ npx winapp --help **App Identity & Debugging:** -- [`pack`](./docs/usage.md#pack) - Create MSIX packages from directories +- [`package`](./docs/usage.md#package) - Create MSIX packages from directories - [`run`](./docs/usage.md#run) - Run app as a packaged application for debugging (loose layout registration) - [`create-debug-identity`](./docs/usage.md#create-debug-identity) - Add sparse package identity to an existing exe -- [`unregister`](./docs/usage.md#unregister) - Remove sideloaded dev packages registered by `run` or `create-debug-identity` - [`manifest`](./docs/usage.md#manifest) - Generate and manage AppxManifest.xml files -See also: [Debugging Guide](./docs/debugging.md) — choosing between `winapp run` and `create-debug-identity`, IDE setup, and debugging scenarios. - **Certificates & Signing:** - [`cert`](./docs/usage.md#cert) - Generate and install development certificates @@ -241,7 +238,7 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft. To build the CLI: ``` -# Build the CLI and package for npm, and NuGet, from the repo root +# Build the CLI and package for npm, VS Code extension, NuGet, and MSIX from the repo root .\scripts\build-cli.ps1 ``` diff --git a/docs/cli-schema.json b/docs/cli-schema.json index f19730b4..97aae409 100644 --- a/docs/cli-schema.json +++ b/docs/cli-schema.json @@ -1,6 +1,6 @@ { "name": "winapp", - "version": "0.2.2", + "version": "0.2.1", "schemaVersion": "1.0", "description": "CLI for Windows app development, including package identity, packaging, managing appxmanifest.xml, test certificates, Windows (App) SDK projections, and more. For use with any app framework targeting Windows", "hidden": false, @@ -998,7 +998,7 @@ "hidden": false, "arguments": { "image-path": { - "description": "Path to source image file (SVG, PNG, ICO, JPG, BMP, GIF)", + "description": "Path to source image file", "order": 0, "hidden": false, "valueType": "System.IO.FileInfo", @@ -1010,18 +1010,6 @@ } }, "options": { - "--light-image": { - "description": "Path to source image for light theme variants (SVG, PNG, ICO, JPG, BMP, GIF)", - "hidden": false, - "valueType": "System.IO.FileInfo", - "hasDefaultValue": false, - "arity": { - "minimum": 1, - "maximum": 1 - }, - "required": false, - "recursive": false - }, "--manifest": { "description": "Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory)", "hidden": false, @@ -1356,19 +1344,6 @@ "required": false, "recursive": false }, - "--debug-output": { - "description": "Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json.", - "hidden": false, - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, "--json": { "description": "Format output as JSON", "hidden": false, @@ -1435,19 +1410,6 @@ "required": false, "recursive": false }, - "--unregister-on-exit": { - "description": "Unregister the development package after the application exits. Only removes packages registered in development mode.", - "hidden": false, - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, "--verbose": { "description": "Enable verbose output", "hidden": false, @@ -1611,82 +1573,6 @@ } } }, - "unregister": { - "description": "Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity').", - "hidden": false, - "options": { - "--force": { - "description": "Skip the install-location directory check and unregister even if the package was registered from a different project tree", - "hidden": false, - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--json": { - "description": "Format output as JSON", - "hidden": false, - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--manifest": { - "description": "Path to the appxmanifest.xml (default: auto-detect from current directory)", - "hidden": false, - "valueType": "System.IO.FileInfo", - "hasDefaultValue": false, - "arity": { - "minimum": 1, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--quiet": { - "description": "Suppress progress messages", - "hidden": false, - "aliases": [ - "-q" - ], - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - }, - "--verbose": { - "description": "Enable verbose output", - "hidden": false, - "aliases": [ - "-v" - ], - "valueType": "System.Boolean", - "hasDefaultValue": true, - "defaultValue": false, - "arity": { - "minimum": 0, - "maximum": 1 - }, - "required": false, - "recursive": false - } - } - }, "update": { "description": "Check for and install newer SDK versions. Updates winapp.yaml with latest versions and reinstalls packages. Requires existing winapp.yaml (created by 'init'). Use --setup-sdks preview for preview SDKs. To reinstall current versions without updating, use 'restore' instead.", "hidden": false, diff --git a/docs/debugging.md b/docs/debugging.md deleted file mode 100644 index 1c0cffbc..00000000 --- a/docs/debugging.md +++ /dev/null @@ -1,141 +0,0 @@ -# Debugging with Package Identity - -Many Windows APIs (push notifications, background tasks, share target, startup tasks, Windows AI APIs) require your app to have **package identity**. During development, you don't want to build a full MSIX installer every time you test — winapp provides two commands to give your app identity on the fly. - -> **Using Visual Studio with a packaging project?** If you are already using Visual Studio for your packaged project, you likely don't need winapp for debugging. Visual Studio already handles package registration, identity, AUMID activation, debugger attachment, and activation-code debugging — all from F5. It also offers **Debug → Other Debug Targets → Debug Installed App Package** for advanced scenarios. The workflows below are most useful for **VS Code users, terminal-based workflows, and frameworks that VS doesn't natively package** (Rust, Flutter, Tauri, Electron, plain C++). - -## Two approaches: `winapp run` vs `create-debug-identity` - -| | `winapp run` | `create-debug-identity` | -|---|---|---| -| **What it registers** | Full loose layout package (entire folder) | Sparse package (single exe) | -| **How the app launches** | Launched by winapp (AUMID activation or execution alias) | You launch the exe yourself (command line, IDE, etc.) | -| **Simulates MSIX install** | Yes — closest to production behavior | No — sparse identity only | -| **Files stay in place** | Copied to an AppX layout directory | Yes — exe stays at its original path | -| **Identity scope** | Entire folder contents (exe, DLLs, assets) | Single executable | -| **Debugger-friendly** | Attach to PID after launch, or use `--no-launch` then launch via alias | Launch directly from your IDE's debugger — the exe has identity regardless | -| **Console app support** | `--with-alias` keeps stdin/stdout in terminal | Run exe directly in terminal | -| **Best for** | Most frameworks (.NET, C++, Rust, Flutter, Tauri) | Electron, or when you need full IDE debugger control (F5) | - -## When to use which - -### Default: `winapp run` - -Use `winapp run` for most development workflows. It simulates a real MSIX install — your app gets the same identity, capabilities, and file associations it would have in production. - -```powershell -# Build your app, then: -winapp run .\build\output -``` - -### Use `create-debug-identity` when: - -- **Your exe is separate from your build output** — e.g., Electron apps where `electron.exe` lives in `node_modules/` -- **You need to debug startup code** and can't attach a debugger fast enough after AUMID launch -- **With some debuggers where you can't launch with AUMID** and need identity on the launched process — `create-debug-identity` registers the exe so it has identity no matter how it's started -- **You're testing sparse package behavior** specifically (AllowExternalContent, TrustedLaunch) - -```powershell -# Register identity for an exe, then launch it however you want: -winapp create-debug-identity .\bin\Debug\myapp.exe -.\bin\Debug\myapp.exe # or F5 in your IDE -``` - -## Debugging scenarios - -### Scenario A: Just run with identity - -The simplest workflow — build, run with identity, done. - -```powershell -winapp run .\build\Debug -``` - -Winapp registers the folder as a loose layout package and launches the app. Identity-requiring APIs work immediately. This covers the majority of development and testing scenarios. - -For **console apps** that need stdin/stdout in the current terminal, add `--with-alias`: - -```powershell -winapp run .\build\Debug --with-alias -``` - -### Scenario B: Attach a debugger to a running app - -Launch with `winapp run`, note the PID, then attach your IDE's debugger. - -```powershell -winapp run .\build\Debug -# Output: Process ID: 12345 -``` - -Then in your IDE: -- **VS Code**: Run and Debug → select "Attach" configuration (see [IDE setup](#ide-setup) below) -- **WinDbg**: `windbg -p 12345` - -> **Limitation:** You'll miss any code that runs before you attach. For startup debugging, use Scenario D (`create-debug-identity`). - -### Scenario C: Register identity, then launch via AUMID or alias from your IDE - -Use `--no-launch` to register the package, then launch the app through its AUMID (reported by `run`) or **execution alias** from your IDE. - -**Step 1:** Register the package without launching: - -```powershell -winapp run .\build\Debug --no-launch -``` - -**Step 2:** Configure your IDE to launch via the AUMID or the **execution alias** (not the exe directly). -* Launching with AUMID: Use the command `start shell:AppsFolder\`. `winapp run` outputs the AUMID when the app is registered. -* Launching with the alias: The alias must be defined in the appxmanifest.xml (or Package.appxmanifest). - -> **Important:** Simply launching the exe in the build folder will **not** give it identity. The app must be started via AUMID activation or its execution alias. This is how loose layout packages work - identity is tied to the activation path, not the exe file. - -### Scenario D: Launch from your IDE with identity (startup debugging) - -This is the best approach for **debugging startup code with full IDE control** - your IDE's debugger controls the process from the very first instruction, and the exe has identity no matter how it's launched. - -```powershell -winapp create-debug-identity .\build\Debug\myapp.exe -``` - -Now launch the exe any way you like — from the terminal, from VS Code's F5, from a script. The exe has identity because Windows registered a **sparse package** pointing directly at it. - -> **How it differs from `winapp run`:** With `create-debug-identity`, identity is tied to the exe itself (via `Add-AppxPackage -ExternalLocation`). With `winapp run`, identity is tied to the loose layout package — the app must be launched through AUMID or an alias. This makes `create-debug-identity` the better choice when you need your IDE to launch and debug the exe directly. - -> This is also the best approach for **Electron apps** where the exe path differs from your source directory. - -### Scenario E: Capture debug output - -Capture `OutputDebugString` messages and first-chance exceptions inline: - -```powershell -winapp run .\build\Debug --debug-output -``` - -> **Important:** This attaches winapp as the debugger. Windows only allows one debugger per process, so you **cannot** also attach Visual Studio, VS Code, or WinDbg. - -## IDE setup - -### VS Code - -**Attach to running process** — add to `.vscode/launch.json`: - -```json -{ - "name": "Attach to Process", - "type": "coreclr", - "request": "attach", - "processId": "${command:pickProcess}" -} -``` - -For C++/Rust, use `"type": "cppvsdbg"` (MSVC) or `"type": "lldb"` (LLDB): - -```json -{ - "name": "Attach (C++)", - "type": "cppvsdbg", - "request": "attach", - "processId": "${command:pickProcess}" -} -``` diff --git a/docs/dotnet-run-support.md b/docs/dotnet-run-support.md index 33ae6c6f..99c05e37 100644 --- a/docs/dotnet-run-support.md +++ b/docs/dotnet-run-support.md @@ -62,6 +62,23 @@ src/ │ ├── win-x64/ │ └── win-arm64/ │ +├── winapp-Templates/ # Templates NuGet package +│ ├── Microsoft.WindowsAppSDK.Templates.csproj +│ ├── README.md +│ └── templates/ +│ └── winui/ # WinUI app template +│ ├── .template.config/ +│ │ └── template.json +│ ├── WinUIApp1.csproj +│ ├── App.xaml +│ ├── App.xaml.cs +│ ├── MainWindow.xaml +│ ├── MainWindow.xaml.cs +│ ├── app.manifest +│ ├── Package.appxmanifest +│ ├── Properties/ +│ └── Assets/ +│ samples/ └── winui-app/ # Sample WinUI app for testing ``` @@ -79,7 +96,6 @@ samples/ | `WinAppCliPath` | (in package) | Path to the winapp.exe CLI | | `WinAppRunUseExecutionAlias` | `false` | Launch via execution alias instead of AUMID. Keeps console I/O in the current terminal. Requires `uap5:ExecutionAlias` in the manifest. Cannot be combined with `WinAppRunNoLaunch`. | | `WinAppRunNoLaunch` | `false` | Only register package identity without launching the app. Cannot be combined with `WinAppRunUseExecutionAlias`. | -| `WinAppRunDebugOutput` | `false` | Attach as a debugger to capture `OutputDebugString` messages and first-chance exceptions. Only one debugger can attach at a time, so Visual Studio or VS Code cannot debug simultaneously. Use `WinAppRunNoLaunch` instead to attach a different debugger. Cannot be combined with `WinAppRunNoLaunch`. | ### Targets (Microsoft.Windows.SDK.BuildTools.WinApp.targets) @@ -114,10 +130,30 @@ The main build script now includes NuGet packaging: ```powershell .\scripts\build-cli.ps1 # Full build including NuGet .\scripts\build-cli.ps1 -SkipNuGet # Skip NuGet packages +.\scripts\build-cli.ps1 -SkipVsc # Skip VS Code extension ``` ## Usage +### Installing the Template + +```bash +# From local build +dotnet nuget add source "path/to/artifacts/nuget" --name WinAppLocal +dotnet new install Microsoft.WindowsAppSDK.Templates::1.0.0-test --nuget-source WinAppLocal + +# From NuGet.org (when published) +dotnet new install Microsoft.WindowsAppSDK.Templates +``` + +### Creating and Running a WinUI App + +```bash +dotnet new winui -n MyApp +cd MyApp +dotnet run +``` + ### Customization Disable run support for a project: @@ -155,13 +191,6 @@ Register identity without launching: ``` -Capture OutputDebugString messages and first-chance exceptions: -```xml - - true - -``` - ## Outstanding Production Blockers ### 1. CLI AOT Build Issues (BLOCKING) diff --git a/docs/fragments/skills/winapp-cli/frameworks.md b/docs/fragments/skills/winapp-cli/frameworks.md index cb985379..bcf5ede5 100644 --- a/docs/fragments/skills/winapp-cli/frameworks.md +++ b/docs/fragments/skills/winapp-cli/frameworks.md @@ -45,20 +45,19 @@ Additional Electron guides: - Projects with NuGet references to `Microsoft.Windows.SDK.BuildTools` or `Microsoft.WindowsAppSDK` **don't need `winapp.yaml`** — winapp auto-detects SDK versions from the `.csproj` - The key prerequisite is `appxmanifest.xml`, not `winapp.yaml` - No native addon step needed — unlike Electron, .NET can call Windows APIs directly - -**If you already have a `Package.appxmanifest`** (e.g., WinUI 3 apps or projects with an existing packaging setup), you likely **don't need `winapp init`** — your project is already configured for packaged builds. Just make sure: -- Your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package (WinUI 3 apps already have this) -- The project properties are set up for packaged builds (e.g., `MSIX` or equivalent) -- WinUI 3 apps created from Visual Studio templates are typically already fully configured +- `winapp init` automatically adds the `Microsoft.Windows.SDK.BuildTools.WinApp` NuGet package, enabling `dotnet run` with automatic identity registration Quick start: ```powershell winapp init --use-defaults -dotnet build -c Debug -p:Platform=x64 -winapp run bin\x64\Debug\\win-x64\ +dotnet run ``` -Replace `` with your target framework (e.g., `net10.0-windows10.0.26100.0`), and adjust `x64` to match your target architecture. +If not using the NuGet package, build and run manually: +```powershell +dotnet build +winapp run ./bin/Debug +``` ### C++ (CMake, MSBuild) C++ projects use winapp primarily for SDK projections (CppWinRT headers) and packaging: @@ -82,25 +81,6 @@ C++ projects use winapp primarily for SDK projections (CppWinRT headers) and pac - Use winapp specifically for **MSIX distribution** and package identity features - winapp adds capabilities beyond what Tauri's built-in bundler provides (identity, sparse packages, Windows API access) -## Debugging by framework - -| Framework | Recommended command | Notes | -|-----------|-------------------|-------| -| **.NET** | `winapp run .\bin\x64\Debug\\win-x64\` | Build with `dotnet build -c Debug -p:Platform=x64` first; GUI apps launch directly; console apps need `--with-alias` | -| **C++** | `winapp run .\build\Debug --with-alias` | Console apps need `--with-alias` + `uap5:ExecutionAlias` in manifest | -| **Rust** | `winapp run .\target\debug --with-alias` | Console apps need `--with-alias` + `uap5:ExecutionAlias` in manifest | -| **Flutter** | `winapp run .\build\windows\x64\runner\Debug` | GUI app — plain `winapp run` works | -| **Tauri** | `winapp run .\dist` | Stage exe to `dist/` first (avoids copying entire `target/` tree); GUI app | -| **Electron** | `npx winapp node add-electron-debug-identity` | Uses Electron-specific identity registration; `winapp run` is **not** recommended for Electron | - -**Key rules:** -- **GUI apps** (Flutter, Tauri, WPF): use `winapp run ` — launches via AUMID activation -- **Console apps** (C++, Rust, .NET console): use `winapp run --with-alias` — launches via execution alias to preserve stdin/stdout. Requires `uap5:ExecutionAlias` in `appxmanifest.xml` -- **Electron**: different mechanism — uses `npx winapp node add-electron-debug-identity` because `electron.exe` is in `node_modules/`, not your build output -- **Startup debugging (any framework)**: use `winapp create-debug-identity ` so your IDE can F5-launch the exe with identity from the first instruction - -For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Related skills - **Setup**: `winapp-setup` — initial project setup with `winapp init` - **Manifest**: `winapp-manifest` — creating and customizing `appxmanifest.xml` diff --git a/docs/fragments/skills/winapp-cli/identity.md b/docs/fragments/skills/winapp-cli/identity.md index 1add2bd1..f8b1baad 100644 --- a/docs/fragments/skills/winapp-cli/identity.md +++ b/docs/fragments/skills/winapp-cli/identity.md @@ -78,53 +78,6 @@ After running, launch your exe normally — Windows will recognize it as having - If you have both a debug identity and an installed MSIX, they may conflict — use `--keep-identity` carefully - For Electron apps, use `npx winapp node add-electron-debug-identity` instead (handles Electron-specific paths) -## Debugging: `winapp run` vs `create-debug-identity` - -| | `winapp run` | `create-debug-identity` | -|---|---|---| -| **What it registers** | Full loose layout package (entire folder) | Sparse package (single exe) | -| **How the app launches** | Launched by winapp (AUMID activation or execution alias) | You launch the exe yourself (command line, IDE, etc.) | -| **Simulates MSIX install** | Yes — closest to production behavior | No — sparse identity only | -| **Files stay in place** | Copied to an AppX layout directory | Yes — exe stays at its original path | -| **Debugger-friendly** | Attach to PID after launch, or use `--no-launch` then launch via alias | Launch directly from your IDE's debugger — the exe has identity regardless | -| **Console app support** | `--with-alias` keeps stdin/stdout in terminal | Run exe directly in terminal | -| **Best for** | Most frameworks (.NET, C++, Rust, Flutter, Tauri) | Electron, or when you need full IDE debugger control (F5 startup debugging) | - -### When to use which - -**Default to `winapp run`** for most development — it simulates a real MSIX install with full identity, capabilities, and file associations: - -```powershell -winapp run .\build\output # GUI apps -winapp run .\build\output --with-alias # console apps (preserves stdin/stdout) -``` - -**Use `create-debug-identity` when:** -- **Debugging startup code** — your IDE launches + debugs the exe directly; identity is attached from the first instruction -- **Exe is separate from build output** — e.g., Electron where `electron.exe` is in `node_modules/` -- **Testing sparse package behavior** — `AllowExternalContent`, `TrustedLaunch` - -```powershell -winapp create-debug-identity .\bin\Debug\myapp.exe -# Now launch any way you like — F5, terminal, script — the exe has identity -``` - -### Common debugging scenarios - -| Scenario | Command | Notes | -|----------|---------|-------| -| **Just run with identity** | `winapp run .\build\Debug` | Simplest workflow; add `--with-alias` for console apps | -| **Attach debugger to running app** | `winapp run .\build\Debug`, then attach to PID | Misses startup code | -| **Register identity, launch via AUMID** | `winapp run .\build\Debug --no-launch` | Launch with `start shell:AppsFolder\` or the execution alias (not the exe directly) | -| **F5 startup debugging** | `winapp create-debug-identity .\bin\myapp.exe` | IDE controls process from first instruction; best for debugging activation/startup code | -| **Capture debug output** | `winapp run .\build\Debug --debug-output` | Captures `OutputDebugString`; **blocks other debuggers** (one debugger per process) | -| **Run and auto-clean** | `winapp run .\build\Debug --unregister-on-exit` | Unregisters the dev package after the app exits | -| **Clean up stale registration** | `winapp unregister` | Removes dev packages for the current project (auto-detects from manifest) | - -> **Using Visual Studio with a packaging project?** VS already handles identity, AUMID activation, and debugger attachment from F5. These workflows are most useful for VS Code, terminal-based development, and frameworks VS doesn't natively package (Rust, Flutter, Tauri, Electron, C++). - -For full details including IDE setup examples, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Related skills - Need a manifest? See `winapp-manifest` to generate `appxmanifest.xml` - Need a certificate? See `winapp-signing` — a trusted cert is required for identity registration @@ -135,6 +88,6 @@ For full details including IDE setup examples, see the [Debugging Guide](https:/ | Error | Cause | Solution | |-------|-------|----------| | "appxmanifest.xml not found" | No manifest in current directory | Run `winapp init` or `winapp manifest generate`, or pass `--manifest` | -| "Failed to add package identity" | Previous registration stale or cert untrusted | Run `winapp unregister` to remove stale packages, then `winapp cert install ./devcert.pfx` (admin) | +| "Failed to add package identity" | Previous registration stale or cert untrusted | `Get-AppxPackage *yourapp* \| Remove-AppxPackage`, then `winapp cert install ./devcert.pfx` (admin) | | "Access denied" | Cert not trusted or permission issue | Run `winapp cert install ./devcert.pfx` as admin | | APIs still fail after registration | App launched before registration completed | Close app, re-run `create-debug-identity`, then relaunch | diff --git a/docs/fragments/skills/winapp-cli/manifest.md b/docs/fragments/skills/winapp-cli/manifest.md index bb8cfcc4..9551e48f 100644 --- a/docs/fragments/skills/winapp-cli/manifest.md +++ b/docs/fragments/skills/winapp-cli/manifest.md @@ -8,7 +8,7 @@ Use this skill when: ## Prerequisites - winapp CLI installed -- Optional: a source image (PNG or SVG, at least 400x400 pixels) for custom app icons +- Optional: a source image (PNG, at least 400x400 pixels) for custom app icons ## Key concepts @@ -59,24 +59,11 @@ Output: # Generate all required icon sizes from one source image winapp manifest update-assets ./my-logo.png -# SVG source images produce the best quality at all sizes -winapp manifest update-assets ./my-logo.svg - # Specify manifest location (if not in current directory) winapp manifest update-assets ./my-logo.png --manifest ./path/to/appxmanifest.xml - -# Generate light theme variants from a separate image -winapp manifest update-assets ./my-logo.png --light-image ./my-logo-light.png - -# Use the same image for both (generates all MRT light theme qualifiers) -winapp manifest update-assets ./my-logo.png --light-image ./my-logo.png ``` -The source image should be at least 400x400 pixels (PNG or SVG recommended). The command reads the manifest to determine which asset sizes are needed and generates: -- **5 scale variants** per asset (100%, 125%, 150%, 200%, 400%) -- **14 plated + 14 unplated targetsize variants** for the app icon (44x44) -- **app.ico** — multi-resolution ICO file for shell integration. If an existing `.ico` file is present in the assets directory, it is replaced in-place (preserving the original filename) -- With `--light-image`: light theme variants using the correct MRT qualifiers per asset type +The source image should be at least 400x400 pixels (PNG recommended). The command reads the manifest to determine which asset sizes are needed and generates them all. ### Add an execution alias @@ -140,7 +127,7 @@ Key fields to edit: - The `sparse` template adds `uap10:AllowExternalContent="true"` for apps that need identity but run outside the MSIX container - You can manually edit `appxmanifest.xml` after generation — it's a standard XML file - Image assets must match the paths referenced in the manifest — `update-assets` handles this automatically -- For logos, transparent PNGs or SVGs work best. SVG source images are rendered as vectors directly at each target size, producing pixel-perfect results. Use a square image for best results across all sizes. +- For logos, transparent PNGs work best. Use a square image for best results across all sizes. - **`$targetnametoken$` placeholder:** When `winapp manifest generate` creates `appxmanifest.xml`, it sets `Application.Executable` to `$targetnametoken$.exe` by default. This is a valid placeholder that gets automatically resolved by `winapp package --executable ` at packaging time — you rarely need to override it during manifest generation. If `--executable` is provided to `winapp manifest generate`, winapp reads `FileVersionInfo` from the actual exe to auto-fill package name, description, publisher, and extract an icon, so the exe must already exist on disk. ## Related skills @@ -152,5 +139,5 @@ Key fields to edit: | Error | Cause | Solution | |-------|-------|----------| | "Manifest already exists" | `appxmanifest.xml` present | Use `--if-exists overwrite` to replace, or edit existing file directly | -| "Invalid source image" | Image too small or wrong format | Use PNG or SVG, at least 400x400 pixels | +| "Invalid source image" | Image too small or wrong format | Use PNG, at least 400x400 pixels | | "Publisher mismatch" during packaging | Manifest publisher ≠ cert publisher | Edit `Identity.Publisher` in manifest, or regenerate cert with `--manifest` | diff --git a/docs/fragments/skills/winapp-cli/setup.md b/docs/fragments/skills/winapp-cli/setup.md index 1dcd17fd..6c870c9e 100644 --- a/docs/fragments/skills/winapp-cli/setup.md +++ b/docs/fragments/skills/winapp-cli/setup.md @@ -19,8 +19,6 @@ npm install --save-dev @microsoft/winappcli You need an **existing app project** — `winapp init` does **not** create new projects, it adds Windows platform files to your existing codebase. -> **Already have a `Package.appxmanifest`?** .NET projects that already have a packaging manifest (e.g., WinUI 3 apps or projects with an existing MSIX packaging setup) likely **don't need `winapp init`**. Ensure your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package and has the right properties for packaged builds (e.g., `MSIX`). WinUI 3 apps created from Visual Studio templates are typically already fully configured — you can go straight to building and using `winapp run` or `winapp package`. - ## Key concepts **`appxmanifest.xml`** is the most important file winapp creates — it declares your app's identity, capabilities, and visual assets. Most winapp commands require it (`package`, `run`, `cert generate --manifest`). @@ -89,32 +87,10 @@ winapp run ./dist --manifest ./out/AppxManifest.xml --args "--my-flag value" # Register identity without launching (useful for attaching a debugger manually) winapp run ./bin/Debug --no-launch - -# Launch and capture OutputDebugString messages and first-chance exceptions -# Note: prevents other debuggers (VS, VS Code) from attaching — use --no-launch if you need those instead -winapp run ./bin/Debug --debug-output ``` Use `winapp run` during iterative development — it creates a loose layout package, registers a debug identity, and launches the app in one step. For identity-only registration without loose layout, use `winapp create-debug-identity` instead. -#### Choosing between `run` and `create-debug-identity` - -| | `winapp run` | `create-debug-identity` | -|---|---|---| -| **Registers** | Full loose layout package (entire folder) | Sparse package (single exe) | -| **App launch** | Winapp launches via AUMID or alias | You launch the exe yourself | -| **Simulates MSIX** | Yes — closest to production | No — identity only | -| **Files** | Copied to AppX layout dir | Exe stays in place | -| **Best for** | Most frameworks (.NET, C++, Rust, Flutter, Tauri) | Electron, or F5 startup debugging | - -**Default to `winapp run`.** Use `create-debug-identity` when you need your IDE to launch and debug the exe directly (startup debugging), or when the exe is separate from your source (Electron). - -For console apps, add `--with-alias` to preserve stdin/stdout in the current terminal. - -> **`--debug-output` caveat:** Captures `OutputDebugString` but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. - -For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Recommended workflow 1. **Initialize** — `winapp init --use-defaults` in your existing project diff --git a/docs/fragments/skills/winapp-cli/troubleshoot.md b/docs/fragments/skills/winapp-cli/troubleshoot.md index 3fa36d1f..1892cfa2 100644 --- a/docs/fragments/skills/winapp-cli/troubleshoot.md +++ b/docs/fragments/skills/winapp-cli/troubleshoot.md @@ -34,7 +34,7 @@ Does the project have an appxmanifest.xml? │ └─ winapp update ├─ Need a dev certificate? │ └─ winapp cert generate (then winapp cert install for trust) - ├─ Need package identity for debugging? (see [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md)) + ├─ Need package identity for debugging? │ ├─ Exe is in your build output folder? (most frameworks) │ │ └─ winapp run │ └─ Exe is separate from app code? (Electron, sparse testing) @@ -60,22 +60,6 @@ Does the project have an appxmanifest.xml? - Projects with NuGet package references (e.g., `.csproj` referencing `Microsoft.Windows.SDK.BuildTools`) can use winapp commands without `winapp.yaml` - For Electron projects, use the npm package (`npm install --save-dev @microsoft/winappcli`) which includes Node.js-specific commands under `npx winapp node` -## Debugging approach quick reference - -| Goal | Command | Key detail | -|------|---------|------------| -| Run with identity (most common) | `winapp run .\build\Debug` | Registers loose layout + launches; add `--with-alias` for console apps | -| Attach debugger to running app | `winapp run .\build\Debug` → attach to PID | Misses startup code | -| Register identity, launch manually | `winapp run .\build\Debug --no-launch` | Launch via `start shell:AppsFolder\` or execution alias — **not** the exe directly | -| F5 startup debugging (IDE launches exe) | `winapp create-debug-identity .\bin\myapp.exe` | Exe has identity regardless of how it's launched; best for debugging activation/startup code | -| Capture OutputDebugString | `winapp run .\build\Debug --debug-output` | **Blocks other debuggers** — use `--no-launch` if you need VS Code/WinDbg | -| Run and auto-clean | `winapp run .\build\Debug --unregister-on-exit` | Unregisters the dev package after the app exits | -| Clean up stale registration | `winapp unregister` | Removes dev-mode packages for the current project | - -> **Visual Studio users:** If you have a packaging project, VS already handles identity and debugging from F5 — you likely don't need winapp for debugging. These workflows are for VS Code, terminal, and frameworks VS doesn't natively package. - -For full details, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Prerequisites & state matrix | Command | Requires | Creates/Modifies | @@ -89,7 +73,6 @@ For full details, see the [Debugging Guide](https://github.com/microsoft/WinAppC | `cert install` | Certificate file + admin | Machine certificate store | | `create-debug-identity` | `appxmanifest.xml` + exe + trusted cert | Registers sparse package with Windows | | `run` | Build output folder + `appxmanifest.xml` | Registers loose layout package, launches app | -| `unregister` | `appxmanifest.xml` (auto-detect or `--manifest`) | Removes dev-mode package registrations | | `package` | Build output + `appxmanifest.xml` | `.msix` file | | `sign` | File + certificate | Signed file (in-place) | | `create-external-catalog` | Directory with executables | `CodeIntegrityExternal.cat` | diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..b1434cb2 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,208 @@ +# Getting Started with WinApp CLI + +Build, run, and debug packaged Windows applications from any editor or terminal — no Visual Studio required. + +## Prerequisites + +- **VS Code** (recommended) — install via `winget install Microsoft.VisualStudioCode` or from [code.visualstudio.com](https://code.visualstudio.com) + +For the .NET scenarios below, you also need: + +- **.NET 10 SDK** (or later) — install via `winget install Microsoft.DotNet.SDK.10` or from [dot.net](https://dot.net) +- **C# Dev Kit extension** for VS Code — install from the Extensions sidebar (required for .NET IntelliSense and build support) + +--- + +## Installation + +Open PowerShell in the folder containing the setup files and run: + +```powershell +.\setup-winapprun.ps1 +``` + +The script will prompt for administrator elevation (required to trust the MSIX signing certificate), then install everything you need to build packaged Windows apps across .NET, Electron, Flutter, C++/CMake, Rust, Tauri, and more: + +- **WinApp CLI** (`winapp`) — installed as an MSIX package, available system-wide. Initialize projects, run apps as packaged, generate manifests, manage certificates, and create MSIX packages. +- **WinApp VS Code Extension** — Command Palette integration and a `winapp` debug configuration for F5 launch-and-attach (.NET, C++, Node.js). +- **dotnet templates** — `dotnet new winui` for WinUI 3 apps pre-configured for packaged development. (This is a temporary template — Niels and Gordon's team are working on updated dotnet templates to replace it.) +- **MSIX Extras NuGet package** — registered as a local NuGet feed. MSBuild targets that make `dotnet run` launch with package identity automatically. + +After the script finishes, verify the installation: + +```powershell +winapp --version # should print the CLI version +dotnet new list winui # should list the WinUI template +``` + +> **Note:** This guide walks through .NET scenarios first. Support for Electron, Flutter, C++, and other stacks is actively being built using the same underlying CLI. + +--- + +## Scenario 1: WinUI App from Template (.NET) + +This is the fastest path for .NET developers. The `dotnet new winui` template comes pre-configured with the MSIX Extras package, an `appxmanifest.xml`, and the correct target framework. No additional initialization is needed. + +### Create and run + +```powershell +mkdir MyWinUIApp +cd MyWinUIApp + +dotnet new winui +dotnet run +``` + +That's it. `dotnet run` builds the project, then the MSIX Extras package automatically calls `winapp run` under the hood. Your app launches with full package identity — you can call any Windows SDK or Windows App SDK API that requires it. + +### Debug with F5 in VS Code + +1. Open the project folder in VS Code: + ```powershell + code . + ``` + +2. Open the **Run and Debug** panel (Ctrl+Shift+D) and click **create a launch.json file**. + +3. Select **WinApp** from the debugger list. This generates a `launch.json` with the following configuration: + + ```jsonc + { + "version": "0.2.0", + "configurations": [ + { + "type": "winapp", + "request": "launch", + "name": "WinApp: Launch and Attach", + "buildOutputManifest": "**/*/AppxManifest.xml" + } + ] + } + ``` + +4. Press **F5**. The extension automatically finds your `AppxManifest.xml`, registers a loose-layout package, launches the app, and attaches the debugger. + +You get the full debugging experience: breakpoints, call stack, locals, watch — all running as a packaged app. The `debuggerType` launch option controls which debugger is used (default is `coreclr` for .NET; set it to `cppvsdbg` for native C++ or `node` for Node.js/Electron apps). + +> **Multiple build configurations:** If you have build output for more than one architecture (e.g., `win-arm64` and `win-x64`), the extension shows a picker so you can choose which one to launch. + +--- + +## Scenario 2: WPF App with WinApp Init (.NET) + +The `winapp init` command detects your technology stack and configures the project for packaged development automatically. + +### Create, initialize, and run + +```powershell +mkdir MyWpfApp +cd MyWpfApp + +dotnet new wpf +winapp init +dotnet run +``` + +`winapp init` detects the `.csproj`, updates the target framework, adds the required NuGet packages (Windows App SDK, SDK Build Tools, MSIX Extras), generates an `appxmanifest.xml` with icon assets, and installs the Windows App SDK runtime. After that, `dotnet run` launches your WPF app with full package identity — just like Scenario 1. + +The `init` command is interactive by default. To accept all defaults: + +```powershell +winapp init --use-defaults +``` + +### Debug with F5 + +The same VS Code debugging setup from Scenario 1 applies. Open the folder in VS Code, create a `launch.json` with the **WinApp** configuration, and press F5. + +--- + +## VS Code Extension Features + +The WinApp extension does more than debugging. Every CLI command is accessible from the Command Palette (Ctrl+Shift+P → type "WinApp"): + +| Command | What it does | +|---------|-------------| +| **WinApp: Initialize Project** | Run `winapp init` with SDK channel selection | +| **WinApp: Run Application** | Run `winapp run` in the integrated terminal | +| **WinApp: Create MSIX Package** | Package your app into an MSIX with optional signing | +| **WinApp: Generate Manifest** | Create an `appxmanifest.xml` from a template | +| **WinApp: Update Manifest Assets** | Regenerate all icon assets from a source image | +| **WinApp: Generate Certificate** | Create a development signing certificate | +| **WinApp: Install Certificate** | Trust a certificate on the local machine | +| **WinApp: Sign Package** | Code-sign an MSIX or executable | +| **WinApp: Restore Packages** | Restore SDKs from `winapp.yaml` | +| **WinApp: Update Packages** | Update SDK packages to latest versions | +| **WinApp: Run SDK Tool** | Access Windows SDK tools (makeappx, signtool, etc.) | + +The extension bundles its own copy of the CLI, so these commands work even if you haven't installed the MSIX package globally. + +--- + +## How It Works + +Understanding the architecture helps when troubleshooting or customizing your setup. The core of the WinApp CLI is technology-agnostic — it operates on a folder with an `AppxManifest.xml` and an executable. The .NET-specific integrations (MSBuild targets, NuGet packages) are layers on top of this foundation. + +### The `winapp run` command + +`winapp run` is the core primitive that the other tools build on. Given a path to an `AppxManifest.xml`, it: + +1. Creates a **loose-layout package** — a folder structure with your app binaries and manifest, registered with Windows via the same APIs that Visual Studio uses. +2. Registers the package identity with the system using `Add-AppxPackage`. +3. Launches the app through the Windows Application Activation Manager. +4. Prints the **process ID** to stdout, which callers (the VS Code extension, MSBuild targets, Electron Forge, etc.) use to attach a debugger. + +This works regardless of how the app was built — whether from `dotnet build`, `cmake`, `npm run make`, or any other build system. + +### The MSIX Extras NuGet package (.NET-specific) + +The `Microsoft.Windows.SDK.BuildTools.WinApp` package contains MSBuild `.props` and `.targets` files that hook into the `dotnet run` lifecycle. When you execute `dotnet run`, MSBuild's `Run` target is intercepted and redirected through `winapp run` with the correct output directory and manifest path. The package also bundles a copy of the WinApp CLI, so no global installation is required for `dotnet run` to work. + +### The VS Code extension debug flow + +When you press F5 with a `winapp` launch configuration: + +1. The extension searches your workspace for `AppxManifest.xml` using the configured glob pattern. +2. It invokes `winapp run --manifest ` and captures the process ID from the output. +3. It starts a child debug session using the specified `debuggerType` (default: `coreclr`) and attaches to the process. +4. When you stop debugging, the session is cleaned up automatically. + +This flow is not tied to .NET — by changing `debuggerType` to `cppvsdbg` or `node`, the same F5 experience works for C++ or Electron apps. + +--- + +## Common Tasks + +```powershell +winapp manifest generate # generate an appxmanifest.xml +winapp manifest update-assets mylogo.png # regenerate icons from a source image +winapp pack ./dist --cert ./devcert.pfx # create and sign an MSIX package +``` + +--- + +## Troubleshooting + +**"Developer Mode is not enabled"** +`winapp init` and `winapp run` will detect this and prompt to enable it automatically (requires elevation). You can also enable it manually under Windows Settings > For Developers. + +**`dotnet run` does not launch as packaged** +Ensure the `Microsoft.Windows.SDK.BuildTools.WinApp` package reference is present in your `.csproj`. If you initialized with `winapp init`, it should already be there. Run `dotnet restore` to make sure NuGet packages are resolved (the local NuGet feed registered by the setup script must be accessible). + +**F5 says "No AppxManifest.xml found"** +Build the project first (`dotnet build`), then try again. The extension looks for the manifest in your build output, not the project root. + +**Certificate trust errors when installing MSIX** +Re-run the setup script as administrator to ensure the development certificate is in the TrustedPeople store. + +--- + +## What's Next + +The scenarios in this guide focus on .NET, but the WinApp CLI is designed to work across the Windows development ecosystem. Support for additional stacks is actively in progress: + +- **Electron** — `winapp init` creates a `winapp.yaml` and manifest; the npm package (`winapp`) wraps the CLI with Electron-specific commands for sparse packaging and debug identity. +- **C++ / CMake** — `winapp init` downloads Windows SDK and Windows App SDK packages, generates C++/WinRT headers, and sets up build tools. +- **Flutter, Rust, Tauri** — same `winapp init` + `winapp pack` workflow, with technology-specific guidance in the [docs/guides](guides/) folder. + +For full CLI reference, see [usage.md](usage.md). For LLM/agent integration, see [using-with-llms.md](using-with-llms.md). diff --git a/docs/gui-usage.md b/docs/gui-usage.md new file mode 100644 index 00000000..86e8e82c --- /dev/null +++ b/docs/gui-usage.md @@ -0,0 +1,64 @@ +# 🧪 Windows Identity App Usage + +This is an **experimental** app/sample (GUI) that wraps the CLI and provides an intuitive, drag-and-drop experience with the following features: + +- Supports .NET (Winforms, WPF..etc) apps, Python scripts/folders, MSIX +- Drop in a WinForms, WPF executable (.exe) to add development/debug app identity (via external location/sparse packaging) in a single click! +- Drop in a WinForms, WPF folder to package your app (MSIX) in a single click +- Drop in an MSIX to sign and register it locally in a single click +- Drop in a Python (.py) file to add debug identity in a single click + +We would love your feedback on this UI-based approach and whether it adds value or doesn't fit with your development workflows. + +
+ + + + + +
+ Windows Identity Tool Interface + + Windows Identity Tool Options +
+
+ +## Install the GUI Tool + +There are 2 ways to run the GUI (experimental). + +### Download the MSIX + +1. **[👉 Download Latest Experimental Build (unsigned .msix)](https://github.com/microsoft/WinAppCli/releases/tag/v0.1.1-gui)** +2. Run Powershell as **Administrator** and `Add-AppPackage -Path -AllowUnsigned` + +`` should be replaced with the full path of the downloaded build (msix file). +This experimental app requires the `winapp` CLI to be added to the user's PATH to function. Ensure you have the [latest CLI version](https://github.com/microsoft/WinAppCli/releases/latest) added to your path (installing the CLI MSIX is the easiest way to do this). + +### Build the repository + +Clone and build this repository. Run winapp.cli in Visual Studio to build and run the app. + +## Usage + +### .NET apps (WPF, WinForms) + +- Drop in an .exe from your binaries folder to add debug identity to it. The app will find the .csproj for the .exe (ie. if your .exe is in the /bin folder, the app will find the parent .csproj and create Assets and appxmanifest in that location). The .exe will be granted app identity via external location (sparse) packaging. +- Drop in a folder, and that app will be packaged into an MSIX + +### Python + +- This is currently a feature we are experimenting with. Currently, python files/scripts (.py) are supported or entire folders for packaging + +### MSIX + +- Drop in an MSIX and a cert will be created, installed and registered locally + +## Feedback for this Experimental App + +Please note that this app is experimental and may have issues as we gather feedback on the functionality, usefulness and value of the UI-based solution. If you see value or issues in this app, please let us know: + +- [File an issue](https://github.com/microsoft/WinAppCli/issues): please ensure that you are not filing a duplicate issue or bug +- Send any feedback to : Do you love this tool? Are there features or fixes you want to see? Let us know! + +The app will add functionality for Electron and mirror the CLI going forward depending on user feedback. diff --git a/docs/guides/cpp.md b/docs/guides/cpp.md index e71ed31f..6b1cab31 100644 --- a/docs/guides/cpp.md +++ b/docs/guides/cpp.md @@ -153,19 +153,24 @@ You can open `appxmanifest.xml` to further customize properties like the display ## 5. Debug with Identity -To test features that require identity (like Notifications) without fully packaging the app, you can use `winapp run`. This registers a loose layout package (just like a real MSIX install) and launches the app in one step. +To test features that require identity (like Notifications) without fully packaging the app, you can use `winapp create-debug-identity`. This applies a temporary identity to your executable using the manifest we just generated. 1. **Build the executable**: ```powershell cmake --build build --config Debug ``` -2. **Run with identity**: +2. **Apply Debug Identity**: + Run the following command on your built executable: ```powershell - winapp run .\build\Debug --with-alias + winapp create-debug-identity .\build\Debug\cpp-app.exe ``` -The `--with-alias` flag launches the app via its execution alias so console output stays in the current terminal. This requires a `uap5:ExecutionAlias` in the manifest — you can add one with `winapp manifest add-alias`. +3. **Run the Executable**: + Run the executable directly: + ```powershell + .\build\Debug\cpp-app.exe + ``` You should now see output similar to: ``` @@ -173,16 +178,23 @@ Package Family Name: cpp-app_12345abcde ``` This confirms your app is running with a valid package identity! -### Alternative: Sparse package identity +### Automating Debug Identity (Optional) -If you need sparse package behavior specifically (identity without copying files), you can use `create-debug-identity` instead: +To streamline your development workflow, you can configure CMake to automatically apply debug identity after building in Debug configuration. Add this to your `CMakeLists.txt`: -```powershell -winapp create-debug-identity .\build\Debug\cpp-app.exe -.\build\Debug\cpp-app.exe +```cmake +# Add a post-build command to apply debug identity in Debug builds +add_custom_command(TARGET cpp-app POST_BUILD + COMMAND $<$:winapp> + $<$:create-debug-identity> + $<$:$> + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND_EXPAND_LISTS + COMMENT "Applying debug identity to executable..." +) ``` -> **Tip:** For advanced debugging workflows (attaching debuggers, IDE setup, startup debugging), see the [Debugging Guide](../debugging.md). +With this configuration, simply running `cmake --build build --config Debug` will automatically apply the debug identity, and you can immediately run the executable with identity without the manual step. ## 6. Using Windows App SDK (Optional) @@ -209,6 +221,15 @@ target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib) # Add Windows App SDK include directory target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include) +# Add post-build command to apply debug identity in Debug builds +add_custom_command(TARGET cpp-app POST_BUILD + COMMAND $<$:winapp> + $<$:create-debug-identity> + $<$:$> + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND_EXPAND_LISTS + COMMENT "Applying debug identity to executable..." +) ``` ### Update main.cpp @@ -258,7 +279,7 @@ Rebuild the application with the Windows App SDK headers: ```powershell cmake --build build --config Debug -winapp run .\build\Debug --with-alias +.\build\Debug\cpp-app.exe ``` You should now see output like: @@ -369,6 +390,15 @@ target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib) # Add Windows App SDK include directory target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include) +# Add a post-build command to apply debug identity in Debug builds +add_custom_command(TARGET cpp-app POST_BUILD + COMMAND $<$:winapp> + $<$:create-debug-identity> + $<$:$> + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND_EXPAND_LISTS + COMMENT "Applying debug identity to executable..." +) ``` With this setup: diff --git a/docs/guides/dotnet.md b/docs/guides/dotnet.md index 72c4b768..9e3341ad 100644 --- a/docs/guides/dotnet.md +++ b/docs/guides/dotnet.md @@ -163,8 +163,6 @@ If you prefer not to use the NuGet package, you can add a custom MSBuild target With this configuration, `dotnet build` applies the debug identity and you can run the executable directly. Note that `dotnet run` may rebuild and overwrite the identity, so run the exe manually after building. -> **Tip:** For advanced debugging workflows (attaching debuggers, IDE setup, startup debugging), see the [Debugging Guide](../debugging.md). - ## 6. Using Windows App SDK If you ran `winapp init` (Step 4), `Microsoft.WindowsAppSDK` was already added as a NuGet package reference to your `.csproj`. If you skipped SDK setup during init, or need to add it manually, run: diff --git a/docs/guides/flutter.md b/docs/guides/flutter.md index 37248239..934d81a9 100644 --- a/docs/guides/flutter.md +++ b/docs/guides/flutter.md @@ -207,16 +207,16 @@ You can open `appxmanifest.xml` to further customize properties like the display ## 5. Debug with Identity -To test features that require identity (like Notifications) without fully packaging the app, you can use `winapp run`. This registers a loose layout package (just like a real MSIX install) and launches the app in one step. +To test features that require identity (like Notifications) without fully packaging the app, you can use `winapp create-debug-identity`. This applies a temporary identity to your executable using the manifest we just generated. -1. **Build the app**: +1. **Apply Debug Identity**: ```powershell - flutter build windows + winapp create-debug-identity .\build\windows\x64\runner\Release\flutter_app.exe ``` -2. **Run with identity**: +2. **Run the executable**: ```powershell - winapp run .\build\windows\x64\runner\Release + .\build\windows\x64\runner\Release\flutter_app.exe ``` You should now see the app with a green indicator showing: @@ -225,7 +225,7 @@ Package Family Name: flutterapp.debug_xxxxxxxx ``` This confirms your app is running with a valid package identity! -> **Tip:** For advanced debugging workflows (attaching debuggers, IDE setup, startup debugging), see the [Debugging Guide](../debugging.md). +> **Note**: After running `flutter clean` or rebuilding, you'll need to re-run `create-debug-identity` since the executable is replaced. ## 6. Using Windows App SDK (Optional) @@ -359,7 +359,8 @@ Rebuild the application: ```powershell flutter build windows -winapp run .\build\windows\x64\runner\Release +winapp create-debug-identity .\build\windows\x64\runner\Release\flutter_app.exe +.\build\windows\x64\runner\Release\flutter_app.exe ``` You should now see output like: diff --git a/docs/guides/rust.md b/docs/guides/rust.md index 4f4b9cfc..6d73db65 100644 --- a/docs/guides/rust.md +++ b/docs/guides/rust.md @@ -93,7 +93,6 @@ When prompted: - **Package name**: Press Enter to accept the default (rust-app) - **Publisher name**: Press Enter to accept the default or enter your name - **Version**: Press Enter to accept 1.0.0.0 -- **Description**: Press Enter to accept the default or enter a description - **Setup SDKs**: Select "Do not setup SDKs" This command will: @@ -157,8 +156,6 @@ Package Family Name: rust-app_12345abcde ``` This confirms your app is running with a valid package identity! -> **Tip:** For advanced debugging workflows (attaching debuggers, IDE setup, startup debugging), see the [Debugging Guide](../debugging.md). - ## 6. Package with MSIX Once you're ready to distribute your app, you can package it as an MSIX using the same manifest. diff --git a/docs/guides/tauri.md b/docs/guides/tauri.md index e337df86..c9235bd0 100644 --- a/docs/guides/tauri.md +++ b/docs/guides/tauri.md @@ -152,21 +152,21 @@ You can open `appxmanifest.xml` to further customize properties like the display ## 4. Debug with Identity -To debug with identity, we need to build the Rust backend and run it with `winapp run`. Since `npm run tauri dev` manages the process lifecycle, it's harder to inject the identity there. Instead, we'll create a custom script. +To debug with identity, we need to build the Rust backend, apply the debug identity to the executable, and then run it directly. Since `npm run tauri dev` manages the process lifecycle, it's harder to inject the identity there. Instead, we'll create a custom script. 1. **Add Script**: Open `package.json` and add a new script `tauri:dev:withidentity`: ```json "scripts": { "tauri": "tauri", - "tauri:dev:withidentity": "cargo build --manifest-path src-tauri/Cargo.toml && (if not exist dist mkdir dist) && copy /Y src-tauri\\target\\debug\\tauri-app.exe dist\\ >nul && winapp run .\\dist" + "tauri:dev:withidentity": "cargo build --manifest-path src-tauri/Cargo.toml && winapp create-debug-identity src-tauri/target/debug/tauri-app.exe && .\\src-tauri\\target\\debug\\tauri-app.exe" } ``` **What this script does:** * `cargo build ...`: Recompiles the Rust backend. - * `copy ... dist\\`: Stages just the exe into a `dist` folder (the `target\debug` folder is very large and contains intermediate build artifacts that aren't part of your app). - * `winapp run .\\dist`: Registers a loose layout package (just like a real MSIX install) and launches the app. + * `winapp create-debug-identity ...`: Applies the temporary identity from your `appxmanifest.xml` to the built executable. + * `...tauri-app.exe`: Runs the executable directly. 2. **Run the Script**: @@ -176,26 +176,24 @@ To debug with identity, we need to build the Rust backend and run it with `winap You should now see the app open and display a "Package family name", confirming it is running with identity! You can now start using and debugging APIs that require package identity, such as Notifications or the new AI APIs like Phi Silica. -> **Tip:** For advanced debugging workflows (attaching debuggers, IDE setup, startup debugging), see the [Debugging Guide](../debugging.md). - ## 5. Package with MSIX Once you're ready to distribute your app, you can package it as an MSIX which will provide the package identity to your application. -First, add a `pack:msix` script to your `package.json`: +### Build Release +Build your application in release mode: -```json -"scripts": { - "tauri": "tauri", - "tauri:dev:withidentity": "...", - "pack:msix": "npm run tauri -- build && (if not exist dist mkdir dist) && copy /Y src-tauri\\target\\release\\tauri-app.exe dist\\ >nul && winapp pack .\\dist --cert .\\devcert.pfx" -} +```powershell +npm run tauri build ``` -**What this script does:** -* `npm run tauri -- build`: Builds the Rust backend in release mode. -* `copy ... dist\\`: Stages just the exe into a `dist` folder (the `target\release` folder is very large and contains intermediate build artifacts that aren't part of your app). -* `winapp pack .\\dist --cert .\\devcert.pfx`: Packages and signs the app as MSIX. +### Prepare Package Directory +Create a directory to hold your package files and copy your release executable. + +```powershell +mkdir dist +copy .\src-tauri\target\release\tauri-app.exe .\dist\ +``` ### Generate a Development Certificate @@ -205,10 +203,13 @@ Before packaging, you need a development certificate for signing. Generate one i winapp cert generate --if-exists skip ``` -### Build, Stage, and Pack +### Sign and Pack + +Now you can package and sign: ```powershell -npm run pack:msix +# package and sign the app with the generated certificate +winapp pack .\dist --cert .\devcert.pfx ``` > Note: The `pack` command automatically uses the appxmanifest.xml from your current directory and copies it to the target folder before packaging. The generated .msix file will be in the current directory. diff --git a/docs/npm-usage.md b/docs/npm-usage.md index dc5319c8..a75b1802 100644 --- a/docs/npm-usage.md +++ b/docs/npm-usage.md @@ -72,7 +72,7 @@ function certGenerate(options?: CertGenerateOptions): Promise | `ifExists` | `IfExists \| undefined` | No | Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) | | `install` | `boolean \| undefined` | No | Install the certificate to the local machine store after generation | | `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml or Package.appxmanifest file to extract publisher information from | +| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml file to extract publisher information from | | `output` | `string \| undefined` | No | Output path for the generated PFX file | | `password` | `string \| undefined` | No | Password for the generated PFX file | | `publisher` | `string \| undefined` | No | Publisher name for the generated certificate. If not specified, will be inferred from manifest. | @@ -208,26 +208,6 @@ function init(options?: InitOptions): Promise --- -### `manifestAddAlias()` - -Add an execution alias (uap5:AppExecutionAlias) to an appxmanifest.xml. This allows launching the packaged app from the command line by typing the alias name. By default, the alias is inferred from the Executable attribute (e.g. $targetnametoken$.exe becomes $targetnametoken$.exe alias). - -```typescript -function manifestAddAlias(options?: ManifestAddAliasOptions): Promise -``` - -**Options:** - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `appId` | `string \| undefined` | No | Application Id to add the alias to (default: first Application element) | -| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | -| `name` | `string \| undefined` | No | Alias name (e.g. 'myapp.exe'). Default: inferred from the Executable attribute in the manifest. | - -*Also accepts [CommonOptions](#commonoptions) (`quiet`, `verbose`, `cwd`).* - ---- - ### `manifestGenerate()` Create appxmanifest.xml without full project setup. Use when you only need a manifest and image assets (no SDKs, no certificate). For full setup, use 'init' instead. Templates: 'packaged' (full MSIX), 'sparse' (desktop app needing Windows APIs). @@ -266,9 +246,8 @@ function manifestUpdateAssets(options: ManifestUpdateAssetsOptions): Promise @@ -322,32 +301,6 @@ function restore(options?: RestoreOptions): Promise --- -### `run()` - -Creates packaged layout, registers the Application, and launches the packaged application. - -```typescript -function run(options: RunOptions): Promise -``` - -**Options:** - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `inputFolder` | `string` | Yes | Input folder containing the app to run | -| `args` | `string \| undefined` | No | Command-line arguments to pass to the application | -| `debugOutput` | `boolean \| undefined` | No | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | -| `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) | -| `noLaunch` | `boolean \| undefined` | No | Only create the debug identity and register the package without launching the application | -| `outputAppxDirectory` | `string \| undefined` | No | Output directory for the loose layout package. If not specified, a directory named AppX inside the input-folder directory will be used. | -| `unregisterOnExit` | `boolean \| undefined` | No | Unregister the development package after the application exits. Only removes packages registered in development mode. | -| `withAlias` | `boolean \| undefined` | No | Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a uap5:ExecutionAlias in the manifest. Use "winapp manifest add-alias" to add an execution alias to the manifest. | - -*Also accepts [CommonOptions](#commonoptions) (`quiet`, `verbose`, `cwd`).* - ---- - ### `sign()` Code-sign an MSIX package or executable. Example: winapp sign ./app.msix ./devcert.pfx. Use --timestamp for production builds to remain valid after cert expires. The 'package' command can sign automatically with --cert. @@ -405,26 +358,6 @@ function tool(options?: ToolOptions): Promise --- -### `unregister()` - -Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity'). - -```typescript -function unregister(options?: UnregisterOptions): Promise -``` - -**Options:** - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `force` | `boolean \| undefined` | No | Skip the install-location directory check and unregister even if the package was registered from a different project tree | -| `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from current directory) | - -*Also accepts [CommonOptions](#commonoptions) (`quiet`, `verbose`, `cwd`).* - ---- - ### `update()` Check for and install newer SDK versions. Updates winapp.yaml with latest versions and reinstalls packages. Requires existing winapp.yaml (created by 'init'). Use --setup-sdks preview for preview SDKs. To reinstall current versions without updating, use 'restore' instead. @@ -755,7 +688,7 @@ type ManifestTemplates = "packaged" | "sparse" | `ifExists` | `IfExists \| undefined` | No | Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) | | `install` | `boolean \| undefined` | No | Install the certificate to the local machine store after generation | | `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml or Package.appxmanifest file to extract publisher information from | +| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml file to extract publisher information from | | `output` | `string \| undefined` | No | Output path for the generated PFX file | | `password` | `string \| undefined` | No | Password for the generated PFX file | | `publisher` | `string \| undefined` | No | Publisher name for the generated certificate. If not specified, will be inferred from manifest. | @@ -836,17 +769,6 @@ type ManifestTemplates = "packaged" | "sparse" | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | -### `ManifestAddAliasOptions` - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `appId` | `string \| undefined` | No | Application Id to add the alias to (default: first Application element) | -| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | -| `name` | `string \| undefined` | No | Alias name (e.g. 'myapp.exe'). Default: inferred from the Executable attribute in the manifest. | -| `quiet` | `boolean \| undefined` | No | Suppress progress messages. | -| `verbose` | `boolean \| undefined` | No | Enable verbose output. | -| `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | - ### `ManifestGenerateOptions` | Property | Type | Required | Description | @@ -868,9 +790,8 @@ type ManifestTemplates = "packaged" | "sparse" | Property | Type | Required | Description | |----------|------|----------|-------------| -| `imagePath` | `string` | Yes | Path to source image file (SVG, PNG, ICO, JPG, BMP, GIF) | -| `lightImage` | `string \| undefined` | No | Path to source image for light theme variants (SVG, PNG, ICO, JPG, BMP, GIF) | -| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | +| `imagePath` | `string` | Yes | Path to source image file | +| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml file (default: search current directory) | | `quiet` | `boolean \| undefined` | No | Suppress progress messages. | | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | @@ -905,23 +826,6 @@ type ManifestTemplates = "packaged" | "sparse" | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | -### `RunOptions` - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `inputFolder` | `string` | Yes | Input folder containing the app to run | -| `args` | `string \| undefined` | No | Command-line arguments to pass to the application | -| `debugOutput` | `boolean \| undefined` | No | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | -| `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) | -| `noLaunch` | `boolean \| undefined` | No | Only create the debug identity and register the package without launching the application | -| `outputAppxDirectory` | `string \| undefined` | No | Output directory for the loose layout package. If not specified, a directory named AppX inside the input-folder directory will be used. | -| `unregisterOnExit` | `boolean \| undefined` | No | Unregister the development package after the application exits. Only removes packages registered in development mode. | -| `withAlias` | `boolean \| undefined` | No | Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a uap5:ExecutionAlias in the manifest. Use "winapp manifest add-alias" to add an execution alias to the manifest. | -| `quiet` | `boolean \| undefined` | No | Suppress progress messages. | -| `verbose` | `boolean \| undefined` | No | Enable verbose output. | -| `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | - ### `SignOptions` | Property | Type | Required | Description | @@ -952,17 +856,6 @@ type ManifestTemplates = "packaged" | "sparse" | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | -### `UnregisterOptions` - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `force` | `boolean \| undefined` | No | Skip the install-location directory check and unregister even if the package was registered from a different project tree | -| `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from current directory) | -| `quiet` | `boolean \| undefined` | No | Suppress progress messages. | -| `verbose` | `boolean \| undefined` | No | Enable verbose output. | -| `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | - ### `UpdateOptions` | Property | Type | Required | Description | diff --git a/docs/usage.md b/docs/usage.md index 73e3af4a..21be454f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -204,7 +204,7 @@ winapp pack ./dist --executable MyApp.exe Create app identity for debugging using [sparse packaging](https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps). The exe stays in its original location — Windows associates identity with it via `Add-AppxPackage -ExternalLocation`. -> **When to use this vs `winapp run`:** Use `create-debug-identity` when the exe is **separate from your app code** (e.g., Electron apps where `electron.exe` is in `node_modules`), or when specifically testing sparse package behavior. For most frameworks where the exe is in your build output folder, use [`winapp run`](#run) instead — it registers a full loose layout package and launches the app. See the [Debugging Guide](debugging.md) for a full comparison. +> **When to use this vs `winapp run`:** Use `create-debug-identity` when the exe is **separate from your app code** (e.g., Electron apps where `electron.exe` is in `node_modules`), or when specifically testing sparse package behavior. For most frameworks where the exe is in your build output folder, use [`winapp run`](#run) instead — it registers a full loose layout package and launches the app. ```bash winapp create-debug-identity [entrypoint] [options] @@ -307,9 +307,9 @@ winapp manifest generate ./src --package-name MyApp --publisher-name "CN=My Comp ### run -Create a loose layout package from a build output folder, register it with Windows, and launch the application — simulating a full MSIX install for debugging. Returns the process ID for debugger attachment. +Create a loose layout package from a build output folder, register it with Windows via `Add-AppxPackage`, and launch the application — simulating a full MSIX install for debugging. Returns the process ID for debugger attachment. -> **This is the preferred command for debugging with package identity** for most frameworks (.NET, C++, Rust, Flutter, Tauri). Unlike [`create-debug-identity`](#create-debug-identity) which registers a sparse package for a single exe, `winapp run` registers the entire folder as a loose layout package, just like a real MSIX install. See the [Debugging Guide](debugging.md) for common debugging workflows. +> **This is the preferred command for debugging with package identity** for most frameworks (.NET, C++, Rust, Flutter, Tauri). Unlike [`create-debug-identity`](#create-debug-identity) which registers a sparse package for a single exe, `winapp run` registers the entire folder as a loose layout package, just like a real MSIX install. ```bash winapp run [options] @@ -325,9 +325,7 @@ winapp run [options] - `--output-appx-directory ` - Output directory for the loose layout package (default: `AppX` inside the input folder directory) - `--args ` - Command-line arguments to pass to the application - `--no-launch` - Only create the debug identity and register the package without launching the application -- `--with-alias` - Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a `uap5:ExecutionAlias` in the manifest (use `winapp manifest add-alias` to add one). Cannot be combined with `--no-launch`. Cannot be combined with `--json`. -- `--debug-output` - Capture `OutputDebugString` messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use `--no-launch` instead if you need to attach a different debugger. Cannot be combined with `--no-launch`. Cannot be combined with `--json`. -- `--unregister-on-exit` - Unregister the development package after the application exits. Only removes packages registered in development mode. Cannot be combined with `--no-launch`. +- `--with-alias` - Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a `uap5:ExecutionAlias` in the manifest (use `winapp manifest add-alias` to add one). Cannot be combined with `--no-launch`. **What it does:** @@ -354,74 +352,6 @@ winapp run ./bin/Debug --no-launch # Launch via execution alias (console apps run in current terminal) winapp run ./bin/Debug --with-alias - -# Launch and capture OutputDebugString messages and first-chance exceptions -winapp run ./bin/Debug --debug-output - -# Combine with execution alias to debug console apps inline -winapp run ./bin/Debug --with-alias --debug-output - -# Run and automatically clean up registration on exit -winapp run ./bin/Debug --with-alias --unregister-on-exit -``` - -**MSBuild properties (NuGet package):** - -When using the `Microsoft.Windows.SDK.BuildTools.WinApp` NuGet package, `dotnet run` automatically invokes `winapp run`. The following MSBuild properties can be set in your `.csproj` to control behavior: - -| Property | Default | Description | -|----------|---------|-------------| -| `EnableWinAppRunSupport` | `true` | Enable/disable the run support functionality | -| `WinAppLaunchArgs` | (empty) | Arguments to pass to the app on launch | -| `WinAppRunUseExecutionAlias` | `false` | Launch via execution alias instead of AUMID activation | -| `WinAppRunNoLaunch` | `false` | Only register identity without launching | -| `WinAppRunDebugOutput` | `false` | Capture `OutputDebugString` messages and first-chance exceptions. Only one debugger can attach at a time (prevents VS/VS Code). Use `WinAppRunNoLaunch` instead to attach a different debugger. | - -```xml - - true - true - -``` - ---- - -### unregister - -Unregister a sideloaded development package. Only removes packages that were registered in development mode (e.g., via `winapp run` or `create-debug-identity`). Store-installed or MSIX-installed packages are never removed. - -```bash -winapp unregister [options] -``` - -**Options:** - -- `--manifest ` - Path to AppxManifest.xml (default: auto-detect from current directory) -- `--force` - Skip the install-location directory check and unregister even if the package was registered from a different project tree -- `--json` - Format output as JSON - -**What it does:** - -- Reads the package name from the manifest -- Searches for both `{name}` and `{name}.debug` packages (the debug variant is created by `create-debug-identity`) -- Verifies each package was registered in development mode (`IsDevelopmentMode == true`) -- Verifies the package's install location is under the current directory tree (unless `--force`) -- Unregisters matching packages - -**Examples:** - -```bash -# Unregister from current directory (auto-detects manifest) -winapp unregister - -# Unregister with explicit manifest -winapp unregister --manifest ./appxmanifest.xml - -# Force unregister even if registered from a different project tree -winapp unregister --force - -# JSON output for scripting -winapp unregister --json ``` --- @@ -468,32 +398,28 @@ winapp manifest update-assets [options] **Arguments:** -- `image-path` - Path to source image file (PNG, JPG, SVG, ICO, GIF, BMP, etc.) +- `image-path` - Path to source image file (PNG, JPG, GIF, etc.) **Options:** - `--manifest ` - Path to AppxManifest.xml file (default: search current directory) -- `--light-image ` - Path to a separate source image for light theme variants **Description:** -Takes a single source image and generates a comprehensive set of MSIX image assets based on the manifest's asset references: - -For each asset referenced in the manifest: -- **5 scale variants** — base (no suffix), `.scale-125`, `.scale-150`, `.scale-200`, `.scale-400` - -For the app icon (Square44x44Logo / AppList, 44×44 base): -- **14 plated targetsize variants** — `.targetsize-{16,20,24,30,32,36,40,48,60,64,72,80,96,256}` -- **14 unplated targetsize variants** — `.targetsize-{size}_altform-unplated` - -Additionally: -- **app.ico** — Multi-resolution ICO file (16, 24, 32, 48, 256) for shell integration. If an existing `.ico` file is found in the assets directory (e.g. `AppIcon.ico` from a project template), it is replaced in-place rather than creating a duplicate - -With `--light-image`: -- **Light theme targetsize variants** — `.targetsize-{size}_altform-lightunplated` (app icon) -- **Light theme scale variants** — `.scale-{factor}_altform-colorful_theme-light` (tiles, store logo) - -**SVG support:** SVG files are fully supported as source images. They are rendered as vectors directly at each target size, producing pixel-perfect results at all resolutions. +Takes a single source image and automatically generates all 12 required MSIX image assets at the correct dimensions: + +- Square44x44Logo.png (44×44) +- Square44x44Logo.scale-200.png (88×88) +- Square44x44Logo.targetsize-24_altform-unplated.png (24×24) +- Square150x150Logo.png (150×150) +- Square150x150Logo.scale-200.png (300×300) +- Wide310x150Logo.png (310×150) +- Wide310x150Logo.scale-200.png (620×300) +- SplashScreen.png (620×300) +- SplashScreen.scale-200.png (1240×600) +- StoreLogo.png (50×50) +- LockScreenLogo.png (24×24) +- LockScreenLogo.scale-200.png (48×48) The command scales images proportionally while maintaining aspect ratio, centering them with transparent backgrounds when needed. Assets are saved to the `Assets` directory relative to the manifest location. @@ -503,18 +429,9 @@ The command scales images proportionally while maintaining aspect ratio, centeri # Generate assets with auto-detected manifest winapp manifest update-assets mylogo.png -# Use an SVG source for best quality at all sizes -winapp manifest update-assets mylogo.svg - # Specify manifest location explicitly winapp manifest update-assets mylogo.png --manifest ./dist/appxmanifest.xml -# Generate light theme variants from a separate image -winapp manifest update-assets mylogo.png --light-image mylogo-light.png - -# Use the same image for both (generates all MRT light theme qualifiers) -winapp manifest update-assets mylogo.png --light-image mylogo.png - # With verbose output winapp manifest update-assets mylogo.png --verbose ``` diff --git a/llms.txt b/llms.txt index 995916b0..b08fa03f 100644 --- a/llms.txt +++ b/llms.txt @@ -26,7 +26,6 @@ The plugin is at `.github/plugin/` and includes: - [CLI Schema](https://raw.githubusercontent.com/microsoft/WinAppCli/main/docs/cli-schema.json): Machine-readable JSON schema of all commands, options, and types - [Usage Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/usage.md): Full documentation for humans -- [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md): `winapp run` vs `create-debug-identity`, IDE setup, and debugging workflows ## Guides diff --git a/plugin.json b/plugin.json deleted file mode 100644 index 2e09a949..00000000 --- a/plugin.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "winappcli", - "description": "Windows app development, packaging, and distribution. Helps with creating Windows installers (MSIX), code signing, certificates, Windows SDK and Windows App SDK setup, package identity for Windows APIs (push notifications, background tasks, share target), appxmanifest authoring, and Microsoft Store distribution. Supports Electron, .NET, C++, Rust, Flutter, and Tauri apps.", - "version": "0.2.2", - "author": { - "name": "Microsoft", - "url": "https://github.com/microsoft/WinAppCli" - }, - "license": "MIT", - "keywords": [ - "windows", - "msix", - "packaging", - "installer", - "identity", - "electron", - "winapp", - "windows app sdk", - "windows sdk", - "appxmanifest.xml", - "appxmanifest", - "code signing", - "certificate", - "pfx", - "signtool", - "makeappx", - "windows store", - "microsoft store", - "windows distribution", - "windows packaging", - "windows installer", - "package identity", - "sparse package", - "push notifications", - "background tasks", - "share target", - "startup task", - "tauri", - "flutter", - "rust", - "dotnet", - "wpf", - "winforms", - "cpp", - "cppwinrt", - "windows api", - "winrt", - "uwp", - "desktop app", - "win32" - ], - "category": "windows-development", - "tags": [ - "windows", - "msix", - "packaging", - "installer", - "desktop", - "notifications", - "sdk", - "signing", - "certificate", - "distribution" - ], - "agents": ".github/plugin/agents/", - "skills": ".github/plugin/skills/winapp-cli/" -} diff --git a/samples/cpp-app-winui/.gitignore b/samples/cpp-app-winui/.gitignore deleted file mode 100644 index 6b6ab294..00000000 --- a/samples/cpp-app-winui/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ - -# Development certificate -devcert.pfx - -# Windows SDK packages and generated files -.winapp - -.winapp-tools \ No newline at end of file diff --git a/samples/cpp-app-winui/Assets/LockScreenLogo.png b/samples/cpp-app-winui/Assets/LockScreenLogo.png deleted file mode 100644 index 180ad62f876530c13fba8bcfa18263686cf5441d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;w=>VS)*ZnZyUf=^`LTN8M$7Mie zoFzei!Ihp#4BMBUvHyCZP-HGpu)x#BF+}2WvVj{D&oiEafHMgfR%)hWq3 z(=vMIHZS72$sn?LqONNp-zMk4Jq?d6Q}P$Ha1;eFGT4;pS$_V;{}O07gQu&X%Q~lo FCIDy-QN{oO diff --git a/samples/cpp-app-winui/Assets/LockScreenLogo.scale-200.png b/samples/cpp-app-winui/Assets/LockScreenLogo.scale-200.png deleted file mode 100644 index 7440f0d4bf7c7e26e4e36328738c68e624ee851e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FqV6|IEGZ*x-#9g>~Mkr+x6^F zy~CDX2QIMs&Gcs3RnRBoxBA!*(Mfw0KTCYuYk0WlEIV>qBmPl! zq4ukrvfADX@#p8fbLY(H47N+k`FZ(FZh?cDro7>{8mkBO3>^oaIx`3!Jl)Qq)HI!+ z(S=1{o~eT)&W^=Ea8C`-17(Jv5(nHFJ{dOjGdxLVkY_y6&S1whfuFI4MM0kF0f&cO zPDVpV%nz;Id$>+0Ga5e9625-JcI)oq=#Pa3p^>8BB}21BUw@eN!-6@w%X+^`+Vn?! zryu|3T>kVWNBYyBc=7Y6H#s1Ah!OI_nezW zXTqOdkv2Az6KKBV=$yHdF^R3Fqw(TZEoNSZX>reXJ#bwX42%f|Pgg&ebxsLQ010xn AssI20 diff --git a/samples/cpp-app-winui/Assets/SplashScreen.png b/samples/cpp-app-winui/Assets/SplashScreen.png deleted file mode 100644 index a634fef0fc2ba26e2910c5e8c41f8bf6e1bb994f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1579 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" - COMMENT "Copying Windows App SDK runtime DLLs..." - ) -endif() diff --git a/samples/cpp-app-winui/README.md b/samples/cpp-app-winui/README.md deleted file mode 100644 index f51003f8..00000000 --- a/samples/cpp-app-winui/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# C++ WinUI 3 Sample Application - -This sample demonstrates a WinUI 3 desktop window built entirely in C++ with CMake - no XAML files, no MIDL, no MSBuild. The UI is constructed programmatically using C++/WinRT projections. - -For the console-only version, see the [C++ Sample](../cpp-app/). - -## What This Sample Shows - -- **Programmatic WinUI 3** — Window, Button, TextBlock, StackPanel created in code -- Using Windows App Model APIs to retrieve package identity -- Using `winapp run` to run the app packaged with MSIX identity -- MSIX packaging with app manifest and assets -- CMake integration with Windows App SDK headers and runtime DLLs - -## Prerequisites - -- Visual Studio with C++ Desktop development workload -- CMake 3.20 or later -- Windows App SDK runtime installed -- WinApp CLI installed - -## Building and Running - -### Build the Application - -```powershell -cmake -B build -cmake --build build --config Debug -``` - -### Run without Identity - -```powershell -.\build\Debug\cpp-app-winui.exe -``` - -Click the **Check Identity** button — it will show "Not packaged". - -### Run with Identity (Debug) - -```powershell -winapp run .\build\Debug -``` - -This registers a loose layout package (just like a real MSIX install), then launches the WinUI 3 window. Click the **Check Identity** button to see the Package Family Name. - -## Technical Notes - -This sample uses a non-standard approach to WinUI 3: all UI is built programmatically in C++ without XAML files. This avoids the need for XAML compilation (which requires MSBuild), but means: - -- Controls use basic styling (no `XamlControlsResources`) -- The `App` class implements a minimal `IXamlMetadataProvider` (returns null for all types) -- `MddBootstrapInitialize2` handles Windows App SDK initialization for both packaged and unpackaged execution diff --git a/samples/cpp-app-winui/appxmanifest.xml b/samples/cpp-app-winui/appxmanifest.xml deleted file mode 100644 index c0b6264b..00000000 --- a/samples/cpp-app-winui/appxmanifest.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - cpp-app-winui - nikolame - Assets\StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/cpp-app-winui/main.cpp b/samples/cpp-app-winui/main.cpp deleted file mode 100644 index f86a55c6..00000000 --- a/samples/cpp-app-winui/main.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Forward-declare bootstrap APIs to avoid header conflicts with C++/WinRT -extern "C" { - HRESULT __stdcall MddBootstrapInitialize2( - UINT32 majorMinorVersion, - PCWSTR versionTag, - PACKAGE_VERSION minVersion, - UINT32 options) noexcept; - void __stdcall MddBootstrapShutdown() noexcept; -} - -// Windows App SDK 1.8 version constants -constexpr UINT32 kWinAppSdkMajorMinor = 0x00010008; -// OnPackageIdentity_NOOP: skip bootstrap when running packaged (via winapp run) -constexpr UINT32 kBootstrapOptions_OnPackageIdentity_NOOP = 0x0010; - -using namespace winrt; -using namespace Microsoft::UI::Xaml; -using namespace Microsoft::UI::Xaml::Controls; - -struct App : ApplicationT -{ - // Minimal IXamlMetadataProvider (no custom XAML types) - Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(Windows::UI::Xaml::Interop::TypeName const&) - { - return nullptr; - } - Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(hstring const&) - { - return nullptr; - } - com_array GetXmlnsDefinitions() - { - return {}; - } - - void OnLaunched(LaunchActivatedEventArgs const&) - { - m_window = Window(); - - auto panel = StackPanel(); - panel.HorizontalAlignment(HorizontalAlignment::Center); - panel.VerticalAlignment(VerticalAlignment::Center); - panel.Spacing(16); - - auto title = TextBlock(); - title.Text(L"WinUI 3 CMAKE C++ Sample"); - title.FontSize(24); - title.HorizontalAlignment(HorizontalAlignment::Center); - - auto infoText = TextBlock(); - infoText.Text(L"Click the button to check package identity"); - infoText.HorizontalAlignment(HorizontalAlignment::Center); - infoText.TextWrapping(TextWrapping::Wrap); - - auto button = Button(); - button.Content(box_value(L"Check Identity")); - button.HorizontalAlignment(HorizontalAlignment::Center); - button.Click([infoText](auto&&, auto&&) { - UINT32 length = 0; - LONG result = GetCurrentPackageFamilyName(&length, nullptr); - - if (result == ERROR_INSUFFICIENT_BUFFER) { - std::wstring familyName(length, L'\0'); - result = GetCurrentPackageFamilyName(&length, familyName.data()); - - if (result == ERROR_SUCCESS) { - familyName.resize(wcslen(familyName.c_str())); - infoText.Text(hstring(L"Package Family Name: ") + hstring(familyName)); - } else { - infoText.Text(L"Error retrieving Package Family Name"); - } - } else { - infoText.Text(L"Not packaged \u2014 run with 'winapp run' for identity"); - } - }); - - panel.Children().Append(title); - panel.Children().Append(button); - panel.Children().Append(infoText); - - m_window.Content(panel); - m_window.Title(L"C++ WinUI 3 Sample"); - m_window.Activate(); - } - -private: - Window m_window{ nullptr }; -}; - -int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) -{ - try - { - // COM must be initialized before the bootstrap - init_apartment(apartment_type::single_threaded); - - // Initialize the Windows App SDK for unpackaged execution. - // When running packaged (winapp run), OnPackageIdentity_NOOP skips this. - PACKAGE_VERSION minVer{}; - minVer.Version = 0x1F4002A304760000u; - HRESULT hr = MddBootstrapInitialize2( - kWinAppSdkMajorMinor, L"", minVer, - kBootstrapOptions_OnPackageIdentity_NOOP); - if (FAILED(hr)) - { - MessageBoxW(nullptr, - L"Failed to initialize Windows App SDK.\n" - L"Make sure the Windows App SDK runtime is installed.", - L"WinUI 3 Error", MB_ICONERROR); - return static_cast(hr); - } - - Application::Start([](auto&&) { make(); }); - - MddBootstrapShutdown(); - } - catch (hresult_error const& ex) - { - MessageBoxW(nullptr, ex.message().c_str(), L"WinUI 3 Error", MB_ICONERROR); - return static_cast(ex.code()); - } - return 0; -} diff --git a/samples/cpp-app-winui/winapp.yaml b/samples/cpp-app-winui/winapp.yaml deleted file mode 100644 index 9f7e3925..00000000 --- a/samples/cpp-app-winui/winapp.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packages: - - name: Microsoft.Windows.CppWinRT - version: 2.0.250303.1 - - name: Microsoft.Windows.SDK.BuildTools - version: 10.0.26100.7705 - - name: Microsoft.WindowsAppSDK - version: 1.8.260317003 - - name: Microsoft.Windows.ImplementationLibrary - version: 1.0.260126.7 - - name: Microsoft.Windows.SDK.CPP - version: 10.0.26100.7705 - - name: Microsoft.Windows.SDK.CPP.x64 - version: 10.0.26100.7705 - - name: Microsoft.Windows.SDK.CPP.arm64 - version: 10.0.26100.7705 diff --git a/samples/cpp-app/CMakeLists.txt b/samples/cpp-app/CMakeLists.txt index e3243138..8ad3ac94 100644 --- a/samples/cpp-app/CMakeLists.txt +++ b/samples/cpp-app/CMakeLists.txt @@ -67,3 +67,13 @@ target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib) # Add Windows App SDK include directory target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include) + +# Add a post-build command to apply debug identity in Debug builds +add_custom_command(TARGET cpp-app POST_BUILD + COMMAND $<$:winapp> + $<$:create-debug-identity> + $<$:$> + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND_EXPAND_LISTS + COMMENT "Applying debug identity to executable..." +) \ No newline at end of file diff --git a/samples/cpp-app/README.md b/samples/cpp-app/README.md index b0ef2ce3..8ffb2063 100644 --- a/samples/cpp-app/README.md +++ b/samples/cpp-app/README.md @@ -8,23 +8,16 @@ For a complete step-by-step guide, see the [C++ Getting Started Guide](../../doc - Basic C++ console application built with CMake - Using Windows App Model APIs to retrieve package identity -- Using `winapp run` to run the app packaged (registers a loose layout package, just like a real MSIX install) +- Configuring CMake to automatically apply debug identity after building in Debug configuration - MSIX packaging with app manifest and assets ## Prerequisites - Visual Studio Native Desktop workload or Visual Studio with C++ development tools - CMake 3.20 or later -- WinApp CLI installed ## Building and Running -### Restore dependencies - -```powershell -winapp restore -``` - ### Build the Application ```powershell @@ -32,20 +25,13 @@ cmake -B build cmake --build build --config Debug ``` -### Run without Identity +### Run -```powershell -.\build\Debug\cpp-app.exe -``` -*Output should be: "Not packaged"* - -### Run with Identity (Debug) +The CMakeLists.txt is configured to automatically apply debug identity when building in Debug configuration. Simply build and run: ```powershell -winapp run .\build\Debug --with-alias +cmake --build build --config Debug +.\build\Debug\cpp-app.exe ``` -This registers a loose layout package (just like a real MSIX install), then launches the app via its execution alias so console output stays in the current terminal. - -*Output should show the Package Family Name.* -> **Note:** The `--with-alias` flag requires a `uap5:ExecutionAlias` in the manifest. This sample's `appxmanifest.xml` already includes one. You can add one to an appxmanifest.xml with `winapp manifest add-alias`. +Output: `Package Family Name: cpp-app_12345abcde` diff --git a/samples/cpp-app/appxmanifest.xml b/samples/cpp-app/appxmanifest.xml index 368b8fbd..bb0a4b1d 100644 --- a/samples/cpp-app/appxmanifest.xml +++ b/samples/cpp-app/appxmanifest.xml @@ -9,8 +9,7 @@ xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" - xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" - IgnorableNamespaces="uap uap2 uap3 rescap desktop desktop6 uap10 uap5"> + IgnorableNamespaces="uap uap2 uap3 rescap desktop desktop6 uap10"> - - - - - - - diff --git a/samples/cpp-app/winapp.yaml b/samples/cpp-app/winapp.yaml index 9f7e3925..2d5f7ee6 100644 --- a/samples/cpp-app/winapp.yaml +++ b/samples/cpp-app/winapp.yaml @@ -2,14 +2,14 @@ packages: - name: Microsoft.Windows.CppWinRT version: 2.0.250303.1 - name: Microsoft.Windows.SDK.BuildTools - version: 10.0.26100.7705 + version: 10.0.26100.7175 - name: Microsoft.WindowsAppSDK - version: 1.8.260317003 + version: 1.8.251106002 - name: Microsoft.Windows.ImplementationLibrary - version: 1.0.260126.7 + version: 1.0.250325.1 - name: Microsoft.Windows.SDK.CPP - version: 10.0.26100.7705 + version: 10.0.26100.7175 - name: Microsoft.Windows.SDK.CPP.x64 - version: 10.0.26100.7705 + version: 10.0.26100.7175 - name: Microsoft.Windows.SDK.CPP.arm64 - version: 10.0.26100.7705 + version: 10.0.26100.7175 diff --git a/samples/dotnet-app/dotnet-app.csproj b/samples/dotnet-app/dotnet-app.csproj index 3ce9c211..0da23353 100644 --- a/samples/dotnet-app/dotnet-app.csproj +++ b/samples/dotnet-app/dotnet-app.csproj @@ -8,8 +8,6 @@ enable true - - diff --git a/samples/electron-winml/package-lock.json b/samples/electron-winml/package-lock.json index 8eb3f9d4..e7844802 100644 --- a/samples/electron-winml/package-lock.json +++ b/samples/electron-winml/package-lock.json @@ -23,7 +23,7 @@ "@electron-forge/plugin-fuses": "^7.10.2", "@electron/fuses": "^1.8.0", "@microsoft/winappcli": "^0.1.8", - "electron": "39.8.4" + "electron": "39.2.7" } }, "node_modules/@electron-forge/cli": { @@ -1840,9 +1840,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", "dev": true, "license": "MIT", "engines": { @@ -2795,9 +2795,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "39.8.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.4.tgz", - "integrity": "sha512-eXYKxr4y+s31xs78keVJYg+XY20tGQMQzyIhZvc5L0XRDH2Gp08mbeFlbR1OjAeM5h5l/T2JYT2MFK2kYe2fMg==", + "version": "39.2.7", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.2.7.tgz", + "integrity": "sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5869,9 +5869,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { diff --git a/samples/electron-winml/package.json b/samples/electron-winml/package.json index d0a9eca4..11729215 100644 --- a/samples/electron-winml/package.json +++ b/samples/electron-winml/package.json @@ -35,6 +35,6 @@ "@electron-forge/plugin-fuses": "^7.10.2", "@electron/fuses": "^1.8.0", "@microsoft/winappcli": "^0.1.8", - "electron": "39.8.4" + "electron": "39.2.7" } } \ No newline at end of file diff --git a/samples/electron/Assets/AppList.png b/samples/electron/Assets/AppList.png deleted file mode 100644 index 3eb4d21db03b2e72f286d4bee7cdde25be6c4951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774 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 M07*qoM6N<$g6*0zXaE2J diff --git a/samples/electron/Assets/AppList.scale-150.png b/samples/electron/Assets/AppList.scale-150.png deleted file mode 100644 index 39bf5d07459ec5e4fb7b635291aeebbc79500e32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2462 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 diff --git a/samples/electron/Assets/AppList.scale-200.png b/samples/electron/Assets/AppList.scale-200.png deleted file mode 100644 index a162005dc973ba4122da5775990fc6b11fb99d10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3466 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 R|4|^2lY^^$t!+^1{{YiqfNTH& diff --git a/samples/electron/Assets/AppList.scale-400.png b/samples/electron/Assets/AppList.scale-400.png deleted file mode 100644 index d24dcbbe964fd33881da42c24ee6d2e753d8e494..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8370 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 diff --git a/samples/electron/Assets/AppList.targetsize-20_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-20_altform-unplated.png deleted file mode 100644 index 0fabfe2c1e97be776a5841bee2427642e5edb68b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmV;919AL`P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D10zX9K~y+TeU!;h z(@_+KkI}>x3s>%SXIz@NbK{?2VjQq7ohWpG77*hQ6Nx($T^d);l)*v^Rm#{xnJI&U zMw9AJ1#?3TaSVFQeLv)j7QTLLln50Cho})J>2zO5*to%9Q zOA+pbz^JJL6oYxwJam1W-+%uxz#|P~gez4D^Ck&j7vYX8a8isQ+hAG3KS+27n0^1% zzzp!lDBLxs$n=n`0x;viTZrWAKQev+6YgAaJC9eoKz5n8Ple@CzxhQB*XolZpLO7xBW6cx5&Nr z_BGHN%&G7=QPv_b+0;0#Cm=9jnY6rzC`~dt^?3u9|MHC`z9eC9vjFQbXDi`uBOJ{N z%!s(laaJGifR(int{7pD3e46}L3YB_s24ielCyw;#(Cm& zOL3T^Sp^~NjRLcFG+xExR2t_a(&0&)m!L zU?fG@V!OlajfA6#xbR4UeYz|QVQN%wCsC@iQ4!jAhuNN3b?HL^DtcGUa@2jM>aNdE zv$>H5HF3h4pQ@<>=*$ebX|v_?4_08-wzjRyQTLgyAl(`obj_y5NfV%^RiCLfR818? kXQplH&umWx=kDkK0ho-JKTy=P(f|Me07*qoM6N<$f>|D)JOBUy diff --git a/samples/electron/Assets/AppList.targetsize-24.png b/samples/electron/Assets/AppList.targetsize-24.png deleted file mode 100644 index e86ebf14712ea01c780023cf2e117dd68af109a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1083 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 Ghy4$%V~!C3 diff --git a/samples/electron/Assets/AppList.targetsize-256_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-256_altform-unplated.png deleted file mode 100644 index d8f2f2aabeaf26f4bdb5bdbc0d43da8f585fa6d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13555 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 Ghy4$%V~!C3 diff --git a/samples/electron/Assets/AppList.targetsize-30.png b/samples/electron/Assets/AppList.targetsize-30.png deleted file mode 100644 index eee71aaaaf32fdf1b6ebb83491a82faf315bfaee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1306 zcmV+#1?BpQP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1g}X%K~zXfz1PWa zn?)D~@JCcRqjKQH1UnovnbNoRE+>6>;dPKueb_jqRj~)7Va& zrD>BiOPhwaG!(Vsz%mVLiYRT>2R-xIT}%>x>4A~vFmw2R-#7ElJMRZ{udU;wYX)7b z2A!7-I#&!j)Rzs~mkin$4U!iOk_!gOd4q)doI&EeLEAZlwpoMLvj(j*1})Q0Z=N)0 zE*iuq4C3Pkv4TM?Z_t!;=AvWkK=M@Cvd22d8(h5#5)Gu2rJz647~)=v*N> zmx+!gqGOS0zd*Dv5XpHWIY%VU6Nz&~oBAx#dX{LNCR(P5mPw*{l86_H))}Jl%qHxY z3N1gpLw%WOUn1HUiDdcV^F-olV3ugp!)J(=640z(0^$=ytU$!_M3Z`sh>j7laiU?= zz%K88!T$pqJ>$$bWCgDnyS&eXHct~VEu%}canzZMj2PHJ(w^=X)tAO5`BL|633P!r zW{IXTA~Gy^+1NnZ4XU8=7Z~fk&g|N4B9tfM$1cw$1gg?DK8t5ZJf_ntdsG8UweL4-+o#uhT>y;+&{QZEhb?1q?Q$%E|s4`M*nxAHz z;M(}>K40w`Blh*XN4+N$(S+xifh{+6$5um?20s|!7uj_SzxDMT@op~>-UaGC?ud==ZOdsTv}8dPDX z8c9`y?E}W%r@Gp7%P8S1_bLr4BPluPoa)FZdKF08Q!TH)wEn0;?cw!*0X$_EWWgL{ QZvX%Q07*qoM6N<$g60u?xBvhE diff --git a/samples/electron/Assets/AppList.targetsize-30_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-30_altform-unplated.png deleted file mode 100644 index eee71aaaaf32fdf1b6ebb83491a82faf315bfaee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1306 zcmV+#1?BpQP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1g}X%K~zXfz1PWa zn?)D~@JCcRqjKQH1UnovnbNoRE+>6>;dPKueb_jqRj~)7Va& zrD>BiOPhwaG!(Vsz%mVLiYRT>2R-xIT}%>x>4A~vFmw2R-#7ElJMRZ{udU;wYX)7b z2A!7-I#&!j)Rzs~mkin$4U!iOk_!gOd4q)doI&EeLEAZlwpoMLvj(j*1})Q0Z=N)0 zE*iuq4C3Pkv4TM?Z_t!;=AvWkK=M@Cvd22d8(h5#5)Gu2rJz647~)=v*N> zmx+!gqGOS0zd*Dv5XpHWIY%VU6Nz&~oBAx#dX{LNCR(P5mPw*{l86_H))}Jl%qHxY z3N1gpLw%WOUn1HUiDdcV^F-olV3ugp!)J(=640z(0^$=ytU$!_M3Z`sh>j7laiU?= zz%K88!T$pqJ>$$bWCgDnyS&eXHct~VEu%}canzZMj2PHJ(w^=X)tAO5`BL|633P!r zW{IXTA~Gy^+1NnZ4XU8=7Z~fk&g|N4B9tfM$1cw$1gg?DK8t5ZJf_ntdsG8UweL4-+o#uhT>y;+&{QZEhb?1q?Q$%E|s4`M*nxAHz z;M(}>K40w`Blh*XN4+N$(S+xifh{+6$5um?20s|!7uj_SzxDMT@op~>-UaGC?ud==ZOdsTv}8dPDX z8c9`y?E}W%r@Gp7%P8S1_bLr4BPluPoa)FZdKF08Q!TH)wEn0;?cw!*0X$_EWWgL{ QZvX%Q07*qoM6N<$g60u?xBvhE diff --git a/samples/electron/Assets/AppList.targetsize-32.png b/samples/electron/Assets/AppList.targetsize-32.png deleted file mode 100644 index 8500b46bd335ca6cd8955705049288f1bd9dacfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1378 zcmV-o1)chdP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1oufqK~z{ry_d;v zlUEeQPq&r2DEoF%i9evbivAImDz#EcVmpIlC(eZ91W*UWqAFc-hWzaqt>(@E791&%}W>V!!MsqVp=zd4=e>OmwUg z9hZpM3K3f-VoOB3Y>|jA5Yc&JpKOk3yGXQNAX;aMmKmaDnrNPK)->t-;q&XTkqh1_ zKn@biLE4ucAeteff-DmI778J4IY_G@&x0n3rZmx%A|ew+WSnS}C5eVHB9bD)=M3zz zEAm$fZ#eJ#p|b|)xNPjPze;%cob!be60mCQoxc=(mS~(Jf~lRs!=uhuKVo2CyB^$* z;WI>dl5Zbc;E}OEh{z-n@dyu{b-wyx30N`qweJ+(JWbT4n7V$ScON_-Zv|J_N;)P6CkKZc-^4$)eK0$f|^cw zcte`6w@fm7_i-WJ3iz7{o+CnD;BjYGK)-?QYHBRo!Bu1eNglm$tC(%Sx!>;-{sd90 zdrc6*#P;yo0q3vjlYn_+yV_Rp76<>}=+$EG=J)6QfB4R)Q{2^>*Sy!;Po>EPjpHn}ZZbE_iL9^H=v8 z*kMBbycAruy?&hUex2c$qc?bH_%|LJ_?6f0JSygIes|V4_*VNEQ5`QGx!^Tth+v$k z?va35O*p0DYAnrZm*c)s!grSVFwSH1rQd?GMyv<4>@A>2@*OezD#);^W?}k3A2s=^^U+i9nYGOdFem<>2`W=Nn;n=0ySV)!Rfx zA5kmtHjxWn-Q#@z(*|~cR0?{5htCtfVRmQ!DuCa-_n2=+5`?ec1H9Xr6>v%dri@L& za`1eGs~Rd0pnKnQWF~j-9^rv5=l7j7uw%2PS1<6;C{Z<7AfVmeyY93cJSRZ#z-jmI zJCPSKTOOV-ua$9jExIh|S-M5+6{O6Adp>f({ioc2)o}wv(po6I!Rr%5ML*Nm@6kNm z;2Yr)!q-;@UKF|D{*&(C2}o(1DG$%rc7L27eVgH)uclq}F9Y|TaK6f85-_1P${Sp@ zT~^sgRH>Y92QP8#dARSmQjn`!UuJS5Dc~J!?(dCX^f&>qA6BXaDOF-5> zL@kQi)wC{YW6{!}xm}~PrdQ2xbpytZQJQ2lOsJo$rK+o`v8cAIysE;f$SBXNWXFwt kaJq9HyCRow?|0e%0DP@90jV5@egFUf07*qoM6N<$g0Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1oufqK~z{ry_d;v zlUEeQPq&r2DEoF%i9evbivAImDz#EcVmpIlC(eZ91W*UWqAFc-hWzaqt>(@E791&%}W>V!!MsqVp=zd4=e>OmwUg z9hZpM3K3f-VoOB3Y>|jA5Yc&JpKOk3yGXQNAX;aMmKmaDnrNPK)->t-;q&XTkqh1_ zKn@biLE4ucAeteff-DmI778J4IY_G@&x0n3rZmx%A|ew+WSnS}C5eVHB9bD)=M3zz zEAm$fZ#eJ#p|b|)xNPjPze;%cob!be60mCQoxc=(mS~(Jf~lRs!=uhuKVo2CyB^$* z;WI>dl5Zbc;E}OEh{z-n@dyu{b-wyx30N`qweJ+(JWbT4n7V$ScON_-Zv|J_N;)P6CkKZc-^4$)eK0$f|^cw zcte`6w@fm7_i-WJ3iz7{o+CnD;BjYGK)-?QYHBRo!Bu1eNglm$tC(%Sx!>;-{sd90 zdrc6*#P;yo0q3vjlYn_+yV_Rp76<>}=+$EG=J)6QfB4R)Q{2^>*Sy!;Po>EPjpHn}ZZbE_iL9^H=v8 z*kMBbycAruy?&hUex2c$qc?bH_%|LJ_?6f0JSygIes|V4_*VNEQ5`QGx!^Tth+v$k z?va35O*p0DYAnrZm*c)s!grSVFwSH1rQd?GMyv<4>@A>2@*OezD#);^W?}k3A2s=^^U+i9nYGOdFem<>2`W=Nn;n=0ySV)!Rfx zA5kmtHjxWn-Q#@z(*|~cR0?{5htCtfVRmQ!DuCa-_n2=+5`?ec1H9Xr6>v%dri@L& za`1eGs~Rd0pnKnQWF~j-9^rv5=l7j7uw%2PS1<6;C{Z<7AfVmeyY93cJSRZ#z-jmI zJCPSKTOOV-ua$9jExIh|S-M5+6{O6Adp>f({ioc2)o}wv(po6I!Rr%5ML*Nm@6kNm z;2Yr)!q-;@UKF|D{*&(C2}o(1DG$%rc7L27eVgH)uclq}F9Y|TaK6f85-_1P${Sp@ zT~^sgRH>Y92QP8#dARSmQjn`!UuJS5Dc~J!?(dCX^f&>qA6BXaDOF-5> zL@kQi)wC{YW6{!}xm}~PrdQ2xbpytZQJQ2lOsJo$rK+o`v8cAIysE;f$SBXNWXFwt kaJq9HyCRow?|0e%0DP@90jV5@egFUf07*qoM6N<$g0Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1>#9WK~z{rt=LI# zQ&$uR@Ppa~>Y^3y22^$3?|@YG8&q9YDv&twl*D!>I|HG^f*q>VWj8En3k3oR4kU2~ zk8vhK$UuOk6@#c)Aj*AI5Ku%4`B&%OcOB>X+3zLllg{RPaeqGN4(C0h?$vwLb5BtB zT|r&91$C_m>b#|*V_8tgl8X4Ep!kBI*iAF$1-0EUV@^=pbwRDODq5xmwM+?Wo;0H% zC_14ck{1-o35sL|MKXfI9BDz(yr9PPeWLEwm-PK3P|u&Bocj$Aq`GCybku#dtDv!giiePg-Td9h2rk(Htm}RT0jB!f6#v zDfL^!m_f2koEeE(0d+WGG^iS83e@a`$$*+nOw+g;Zy06JijZZJS#iR+%#0(3Atji` zF;FA}3XK>f7cZ}w|D#M%ml$0pjcFWJW80ur2aH3RTBkta0w`L*zN#2YnKY(h1Qbqz zLP>+<*5=(*5#x{<#xzXelkes*_22~#eLD+k%-LXU$|Nx+h{uD&3|itdXm1$3cmp|% ztvv@nhfd6b8ZzY=yE17^D5=JSgC>a2nKFz+nHa;_smq|L_nySO-h7LLCuTsQw7W8C zOlVk*2d)_;PhdWScAFWFKn|asoX5uII|+LA<}D8POe>I^GHFb3NR2Ddf{^FWt}v6J zNCEpA^O$?|Qew72Q=m}FU6~jY8sbu^G5=MA^?Ibz6=ikw6p_L4zglw%A_%Y9iVx>)!k#7^7x>&fcwuj ztR53o00qXXV|1A`CU8}a`x6Gq_JPl!s+f}7xh{>Zf;O0v79x_z{%{V{ z4_{afWe=l_^*1nHZQ}Ktw>W%i22?X(gVCS_XT}8e86;ay?)0pgDZRBDviS6eSq$A4 z=wJRD{R>YqdRO4zcUxA^j%R#LV84G5jMP_Ob|EZ+yh(7RI!Cf%X8*HJTJ>) z_iSwepP!lreZ&8}6k{oq#`rFS0trz4B?e6ix#g{8s)XSaI5-ARJXaCa-MUs^5uPH^414iFX!BKePSzFNH%2UwMgvy3nO#LN3gGxaBs!|nWX(0k5@WeA^ zpvTWQ;E9faY7_3tq%n02>IZo*GN>Tr8B`Ht(?T#PmM#NrubW+Drmj~7k9#i|B;O|a z461~&Ya#r}GEkRXH@p`1fbm{bW3}f^P%DG1m@1u~e;A(j6oh$d_bomF9pMRi-Rz=F z8soj7#%j+QB;U3946xQ@Z9x>kYpxWQnSj|}mWrf`GPGz$8Gl4-+-2lkT7vmnw#F=4C z?Kw48^BaRQ{JF%rOm;1VJZs!yY|5lDH4M4{I>sRWKEqqysZ4HK2)7trCXJ~%Yl41d z5PvM1u)(6}P=!*bKBoCRON+ai_;f8C=2vbuV%7 zb-*WZ5orS~?y_Fcku&QIdO*>fAwL%J_Zj{IAtPzXcWr*X(esAJ80~rZJ_Q2eB za;L}56}JyEE*bJ(;^6CmPhdXucw2MuZsG;ZYlh3jnc=Y`XCC|q=$NMkwbFkS00000 LNkvXXu0mjf>xkl; diff --git a/samples/electron/Assets/AppList.targetsize-36_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-36_altform-unplated.png deleted file mode 100644 index 9ccdb31259ae55a9dc01e00edaf9c1dccc7d34f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1613 zcmV-T2D15yP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1>#9WK~z{rt=LI# zQ&$uR@Ppa~>Y^3y22^$3?|@YG8&q9YDv&twl*D!>I|HG^f*q>VWj8En3k3oR4kU2~ zk8vhK$UuOk6@#c)Aj*AI5Ku%4`B&%OcOB>X+3zLllg{RPaeqGN4(C0h?$vwLb5BtB zT|r&91$C_m>b#|*V_8tgl8X4Ep!kBI*iAF$1-0EUV@^=pbwRDODq5xmwM+?Wo;0H% zC_14ck{1-o35sL|MKXfI9BDz(yr9PPeWLEwm-PK3P|u&Bocj$Aq`GCybku#dtDv!giiePg-Td9h2rk(Htm}RT0jB!f6#v zDfL^!m_f2koEeE(0d+WGG^iS83e@a`$$*+nOw+g;Zy06JijZZJS#iR+%#0(3Atji` zF;FA}3XK>f7cZ}w|D#M%ml$0pjcFWJW80ur2aH3RTBkta0w`L*zN#2YnKY(h1Qbqz zLP>+<*5=(*5#x{<#xzXelkes*_22~#eLD+k%-LXU$|Nx+h{uD&3|itdXm1$3cmp|% ztvv@nhfd6b8ZzY=yE17^D5=JSgC>a2nKFz+nHa;_smq|L_nySO-h7LLCuTsQw7W8C zOlVk*2d)_;PhdWScAFWFKn|asoX5uII|+LA<}D8POe>I^GHFb3NR2Ddf{^FWt}v6J zNCEpA^O$?|Qew72Q=m}FU6~jY8sbu^G5=MA^?Ibz6=ikw6p_L4zglw%A_%Y9iVx>)!k#7^7x>&fcwuj ztR53o00qXXV|1A`CU8}a`x6Gq_JPl!s+f}7xh{>Zf;O0v79x_z{%{V{ z4_{afWe=l_^*1nHZQ}Ktw>W%i22?X(gVCS_XT}8e86;ay?)0pgDZRBDviS6eSq$A4 z=wJRD{R>YqdRO4zcUxA^j%R#LV84G5jMP_Ob|EZ+yh(7RI!Cf%X8*HJTJ>) z_iSwepP!lreZ&8}6k{oq#`rFS0trz4B?e6ix#g{8s)XSaI5-ARJXaCa-MUs^5uPH^414iFX!BKePSzFNH%2UwMgvy3nO#LN3gGxaBs!|nWX(0k5@WeA^ zpvTWQ;E9faY7_3tq%n02>IZo*GN>Tr8B`Ht(?T#PmM#NrubW+Drmj~7k9#i|B;O|a z461~&Ya#r}GEkRXH@p`1fbm{bW3}f^P%DG1m@1u~e;A(j6oh$d_bomF9pMRi-Rz=F z8soj7#%j+QB;U3946xQ@Z9x>kYpxWQnSj|}mWrf`GPGz$8Gl4-+-2lkT7vmnw#F=4C z?Kw48^BaRQ{JF%rOm;1VJZs!yY|5lDH4M4{I>sRWKEqqysZ4HK2)7trCXJ~%Yl41d z5PvM1u)(6}P=!*bKBoCRON+ai_;f8C=2vbuV%7 zb-*WZ5orS~?y_Fcku&QIdO*>fAwL%J_Zj{IAtPzXcWr*X(esAJ80~rZJ_Q2eB za;L}56}JyEE*bJ(;^6CmPhdXucw2MuZsG;ZYlh3jnc=Y`XCC|q=$NMkwbFkS00000 LNkvXXu0mjf>xkl; diff --git a/samples/electron/Assets/AppList.targetsize-40.png b/samples/electron/Assets/AppList.targetsize-40.png deleted file mode 100644 index 49f9a6a0f7be25901dd27ae796b0a088552aed32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1683 zcmV;E25k9>P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1}I5HK~z{r&DhIt zRCN>w@Izd%P!a~x&h$Z_(8P_Jm|(&NTtE%V8-&v5 zOkXo?r=_%|&?l`(F{05_@CPKdK&+at`Tl-)puL^8cc#SfOHL-acX2;CbMHCl_Y>JS zGb^>TN@}K+R8J|Xo-|Q4p`>bDNhM>FRn<(yAQru@EoG=mURTAz|66!Wl6t^O#B;2bc*flFsGczxr+=0~o0NKyD3E9W^ z9#S)7#WbY)2BdllQauT&VoX4)#vzrAF-Ya8iHhrxiffSa5lHz}6MKgtdxs!pj6q1* z6-X(-pQqpgtxDQgCfP@p~UZ8VcbmnSrt$cVT!+LlHV-!+;9a5fr_y}Y#AD#k` ze0V7zZUKsIfN(D))B_21LqchDf(GP)q)9s-rx+a7 z5ZDbVY_ouT=UyaSpu^lNj4IJzD?fK#Vcifxc$cyyG^fWz_oBG zGDn=h$-uoyAd9q99Lq_#50V$d*MoC**VDE0&?qE_1$Q;vDw!kBM_>fvZBU{IO%`dV zxRO3dZVVsPP1{Y&*1H-QT?MyB=7{q(vPeU`my*CyXE^rOf^KZD8Gu^&+iqI6b{rgm z>|s{gAU?iNQD>PW&es4bXo2Ku;IIKY!G#jKJ4C1N+dV7RfzF?>E!csr<#B8+k6}v* zTbF~(5$C&P?&Z}JIK+L%`Z!J{zBsBHf3#axT@M~D;{L*-{=Ic~0WXJ`&E~Z;$M6!^ z1j)OoL~n4ohgc6+)Qim}2_$Ym#LV0yOwTP~`nNxEcm6NCE9Lq$GK)>_#`QBtoVVTr zULbIgJF~TM$&o4CgH7zM><+;=#M6e|$ENI-6)Q4z4?nuIUeO!(ATA=BR>7^3IpT7c0sDAdkv@(cg21CIfqd^?D{g~$ z8dkxrkvZaW&zpOmvq|7!`Z%^PR|~R^uAFAxIWPd(&A&DkZk@~#mwV1!_nc9p4{>-_ zk}i(D)z!QLsNcJf_>!l>ImjGwIcLp1&uI(DBAqUdjo;OTtRtgOarI+!kZeAj@0}gz zFmpK;kk{P=@(7GYI$azae|95w)tL<5QIGLzQN()h?6@>C$MBpn*WITG?BV%c`Z!J{ zu136f_$nSR{)4x^=!fjG-n)%tjyTV0bKQNCz;2~$U|Kk9D)F~sb1;H8YT5RlQ|5^C zoHAh7QlMj;ZJuL)O^M4ea}0M9ctVLja%By4giB+d%QSPuxlfww&wvRBIERz)Ml(m8 z>!i7!eVo7;546|8{g0U=&UL~9eyc=+UGuuk@Jv{L$=1B5|8fh>gB+rH9KjvG9nK97 zrxn6EXfim@aUx-)gfBren@PAvv&@`_WPdYH;H)k+Jh0}OFpph%aLC~K9FM?w%EWUL z26y{98kOF+a%;%l7dJTELvT#zB+Oxx;}U~SpTXYB_QejtBFs9+Qo^s5?^`zGxYC*9 dwLQmX{{yIfVntfV`6vJY002ovPDHLkV1mQcBhvr? diff --git a/samples/electron/Assets/AppList.targetsize-40_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-40_altform-unplated.png deleted file mode 100644 index 49f9a6a0f7be25901dd27ae796b0a088552aed32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1683 zcmV;E25k9>P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1}I5HK~z{r&DhIt zRCN>w@Izd%P!a~x&h$Z_(8P_Jm|(&NTtE%V8-&v5 zOkXo?r=_%|&?l`(F{05_@CPKdK&+at`Tl-)puL^8cc#SfOHL-acX2;CbMHCl_Y>JS zGb^>TN@}K+R8J|Xo-|Q4p`>bDNhM>FRn<(yAQru@EoG=mURTAz|66!Wl6t^O#B;2bc*flFsGczxr+=0~o0NKyD3E9W^ z9#S)7#WbY)2BdllQauT&VoX4)#vzrAF-Ya8iHhrxiffSa5lHz}6MKgtdxs!pj6q1* z6-X(-pQqpgtxDQgCfP@p~UZ8VcbmnSrt$cVT!+LlHV-!+;9a5fr_y}Y#AD#k` ze0V7zZUKsIfN(D))B_21LqchDf(GP)q)9s-rx+a7 z5ZDbVY_ouT=UyaSpu^lNj4IJzD?fK#Vcifxc$cyyG^fWz_oBG zGDn=h$-uoyAd9q99Lq_#50V$d*MoC**VDE0&?qE_1$Q;vDw!kBM_>fvZBU{IO%`dV zxRO3dZVVsPP1{Y&*1H-QT?MyB=7{q(vPeU`my*CyXE^rOf^KZD8Gu^&+iqI6b{rgm z>|s{gAU?iNQD>PW&es4bXo2Ku;IIKY!G#jKJ4C1N+dV7RfzF?>E!csr<#B8+k6}v* zTbF~(5$C&P?&Z}JIK+L%`Z!J{zBsBHf3#axT@M~D;{L*-{=Ic~0WXJ`&E~Z;$M6!^ z1j)OoL~n4ohgc6+)Qim}2_$Ym#LV0yOwTP~`nNxEcm6NCE9Lq$GK)>_#`QBtoVVTr zULbIgJF~TM$&o4CgH7zM><+;=#M6e|$ENI-6)Q4z4?nuIUeO!(ATA=BR>7^3IpT7c0sDAdkv@(cg21CIfqd^?D{g~$ z8dkxrkvZaW&zpOmvq|7!`Z%^PR|~R^uAFAxIWPd(&A&DkZk@~#mwV1!_nc9p4{>-_ zk}i(D)z!QLsNcJf_>!l>ImjGwIcLp1&uI(DBAqUdjo;OTtRtgOarI+!kZeAj@0}gz zFmpK;kk{P=@(7GYI$azae|95w)tL<5QIGLzQN()h?6@>C$MBpn*WITG?BV%c`Z!J{ zu136f_$nSR{)4x^=!fjG-n)%tjyTV0bKQNCz;2~$U|Kk9D)F~sb1;H8YT5RlQ|5^C zoHAh7QlMj;ZJuL)O^M4ea}0M9ctVLja%By4giB+d%QSPuxlfww&wvRBIERz)Ml(m8 z>!i7!eVo7;546|8{g0U=&UL~9eyc=+UGuuk@Jv{L$=1B5|8fh>gB+rH9KjvG9nK97 zrxn6EXfim@aUx-)gfBren@PAvv&@`_WPdYH;H)k+Jh0}OFpph%aLC~K9FM?w%EWUL z26y{98kOF+a%;%l7dJTELvT#zB+Oxx;}U~SpTXYB_QejtBFs9+Qo^s5?^`zGxYC*9 dwLQmX{{yIfVntfV`6vJY002ovPDHLkV1mQcBhvr? diff --git a/samples/electron/Assets/AppList.targetsize-48.png b/samples/electron/Assets/AppList.targetsize-48.png deleted file mode 100644 index e08734f840b44bf5e819729886977c3792428c12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 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 diff --git a/samples/electron/Assets/AppList.targetsize-60_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-60_altform-unplated.png deleted file mode 100644 index bb700593c201baeed2cb060b7b67d0226a812358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2067 zcmV+u2<-QXP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2dGIzK~!i%-P_Ag zR974a@L#(y>833vU3Jk#w=Vi0bmzjP%eJ<{%rFmzhZY8q+K0QEv|Ti5W4A>`5mA8= z0byof1_gO3DhNt_wKgW29;`uYeZ*h${hf2iJ6A!3d&gu>@(qi77V|mW$M2kbF44A` z%k<6fiW+7VHJmq@Rma~fLkuy4VCiRnw>LwM{p4O>5rKlE}Fd5g0A!8<^CL@Y! ztejL-Gpwi@8G4#8PM8epY(X+Qo71)oD5^sGO;U;~tt55OuOfMwXxq#JZU2Mm>p7jT zE}C4>**0r3qw^(l-ej7o0h!X-dX8!9SzFHN)KA)STBq)m$pll~I8!Y$W-`hYvofMn zbMk4bhfRi%J9<1(N?Ppw1uPfFEzg0P6AMtxPi2A?C-| zA`>PSV4Nv7X5v89SP1)%w*zd5EWm--Jit_i^qZuZDy<}SDiS*ATi&NgPGNux8H=EA z)B@uaE@W&)pY9j!1$f#9E>LW_1wN)o?!ve^6*9KGSNF{U7c#c|nC@5BW59Uq6f!mn zaMA*wHQ)lqMtcmnTai>yC@5YPHkxLt%-9MB53mAGFhvA>MuT4kwj#qHe1DqPemTrk zmGKT6?bdxFT?UMDF`wA#<4mRf{K;>#j8=YNj;S>19kvYMJ_E)i0CjP`u=vlGrTM+> zqkQN7V-0SZV+sK55w@($fI9)6G~hg8HxDvJ2Do>2Ua)A<{Q#SK*9$CyKH)5QIzL#% zpW=r2)ht$nFERyC4S9quJF5GHj~Fl}4*9@_2YB6%34ZYS$s%Bnun{{L6L-|nUSlgV zyrwF{Q#bDAV6$L9%0G{=kxr)a9;R^I2KEw*=W8IzKP0a^tC5sZbK0XL+wmuor{52h(#*WyrwG6Q#bB9O-r^=(|HZ{3X7m$@G!umN?v`e z7K_QdFTp<~|8#m@*tWLKF@2013(7wP&rIB8fO7#GYSVpk!K=gu61;xrS*K-LZTiLn zzrQWRMb$|zilw+HhQCREtEP`BY(ZCn4FVjuz$19;w>oTefZvJ@@-=neX<2r|3~K)F zL!Q5vo66>CrcLIivr=sEFjEBJLyF{erGQt6txEIjkrczzXDaSk9ag{{ zcJRc?uvmkt9N;%%8Sb5#=aH*-a&!3~zIgK5wLmFIq5a5$a z-p{TCD{D|`y;4zzbzcOlgC>DvytY2gxBq?Qw7jsT=MfDInh$4grk$u;4x`_?6>f-{&wjcq! zjKxGCynOJ6pGTQC9`*(sIAFjn7I?&cEG7cs9xkd)y%4OgTRba0!dhUzfCn|$ZLGZ7 z=#koZDX_k7Q3c_;^#~hiVJbbu8>Og;;YI@)clfRA!f z&9mUHshdp2?fSaS2iD)LkNfs2l3k_(b{UIhnIOP@IpDu;-{*I>^f39_^M~~Te83JK zavzIjnP8`YZ`;5h{y4<65s#oeVtxAzxXA*aa33ox{hj=FbuT|O?}NW?pJn>wV1ECz zzzVp9sYJj7*o5sq7Kt3?*Zgt*DRG5&4PR&STi0!#vA(_fxPY;f995RvSZfU`+{xtY zU@8H^c9Y5{w!{MO0l43QJ-}L1JRn1_;54?dB~AKxaTYx29X20D#fz|6@NR%p_;{kQ zvBgdLxPbBHhky$hTfD~!#&T-`V+FjAY11x%@l8*ru(8FvnM!iN>B7ct0(dXerbb0n zo?KYEv^3W?_8VIRB7aWUu@4F+`v}&ZJM<#CH6U1BltHlu z_y46KhJr?8|^AOF1Z^8eMtW%yq8v+Do=002ovPDHLkV1i6N&4B;_ diff --git a/samples/electron/Assets/AppList.targetsize-64.png b/samples/electron/Assets/AppList.targetsize-64.png deleted file mode 100644 index 2c32c2bc5bcb042e9804c145a1ad16054351d3d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2210 zcmV;T2wnGyP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2scSYK~#8N<(tWG z8&w>~e;^^?fS?CXfDkthNc;r|i38#Q+zKU$?KoayH(j!|ltRS`0ph|Y5Q~&_q3N19 zakDhBV>{`ReQ85iDg>0Wlqr=G)Dl{L==3|ea@rIIvgjhTc@!>}!JgH|K4bTB6zJ4dwX#3F6^fobzurp;%V zHlJpC138t>rjsfsWL`(URas!#h#Xfr#W%j3#d5+J$i-3b8|1Yh6Jw0ejs*k=kjQGR$|f+?6_T2}&i1aksHz?>9NF8H;h zOraF7YCFZxd^pb(O6rHN?~`)^y-EOT`*OgqA7%0m^QyL!jIn?2B9k|!A3kUofUXXG zK&jzz+Xs{U_?B^Ayn0OtXdnAk3D685>}3icWC}zfU{(nzG5nelra*!RPyH$2(*n?_ zXog2H$8QN(C@nlv8|OEAzPI2r1!#s39*}eVJxTx?p;E&8hWV*?rg-_`T(8qsO&;XexPZT zfBf^RliM0n0P1wj@cte-&liD!DeU^{g~uz;H_RQ=zdM;TM*G+WCXXpVBfJpM>lB~~ z9=E>w+o0e`8pwBLJ24mJQ7awy{nTv{o|sOG3Q7N z&;hT&MY^PbI!nMz@!+wU;ThtOlINX#w;W#|I?v=nr=}Sm!MwWt5HM~NPy~2yjGy~> z-pQ8N=wG|$27ogyMopMg5fFi&LhxwtFQU3Yz&8xj!J5M`ViZ+)1zQ&Ji zh%tG43Wpa0B6a~&g~8+M2jUz#c9D(0midn*M;4d)dj4<2tmCCmCz-18@G1g)-99P6 zyB7kEDgk#0Z*E{VB>4Wu6hGKHqLShVTU5d^Zrpq4cgK4J-!ZMiRVxxa;_tExm@Ew5 z+`|jwAJ>>n5dXX&gG|-E{PO2>POiMh$XSE$s>OQ6tzRVgy1nvu?;Z#^f)AJqhtIW* zh9P6c^rW7l5y^@G+7uc$BqwDBwk`d))e{+KLH}_&ZGj zhm`` z-;A7MdIb$hnc#(hT}&<^Ac3 z?Q%|yDF9Vq$>B9sc=^C1ZV1>40dbpvQo)z;$|E(~dBAPU76bC<&E*?==PrXUfU zATtnzFEAlV{EcmjI-nAD?Db*y2f-g7D2_}yXplNHCHa&j?Bv)5WnC6FL3r8=K}r&? zyr=_qF;%rKk~l(+Y7hsBah4XRDMbbhajp(0t3>(?amEIxEJUJ)_}(5L9Gk(Xry>y< ze20e*k*stY;@-ZOvf+PNYL+ry=@zbaW!y4N=;PY?Hxz9#49a kc0=6qB5m^b*V@kg2S702EHgig2mk;807*qoM6N<$f+q0!vj6}9 diff --git a/samples/electron/Assets/AppList.targetsize-64_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-64_altform-unplated.png deleted file mode 100644 index 2c32c2bc5bcb042e9804c145a1ad16054351d3d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2210 zcmV;T2wnGyP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2scSYK~#8N<(tWG z8&w>~e;^^?fS?CXfDkthNc;r|i38#Q+zKU$?KoayH(j!|ltRS`0ph|Y5Q~&_q3N19 zakDhBV>{`ReQ85iDg>0Wlqr=G)Dl{L==3|ea@rIIvgjhTc@!>}!JgH|K4bTB6zJ4dwX#3F6^fobzurp;%V zHlJpC138t>rjsfsWL`(URas!#h#Xfr#W%j3#d5+J$i-3b8|1Yh6Jw0ejs*k=kjQGR$|f+?6_T2}&i1aksHz?>9NF8H;h zOraF7YCFZxd^pb(O6rHN?~`)^y-EOT`*OgqA7%0m^QyL!jIn?2B9k|!A3kUofUXXG zK&jzz+Xs{U_?B^Ayn0OtXdnAk3D685>}3icWC}zfU{(nzG5nelra*!RPyH$2(*n?_ zXog2H$8QN(C@nlv8|OEAzPI2r1!#s39*}eVJxTx?p;E&8hWV*?rg-_`T(8qsO&;XexPZT zfBf^RliM0n0P1wj@cte-&liD!DeU^{g~uz;H_RQ=zdM;TM*G+WCXXpVBfJpM>lB~~ z9=E>w+o0e`8pwBLJ24mJQ7awy{nTv{o|sOG3Q7N z&;hT&MY^PbI!nMz@!+wU;ThtOlINX#w;W#|I?v=nr=}Sm!MwWt5HM~NPy~2yjGy~> z-pQ8N=wG|$27ogyMopMg5fFi&LhxwtFQU3Yz&8xj!J5M`ViZ+)1zQ&Ji zh%tG43Wpa0B6a~&g~8+M2jUz#c9D(0midn*M;4d)dj4<2tmCCmCz-18@G1g)-99P6 zyB7kEDgk#0Z*E{VB>4Wu6hGKHqLShVTU5d^Zrpq4cgK4J-!ZMiRVxxa;_tExm@Ew5 z+`|jwAJ>>n5dXX&gG|-E{PO2>POiMh$XSE$s>OQ6tzRVgy1nvu?;Z#^f)AJqhtIW* zh9P6c^rW7l5y^@G+7uc$BqwDBwk`d))e{+KLH}_&ZGj zhm`` z-;A7MdIb$hnc#(hT}&<^Ac3 z?Q%|yDF9Vq$>B9sc=^C1ZV1>40dbpvQo)z;$|E(~dBAPU76bC<&E*?==PrXUfU zATtnzFEAlV{EcmjI-nAD?Db*y2f-g7D2_}yXplNHCHa&j?Bv)5WnC6FL3r8=K}r&? zyr=_qF;%rKk~l(+Y7hsBah4XRDMbbhajp(0t3>(?amEIxEJUJ)_}(5L9Gk(Xry>y< ze20e*k*stY;@-ZOvf+PNYL+ry=@zbaW!y4N=;PY?Hxz9#49a kc0=6qB5m^b*V@kg2S702EHgig2mk;807*qoM6N<$f+q0!vj6}9 diff --git a/samples/electron/Assets/AppList.targetsize-72.png b/samples/electron/Assets/AppList.targetsize-72.png deleted file mode 100644 index 9eb82f00cf892ac94e382d9fc2c92c4866708e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2767 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 Vs@0MM6ubZc002ovPDHLkV1ie43V8qk diff --git a/samples/electron/Assets/AppList.targetsize-72_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-72_altform-unplated.png deleted file mode 100644 index 9eb82f00cf892ac94e382d9fc2c92c4866708e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2767 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 Vs@0MM6ubZc002ovPDHLkV1ie43V8qk diff --git a/samples/electron/Assets/AppList.targetsize-80.png b/samples/electron/Assets/AppList.targetsize-80.png deleted file mode 100644 index df4cea4c5bf34a8787cd44803b30c426453a5ca1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2851 zcmW+&2{@GN7yo7qX3*Hz8KlM7O0--wmZt2xELUnqM9GphpOCL8Da*}2v@j)0mWBz9 zFf>CiInTSC^Pb=DoafDObFtkfp(X(Uz&3lkV;-<} z`z>fOc+Qqxrojpk<6%nzYWncsU;}mbi1QHuc$OpiG~Ht5`TWs`I63BQ_rmwSfiDvW z$NC)}&`ZhMpw6{N&6*&7jIi@k@$k{a1vGJCJ&HedpjcY2WI?ibD^36l?>wG1At zhlkP{;pg-1y~H`!>BEO(f~EQ!k|al&_tKKPjeXAKXU>%w)SYx_*T@sypCvSi={nm{ zc(3c@t^;ntf0p;ol{-Cis&m?PT6tcj*G~YMPCQtQVNSaul?*0~nuXg6#q3D`$R!k(N%BdvDacqKcdYm~1;Qg?l2w`X@)S|3f-$-3Mf@nMKLk%1WD5}AuT7q(M^Q0ga>;TuyV@N(ih6NZ#!>|bt}UVbosbJV zS<;6D@QP|KlxoJe;niOw03{NFI@Cx7aTq+9+GXmc%L>JTO?)qTe^l{|0fOvsqvh5% z;wCt7R+Uf2ennj#cBki>qm`Qd$1dTEY45zlz zmZXXsXW05~yh$(wN<>F~)TVOs8ijb0wN&+|2sH7;1>1!kcl52)d8u6aZv{{*%7h0D z%4n2DqyG4HT0%2rHBN$V>I6k=Oq-Hl-utAb9+@>hkTc#`HKEefmfN1FM7IOIlDn3Q z75E-X#GFKGv!`y(gkNg>JHcCo`9cD|HWv_=C7Gj43S{c79GN>jHFnE%JbY=MPDv4F zEf5e>#J>M=>sgK>VTA=yD3GI98;qiiMq!n=eo{WWJpCegDZ~ATSi=WDF}%RMR=k@R zUyW7bn7^!}!4zaHqnS({V$o9^3VNg*|3==8ZPKm2$V4C8$K*$o--+mp=)Xl*+cIzOBe49f^40w z3B#CXUR5_x8s(uSs9PasE6?RZkCCd6b~5pa8f6zzl&3hh{zKx2WbaeRenUb#LvV5? z0dFY!TEAbGn#wJ)kG4kry1Zc7oJAWTmWl*-)K?Le;iAwKDRs|R1`pd$ zA0Wo!*seko0-TKx!7!iLE*MZ7Ll3=)P?CXBGs_+e#dC7J>&C**_-Q}Ffu?Xc1Rzls z83J8g!mkk9t2GrZ@t+ULjWu2{kV%Z;ChF>$b4s2+V zGLY|=p6b;CAbY<}rTW^)M=*>ZH00ia=STCL7@_A)AbA+&3ZY&hp|>P=l*ZCDOpf1A6~cgx+GGCN*d zNHU3+U}$3PSnnhv4@puxs86WF-_xT~c=_l)?0lsh*BvvO+MR1ETN6En8mL?8HXVsgfbHpSsw+1X}$a*3tw0 zjj7}7dMlqd)YAql{%#o&Dik^dDU+@NOdZ6jnS>u%SDH;o`+AHsWmHJ$*SwoYP!!9H zdqxJ;@wCdr(o3Q_nyve0$ERJFIL_`U ziqH6xo{LtRhaza9rENs!`(}-Ii_w!0rLcUY`_%k=R?Pos+`~B>#K%}wvDS96uszGyDee1@Lh2)vUSohg4jNX2s2|1x9DjNar)e^&W zvPv>;9%aQpzBCpiV(18anBY60Qil z$`UbScb6=`C%OW`mvN4i*w#kh74zmZx^h&cfg{32LEIHs`-0M`S!WdHgArkV58ztM zYgFanpg=KyBf+`cxkl-8Cl}-HCL3O0;$f=jhaRoi&XSk=%f^?nv^6BKECLy_}BGP%i=bl9h>B5OI#N_)}%cYULw^W zNPGa*Zl$im$DT51F!lJJ$a!>G2xQHYhd%ov#>(0ntu0bt;|z1CoF#5Hj&mPUuH!e2 z2v?>VZ9UNK>UG&4Md4x|VDD&#y= zzkvfQ+ZxrV1B^XxJo%7=0uRVmVf)YI2wA~<9lfDc`tzfkopD!vSa3E_d<^DK@c4S&FOPg|Bpw;lMew=bH$D(PQB(y73=9J0g}_<81+B0?p3=ZAy8 zCg6GT?@js!Nzj`#m0jRfPF0^GQ5x>kLiN*79KIhjYW5A@qt=h1dQH(phzL-s_#Zm5 zbp78ER{bXi&mQJrn-W&V2#~zGj$b8a`@Fr%pN0-_>#`bM;A`X|njAeJunn8=^%^yM zrEhA0ke2n|cvC-VP@R$aUsVh1B1L&Q{O4kW&h> z%JX}V=NfnAE+&q!z0|@NdCH*3_-v-9o7owc`a%aQQ=a|)pl}01<{gjhZv6g*cRsejx&yi%wr7EXKnhTfpAhCiInTSC^Pb=DoafDObFtkfp(X(Uz&3lkV;-<} z`z>fOc+Qqxrojpk<6%nzYWncsU;}mbi1QHuc$OpiG~Ht5`TWs`I63BQ_rmwSfiDvW z$NC)}&`ZhMpw6{N&6*&7jIi@k@$k{a1vGJCJ&HedpjcY2WI?ibD^36l?>wG1At zhlkP{;pg-1y~H`!>BEO(f~EQ!k|al&_tKKPjeXAKXU>%w)SYx_*T@sypCvSi={nm{ zc(3c@t^;ntf0p;ol{-Cis&m?PT6tcj*G~YMPCQtQVNSaul?*0~nuXg6#q3D`$R!k(N%BdvDacqKcdYm~1;Qg?l2w`X@)S|3f-$-3Mf@nMKLk%1WD5}AuT7q(M^Q0ga>;TuyV@N(ih6NZ#!>|bt}UVbosbJV zS<;6D@QP|KlxoJe;niOw03{NFI@Cx7aTq+9+GXmc%L>JTO?)qTe^l{|0fOvsqvh5% z;wCt7R+Uf2ennj#cBki>qm`Qd$1dTEY45zlz zmZXXsXW05~yh$(wN<>F~)TVOs8ijb0wN&+|2sH7;1>1!kcl52)d8u6aZv{{*%7h0D z%4n2DqyG4HT0%2rHBN$V>I6k=Oq-Hl-utAb9+@>hkTc#`HKEefmfN1FM7IOIlDn3Q z75E-X#GFKGv!`y(gkNg>JHcCo`9cD|HWv_=C7Gj43S{c79GN>jHFnE%JbY=MPDv4F zEf5e>#J>M=>sgK>VTA=yD3GI98;qiiMq!n=eo{WWJpCegDZ~ATSi=WDF}%RMR=k@R zUyW7bn7^!}!4zaHqnS({V$o9^3VNg*|3==8ZPKm2$V4C8$K*$o--+mp=)Xl*+cIzOBe49f^40w z3B#CXUR5_x8s(uSs9PasE6?RZkCCd6b~5pa8f6zzl&3hh{zKx2WbaeRenUb#LvV5? z0dFY!TEAbGn#wJ)kG4kry1Zc7oJAWTmWl*-)K?Le;iAwKDRs|R1`pd$ zA0Wo!*seko0-TKx!7!iLE*MZ7Ll3=)P?CXBGs_+e#dC7J>&C**_-Q}Ffu?Xc1Rzls z83J8g!mkk9t2GrZ@t+ULjWu2{kV%Z;ChF>$b4s2+V zGLY|=p6b;CAbY<}rTW^)M=*>ZH00ia=STCL7@_A)AbA+&3ZY&hp|>P=l*ZCDOpf1A6~cgx+GGCN*d zNHU3+U}$3PSnnhv4@puxs86WF-_xT~c=_l)?0lsh*BvvO+MR1ETN6En8mL?8HXVsgfbHpSsw+1X}$a*3tw0 zjj7}7dMlqd)YAql{%#o&Dik^dDU+@NOdZ6jnS>u%SDH;o`+AHsWmHJ$*SwoYP!!9H zdqxJ;@wCdr(o3Q_nyve0$ERJFIL_`U ziqH6xo{LtRhaza9rENs!`(}-Ii_w!0rLcUY`_%k=R?Pos+`~B>#K%}wvDS96uszGyDee1@Lh2)vUSohg4jNX2s2|1x9DjNar)e^&W zvPv>;9%aQpzBCpiV(18anBY60Qil z$`UbScb6=`C%OW`mvN4i*w#kh74zmZx^h&cfg{32LEIHs`-0M`S!WdHgArkV58ztM zYgFanpg=KyBf+`cxkl-8Cl}-HCL3O0;$f=jhaRoi&XSk=%f^?nv^6BKECLy_}BGP%i=bl9h>B5OI#N_)}%cYULw^W zNPGa*Zl$im$DT51F!lJJ$a!>G2xQHYhd%ov#>(0ntu0bt;|z1CoF#5Hj&mPUuH!e2 z2v?>VZ9UNK>UG&4Md4x|VDD&#y= zzkvfQ+ZxrV1B^XxJo%7=0uRVmVf)YI2wA~<9lfDc`tzfkopD!vSa3E_d<^DK@c4S&FOPg|Bpw;lMew=bH$D(PQB(y73=9J0g}_<81+B0?p3=ZAy8 zCg6GT?@js!Nzj`#m0jRfPF0^GQ5x>kLiN*79KIhjYW5A@qt=h1dQH(phzL-s_#Zm5 zbp78ER{bXi&mQJrn-W&V2#~zGj$b8a`@Fr%pN0-_>#`bM;A`X|njAeJunn8=^%^yM zrEhA0ke2n|cvC-VP@R$aUsVh1B1L&Q{O4kW&h> z%JX}V=NfnAE+&q!z0|@NdCH*3_-v-9o7owc`a%aQQ=a|)pl}01<{gjhZv6g*cRsejx&yi%wr7EXKnhTfpAh|9fW8T)=InNT-dmMkGIW0|xNZV{Szhly%zMNF4aDpzHx z+)BA)ERnL@EKxGvDc4e#SGFnKe$(&w`{Oy!c|M=#bI$ob=lh)JITySpc;+z9dqEIqz zKLp(w`$r@SC3VPUuY+$=$lSHJCyAY!owkJ${`xYEjSpG7w3BgL8wK9UFUn#4;?VGn zn?q$g^Xmg#Ixyjqlq;_PFkNox-&c@zLS?YoCu&yv56L?^N;*Rh-uf^-n$ot7rO6i+ ze@j^FUS0la*34qGB-;P%@mqJUo)s|@QwIjdEcU#pb-NqccX40Hvp+6f;}RT;0z#e} zy6&jDlxRliY6zRW)qV74LI}A=)HU$)-Af1UqRw@5725X`c90nt8IqF?`7T9D-RvE8 z*={cN(Zd$HI<=P$+nEV1ZeP;f@9WHFlyKwBr7NSGI}rVC&x~setZRX{e*uwe=J{j( z>|lv65q1l>MoeTem($gh*+^pGUU=AX9DBDU`}OVEMwo2qbGxcw{;L1>lwqHgo5A6C zrLd}=PuDEJRLQ)n|3*}LROvs8-On85?H|2Tb-TTzH+}rIN(g1SI1h1$i!evFo2vef zsnyKjFUQeiQYMpZ?j7t7)Z1xUMgkNHBs9+HrYl^Z;bY>6ht!fr&5rV|J`0iGBMjZS ze-vW#W5QdzEW`H&B4^Yb)vhxn)4nQ-dRwZ1@TU+#&B*#u%W%smxZ9_(9?z2zIEr$% zt!l35q7_BCE!LQ9XFwmh#u|r!8&4sfoYh3Jl`(F*9mhX$804Kqq|t3^*OdeH0Q|Yo znYqX;6a4vUK*=g4-jB7Qv5^_JYL}oVUO|(U*RuIJb1(W_Yu(_+ERui#8!1FtB{297 z|FnGSWIi9~a1{+zNYCb(OU*ZSABqSGu( z9cYjofGLwn#iiKJz$KueHdtt}k!iDQjD{;oqiykozxU|3P6401?6J;tds(kq#B3!# z>BN+F>37(ezNEpEYF9s?oH_<+qoUf(Tm73Rttl{pWmC%iu6!;pCaOKcb5U*j6M+K#W$5r zt23ro?@F=V$qXf?iq^rT<*!p>4=$81jhvjBn?UXUn}X-I4aNSmrP)lz(=xB+ocdbi z{60dWkp()pZ3_5Fa$Fl#Oz=iCP+BIn%mdThf1MAXIOc0_R4zJ*=NZC8JfBQ|y-%bx z#0>s2b%D=GYosoD9BuI+@~3{~WQOkVnW6;CHEp9~&-ZEDhly-%`zU8bjZ)yYKPAl< z3XAmZ%|xt_N3j=CenRUUbakG?_+{OP9;4j=k-eY{!t(K@xxR|D4$aKDYX;_1**Q2^ zVO(DvDhZY-1;KKMrX#a2BKPOlZy-I^Rl@nrzQ|tHp!y}e0&K%vWS4+6MbmtD`|F6& zr@86ojSd@T^-(E#gKGb0Z`BlGd2j;1yI7D5BAZq1WY|zJcjG5!NJ^@rN7|nyHCMp^ zTseWNM3ZPH`$5~0`^!mWBxAhCA(Jz9&q$AdRP+FnLd?*H4r4bUM`2LFQck}@Mvf8A zCheMg{lq92#rp!^M>y4Aca+63L^R^xbp-C~>9YZM_?<39PZgP6lgF%sEO1_Oyt=Hp zSFZWm6@`ddnQ0U9=5n`X-C?YkB2AHANOG`u5kBP9M7`@2mOoy2wSP$*f9cTVitWsg zSXr=8E!9WEnS`%g7`vQ2IhIZ_-csFiboX5$hw^?qy#ACmxDt;}Qn2*sn81vG5ah~e z`KV{cbQUv;K&yuk^Ng(wzY0LGHWz@~I%pkeA&+kzQto&75-&K^jq`Fh}g+ zEawG?q~ntw=*c`Be#6KGA5X~9t(bxKEaWWZEia@1+KweH@xX%%0>uCV-`{r!ACAGxOrJgzL>^=d$wfPu}MFSJvOx!L*M}?PBh+uQH~;*&!0m;9Hi` zLJF@23g(&LRNptPqfQ9i%JYWj+dR+I@g+{1sB2hUi;H2$Dw#gVjEs%G_SYN!1wQ_a zB79f#{Y8^&zj0cUa@q3QM}4a)_>3(JlY&9pXw71nP48Bm;K2{g3>lr{L4q<#@8>4j z7c))pF92~aX)XN%j!0>JP9*{3|NfKRT@g9wdEu`8oil72&yasi%qRsa+^|n;k)h5E zyC8Z*W!^G!Q1+Oc`L*Y=;>bV(__XhAbh5Hv?8D)Cen*D`=>+ZP&_R>koe#(z$-pR!ih-LU0&n=TOEw;9aKbaW96(najptkA>L)eqKmVK8KeWjd6Ye5ZM~JJ!dSQ6m zNXITfkMGYnXD2js9~2P8l#>vtydU1orB-%VtchHmqZ3)t z^B+qliL5>lKGb6f#9eFWq?Mw-nCC7{YzZaOC7H%>JmL-@SD3z#BNBh1;;~63G@g0z zEh#9Zc_SEp1>DOtA=um`aO>q#bH#YKdw|(DvD|>hu9q@zHcN#D-iht-s+F&Ow)mx> zDgI69lh_mIndF?6xQ1r_X@qHiK0I7(z#I;HWTyZ*@t+(*;stNUB|VeZ>62Prk_o%M zXB{O4#rXwZZL2fGiK|zzk?5Oap#N58)Pl;Xo-xz{3nck9HHXfN+Y!k|RB84BfY(6N zih6MD%t+yf@ABf9Qk5YSa>w-}Ztzt9XqO!Cjz!=>e%%gj{CQVkmfZ)ANc<0KMS$oj z^ofYRC5BY5_WuMImki?q6w^HH`j65eGbURM&_YE@S2XR0s4zDF(-tZz-Q<4Vk4|Q# zC0g9>#AL}P4Q?9n~oIsv;*-y!d%u$!ey!;}e%?d7Ev`#xO z@#{wu=eDXGm6lFv1lF9MxvBc4ztAwa%7u)pe9(^-i*uV0tn|fwLD&X_wzbtO rlI&AQ8hQt2m1%1aV;;{3ZJKb7{M()G|5{1>^+37&;<2~Zk)Hk^uC?}F diff --git a/samples/electron/Assets/AppList.targetsize-96_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-96_altform-unplated.png deleted file mode 100644 index ad36b4b3d672ef6abbc0872831246843cbc5d71d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3269 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 diff --git a/samples/electron/Assets/LockScreenLogo.png b/samples/electron/Assets/LockScreenLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..770ffb20223c21033d41215b591c63a33fefb555 GIT binary patch literal 1192 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 diff --git a/samples/electron/Assets/MedTile.scale-125.png b/samples/electron/Assets/MedTile.scale-125.png deleted file mode 100644 index 7b3430d080f1d8758f36ec41a74c50a1c1863e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8537 zcmaJ`3pmqz+-DnenaTZ@DGov|xt1Y{RBn}UN}FqEog%8zR&A<_&*!=``$mF@8@@9i?_R+jHZm3n3&wAjjr3k zlMa1LNrInuH?>{|5AoP-?(4*AI+l)t7YKrjmy4Jfr9gH*L;}2){(Iw&STQkl3-m31 zFZxQjnAo!MO|CB66Y*o5z@xZl`;h|9v0IyWKFPW0XWZ@jc>S|mepQ#`tBtF?Fi)KP z_DgMIQj0(Ab_mmg*WWpK>&S^Kk(+xi+8KFiE+vOCXAadSj)!T<&*bM(C|=AZW|H#3 zvG)$u?j%jbb~t_gRQ+&}P<97W?VP1KqqY1{Flato+CH znuEc}+r9aJecW@R#KQf|(x~C~y_l`T$DAIbHDjC$zvQia{6~c@NedW=@xpk=WEsnU zxYKl6+KXrxgR_-4zIpw4+im{SaQ71tei|cnS58wlw9ZY-mlnM0FgsSq+y+(LS!$ z*1>Jx$P8@Dzn>dw=Q5oEy%m*Nu7s6svLGVLkyyCn3h;!}4rpaDabfP<{bZ}ItkA`$ z7yCUIxlIsSikG74CrUR;CR^~GSbBf=vQ3#D_vN@*AyhH*<#y?IAqA|0OWZcx8T&-n znwIjlLx%yb{WT;)d&^V0nci3-SKUP#Gem5|Za&QiAifI4Da^Cz2u?c*{es~LAKZnZs0@|P1-x%$JB0v0(iz37-0lXkZwvlEUx zB69W>QNj^HY~_sdJIKAPXb)EI2zxTUs4@aFO34A1kjH_~1cUG<5ulsd7Zgq&@r%0>wBv;9yFo zJ4pCqfRHUevZ_i)i%Pt8OeRT6WJF7_&$&r%!JlN4|3*mu5KeaCID&qFE(6X-^Qsol z&(q#Fk^u}$A4H^j$l&?!_rMuAvPZ)zDf(tmU6CMhcnSU_b=$3A3kh=~|F#6ECbR>5 zsh+s}5gc;x08Si~iguzBQ3BX$%ACH*U98Ffy9l5HM)GxEF>bJaWKa}O@FB7S#TY1? z@mya%W3ym)sQbX0%A$N=;3!`dEqhWQTcH7lm2dK*{A!^z-)L&3P<>v^jZqT8)iF`{ zkBNBa37yMVg7!{0v^ysp{9(4NUxvU79-gTza-F z(2)0<>bb>Qr<=yiXHEu-I)`aWj`)ugsomZ=jqZD9%kGU11gi1o`_)FPdWWjazs#&~ zn{?L>{a!*iWO+*zOb3PiR^MZn>b^dhU-hnWay2e-BIUvJcD+L7$C!NwwDw?_~5G9`%jRgx2fi~A!@h(8q&yli%KBZZS~xuKtte{9$JFC+A;ulk`E* zhF$SayGqM$9+u*9DqSb^n2!yAjlq>2Jl)BoTwHK(1|b~`sO6OR%o5ND1RXUV=Dhhr zZ^*63r!fxMKc(NFZN-%!Iwq>I6%x^W#V8b*DB2k#tPbbS(4f*dVFA9maDz8Jr@n7n z*2qs2?mYWkT`y~`b2mLtYEa_aq+Fb1eI0{qH|Wqp4CR~_WE%ZdA1pPU^=}8{h6XGaU+KbD zDVmQ!3iOVsixbgv0}72FpGzmro}zZ&^s~vJzT+E-Fvx;$4**CO3_LGxQ8-I4M*Bzp zeF+mf7lyvJuaBF#)7wpnZ{w1lTVY`q>jp$yWVIgd$9cgBHIh{4XqDt zuNXT1UL*&*cM^)*nskyl|4Z1^IX5itYEdpH zI^Yy)5W$cm2f74+L8!_O69gKa)fzfIuvVjDVfp`>jhq8tUTDYl&2Mc?9W$DG5o42O z2S5wZ31)6+^uA2vH>nbd*E zPsr3}nR~HF*%%o1m2jxcNBx3leI4!MD6I_((ag zxecR*RQ@F;!k}vj{(%f&to8zxvz5A_N+7m_QN+ILfHT}co3A))=Siz@$Tnm6!5u_2 zxFeXFS#vj^sU2ET=OR(gij-r#0|>vLoI8c;E=rqKXT-jAj2o@K#qCFvDy5A>r{Vun z$O_~fUEwV8UKj!ymspFKuU7=a6|hYvmQ^EOz}_v$4j*{lwLaK=AOlNV?Nu9n5+iRN z)u}2Ast*wbPBqD7Xv`0)e#={DR?18D6ms;*@uZo9Tn_FeLR6(WK3fz>W&%aQF%d;V zlR?rPTc-NbI0fh|KPA8o6jo<(WEue*o<8a**<5_S@4XEdBAa-9z@O;so_%wdo1I*I zR`2RMPCel+Tkj`EV%3bJuvr7DbETiEMKo#=6-L_3qFg#OFGfd_WaK2Pld|PA!EDBQ zZmHriA#(A921p6rUQe+Gx9*G^GBOTf4{l#IvjYZEPwfmSQ!-)Z=%3=GM%&nF@ufRK zc%HHC0SaJ)b|MV|@q-2XGF`258gYZx-5W7rT`> z13VN3%nKBuG+0r3tDu5c6c7O@ndjibXT>-T3(|1`JFl|lS_j`MZ8u|-Y!le=SEWM~ zanU!mHmB$VPG7UK@M>|xrWgRxs@!UF0KM+s4uULX?BWQgX5y01zOdSO!EmfINM@@g zgh?~{`3%V>chO%h;#QC{ZS}be31=2|bFFbJUccV%Ws{YV0nMixcqeFK`8-EBp5Ck} z3dz1Ac3<1H6><-YVc4}(|C%_6zz=td0mA6k-R6Qrj^bQb@)|z7*w63%uwFLVAC{Wy z&M)F3R4DhjcH5rTEfz|=XifyO%$&T&Fc-(}U9*Ym^jDg>3+gT~B)YDn23hc9tqW{x z+H=&Y3+oy1Wq1WcNQ}rIj8-amhO#AS}@8 zVH&FD@yUtUQ}e)U8wHd4(XTOPbcI{2#4sIj(KXPJv)dX{#w|FjUZ(kI1bnKv z;N$zW8U{K_xfpb=6^QybKig8r)ZJna@vMe89uxQ*!Yu}Yq4^*F>2Eh`+)$~YHP@?K zU*KV7SYn#9*W1!3*cJ*whg{fif{4`uvv2Ig474-4XJYVHZh7g-XczbHr1?khr>f%( zc{%S7jppdL#I-%~Y;Asx`qT2v@FAC+KcX+~@lV>Aeqg|zFntu)+xMP3F~X!V7jr)W zquuAb?5sa$FMmkoYEe*H5kB?8?fGEdrrF8peBY;xVoLtw?SDw_G)xS@ixRRIM+2x*u*q%WvBgb~h0eJD!PHl!QuEc=7T;@s$2Nh;N zxrJH#Q;;nWYMN&UqjLVZ)Zb+!+Ir^h*1!tx-{$K4m2S7rJd=aqAcc=CE~TAxB7Q-? znBwi2C;5!N_|1Nv@1^{>Iv#?|nvDeN-STw-ndV0q8C}8=+(p8%qyWeKv! zJh4|9S0PbwKOT77W)EVtd}rgr$wED@`KcGlGbr4kAsV?PVb8VvvMjt9356f1qQE*s zrp21{`7#0={8nhE?2KK-llV7YEy|DC&fUpiMkMdy8w(B(NY1AyKMt&rXfDXWBSiEJ zHMRPJ@~6D6%gt+CN_5IDekiPcGV@Qs&WA^e{v5d~Wc1X{l)H~w?werPo}^^rC55Vn zR}SOC$0c(b!vsn)fNhDS5RRd6=UaJS>*y7aD-GbvkDu+vy2aMt`#NN^*4}l_yTaY) zRE<_I<}&YW+#4TSaCE%7Z_8j9qVpSbpUz7OIV+)e_M-bgQ`b*$TCE=k*gnb0?jLzs z*HakXZCKOqy_MQv;=3F2!ohHnw?t)i;9Hvhc2(-xH~#3VmM*S;&!+_|-nA3)uO4Yl zWe=%W?Rh?%6E;$by;}ol@3a_z0eP-lcC?-#;LD!1Zb7(0Przv3Gqc;F0*GWeL>;O8fSN9uqBf;*p7gbl2ECRZk*}o%!%DUv35u<*})G@ zJ5y7A3fcU!OE#`py>6P@pq9Y?ffg}ZO!$JA&yw|vp(N%8|k7T^uZ!C zCo6646=~{oe}b>I7r)^Rj3${J0yzvQwy~hT>-M4`6LqhQA;+vpdk;$)OPlt!PDvr5 z!stYyG6~h>bg|D-Bl^^sEC6o|fKU_kLbH3td7kmAKg#X`mNtl3Xk3|yh>~SIV0(4N zhSpllWObHa@EKfv=0>>XI&-)Pn~PWzA;*B#BF+UZq$T74<@qC4wLPco_WG0;UM=WX zXaFP>%s71ZDlvxY2W(tlCZw-?mk*-vVzP#|tr1Xb)_QmMKDTGqhzF@d1s<(JQ<*`k zK*~m^a}_gpY(v!00>aJ8F1&qAitfo@I>ltuBxd53<2kR@MNagG)~e5}?=1Bp=Z^H} zy%u%>-mP#)Zbp@SkK22VSyEXfQW?g@X!mcJ-bxDk%xyEhZ=O??R5xzt3>-QFxt&rg zLzl+F8IeGu%MDi3I_ob6_UA#5=T&%9%;!i60O9(?+7@-mHQ6}X|EO9j;^wE{wIzhV zs-9c|yp{t90W8>moo&NaprIB*;377Dn0B!Pk8HlPUC^IT-`LzoJ$OD*PalCdAfl5b z$XIb8Y#;^I!D?9HX({|l_phxtS&zODUGb_!6`&fy2Uh{bhi33PMPGl|kD1>v={$Xa z5ial;cigPQQ(Uc(Sj;xDb$O$h)jYRX+0;K{QN(F9;p=Ax)xTAD3#hHylOMI>LQBUo zYJ2{D4Ja|Fm_R;fdW$u|OB@Jbi{MSWi&vZ>1ntjtYT}9KxF!@G>ksZtw*w;sbDWxk zgAX3&NP&3^DS?oxrU{y$!EL~>lkQ#dIlFrpZXmsX2YU@qr41}Cgwv)XRR+>CabX3l zXc?IWkFRw_=WB0l%J(U%EXu%1V2<~v%cnEmV<@%1A)7o!^hEwJuCz*d$vjS}`QpPi zq{oK~a480gA8Fy@2E?0$Khx7T9vZX-#SYZ6M<{qICu3yPa@8LN(}gZPpk&2F@DG== z#^cpnfUD)}IztOsx#;H2kfhxd_2L98Hh;K?_N z)1N^Tv3v|mCvCxIc01k7k8fCr1cj%G)s@dHbBW5stpWjv7p(PR<+d!z1VRJY&(p#A zYiD*xU@q{kPrqrc5sz-Ifek^#EMCz2T(lI3)(M~(`|`?}K|tV{on{%XIII7>>eUya zB84=@7pEjtpWh>Vw{k}MUs?i$M`ZK#)b)S7L!L~IouhpB<~p->o#3@51o`8(<6w=h zSWi8;c_4G1HZkGdNV@vNp~Fkbxrcizu1XBCItUTTO;>Z#=H#!Bo5#E{pKrG>mf`v^ z!m+@xPROr1fBo8ONdWn1x-<<2Iy;0JZ9l*6W=n1Q-h+$DSx_t$EgJykp$!QV!B+>X z6?fauu)@r{RlbDc{t~8=j<6X-;R#AFtJW15`CYZFfmei1L~73qD0OlBhu%&NRNaIU z^wBspAR{2S;?v~t?XxVPuQixcI?Tvev9Hvjib`I@t2o|Gz!~}_u)0*HS=LZBv*tUL z8Utw4o>8VDn%KCr`qvA#S;sm$7-lh3z$dy7uK_APklbLkKv33+_`w^9u7kP|1q^&; zYMGPX9M;?O&x5L2UA`ig29zkyp)#EG&zA>L8H{B!J?71BX-jK8RNT8y9! z6G2Z}^0FO|wi*bpIvl+kQgO#}f20lASQhUKL|TpTIkWk5huhhOG~|=)yX(TB*!n>w z4sD%)XqD%bIkuTuMmk11&gLk@7nx=R_x7~kxaVm!?8RWH(klbDs49<-f5W#{R#5VgPz;LzYT9~wC&Ov zu=I4VNXf&ejp&Z0z4LpgSYr6hMK4A!E`ZHIhp7*bWv0B%8IIQ%_?J0KKvzHC)k+Mw z6Y%jQL{=7<4Q{CT-PXC8vd07r^pInL>E=hF5ef}vP&O)wy&&>di$HpK9S^lVSd5pSTEPe)xQvUBWbf1lHC1u6~g8IItAr z4TK#C8#|)Lytm}P`V#Rm!cRl{JJ6#(*0a!E5u;5d=SRV9j@2)Un>RUvsV3r4fo+Rm zPmkbh3i8mku(f{nBy8|VB~=#S9ZaZU(z?%1`EkR;RRBHWxgf9_`tJ!EJ0j_!7SvNh z9uAJ7Jme;*f4iQ8gg$6dfSSHm3Z%cK4NUMF_)u}>gZpq%PuIXF)Pd^FH!)gX3;T~D zNiBF}+DPAuot3ao=URj?6UWg25wan=b_7q`9|c(p>+E@!5=;OLs$w7E# za@l>pMKr(P0XA1diC}6`2uvRW>tFfthgBBBfp4M?q=UqB*NghXR`d*jw&L`Z*|Jwt z^62DZaAzTng+c1oZYV5Sp|x3GcgW2*pcsl!&1#5vnvKx5qEZ)#n-4T+mSe#1g*NsC zd(iKCLXR7D_dXYkgEys;bAkctVcGipoucxAj)o-8pV8sEh%bxo7euE*(FC+DP8+Hr zqAR4vJZu$hM%pAorZJq_20=lpv^U_*+VdZk>8nx8rGP@~&+flb7bq7dqEx^Pqb{%< z23+LM{k3>o@NJ(a)|N4VC^I+pMk@8F%eW$>b^s*>e6n7&22F-8^KG?xsKve9#zooy zf!>`9Y=ZXWq9bKl;7S$PlYbu)JV3101XJM?Nx127!@5$|Z`Wi!9|%Q1>wkN4P*P2c^zvofr3S&-6XOO;D_AmWOSLE;uC2CkNp3q_ zk$Yj(&O%{rP|=3uiVcmt)=|A(tw!8Yw`Y1y&&}vlu)>@6n|j(V(kjQjydM^tt>`%z zpKvq!sP(RGyTV`HIkP&^#mH=B29CaP3)_*qYX0h;m+ZbPX%>&2^-}uw280LdP1f~Y zyMOvO#+k=CdpBFk*KIAF>)a4)XRN!RRoHiXZ&$cuVSL_Hc%A=oo3jJG8|{inpW~jz z6c{AfS(=pid8Q7Q7WdtF8t$e@X(@_q6()R`PWUs1p!nfdnB!XC@cnw{FZAl-2`{t_ azv_H3iaq2#r?emjzBajeyVk4=&io(Yf?P=e diff --git a/samples/electron/Assets/MedTile.scale-150.png b/samples/electron/Assets/MedTile.scale-150.png deleted file mode 100644 index 5df9f2e3e36ce92eb6f8e461a72f55da74467d6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12278 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 diff --git a/samples/electron/Assets/MedTile.scale-400.png b/samples/electron/Assets/MedTile.scale-400.png deleted file mode 100644 index 60b8401cd176f6f621c248e3bf10cfa4ab6b649c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46433 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 literal 0 HcmV?d00001 diff --git a/samples/electron/Assets/SplashScreen.scale-200.png b/samples/electron/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..5890acb74faa56a2c260843ac9c33ea4b9a044e8 GIT binary patch literal 100434 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 Ch0$IB literal 0 HcmV?d00001 diff --git a/samples/electron/Assets/Square150x150Logo.png b/samples/electron/Assets/Square150x150Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b45ced339684dffcd3504854780df32ef4b28232 GIT binary patch literal 1696 zcmZ`)Yfuwc6b=aik`Pc3kO-($6xNI_(kSvM8x11hV}o}!LBT{o>oTV<)trd!l%Bmp9=rmHPP(-1^_~;GVX=gh9arfM_d(U^C_uKv0 z(m1hDBIIy5;`l|1Bq)vc<>CaNQ4X6PL*bB}6gQu9?xx>Us5q{dFOhRN=Zi(JwS1@x zzFMTt=5X8^?aSf%mvlOZ<0Ok$$ibG>zRvYooeRh5wa1rN1{c;Sn&v$?wdrPU@S*DL z!^g90i+T4rirxUJ&yj~I($P1}!%0z=nA%THWVF65o%?3h@Zw9m6uag|rwpyEjA z`%KLCL;Z82RYMt-A2$WiqxGj+oBB)hm`lE+l>$H&mhNUqK62ukBf@9gae)X#>=yuG zTOg20F|x^nZ7F`?3aHs}td7rYp(F=-Y5mwn$Lt-GMG1{uFWMFJ&ui235zN;_Xh?lI^RzZ&$PeJk{)rI?QFt<2{m zGv9+6+EKhA>mU(iuCE|8@rx-*BzEH0b_>^d&5=1w5>wB*(BGMoqovpBb6xuz3U_na z+dTHf#Ir8JnD{8yHXip`;n?P?)b*&(_|XyOKnj%+6r+trN#Z5yRoBH!)NBf%^W}RDHEOEY%4I?A5G_$rM>}dIxF9o9+2VWmZUv(yiKGYH?6b9m z_#Vv}$enaImlF1z>#1v}>XI8f0)Y4Cwo^Z-O^9}6KD#mDm=_{$c4r2Tu0xLbGqa>? zXsh$q<@cqqk*`fhxo6wU>qb}Xwt2GYnwIgnvoQ=D)5|hpdkg3}g3MuB2z&m8&?o|8 zsepJ(TPq5d{K9A6aN(%%*y);!a?87Qu10t8sG%{UM`+O7oY@HN#RaU+CSuoV;<2z; z_Ud2(QctO`uP%o%DJ*WPjY`%$fgY(ymPzl?!`(lJiJP36Avht&slvuV5~*LD%63-N zm1{=i7DPoQnRl3yVZ!+3)CLIU*JCbovQ5AR%`=cpmE>?55E?%MkG}qf{WQ1-w)nWI zCcjEfGNf%B+p8gwOM}T}TVpdEM8KnM9^+(ml<3AUT!_lGdKoe zKWazN3}+D-%L5M&j9f>R#A1DyJM=J{i7KjOh$1v23%vT3VqZF4gc-~`$|de1;8Wgv zHA_#pNHH-Sl&pP(8s0&Y_`?wOqLA=~`~3_VByX9Fhu_?E3zd1Ww)gdmg%@l$;@0fU z4ES#9hMbVlIJk##_l6vkV{adXzCq8rvKpq%yjiwQP`CyO13}Vm`cSCw1G@!KOA&8xKYEagGMs0$lX}8ZUYYoG0pjC2M3Tw#`c)T42rQ7Tu l1@vQR&L}`>S@S#R^nFi@YMxDb0{<$U_}Hb2bMrO%e*sQ2(i8vy literal 0 HcmV?d00001 diff --git a/samples/electron/Assets/MedTile.scale-200.png b/samples/electron/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from samples/electron/Assets/MedTile.scale-200.png rename to samples/electron/Assets/Square150x150Logo.scale-200.png diff --git a/samples/electron/Assets/Square44x44Logo.png b/samples/electron/Assets/Square44x44Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6a037f3d2b0736ed52eac0cff5c05b333a43c66e GIT binary patch literal 727 zcmV;|0x127P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0&Gb{K~z{r)z-^W z6HyQb;C{TkLm-ia#6%ec8HEQhSo#d9ieS9ouXu@$5EK;x3JMGHx(Z=QmD}IynKPLr zhLFkWS)8u=_ur}dtNRem?0{yrLDQVCpy@B5vs+%yY^r<)*_=-xdjmAZSqDwkK&RJK zR=rGqRQUj!d=Hx7tbiuoflj^kGQO-*1zDUWkhKUJFAfvGP zmms6C`43g6-vAwI{4}%4<>AOBSB-YwKq6)z{ zbAs5^9R`QRuA#Ws$qF-d*n?s{a2V_&=)&(V2crLRN*dVIZBQueVIP_EKl^*c=7t_N zb&J88*nYwFUBq;;BQ97I%X@bADDVM_1p(~HO^`O0_w4MCo?xdWgl&R#u)Jq`f!*E; zVUOJaX=C{)6b=Eqy%od;U=1uEg~InPVD7CjHhG=FnpnPIh28(aPALLx@)}4R%jdQe z%)N~Qo4m?kO)UQlTEX>ih((1>xL{3e8~6ala}i<_S3ufW{>Uh>*V_oOi86yVu@dLD zU+l08HV8X>8Dtc;2{r{gG!HTg8-NYL#^)Gp5H@}ZWE3`D!asS0c}(AJtkt8g_;36Nkrtr28MP{MGN)` zi8JUP0UiVbfxcaomIHjg%T`dph&1tIfrAd`WNabm%&T62z)dd977_@z&eGl=AcL3+ zi}oER5Euh4gB~2ZUJN*j?PB!+9Eql!3Y3W>n&p$sc_{t-8rzA={g4oi>OMR0?uSmzQEpX)-dnOZ(XKxN&%ACnE zGiIcJ7x+ns5+6OeKHWCrv2gA=?dGG6N!L-!%*p35W{yrMA$DPd@Y+sO+*e8SyP^G( zHQ0d9m?L0dp_;4nw%KnJT)0%GQBcr#R5{BU!ULotY-_Si6PV;2Dk2|5w73u0UmvaJ z>NeWiGZH&Z+STX~xeH5&sR;vEOcf?7q4=S%+bL~vwz*Gx!c`lmMzeQF*a-^ERFXWm z|Ahxm-(l->Z!7Ua8>UJ>e0D#P@6UteG^>D~EK9XP!7v5ZwD$NInTWa%c6UbO9Gv$$ zCvM$L8jpEqzgJl83;4oa`3WgE!zcCx(M4x3`%(>KJ6%jv#(VD0wYrR7!%Gnl^;FzqZs zOYm>~4kDNxGpxYgRD#Hrore1HDZEzY{9OxYG%6fW@iSd~dX$Gh9c9C17*||-ijZ#P z+AP(%M(vR>n#PS{ELZ0mONzYD79W^{NcK!T7RhLSXRO$DqYg93u)6(1JPav1PhnC$ zPqr>X5t`oz%Vu}hW|lL31y#9F#OZM}XkmO(#qe&fZXCRzcqOugG)1OLQs503x9X;; zVM%Z?%STYP6^b|!xEPyeW?V{W1=kl*rzj1$a&>Q^h?PeK(993*O~0x{{(>qg6ga4z zzjp3$is$>4<%OA~?wv%EJyBbk8PynR|H6=fIrehIc!4T;vxGR<&qO_LLDt^K&W^fq z^dUs)*Loe*R0y&qRL}ZAcBgIP4np;(<=IX(?X8szhoWq#+v89C6mz-xdz5*TW{MvRHOcUyW_%@fpe^vRnGg zANhr$R|t9GYNV2KbP#!n zX958+nt`O&+r+^xeRRL60m=gcacQl|z_PAVPJPQbh!j1Tg@KN||JP-bSz%xazLc=X r94&pb=l}Y{5nx;kQh6>K!ajIWzj0oCYzdqKe-UB(HgTG6i>&59kSZD( literal 0 HcmV?d00001 diff --git a/samples/electron/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/samples/electron/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000000000000000000000000000000000000..5147944ecf4a8ee97b520847ae48085de37fc692 GIT binary patch literal 480 zcmV<60U!Q}P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0d+}4K~zXfwbDsW z0$~t_;qRV`$RHvDGBn}=bmJL%qo8q&V^o|0HKG!fX4n!pPD>8(nO}$u3SGVEI>7TL zRRzq|iDZpPR)|@HNR)}h3!yy|@e&a)60s*D_DDn@h-iU`+!K*IV&;|z-w@$E5y}z4 zED^jSrZ0)W1razWY{jR}RKP~ue@fVj`%eg4@yTNqu!t|dvSHL^xbKLtgolgNKS>%j z8SXnI=I@;QeCU*4oGm%jtUIHgSFn({4m8HVFx?4yu)V`9>ydj=MGp39b&>6DXHt`1raCnB(eOI)OK8Qrx*l zNO1Lvtw59dyan93N=R_^=PJ<1TwBA(R#dS>iYK WTQ`5>R|NzB0000RW9JZ zZ)g-H8;c7fpx}iU(HnzY2DtsLo;im=Y7#=yovO@V_217|b$YsqCO1Ko8=wj9575MS z(0T5<-*aoGt00H_&2$BH_N(bK=*$<>&wfvTGW}?}1RCcqf`7&rK&QC#e#Z)?c~Fv@ zGtGiVGp2K(lOIgqgHCW~{f@i?g#zJ0!hjfg%a9NthCN6K5W{IuC=kPM7!m@+kcD(Q zV(2v}6o?9>!x4is3~2)*^#?_4!_h(vz5=Nu#+Kp!1AslaDJ6st(M04CncZWBfhZvo zFD*m|;Xf&xU4OV&l@N&+3{gTPe`0PQaS)<_@E|IPJe>7I3IkC>B%WJ{EP|%Ds}(-v ziV|YLg9sy%n^jNB#zGVj1J6LRh+M#vvSEl4q5_dYRDQKV*@b&q3DN(QAu@==zuKTM zLaPyS4%|yhh<^&Cc?6|?AMvDYEJOhjf6R~; z5Xp@izuKTML+%L|R4okme9H6|}vZ$l6Nli1@h_=(P|L1b5*Gh?xs%MDIP29D)~JeTX*~8i)!+0>PcH4dJ=aLiF5a zh!ld)jzSGc363VB=MG2?!FN*-qzw0hGQx*QA%Y>rbXFe`yKNy72)<$qa7zait2`HN zK*Vk_L<+&Dc>Vz7FPwHEJct;APw||A@LaSJ5mTGC5DA2T07lYf6w{gZBDy_@971(Z z)I)SnfkJ_(Ksp@Jb(0|>KzNWaAi8caBm{`4g>*V1dL0xBMD!Z|1Fv?LBqh8#YXATM M07*qoM6N<$g5O6s^Z)<= delta 1744 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 Hu0mjfbnBbH diff --git a/samples/electron/Assets/StoreLogo.scale-150.png b/samples/electron/Assets/StoreLogo.scale-150.png deleted file mode 100644 index 68330b4712cf5de35d359a1c32d543d5db7235e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2416 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 AivR!s diff --git a/samples/electron/Assets/Wide310x150Logo.png b/samples/electron/Assets/Wide310x150Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6d748b09d489a7518a3b24df4e1eec85ddab09fd GIT binary patch literal 3278 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 literal 0 HcmV?d00001 diff --git a/samples/electron/Assets/Wide310x150Logo.scale-200.png b/samples/electron/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..106123b0f6373b82248a17089b9fbd3d243a2c86 GIT binary patch literal 13947 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 literal 0 HcmV?d00001 diff --git a/samples/electron/Assets/WideTile.png b/samples/electron/Assets/WideTile.png deleted file mode 100644 index fca4b440368d6ec3fb57dc17929503b57dd06bd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4541 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 K)vnTp%=j-0Tx7HW diff --git a/samples/electron/Assets/WideTile.scale-125.png b/samples/electron/Assets/WideTile.scale-125.png deleted file mode 100644 index 8b2f47a6a5c04340d584b5482802f3e91c11a4a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9760 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

Naq6kdP_v@B2vEvd=VG;I z^!=fjR!>9XwM3nms{zQzJraV;sXBIHjRJEgia3?-0`r8EdTXg|;X$DMi)Al&ggs2J zWpp)aF>C|QlcnyJo}fV8Enc&hIx~>iT2?nX^6XT2&_S-80gx4%0VRt#5XD}Ctq(gu zd@eH{MuA#ru$)U^EWtpA{@iN#*2ap^u9=Z$bpa0Y0Cg1@_p4!w{>2>%H$c4?PpANm zOrUOL=oPs;T5E48vx|Lu5Xi_tEdv-pp(2aM+&q+%VfCUC3Mn zfghGR)p)S`I_*Wwr0&?D?r52hq4^pWev#`8k5$R+lw{a02ReZ$lE*O(A^74|%tITa zPc)s*r~THYKHnp_dszX)*?)`v0$9p%ZuX69uaLX~CLXBL`bWJyMluKdO+R@NN}NH| z9D^q;G&y)hV1DuEpv7-g9S;u%p!bgRif_m13-BJNKpZ2l;&}ZafzFV5V+0s0pt$K_ zrz(OiK2;=`d|6U7i~HtJejbh=K;o&_-zGqtzewa1Z60BG;@o3`oxHN6k*rF1A23XL3qTOHI$2|l7 z0#%#^s`#Q&4k^+qRkxXw^bw<&f*4ed1V@G7w2a#z6K``iJ%TaY{$$(2=_<%`+dz`Q zMoZsaIts=tg1g=Zjy{$I;9~x?n-H|Vl<5YDf_3<%ABIfaM>lnwvH0+_*t@OI%Rju5 zEDJtUvCo8WR?5Rd>mRY?BL6J#2T77m8&5FxC|=~rez49T=LiP8gr-P2}qVF4{3B)FLW z*H6v6xMQ93&LJ54dHQRASY}BNPV)8Pf{KyR$+D90QlIFhKivwp7K;xEXFeE!=S?ED z7#eyQXtHzsWJ#sc){uaiwy%dwSQU>CR5S&DaSi_(Ul9_OagsT8&G&Y*uo1go;~x}9 z!h$UiN3#WdjC4CvGPw`1ho%ryQ|6R zOn@%=Ax7xu5nFJ>QsBU7>C54XWUlQt&oO2@(n0Rn-m$MCp`V_iQ|0-%&-~X2`G2KQ zwy!-4xG7&>?Z+W+W)Mf(yP-mv+PWU(?3?>MzAa>YaG4v_3!&now2=Z}_hHa)yOyXo zO7SM?oB_3{Yl7gG1!kWZ-R?0YkYoP@-@wnr{9*SgLe^UWu!5=MP6tW;FXt#E?Fe^^ zdJ(=3{twQOejW+yxZ+^`d3KVLkJ3NOg7sji1eNTg0@?AmF-|>V4WMLKHXiE+*O!I> z)7D%=Z%zS$z8J=E>%pWt99G##sxmO39VLHYfI*`EMKpjWsVHcMTKgP;MnZ6@$gv4g zjQ?&JVv(L`y(Xs+z3aS|ur>%zpn@x>TH+28O27s7KJan*S-n3GNgrso@~t}*X=87oadhCj+rVbe z0(7+HFwVt+GNQi;#O`@hyr`+!>|`V~>BkuzfT7ep?+~Lv1Ey=J=+w;|SOXl|#iJUF zq)k{%n8g!Kh@R`K(FP+B8^SOG;OUG5WT3g-=L;f|tkhdHZ6#bi&rB(U)r+l)y)0h*ARQ~qj zkBScc7OhgLZ>Y5=Mh_wg%ICDhQPu`8M4`c-MM~COmk|odaikb>;Pma&0lTg!g_`{m ze@&Ss+r0m9yzHuPO1GXVdF);~^k={4?xn#2lA~SwlkN>D3O+6kwf|4?kL07DW13za zt*aCY91Cn!dY}J4VWVBwYY^t|+|Wvc)rKGT##ucQASIJnvi-|jhTP4Uw?QcZzY> z*WdHED7hlpVyy?Psk?1Ob|vXmt%frJKxawFn|XYnS;KnOr~H)74v;A>t90M|E*V|C=GUSv%nKE%(L#Ka%bptN;K2 diff --git a/samples/electron/Assets/WideTile.scale-200.png b/samples/electron/Assets/WideTile.scale-200.png deleted file mode 100644 index 91abeb7241cca1be40699df79911a7a916920b34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6344 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 LHdt#P%~3jD|Bv=-6iZsA(g`l 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 + - + xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" + IgnorableNamespaces="uap uap2 uap3 rescap desktop desktop6 uap10"> - - + + @@ -41,9 +41,9 @@ DisplayName="Electron winapp Sample" Description="Electron Application" BackgroundColor="transparent" - Square150x150Logo="Assets\MedTile.png" - Square44x44Logo="Assets\AppList.png"> - + Square150x150Logo="Assets\Square150x150Logo.png" + Square44x44Logo="Assets\Square44x44Logo.png"> + diff --git a/samples/electron/package-lock.json b/samples/electron/package-lock.json index fb2f81b8..81881aba 100644 --- a/samples/electron/package-lock.json +++ b/samples/electron/package-lock.json @@ -22,8 +22,8 @@ "@electron-forge/plugin-auto-unpack-natives": "^7.10.2", "@electron-forge/plugin-fuses": "^7.10.2", "@electron/fuses": "^1.8.0", - "@microsoft/winappcli": "^0.2.0", - "electron": "39.8.5", + "@microsoft/winappcli": "^0.1.8", + "electron": "39.2.7", "nan": "^2.24.0", "node-addon-api": "^8.5.0", "node-gyp": "^12.1.0" @@ -1178,9 +1178,9 @@ } }, "node_modules/@microsoft/winappcli": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@microsoft/winappcli/-/winappcli-0.2.0.tgz", - "integrity": "sha512-PU6kmCxE1F+fLyzcnkhnGqJFLKWBMGr8FJ1ooCQgzkJOsEj0vbGLG1opie8XeVaYbjQBN77vZ4AYqwB2pPTupQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@microsoft/winappcli/-/winappcli-0.1.8.tgz", + "integrity": "sha512-n6WGOtqfIF748GlPIPOWI5J2yMNTeyWZ8mFcYOlmv9zAa5ztWPxnNQIZNrw5xSvRLx23x/hTt/3geQn3qYx7Yg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1188,10 +1188,10 @@ "win32" ], "bin": { - "winapp": "dist/cli.js" + "winapp": "cli.js" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -1671,9 +1671,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", "dev": true, "license": "MIT", "engines": { @@ -2610,9 +2610,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "39.8.5", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.5.tgz", - "integrity": "sha512-q6+LiQIcTadSyvtPgLDQkCtVA9jQJXQVMrQcctfOJILh6OFMN+UJJLRkuUTy8CZDYeCIBn1ZycqsL1dAXugxZA==", + "version": "39.2.7", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.2.7.tgz", + "integrity": "sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4540,9 +4540,9 @@ } }, "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true, "license": "MIT" }, @@ -5892,9 +5892,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -7008,9 +7008,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { diff --git a/samples/electron/package.json b/samples/electron/package.json index 756005b0..a19cc146 100644 --- a/samples/electron/package.json +++ b/samples/electron/package.json @@ -34,8 +34,8 @@ "@electron-forge/plugin-auto-unpack-natives": "^7.10.2", "@electron-forge/plugin-fuses": "^7.10.2", "@electron/fuses": "^1.8.0", - "@microsoft/winappcli": "^0.2.0", - "electron": "39.8.5", + "@microsoft/winappcli": "^0.1.8", + "electron": "39.2.7", "nan": "^2.24.0", "node-addon-api": "^8.5.0", "node-gyp": "^12.1.0" diff --git a/samples/electron/winapp.yaml b/samples/electron/winapp.yaml index 3ae0ae46..23dbb170 100644 --- a/samples/electron/winapp.yaml +++ b/samples/electron/winapp.yaml @@ -2,14 +2,14 @@ packages: - name: Microsoft.Windows.CppWinRT version: 2.0.250303.1 - name: Microsoft.Windows.SDK.BuildTools - version: 10.0.28000.1-RTM + version: 10.0.26100.6584 - name: Microsoft.WindowsAppSDK - version: 2.0.0-experimental6 + version: 2.0.250930001-experimental1 - name: Microsoft.Windows.ImplementationLibrary - version: 1.0.260126.7 + version: 1.0.250325.1 - name: Microsoft.Windows.SDK.CPP - version: 10.0.28000.1-RTM + version: 10.0.26100.6584 - name: Microsoft.Windows.SDK.CPP.x64 - version: 10.0.28000.1-RTM + version: 10.0.26100.6584 - name: Microsoft.Windows.SDK.CPP.arm64 - version: 10.0.28000.1-RTM + version: 10.0.26100.6584 diff --git a/samples/flutter-app/README.md b/samples/flutter-app/README.md index 961efd05..779ea86d 100644 --- a/samples/flutter-app/README.md +++ b/samples/flutter-app/README.md @@ -38,8 +38,11 @@ winapp cert generate --if-exists skip # Build the app flutter build windows -# Run with identity -winapp run .\build\windows\x64\runner\Release +# Apply debug identity +winapp create-debug-identity .\build\windows\x64\runner\Release\flutter_app.exe + +# Run the app +.\build\windows\x64\runner\Release\flutter_app.exe ``` The Flutter window will display: diff --git a/samples/flutter-app/lib/main.dart b/samples/flutter-app/lib/main.dart index 3803fdd6..0e6101e1 100644 --- a/samples/flutter-app/lib/main.dart +++ b/samples/flutter-app/lib/main.dart @@ -54,17 +54,10 @@ Future getWindowsAppRuntimeVersion() async { } /// Shows a Windows App SDK toast notification via a native method channel. -Future showNotification() async { - if (!Platform.isWindows) return null; - try { - const channel = MethodChannel('com.example/winapp_sdk'); - await channel.invokeMethod('showNotification'); - return null; - } on PlatformException catch (e) { - return '${e.code}: ${e.message}'; - } catch (e) { - return e.toString(); - } +Future showNotification() async { + if (!Platform.isWindows) return; + const channel = MethodChannel('com.example/winapp_sdk'); + await channel.invokeMethod('showNotification'); } void main() { @@ -178,17 +171,7 @@ class _MyHomePageState extends State { const SizedBox(height: 24), if (_packageFamilyName != null) ElevatedButton.icon( - onPressed: () async { - final error = await showNotification(); - if (error != null && mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Notification error: $error'), - backgroundColor: Colors.red, - ), - ); - } - }, + onPressed: showNotification, icon: const Icon(Icons.notifications), label: const Text('Show Notification'), ) diff --git a/samples/flutter-app/pubspec.lock b/samples/flutter-app/pubspec.lock index 77c0e79a..3e1328b2 100644 --- a/samples/flutter-app/pubspec.lock +++ b/samples/flutter-app/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.13.1" + version: "2.13.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" clock: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.9" + version: "1.0.8" fake_async: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.5" flutter: dependency: "direct main" description: flutter @@ -119,18 +119,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.11.1" meta: dependency: transitive description: @@ -196,10 +196,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.7" vector_math: dependency: transitive description: diff --git a/samples/flutter-app/winapp.yaml b/samples/flutter-app/winapp.yaml index 9f7e3925..804022c5 100644 --- a/samples/flutter-app/winapp.yaml +++ b/samples/flutter-app/winapp.yaml @@ -2,14 +2,14 @@ packages: - name: Microsoft.Windows.CppWinRT version: 2.0.250303.1 - name: Microsoft.Windows.SDK.BuildTools - version: 10.0.26100.7705 + version: 10.0.26100.7463 - name: Microsoft.WindowsAppSDK - version: 1.8.260317003 + version: 1.8.260101001 - name: Microsoft.Windows.ImplementationLibrary version: 1.0.260126.7 - name: Microsoft.Windows.SDK.CPP - version: 10.0.26100.7705 + version: 10.0.26100.7463 - name: Microsoft.Windows.SDK.CPP.x64 - version: 10.0.26100.7705 + version: 10.0.26100.7463 - name: Microsoft.Windows.SDK.CPP.arm64 - version: 10.0.26100.7705 + version: 10.0.26100.7463 diff --git a/samples/flutter-app/windows/runner/winapp_sdk_plugin.cpp b/samples/flutter-app/windows/runner/winapp_sdk_plugin.cpp index bc9c39db..cc9223ba 100644 --- a/samples/flutter-app/windows/runner/winapp_sdk_plugin.cpp +++ b/samples/flutter-app/windows/runner/winapp_sdk_plugin.cpp @@ -10,7 +10,6 @@ #include void RegisterWinAppSdkPlugin(flutter::FlutterEngine* engine) { - auto channel = std::make_unique>( engine->messenger(), "com.example/winapp_sdk", &flutter::StandardMethodCodec::GetInstance()); @@ -34,6 +33,7 @@ void RegisterWinAppSdkPlugin(flutter::FlutterEngine* engine) { } } else if (call.method_name() == "showNotification") { try { + // Build a simple toast notification using Windows App SDK auto builder = winrt::Microsoft::Windows::AppNotifications::Builder:: AppNotificationBuilder(); builder.AddText(L"Hello from Flutter!"); diff --git a/samples/nuget.config b/samples/nuget.config new file mode 100644 index 00000000..2903eea8 --- /dev/null +++ b/samples/nuget.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/rust-app/Cargo.lock b/samples/rust-app/Cargo.lock index 5af8482b..68a80516 100644 --- a/samples/rust-app/Cargo.lock +++ b/samples/rust-app/Cargo.lock @@ -46,54 +46,32 @@ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "windows" -version = "0.62.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core", + "windows-targets", ] [[package]] name = "windows-core" -version = "0.62.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ "windows-implement", "windows-interface", - "windows-link", "windows-result", "windows-strings", -] - -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core", - "windows-link", - "windows-threading", + "windows-targets", ] [[package]] name = "windows-implement" -version = "0.60.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", @@ -102,9 +80,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.3" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", @@ -112,44 +90,84 @@ dependencies = [ ] [[package]] -name = "windows-link" -version = "0.2.1" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] [[package]] -name = "windows-numerics" -version = "0.3.1" +name = "windows-strings" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-core", - "windows-link", + "windows-result", + "windows-targets", ] [[package]] -name = "windows-result" -version = "0.4.1" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] -name = "windows-strings" -version = "0.5.1" +name = "windows_aarch64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows-threading" -version = "0.2.1" +name = "windows_aarch64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link", -] +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/samples/rust-app/Cargo.toml b/samples/rust-app/Cargo.toml index 91c74d3a..420e882d 100644 --- a/samples/rust-app/Cargo.toml +++ b/samples/rust-app/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] -windows = { version = "0.62.2", features = ["ApplicationModel", "UI_Notifications", "Data_Xml_Dom"] } +windows = { version = "0.58", features = ["ApplicationModel", "UI_Notifications", "Data_Xml_Dom"] } diff --git a/samples/tauri-app/README.md b/samples/tauri-app/README.md index 487e6726..441faf7c 100644 --- a/samples/tauri-app/README.md +++ b/samples/tauri-app/README.md @@ -1,68 +1,7 @@ -# Tauri Sample Application +# Tauri + Vanilla -This sample demonstrates how to use winapp CLI with a Tauri application to add package identity, use Windows APIs, and package as MSIX. +This template should help get you started developing with Tauri in vanilla HTML, CSS and Javascript. -For a complete step-by-step guide, see the [Tauri Getting Started Guide](../../docs/guides/tauri.md). +## Recommended IDE Setup -## What This Sample Shows - -- Tauri desktop application (Rust + vanilla HTML/JS) -- Using Windows `ApplicationModel` APIs from Rust to retrieve package identity -- Sending Windows toast notifications via `windows::UI::Notifications` -- Conditionally enabling UI features based on package identity status -- MSIX packaging with app manifest and assets - -## Prerequisites - -- [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) (Rust, system dependencies) -- Node.js -- winapp CLI installed via winget: `winget install Microsoft.winappcli --source winget` - -## Building and Running - -### First Time Setup - -```powershell -# Install npm dependencies -npm install - -# Generate a dev certificate (first time only) -winapp cert generate --if-exists skip -``` - -### Run - -```powershell -# Build the Rust backend -cargo build --manifest-path src-tauri/Cargo.toml - -# Stage the exe (the target\debug folder contains build artifacts that aren't needed) -mkdir -Force dist | Out-Null -copy .\src-tauri\target\debug\tauri-app.exe .\dist\ - -# Run with identity -winapp run .\dist -``` - -Or use the npm script which does all of the above: - -```powershell -npm run tauri:dev:withidentity -``` - -The Tauri window will display the Package Family Name. The "Send Notification" button is enabled only when the app has package identity — click it to send a Windows toast notification. - -### Package as MSIX - -```powershell -npm run pack:msix -``` - -This builds in release mode, stages the exe to `dist/`, and packages+signs it. Then install: - -```powershell -# Install certificate (first time only, requires admin) -winapp cert install .\devcert.pfx -``` - -Double-click the generated `.msix` file to install. The app will be available in your Start Menu. +- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) diff --git a/samples/tauri-app/package.json b/samples/tauri-app/package.json index 3592c41f..35cc494d 100644 --- a/samples/tauri-app/package.json +++ b/samples/tauri-app/package.json @@ -5,8 +5,7 @@ "type": "module", "scripts": { "tauri": "tauri", - "tauri:dev:withidentity": "cargo build --manifest-path src-tauri/Cargo.toml && (if not exist dist mkdir dist) && copy /Y src-tauri\\target\\debug\\tauri-app.exe dist\\ >nul && winapp run .\\dist", - "pack:msix": "npm run tauri -- build && (if not exist dist mkdir dist) && copy /Y src-tauri\\target\\release\\tauri-app.exe dist\\ >nul && winapp pack .\\dist --cert .\\devcert.pfx" + "tauri:dev:withidentity": "cargo build --manifest-path src-tauri/Cargo.toml && winapp create-debug-identity src-tauri/target/debug/tauri-app.exe && .\\src-tauri\\target\\debug\\tauri-app.exe" }, "devDependencies": { "@tauri-apps/cli": "^2" diff --git a/samples/tauri-app/src-tauri/Cargo.toml b/samples/tauri-app/src-tauri/Cargo.toml index fb3139ff..230934e7 100644 --- a/samples/tauri-app/src-tauri/Cargo.toml +++ b/samples/tauri-app/src-tauri/Cargo.toml @@ -24,5 +24,5 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" [target.'cfg(windows)'.dependencies] -windows = { version = "0.62.2", features = ["ApplicationModel", "UI_Notifications", "Data_Xml_Dom"] } +windows = { version = "0.58", features = ["ApplicationModel", "UI_Notifications", "Data_Xml_Dom"] } diff --git a/samples/wpf-app/wpf-app.csproj b/samples/wpf-app/wpf-app.csproj index 7f7c88c7..8ff39233 100644 --- a/samples/wpf-app/wpf-app.csproj +++ b/samples/wpf-app/wpf-app.csproj @@ -10,10 +10,10 @@ - + - + diff --git a/scripts/build-cli.ps1 b/scripts/build-cli.ps1 index 35e224bc..59c9ac21 100644 --- a/scripts/build-cli.ps1 +++ b/scripts/build-cli.ps1 @@ -13,6 +13,8 @@ Exit with error code if tests fail (default: true, stops build on test failures) .PARAMETER SkipNpm Skip npm package creation +.PARAMETER SkipVsc + Skip VS Code extension packaging .PARAMETER SkipNuGet Skip NuGet package creation (BuildTools.WinApp) .PARAMETER SkipMsix @@ -27,6 +29,8 @@ .\scripts\build-cli.ps1 -SkipTests .EXAMPLE .\scripts\build-cli.ps1 -SkipNpm +.EXAMPLE + .\scripts\build-cli.ps1 -SkipVsc .EXAMPLE .\scripts\build-cli.ps1 -SkipNuGet .EXAMPLE @@ -40,6 +44,7 @@ param( [switch]$SkipTests = $false, [switch]$FailOnTestFailure = $true, [switch]$SkipNpm = $false, + [switch]$SkipVsc = $false, [switch]$SkipNuGet = $false, [switch]$SkipMsix = $false, [switch]$SkipDocs = $false, @@ -81,84 +86,7 @@ try Write-Host "[SETUP] Creating artifacts directory..." -ForegroundColor Blue New-Item -ItemType Directory -Path $ArtifactsPath -Force | Out-Null - # Step 1: Calculate version - Write-Host "[VERSION] Calculating package version..." -ForegroundColor Blue - - # Read base version from version.json - $VersionJsonPath = "$ProjectRoot\version.json" - if (-not (Test-Path $VersionJsonPath)) { - Write-Error "version.json not found at $VersionJsonPath" - exit 1 - } - - $VersionJson = Get-Content $VersionJsonPath | ConvertFrom-Json - $BaseVersion = $VersionJson.version - - # Get build number - $BuildNumber = & "$PSScriptRoot\get-build-number.ps1" - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to get build number" - exit 1 - } - - # Determine prerelease label based on current branch - # - main and rel/* branches use "prerelease" (default) - # - all other branches use a sanitized branch name (e.g., dev/my-feature -> dev-my-feature) - $PrereleaseLabel = & "$PSScriptRoot\get-prerelease-label.ps1" - Write-Host "[VERSION] Prerelease label: $PrereleaseLabel" -ForegroundColor Gray - - # Construct full version based on Stable flag - if ($Stable) { - # Stable build: use semantic version without prerelease suffix (e.g., "0.1.0") - $FullVersion = $BaseVersion - Write-Host "[VERSION] Using stable version (no prerelease suffix)" -ForegroundColor Cyan - } else { - # Prerelease build: add prerelease label suffix (e.g., "0.1.0-prerelease.73" or "0.1.0-dev-my-feature.73") - $FullVersion = "$BaseVersion-$PrereleaseLabel.$BuildNumber" - Write-Host "[VERSION] Using prerelease version (with $PrereleaseLabel suffix)" -ForegroundColor Cyan - } - Write-Host "[VERSION] Package version: $FullVersion" -ForegroundColor Cyan - - # Extract semantic version components for assembly versioning - # BaseVersion should be in format major.minor.patch (e.g., "0.1.0") - $VersionParts = $BaseVersion -split '\.' - $MajorVersion = $VersionParts[0] - $MinorVersion = $VersionParts[1] - $PatchVersion = $VersionParts[2] - - # Assembly version uses format: major.minor.patch.buildnumber (e.g., "0.1.0.73") - $AssemblyVersion = "$MajorVersion.$MinorVersion.$PatchVersion.$BuildNumber" - Write-Host "[VERSION] Assembly version: $AssemblyVersion" -ForegroundColor Cyan - - # InformationalVersion shows in --version output (e.g., "0.1.0-prerelease.73") - $InformationalVersion = $FullVersion - - # Step 2: Publish CLI for x64 and arm64 (implicitly builds the CLI project) - Write-Host "[PUBLISH] Publishing CLI for x64..." -ForegroundColor Blue - dotnet publish $CliProjectPath -c Release -r win-x64 --self-contained -o "$ArtifactsPath\cli\win-x64" ` - /p:Version=$AssemblyVersion ` - /p:AssemblyVersion=$AssemblyVersion ` - /p:FileVersion=$AssemblyVersion ` - /p:InformationalVersion=$InformationalVersion ` - /p:IncludeSourceRevisionInInformationalVersion=false - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to publish CLI for x64" - exit 1 - } - - Write-Host "[PUBLISH] Publishing CLI for arm64..." -ForegroundColor Blue - dotnet publish $CliProjectPath -c Release -r win-arm64 --self-contained -o "$ArtifactsPath\cli\win-arm64" ` - /p:Version=$AssemblyVersion ` - /p:AssemblyVersion=$AssemblyVersion ` - /p:FileVersion=$AssemblyVersion ` - /p:InformationalVersion=$InformationalVersion ` - /p:IncludeSourceRevisionInInformationalVersion=false - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to publish CLI for arm64" - exit 1 - } - - # Step 3: Build test project (CLI is already built from publish, this mainly compiles tests) + # Step 1: Build CLI solution Write-Host "[BUILD] Building CLI solution..." -ForegroundColor Blue dotnet build $CliSolutionPath -c Release if ($LASTEXITCODE -ne 0) { @@ -166,7 +94,7 @@ try exit 1 } - # Step 4: Build Node CLI so E2E tests that invoke node cli.js can run + # Step 1b: Build Node CLI so E2E tests that invoke node cli.js can run if (-not $SkipNpm) { Write-Host "[BUILD] Building Node CLI (for tests)..." -ForegroundColor Blue Push-Location (Join-Path $ProjectRoot "src\winapp-npm") @@ -188,7 +116,7 @@ try } } - # Step 5: Run tests (unless skipped) + # Step 2: Run tests (unless skipped) if (-not $SkipTests) { Write-Host "[TEST] Running tests..." -ForegroundColor Blue dotnet run --project $CliTestsProjectPath -c Release --no-build --results-directory $CliSolutionDir\TestResults --report-trx --coverage --coverage-output-format cobertura @@ -236,6 +164,84 @@ try Write-Host "[TEST] Skipping tests (SkipTests flag set)" -ForegroundColor Yellow } + # Step 3: Calculate version with build number (moved before publish) + Write-Host "[VERSION] Calculating package version..." -ForegroundColor Blue + + # Read base version from version.json + $VersionJsonPath = "$ProjectRoot\version.json" + if (-not (Test-Path $VersionJsonPath)) { + Write-Error "version.json not found at $VersionJsonPath" + exit 1 + } + + $VersionJson = Get-Content $VersionJsonPath | ConvertFrom-Json + $BaseVersion = $VersionJson.version + + # Get build number + $BuildNumber = & "$PSScriptRoot\get-build-number.ps1" + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to get build number" + exit 1 + } + + # Determine prerelease label based on current branch + # - main and rel/* branches use "prerelease" (default) + # - all other branches use a sanitized branch name (e.g., dev/my-feature -> dev-my-feature) + $PrereleaseLabel = & "$PSScriptRoot\get-prerelease-label.ps1" + Write-Host "[VERSION] Prerelease label: $PrereleaseLabel" -ForegroundColor Gray + + # Construct full version based on Stable flag + if ($Stable) { + # Stable build: use semantic version without prerelease suffix (e.g., "0.1.0") + $FullVersion = $BaseVersion + Write-Host "[VERSION] Using stable version (no prerelease suffix)" -ForegroundColor Cyan + } else { + # Prerelease build: add prerelease label suffix (e.g., "0.1.0-prerelease.73" or "0.1.0-dev-my-feature.73") + $FullVersion = "$BaseVersion-$PrereleaseLabel.$BuildNumber" + Write-Host "[VERSION] Using prerelease version (with $PrereleaseLabel suffix)" -ForegroundColor Cyan + } + Write-Host "[VERSION] Package version: $FullVersion" -ForegroundColor Cyan + + # Extract semantic version components for assembly versioning + # BaseVersion should be in format major.minor.patch (e.g., "0.1.0") + $VersionParts = $BaseVersion -split '\.' + $MajorVersion = $VersionParts[0] + $MinorVersion = $VersionParts[1] + $PatchVersion = $VersionParts[2] + + # Assembly version uses format: major.minor.patch.buildnumber (e.g., "0.1.0.73") + $AssemblyVersion = "$MajorVersion.$MinorVersion.$PatchVersion.$BuildNumber" + Write-Host "[VERSION] Assembly version: $AssemblyVersion" -ForegroundColor Cyan + + # InformationalVersion shows in --version output (e.g., "0.1.0-prerelease.73") + $InformationalVersion = $FullVersion + + # Step 4: Publish CLI for x64 with version properties + Write-Host "[PUBLISH] Publishing CLI for x64..." -ForegroundColor Blue + dotnet publish $CliProjectPath -c Release -r win-x64 --self-contained -o "$ArtifactsPath\cli\win-x64" ` + /p:Version=$AssemblyVersion ` + /p:AssemblyVersion=$AssemblyVersion ` + /p:FileVersion=$AssemblyVersion ` + /p:InformationalVersion=$InformationalVersion ` + /p:IncludeSourceRevisionInInformationalVersion=false + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to publish CLI for x64" + exit 1 + } + + # Step 5: Publish CLI for arm64 with version properties + Write-Host "[PUBLISH] Publishing CLI for arm64..." -ForegroundColor Blue + dotnet publish $CliProjectPath -c Release -r win-arm64 --self-contained -o "$ArtifactsPath\cli\win-arm64" ` + /p:Version=$AssemblyVersion ` + /p:AssemblyVersion=$AssemblyVersion ` + /p:FileVersion=$AssemblyVersion ` + /p:InformationalVersion=$InformationalVersion ` + /p:IncludeSourceRevisionInInformationalVersion=false + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to publish CLI for arm64" + exit 1 + } + # Step 6: Generate CLI schema and agent skills (optional) if (-not $SkipDocs) { Write-Host "" @@ -288,6 +294,44 @@ try Write-Host "[NPM] Skipping npm package creation (use -SkipNpm:`$false to enable)" -ForegroundColor Gray } + # Step 8: Create VS Code extension package (optional) + if (-not $SkipVsc) { + Write-Host "" + Write-Host "[VSC] Creating VS Code extension package..." -ForegroundColor Blue + + $PackageVscScript = Join-Path $PSScriptRoot "package-vsc.ps1" + + & $PackageVscScript -Version $FullVersion -Stable:$Stable + + if ($LASTEXITCODE -ne 0) { + Write-Warning "VS Code extension packaging failed, but continuing..." + } else { + Write-Host "[VSC] VS Code extension packaged successfully!" -ForegroundColor Green + } + } else { + Write-Host "" + Write-Host "[VSC] Skipping VS Code extension packaging (use -SkipVsc:`$false to enable)" -ForegroundColor Gray + } + + # Step 9: Create NuGet packages (optional) + if (-not $SkipNuGet) { + Write-Host "" + Write-Host "[NUGET] Creating NuGet packages..." -ForegroundColor Blue + + $PackageNuGetScript = Join-Path $PSScriptRoot "package-nuget.ps1" + + & $PackageNuGetScript -Version $FullVersion -Stable:$Stable + + if ($LASTEXITCODE -ne 0) { + Write-Warning "NuGet packages creation failed, but continuing..." + } else { + Write-Host "[NUGET] NuGet packages created successfully!" -ForegroundColor Green + } + } else { + Write-Host "" + Write-Host "[NUGET] Skipping NuGet packages creation (use -SkipNuGet:`$false to enable)" -ForegroundColor Gray + } + # Step 8: Create NuGet packages (optional) if (-not $SkipNuGet) { Write-Host "" diff --git a/scripts/generate-llm-docs.ps1 b/scripts/generate-llm-docs.ps1 index 815371f1..8a9eac46 100644 --- a/scripts/generate-llm-docs.ps1 +++ b/scripts/generate-llm-docs.ps1 @@ -237,7 +237,7 @@ $SkillDescriptions = @{ "package" = "Package a Windows app as an MSIX installer for distribution or testing. Use when creating a Windows installer, packaging an Electron/Flutter/.NET/Rust/C++/Tauri app for Windows, building an MSIX, distributing a desktop app, packaging a console app or CLI tool, or adding MSIX packaging to a build script or CI/CD pipeline." "identity" = "Enable Windows package identity for desktop apps to access Windows APIs like push notifications, background tasks, share target, and startup tasks. Use when adding Windows notifications, background tasks, or other identity-requiring Windows features to a desktop app." "signing" = "Create and manage code signing certificates for Windows apps and MSIX packages. Use when generating a certificate, signing a Windows app or installer, or fixing certificate trust issues." - "manifest" = "Create and edit Windows app manifest files (appxmanifest.xml) that define app identity, capabilities, and visual assets, or generate new assets from existing images. Use when creating a Windows app manifest for any app type (GUI, console, CLI tool, service), adding Windows capabilities, generating new app icons and assets, or adding execution aliases, file associations, protocol handlers, or other app extensions." + "manifest" = "Create and edit Windows app manifest files (appxmanifest.xml) that define app identity, capabilities, and visual assets. Use when creating a Windows app manifest for any app type (GUI, console, CLI tool, service), adding Windows capabilities, updating app icons and assets, or adding execution aliases, file associations, protocol handlers, or other app extensions." "troubleshoot" = "Diagnose and fix common Windows app packaging, signing, identity, and SDK errors. Use when encountering errors with MSIX packaging, certificate signing, Windows SDK setup, or app installation." "frameworks" = "Framework-specific Windows development guidance for Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Use when packaging or adding Windows features to an Electron app, .NET desktop app, Flutter app, Tauri app, Rust app, or C++ app." } diff --git a/scripts/package-msix.ps1 b/scripts/package-msix.ps1 index bf8e35e6..412f30af 100644 --- a/scripts/package-msix.ps1 +++ b/scripts/package-msix.ps1 @@ -223,19 +223,18 @@ try Write-Host "[COPY] Creating $Architecture package layout..." -ForegroundColor Blue - # Copy exe and native runtime dependencies (e.g., libSkiaSharp.dll), exclude PDBs - Write-Host " - Copying binaries from $SourceBinPath..." -ForegroundColor Gray + # Copy only the exe from the source + Write-Host " - Copying winapp.exe from $SourceBinPath..." -ForegroundColor Gray $SourceExe = Join-Path $SourceBinPath "winapp.exe" + $TargetExe = Join-Path $LayoutPath "winapp.exe" if (-not (Test-Path $SourceExe)) { Write-Error "winapp.exe not found at $SourceExe" return } - Get-ChildItem -Path $SourceBinPath -File | Where-Object { $_.Extension -ne '.pdb' } | ForEach-Object { - Copy-Item $_.FullName $LayoutPath -Force - Write-Host " - Copied $($_.Name)" -ForegroundColor Gray - } + Copy-Item $SourceExe $TargetExe -Force + Write-Host " - Copied winapp.exe" -ForegroundColor Gray # Copy Assets folder $TargetAssetsPath = Join-Path $LayoutPath "Assets" diff --git a/scripts/package-nuget.ps1 b/scripts/package-nuget.ps1 index 020db603..bd877a21 100644 --- a/scripts/package-nuget.ps1 +++ b/scripts/package-nuget.ps1 @@ -10,6 +10,8 @@ If not specified, reads from version.json and calculates based on Stable flag. .PARAMETER Stable Use stable build configuration (default: false, uses prerelease config) +.PARAMETER SkipTemplates + Skip building the templates package .EXAMPLE .\scripts\package-nuget.ps1 .\scripts\package-nuget.ps1 -Version "1.0.0" -Stable @@ -33,6 +35,7 @@ try $CliBinariesPath = Join-Path $ProjectRoot "artifacts\cli" $OutputPath = Join-Path $ProjectRoot "artifacts\nuget" $ExtrasProjectPath = Join-Path $ProjectRoot "src\winapp-NuGet" + $TemplatesProjectPath = Join-Path $ProjectRoot "src\winapp-Templates" Write-Host "[NUGET] Starting NuGet package creation..." -ForegroundColor Green Write-Host "[INFO] Project root: $ProjectRoot" -ForegroundColor Gray @@ -144,13 +147,8 @@ try New-Item -ItemType Directory -Path $ToolsX64Path -Force | Out-Null New-Item -ItemType Directory -Path $ToolsArm64Path -Force | Out-Null - # Copy all files except PDBs (includes native runtime dependencies like libSkiaSharp.dll) - Get-ChildItem -Path $X64Path -File | Where-Object { $_.Extension -ne '.pdb' } | ForEach-Object { - Copy-Item $_.FullName $ToolsX64Path -Force - } - Get-ChildItem -Path $Arm64Path -File | Where-Object { $_.Extension -ne '.pdb' } | ForEach-Object { - Copy-Item $_.FullName $ToolsArm64Path -Force - } + Copy-Item -Path "$X64Path\*.exe" -Destination $ToolsX64Path -Recurse -Force + Copy-Item -Path "$Arm64Path\*.exe" -Destination $ToolsArm64Path -Recurse -Force Write-Host "[COPY] CLI binaries copied successfully" -ForegroundColor Green @@ -168,6 +166,46 @@ try Write-Host "[NUGET] Microsoft.Windows.SDK.BuildTools.WinApp package created successfully!" -ForegroundColor Green + # ============================================================================ + # Step 2: Build Microsoft.WindowsAppSDK.Templates package + # ============================================================================ + if (-not $SkipTemplates) { + Write-Host "" + Write-Host "[NUGET] Building Microsoft.WindowsAppSDK.Templates package..." -ForegroundColor Blue + + $TemplatesCsproj = Join-Path $TemplatesProjectPath "Microsoft.WindowsAppSDK.Templates.csproj" + + # Update the template.json to reference the correct Extras version + $TemplateJsonPath = Join-Path $TemplatesProjectPath "templates\winui\.template.config\template.json" + Write-Host "[TEMPLATE] Updating ExtrasVersion in template.json to $Version..." -ForegroundColor Blue + + # Backup original template.json before modifying + Copy-Item $TemplateJsonPath "$TemplateJsonPath.backup" -Force + + $TemplateJson = Get-Content $TemplateJsonPath -Raw | ConvertFrom-Json + $TemplateJson.symbols.ExtrasVersion.defaultValue = $Version + $TemplateJson | ConvertTo-Json -Depth 10 | Set-Content $TemplateJsonPath -Encoding UTF8 + + dotnet pack $TemplatesCsproj -c Release -o $OutputPath /p:Version=$Version /p:PackageVersion=$Version + $PackResult = $LASTEXITCODE + + # Restore original template.json + Write-Host "[TEMPLATE] Restoring original template.json..." -ForegroundColor Blue + if (Test-Path "$TemplateJsonPath.backup") { + Move-Item "$TemplateJsonPath.backup" $TemplateJsonPath -Force + } + + if ($PackResult -ne 0) { + Write-Error "Failed to create Microsoft.WindowsAppSDK.Templates NuGet package" + exit 1 + } + + Write-Host "[NUGET] Microsoft.WindowsAppSDK.Templates package created successfully!" -ForegroundColor Green + } else { + Write-Host "" + Write-Host "[NUGET] Skipping templates package (SkipTemplates flag set)" -ForegroundColor Yellow + } + # ============================================================================ # Summary # ============================================================================ @@ -184,6 +222,9 @@ try Write-Host "[INFO] To test locally, add the output path as a NuGet source:" -ForegroundColor Cyan Write-Host " dotnet nuget add source `"$OutputPath`" --name WinAppLocal" -ForegroundColor Gray Write-Host "" + Write-Host "[INFO] Then install the template:" -ForegroundColor Cyan + Write-Host " dotnet new install Microsoft.WindowsAppSDK.Templates::$Version --nuget-source WinAppLocal" -ForegroundColor Gray + Write-Host "" Write-Host "[INFO] And create a new project:" -ForegroundColor Cyan Write-Host " dotnet new winui -n MyApp" -ForegroundColor Gray Write-Host " cd MyApp" -ForegroundColor Gray diff --git a/scripts/package-vsc.ps1 b/scripts/package-vsc.ps1 new file mode 100644 index 00000000..2f9b8236 --- /dev/null +++ b/scripts/package-vsc.ps1 @@ -0,0 +1,237 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Package Windows App Development CLI as VS Code extension (VSIX) +.DESCRIPTION + This script creates a VSIX package from pre-built CLI binaries for x64 and arm64 architectures. + Uses artifacts/cli for binaries and outputs to artifacts directory. +.PARAMETER Version + Version number for the VSIX package (e.g., "0.1.0" or "0.1.0-prerelease.73"). + If not specified, reads from version.json and calculates based on Stable flag. +.PARAMETER Stable + Use stable build configuration (default: false, uses prerelease config) +.EXAMPLE + .\scripts\package-vsc.ps1 + .\scripts\package-vsc.ps1 -Version "0.1.0" -Stable + .\scripts\package-vsc.ps1 -Version "0.1.0-prerelease.73" +#> + +param( + [Parameter(Mandatory=$false)] + [string]$Version, + + [Parameter(Mandatory=$false)] + [switch]$Stable = $false +) + +# Ensure we're running from the project root +$ProjectRoot = $PSScriptRoot | Split-Path -Parent +Push-Location $ProjectRoot +try +{ + # Define standard paths + $CliBinariesPath = Join-Path $ProjectRoot "artifacts\cli" + $OutputPath = Join-Path $ProjectRoot "artifacts" + + Write-Host "[VSC] Starting VS Code extension packaging..." -ForegroundColor Green + Write-Host "[INFO] Project root: $ProjectRoot" -ForegroundColor Gray + Write-Host "[INFO] CLI binaries path: $CliBinariesPath" -ForegroundColor Gray + Write-Host "[INFO] Output path: $OutputPath" -ForegroundColor Gray + + # Validate that the CLI binaries path exists + if (-not (Test-Path $CliBinariesPath)) { + Write-Error "CLI binaries path does not exist: $CliBinariesPath. Run build-cli.ps1 first." + exit 1 + } + + # Validate that required architecture folders exist + $X64Path = Join-Path $CliBinariesPath "win-x64" + $Arm64Path = Join-Path $CliBinariesPath "win-arm64" + + if (-not (Test-Path $X64Path)) { + Write-Error "win-x64 folder not found at: $X64Path" + exit 1 + } + + if (-not (Test-Path $Arm64Path)) { + Write-Error "win-arm64 folder not found at: $Arm64Path" + exit 1 + } + + Write-Host "[VALIDATE] Found CLI binaries:" -ForegroundColor Green + Write-Host " - x64: $X64Path" -ForegroundColor Gray + Write-Host " - arm64: $Arm64Path" -ForegroundColor Gray + + # Validate that the main executable exists in both folders + $X64Exe = Join-Path $X64Path "winapp.exe" + $Arm64Exe = Join-Path $Arm64Path "winapp.exe" + + if (-not (Test-Path $X64Exe)) { + Write-Error "winapp.exe not found in x64 folder: $X64Exe" + exit 1 + } + + if (-not (Test-Path $Arm64Exe)) { + Write-Error "winapp.exe not found in arm64 folder: $Arm64Exe" + exit 1 + } + + Write-Host "[VALIDATE] All required files found!" -ForegroundColor Green + + # Calculate version if not provided + if ([string]::IsNullOrEmpty($Version)) { + Write-Host "[VERSION] Calculating package version..." -ForegroundColor Blue + + # Read base version from version.json + $VersionJsonPath = "$ProjectRoot\version.json" + if (-not (Test-Path $VersionJsonPath)) { + Write-Error "version.json not found at $VersionJsonPath" + exit 1 + } + + $VersionJson = Get-Content $VersionJsonPath | ConvertFrom-Json + $BaseVersion = $VersionJson.version + + # Get build number + $GetBuildNumberScript = Join-Path $PSScriptRoot "get-build-number.ps1" + $BuildNumber = & $GetBuildNumberScript + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to get build number" + exit 1 + } + + # Construct full version based on Stable flag + if ($Stable) { + $Version = $BaseVersion + Write-Host "[VERSION] Using stable version (no prerelease suffix)" -ForegroundColor Cyan + } else { + $Version = "$BaseVersion-prerelease.$BuildNumber" + Write-Host "[VERSION] Using prerelease version (with prerelease suffix)" -ForegroundColor Cyan + } + } + + Write-Host "[VERSION] Package version: $Version" -ForegroundColor Cyan + + # Ensure output directory exists + if (-not (Test-Path $OutputPath)) { + New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null + Write-Host "[SETUP] Created output directory: $OutputPath" -ForegroundColor Blue + } + + # Navigate to VSC project directory + $VscProjectPath = Join-Path $ProjectRoot "src\winapp-VSC" + if (-not (Test-Path $VscProjectPath)) { + Write-Error "VS Code extension project path does not exist: $VscProjectPath" + exit 1 + } + + Write-Host "[VSC] Preparing VS Code extension..." -ForegroundColor Blue + + Push-Location $VscProjectPath + + # Clean bin and out directories + npm run clean + if ($LASTEXITCODE -ne 0) { + Write-Warning "npm clean failed, continuing..." + } + + # Install dependencies + Write-Host "[VSC] Installing dependencies..." -ForegroundColor Blue + npm ci + if ($LASTEXITCODE -ne 0) { + Write-Error "npm ci failed" + Pop-Location + exit 1 + } + + # Compile TypeScript + Write-Host "[VSC] Compiling TypeScript..." -ForegroundColor Blue + npm run compile + if ($LASTEXITCODE -ne 0) { + Write-Error "TypeScript compilation failed" + Pop-Location + exit 1 + } + + # Copy CLI binaries from artifacts + Write-Host "[VSC] Copying CLI binaries to extension..." -ForegroundColor Blue + $VscBinPath = "bin" + New-Item -ItemType Directory -Path "$VscBinPath\win-x64" -Force | Out-Null + New-Item -ItemType Directory -Path "$VscBinPath\win-arm64" -Force | Out-Null + + Copy-Item "$CliBinariesPath\win-x64\*.exe" "$VscBinPath\win-x64\" -Force + Copy-Item "$CliBinariesPath\win-arm64\*.exe" "$VscBinPath\win-arm64\" -Force + + # Copy LICENSE from project root + Copy-Item "$ProjectRoot\LICENSE" "LICENSE" -Force + + # Backup original package.json + Write-Host "[VSC] Setting package version to $Version..." -ForegroundColor Blue + $PackageJsonPath = "package.json" + Copy-Item $PackageJsonPath "$PackageJsonPath.backup" -Force + + # Update package.json version temporarily + $PackageJson = Get-Content $PackageJsonPath | ConvertFrom-Json + $PackageJson.version = $Version + $PackageJson | ConvertTo-Json -Depth 100 | Set-Content $PackageJsonPath + + # Check if vsce is available, install if needed + $VsceCmd = Get-Command vsce -ErrorAction SilentlyContinue + if (-not $VsceCmd) { + Write-Host "[VSC] Installing @vscode/vsce..." -ForegroundColor Blue + npm install -g @vscode/vsce + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to install @vscode/vsce" + # Restore package.json before exiting + Move-Item "$PackageJsonPath.backup" $PackageJsonPath -Force + Pop-Location + exit 1 + } + } + + # Package the VSIX + Write-Host "[PACK] Creating VSIX package..." -ForegroundColor Blue + + $RelativeOutputPath = [System.IO.Path]::GetRelativePath($VscProjectPath, $OutputPath) + + vsce package --no-dependencies -o "$RelativeOutputPath\winapp-$Version.vsix" + $PackResult = $LASTEXITCODE + + # Restore original package.json + Write-Host "[VSC] Restoring original package.json..." -ForegroundColor Blue + if (Test-Path "$PackageJsonPath.backup") { + Move-Item "$PackageJsonPath.backup" $PackageJsonPath -Force + } + + # Remove copied LICENSE + if (Test-Path "LICENSE") { + Remove-Item "LICENSE" -Force + } + + Pop-Location + + if ($PackResult -ne 0) { + Write-Error "Failed to create VSIX package" + exit 1 + } + + # Find the created VSIX and report success + $CreatedVsix = Get-ChildItem -Path $OutputPath -Filter "winapp-*.vsix" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + + if ($CreatedVsix) { + $VsixSize = [math]::Round($CreatedVsix.Length / 1MB, 2) + Write-Host "" + Write-Host "[SUCCESS] VS Code extension packaged successfully!" -ForegroundColor Green + Write-Host "[INFO] Package: $($CreatedVsix.Name) ($VsixSize MB)" -ForegroundColor Cyan + Write-Host "[INFO] Location: $($CreatedVsix.FullName)" -ForegroundColor Cyan + } else { + Write-Warning "VSIX was created but could not be located in $OutputPath" + } + + Write-Host "[DONE] VS Code extension packaging complete!" -ForegroundColor Green +} +finally +{ + # Restore original working directory + Pop-Location +} diff --git a/scripts/setup-winapprun.ps1 b/scripts/setup-winapprun.ps1 index 7877aa0e..4f6eae7d 100644 --- a/scripts/setup-winapprun.ps1 +++ b/scripts/setup-winapprun.ps1 @@ -1,11 +1,12 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Set up the winapp run command for selfhosting. + Set up the winapp run command and VS Code extension for selfhosting. .DESCRIPTION This script installs (or upgrades) the following from the local artifacts folder: 1. winapp MSIX package (architecture-matched) + its signing certificate - 2. Registers a local NuGet feed for Microsoft.Windows.SDK.BuildTools.WinApp + 2. winapp VS Code extension (.vsix) + 3. Registers a local NuGet feed for Microsoft.Windows.SDK.BuildTools.WinApp Re-running with newer files will upgrade everything in place. .PARAMETER Elevated @@ -202,7 +203,31 @@ try { } # ═══════════════════════════════════════════════════════════════════════════════ -# 2. Local NuGet feed for BuildTools.MSIX.Extras +# 2. VS Code extension (.vsix) +# ═══════════════════════════════════════════════════════════════════════════════ +Write-Step "Installing VS Code extension" + +$vsix = Get-ChildItem -Path $ScriptDir -Filter "*.vsix" | Select-Object -First 1 +if (-not $vsix) { + Write-Skip "No .vsix file found in $ScriptDir -- skipping" +} else { + Write-Detail "VSIX : $($vsix.Name)" + + $codeCli = Get-Command code -ErrorAction SilentlyContinue + if (-not $codeCli) { + Write-Skip "'code' not found on PATH -- install the VSIX manually in VS Code" + } else { + try { + & code --install-extension $vsix.FullName --force 2>&1 | Out-Null + Write-Ok "VS Code extension installed / upgraded" + } catch { + Write-Err "Failed to install VSIX: $_" + } + } +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# 3. Local NuGet feed for BuildTools.MSIX.Extras # ═══════════════════════════════════════════════════════════════════════════════ Write-Step "Setting up local NuGet feed for BuildTools.MSIX.Extras" @@ -257,6 +282,7 @@ Write-Host " All done!" -ForegroundColor Green Write-Host "=======================================" -ForegroundColor Green Write-Host "" Write-Host " winapp CLI : winapp --version" -ForegroundColor Gray +Write-Host " VS Code ext : Extensions sidebar -> search 'winapp'" -ForegroundColor Gray Write-Host " NuGet feed : dotnet nuget list source" -ForegroundColor Gray Write-Host "" diff --git a/scripts/test-e2e-electron.ps1 b/scripts/test-e2e-electron.ps1 index c11651e6..e26e5eb2 100644 --- a/scripts/test-e2e-electron.ps1 +++ b/scripts/test-e2e-electron.ps1 @@ -371,7 +371,7 @@ try { Write-TestStep "Adding Electron debug identity..." 11 - $addIdentityCommand = "npx winapp node add-electron-debug-identity" + $addIdentityCommand = "npx winapp node add-electron-debug-identity --no-install" Assert-Command $addIdentityCommand "Failed to add Electron debug identity" # ======================================================================== diff --git a/src/winapp-CLI/Directory.Packages.props b/src/winapp-CLI/Directory.Packages.props index 8985472c..0c457ae5 100644 --- a/src/winapp-CLI/Directory.Packages.props +++ b/src/winapp-CLI/Directory.Packages.props @@ -1,14 +1,13 @@ - - + + - - + + - - + diff --git a/src/winapp-CLI/WinApp.Cli.Tests/AppLauncherServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/AppLauncherServiceTests.cs deleted file mode 100644 index 23aff463..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/AppLauncherServiceTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class AppLauncherServiceTests -{ - private readonly AppLauncherService _service = new( - new Microsoft.Extensions.Logging.Abstractions.NullLogger()); - - // Known publisher → publisherId mappings obtained from Get-AppxPackage on Windows. - // These are the ground truth values computed by the Windows platform. - - [TestMethod] - [DataRow( - "CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", - "cw5n1h2txyewy", - DisplayName = "Microsoft Windows publisher")] - [DataRow( - "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", - "8wekyb3d8bbwe", - DisplayName = "Microsoft Corporation publisher")] - [DataRow( - "CN=CA0D5344-F590-41F9-BE2C-16BE6FCEE1DF", - "rn9aeerfb38dg", - DisplayName = "GUID-style publisher")] - [DataRow( - "CN=83564403-0B26-46B8-9D84-040F43691D31", - "dt26b99r8h8gj", - DisplayName = "GUID-style publisher 2")] - [DataRow( - "CN=Metulev", - "j3adjyj8sqwmw", - DisplayName = "Simple CN publisher")] - public void ComputePackageFamilyName_MatchesWindowsValue(string publisher, string expectedPublisherId) - { - var pfn = _service.ComputePackageFamilyName("TestPackage", publisher); - - Assert.AreEqual($"TestPackage_{expectedPublisherId}", pfn); - } - - [TestMethod] - public void ComputePackageFamilyName_PublisherIsCaseSensitive() - { - // Windows treats publisher DN as case-sensitive for hash computation. - // "CN=Test" and "cn=test" produce different publisher IDs. - var pfn1 = _service.ComputePackageFamilyName("Pkg", "CN=Test"); - var pfn2 = _service.ComputePackageFamilyName("Pkg", "cn=test"); - - Assert.AreNotEqual(pfn1, pfn2, "Publisher comparison should be case-sensitive"); - } - - [TestMethod] - public void ComputePackageFamilyName_PublisherIdIs13Chars() - { - var pfn = _service.ComputePackageFamilyName("Pkg", "CN=AnyPublisher"); - - // Format: {name}_{publisherId} where publisherId is exactly 13 chars - var parts = pfn.Split('_'); - Assert.AreEqual(2, parts.Length, "PFN should have exactly one underscore"); - Assert.AreEqual(13, parts[1].Length, "Publisher ID should be exactly 13 characters"); - } - - [TestMethod] - public void ComputePackageFamilyName_PublisherIdIsLowercase() - { - var pfn = _service.ComputePackageFamilyName("Pkg", "CN=SomePublisher"); - var publisherId = pfn.Split('_')[1]; - - Assert.AreEqual(publisherId, publisherId.ToLowerInvariant(), - "Publisher ID should be lowercase"); - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs deleted file mode 100644 index 07068827..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs +++ /dev/null @@ -1,780 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class AppxManifestDocumentTests -{ - private const string MinimalManifest = """ - - - - - Test App - - - - - - - - - - - - - - """; - - private const string BareMinimalManifest = """ - - - - - """; - - #region Parse / ToXml Round-Trip - - [TestMethod] - public void Parse_AndToXml_RoundTrips_PreservesContent() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - var xml = doc.ToXml(); - - Assert.Contains("TestApp", xml); - Assert.Contains("CN=Test", xml); - Assert.Contains("1.0.0.0", xml); - } - - [TestMethod] - public void Load_FromFile_AndSave_RoundTrips() - { - var tempDir = Path.Combine(Path.GetTempPath(), $"AppxDocTest_{Guid.NewGuid():N}"); - Directory.CreateDirectory(tempDir); - try - { - var filePath = Path.Combine(tempDir, "appxmanifest.xml"); - File.WriteAllText(filePath, MinimalManifest); - - var doc = AppxManifestDocument.Load(filePath); - Assert.AreEqual("TestApp", doc.IdentityName); - - doc.IdentityName = "ModifiedApp"; - var savePath = Path.Combine(tempDir, "saved.xml"); - doc.Save(savePath); - - var reloaded = AppxManifestDocument.Load(savePath); - Assert.AreEqual("ModifiedApp", reloaded.IdentityName); - - // Verify UTF-8 no BOM - var bytes = File.ReadAllBytes(savePath); - Assert.IsFalse(bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF, - "File should not have a UTF-8 BOM"); - } - finally - { - Directory.Delete(tempDir, true); - } - } - - [TestMethod] - public void Load_FromStream_Works() - { - using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(MinimalManifest)); - var doc = AppxManifestDocument.Load(stream); - - Assert.AreEqual("TestApp", doc.IdentityName); - Assert.AreEqual("CN=Test", doc.IdentityPublisher); - } - - #endregion - - #region Identity Properties - - [TestMethod] - public void IdentityName_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("TestApp", doc.IdentityName); - doc.IdentityName = "NewName"; - Assert.AreEqual("NewName", doc.IdentityName); - } - - [TestMethod] - public void IdentityPublisher_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("CN=Test", doc.IdentityPublisher); - doc.IdentityPublisher = "CN=NewPublisher"; - Assert.AreEqual("CN=NewPublisher", doc.IdentityPublisher); - } - - [TestMethod] - public void IdentityVersion_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("1.0.0.0", doc.IdentityVersion); - doc.IdentityVersion = "2.0.0.0"; - Assert.AreEqual("2.0.0.0", doc.IdentityVersion); - } - - [TestMethod] - public void IdentityProcessorArchitecture_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("x64", doc.IdentityProcessorArchitecture); - doc.IdentityProcessorArchitecture = "arm64"; - Assert.AreEqual("arm64", doc.IdentityProcessorArchitecture); - } - - [TestMethod] - public void IdentityProperties_SetNull_RemovesAttribute() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.IdentityProcessorArchitecture = null; - Assert.IsNull(doc.IdentityProcessorArchitecture); - - // Other attributes should be unaffected - Assert.AreEqual("TestApp", doc.IdentityName); - } - - [TestMethod] - public void IdentityProperties_CreatesIdentityElement_WhenMissing() - { - // A manifest with no Identity element - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - Assert.IsNull(doc.IdentityName); - - doc.IdentityName = "CreatedApp"; - Assert.AreEqual("CreatedApp", doc.IdentityName); - Assert.IsNotNull(doc.GetIdentityElement()); - } - - [TestMethod] - public void IdentityProperties_SetNullOnMissingElement_IsNoOp() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - // Should not throw or create an Identity element - doc.IdentityName = null; - Assert.IsNull(doc.GetIdentityElement()); - } - - #endregion - - #region Application Properties - - [TestMethod] - public void ApplicationId_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("App", doc.ApplicationId); - doc.ApplicationId = "NewApp"; - Assert.AreEqual("NewApp", doc.ApplicationId); - } - - [TestMethod] - public void ApplicationExecutable_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("TestApp.exe", doc.ApplicationExecutable); - doc.ApplicationExecutable = "Other.exe"; - Assert.AreEqual("Other.exe", doc.ApplicationExecutable); - } - - [TestMethod] - public void ApplicationEntryPoint_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("Windows.FullTrustApplication", doc.ApplicationEntryPoint); - doc.ApplicationEntryPoint = "App.Main"; - Assert.AreEqual("App.Main", doc.ApplicationEntryPoint); - } - - [TestMethod] - public void ApplicationProperties_ReturnNull_WhenNoApplicationElement() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - Assert.IsNull(doc.ApplicationId); - Assert.IsNull(doc.ApplicationExecutable); - Assert.IsNull(doc.ApplicationEntryPoint); - } - - [TestMethod] - public void ApplicationProperties_SetOnMissing_IsNoOp() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.ApplicationId = "ShouldNotThrow"; - Assert.IsNull(doc.ApplicationId); // still null because there's no Application element - } - - #endregion - - #region VisualElements - - [TestMethod] - public void VisualElementsDisplayName_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("Test App", doc.VisualElementsDisplayName); - doc.VisualElementsDisplayName = "New Name"; - Assert.AreEqual("New Name", doc.VisualElementsDisplayName); - } - - [TestMethod] - public void VisualElementsDisplayName_ReturnsNull_WhenMissing() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - Assert.IsNull(doc.VisualElementsDisplayName); - } - - #endregion - - #region Resource Languages - - [TestMethod] - public void GetResourceLanguages_ReturnsList() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - var languages = doc.GetResourceLanguages(); - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("en-US", languages[0]); - } - - [TestMethod] - public void GetResourceLanguages_ReturnsEmpty_WhenNoResources() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - var languages = doc.GetResourceLanguages(); - Assert.AreEqual(0, languages.Count); - } - - [TestMethod] - public void SetResourceLanguages_SingleLanguage() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetResourceLanguages(["fr-FR"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("fr-FR", languages[0]); - } - - [TestMethod] - public void SetResourceLanguages_MultipleLanguages() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetResourceLanguages(["en-US", "fr-FR", "de-DE"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(3, languages.Count); - Assert.AreEqual("en-US", languages[0]); - Assert.AreEqual("fr-FR", languages[1]); - Assert.AreEqual("de-DE", languages[2]); - } - - [TestMethod] - public void SetResourceLanguages_ReplacesExisting() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - // Replace en-US with ja-JP - doc.SetResourceLanguages(["ja-JP"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("ja-JP", languages[0]); - Assert.DoesNotContain("en-US", doc.ToXml()); - } - - [TestMethod] - public void SetResourceLanguages_CreatesResourcesElement_WhenMissing() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.SetResourceLanguages(["en-US"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("en-US", languages[0]); - } - - #endregion - - #region Namespace Management - - [TestMethod] - public void AddIgnorableNamespace_AddsNew() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.AddIgnorableNamespace("rescap"); - - var result = doc.ToXml(); - Assert.Contains("IgnorableNamespaces=\"uap rescap\"", result); - } - - [TestMethod] - public void AddIgnorableNamespace_SkipsDuplicate() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.AddIgnorableNamespace("rescap"); - - var result = doc.ToXml(); - // Should not duplicate - Assert.AreEqual(1, CountOccurrences(result, "rescap")); - } - - [TestMethod] - public void AddIgnorableNamespace_CreatesAttribute_WhenMissing() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.AddIgnorableNamespace("build"); - - var result = doc.ToXml(); - Assert.Contains("IgnorableNamespaces=\"build\"", result); - } - - [TestMethod] - public void EnsureNamespace_AddsXmlnsDeclaration() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.EnsureNamespace("build", AppxManifestDocument.BuildNs); - - var result = doc.ToXml(); - Assert.Contains("xmlns:build=\"http://schemas.microsoft.com/developer/appx/2015/build\"", result); - } - - [TestMethod] - public void EnsureNamespace_DoesNotDuplicate() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.EnsureNamespace("build", AppxManifestDocument.BuildNs); - - var result = doc.ToXml(); - Assert.AreEqual(1, CountOccurrences(result, "xmlns:build=")); - } - - #endregion - - #region Capabilities - - [TestMethod] - public void EnsureCapability_AddsNew() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.EnsureCapability("runFullTrust"); - - var xml = doc.ToXml(); - Assert.Contains("runFullTrust", xml); - Assert.Contains(" - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - // Adding the same capability (case-insensitive match) should not duplicate - doc.EnsureCapability("runFullTrust"); - - var result = doc.ToXml(); - Assert.AreEqual(1, CountOccurrences(result, "runFullTrust")); - } - - [TestMethod] - public void EnsureCapability_WithCustomNamespace() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.EnsureCapability("runFullTrust", AppxManifestDocument.RescapNs); - - var result = doc.ToXml(); - Assert.Contains("runFullTrust", result); - } - - [TestMethod] - public void EnsureCapability_CreatesCapabilitiesElement_WhenMissing() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - Assert.IsNull(doc.GetCapabilitiesElement()); - - doc.EnsureCapability("internetClient"); - - Assert.IsNotNull(doc.GetCapabilitiesElement()); - Assert.Contains("internetClient", doc.ToXml()); - } - - #endregion - - #region Build Metadata - - [TestMethod] - public void SetBuildMetadata_CreatesSection() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.SetBuildMetadata("Microsoft.WinAppCli", "1.0.0"); - - var xml = doc.ToXml(); - Assert.Contains("Microsoft.WinAppCli", xml); - Assert.Contains("1.0.0", xml); - } - - [TestMethod] - public void SetBuildMetadata_UpdatesExisting() - { - var xml = """ - - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.SetBuildMetadata("Microsoft.WinAppCli", "2.0.0"); - - var result = doc.ToXml(); - Assert.Contains("2.0.0", result); - Assert.DoesNotContain("0.0.1", result); - Assert.AreEqual(1, CountOccurrences(result, "Microsoft.WinAppCli")); - } - - [TestMethod] - public void SetBuildMetadata_AddsAlongsideExisting() - { - var xml = """ - - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.SetBuildMetadata("Microsoft.WinAppCli", "3.0.0"); - - var result = doc.ToXml(); - Assert.Contains("OtherTool", result); - Assert.Contains("Microsoft.WinAppCli", result); - } - - #endregion - - #region Package-Level Extensions - - [TestMethod] - public void GetOrCreatePackageLevelExtensionsElement_CreatesAfterApplications() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - Assert.IsNull(doc.GetExtensionsElement()); // no package-level extensions yet - - var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); - Assert.IsNotNull(extensions); - - var xml = doc.ToXml(); - var applicationsClose = xml.IndexOf("", StringComparison.Ordinal); - var extensionsOpen = xml.IndexOf(" applicationsClose, - "Package-level Extensions should be after "); - } - - [TestMethod] - public void GetOrCreatePackageLevelExtensionsElement_ReturnsExisting() - { - var xml = """ - - - - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); - - Assert.IsNotNull(extensions); - Assert.IsTrue(extensions.HasElements, "Should return existing Extensions with children"); - } - - [TestMethod] - public void AddInProcessServerExtension_AddsCorrectStructure() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.AddInProcessServerExtension("MyRuntime.dll", ["My.Namespace.ClassA", "My.Namespace.ClassB"]); - - var xml = doc.ToXml(); - Assert.Contains("MyRuntime.dll", xml); - Assert.Contains("My.Namespace.ClassA", xml); - Assert.Contains("My.Namespace.ClassB", xml); - Assert.Contains("windows.activatableClass.inProcessServer", xml); - Assert.Contains("ThreadingModel=\"both\"", xml); - } - - [TestMethod] - public void GetRegisteredExtensionDllPaths_ReturnsAllPaths() - { - var xml = """ - - - - - RuntimeA.dll - - - - - - RuntimeB.dll - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - var paths = doc.GetRegisteredExtensionDllPaths(); - - Assert.AreEqual(2, paths.Count); - Assert.IsTrue(paths.Contains("RuntimeA.dll")); - Assert.IsTrue(paths.Contains("RuntimeB.dll")); - } - - [TestMethod] - public void GetRegisteredExtensionDllPaths_IsCaseInsensitive() - { - var xml = """ - - - - - Runtime.dll - - - - - runtime.dll - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - var paths = doc.GetRegisteredExtensionDllPaths(); - - // Case-insensitive dedup: should be only 1 - Assert.AreEqual(1, paths.Count); - } - - [TestMethod] - public void GetRegisteredExtensionDllPaths_ReturnsEmpty_WhenNoPaths() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - var paths = doc.GetRegisteredExtensionDllPaths(); - Assert.AreEqual(0, paths.Count); - } - - #endregion - - #region Package Dependencies - - [TestMethod] - public void HasPackageDependency_ReturnsFalse_WhenNone() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - Assert.IsFalse(doc.HasPackageDependency("Microsoft.WindowsAppRuntime")); - } - - [TestMethod] - public void SetPackageDependency_AddsNew() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetPackageDependency( - "Microsoft.WindowsAppRuntime.1.5", - "5001.178.1908.0", - "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"); - - Assert.IsTrue(doc.HasPackageDependency("Microsoft.WindowsAppRuntime")); - var xml = doc.ToXml(); - Assert.Contains("Microsoft.WindowsAppRuntime.1.5", xml); - Assert.Contains("5001.178.1908.0", xml); - } - - [TestMethod] - public void SetPackageDependency_UpdatesExisting() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetPackageDependency("MyDep", "1.0.0.0", "CN=Pub"); - doc.SetPackageDependency("MyDep", "2.0.0.0", "CN=Pub"); - - var xml = doc.ToXml(); - Assert.Contains("MinVersion=\"2.0.0.0\"", xml); - // The old MinVersion should be gone (but 1.0.0.0 exists in Identity, so check specifically) - Assert.DoesNotContain("MinVersion=\"1.0.0.0\"", xml); - Assert.AreEqual(1, CountOccurrences(xml, "MyDep")); - } - - [TestMethod] - public void SetPackageDependency_CreatesDependenciesElement_WhenMissing() - { - // BareMinimalManifest has no Dependencies element - var xml = """ - - - - """; - var doc = AppxManifestDocument.Parse(xml); - Assert.IsNull(doc.GetDependenciesElement()); - - doc.SetPackageDependency("TestDep", "1.0.0.0", "CN=Test"); - - Assert.IsNotNull(doc.GetDependenciesElement()); - Assert.IsTrue(doc.HasPackageDependency("TestDep")); - } - - #endregion - - #region Element Accessors - - [TestMethod] - public void GetFirstApplicationElement_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - var app = doc.GetFirstApplicationElement(); - - Assert.IsNotNull(app); - Assert.AreEqual("App", app.Attribute("Id")?.Value); - } - - [TestMethod] - public void GetVisualElements_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - var ve = doc.GetVisualElements(); - - Assert.IsNotNull(ve); - Assert.AreEqual("Test App", ve.Attribute("DisplayName")?.Value); - } - - [TestMethod] - public void GetResourcesElement_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - Assert.IsNotNull(doc.GetResourcesElement()); - } - - [TestMethod] - public void GetDependenciesElement_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - Assert.IsNotNull(doc.GetDependenciesElement()); - } - - [TestMethod] - public void AllAccessors_ReturnNull_ForBareManifest() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - Assert.IsNull(doc.GetFirstApplicationElement()); - Assert.IsNull(doc.GetVisualElements()); - Assert.IsNull(doc.GetResourcesElement()); - Assert.IsNull(doc.GetExtensionsElement()); - Assert.IsNull(doc.GetCapabilitiesElement()); - } - - #endregion - - #region Helpers - - private static int CountOccurrences(string text, string pattern) - { - int count = 0; - int index = 0; - while ((index = text.IndexOf(pattern, index, StringComparison.Ordinal)) != -1) - { - count++; - index += pattern.Length; - } - return count; - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/EndToEndTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/EndToEndTests.cs index 63dfaab0..6b88be26 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/EndToEndTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/EndToEndTests.cs @@ -19,6 +19,7 @@ public class EndToEndTests : BaseCommandTests protected override IServiceCollection ConfigureServices(IServiceCollection services) { return services + .AddSingleton() .AddSingleton(); } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs index e3d3966f..dd2fa42e 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs @@ -11,9 +11,7 @@ namespace WinApp.Cli.Tests; internal class FakeAppLauncherService : IAppLauncherService { public List<(string Aumid, string? Arguments)> LaunchCalls { get; } = []; - public List<(string? PackageFullName, uint ProcessId)> TerminateCalls { get; } = []; public uint FakeProcessId { get; set; } = 12345; - public string? FakePackageFullName { get; set; } = "FakePackage_1.0.0.0_x64__fakefamily"; public uint LaunchByAumid(string aumid, string? arguments = null) { @@ -25,14 +23,4 @@ public string ComputePackageFamilyName(string packageName, string publisher) { return $"{packageName}_fakefamily"; } - - public string? GetPackageFullName(string packageFamilyName) - { - return FakePackageFullName; - } - - public void TerminatePackageProcesses(string? packageFullName, uint processId) - { - TerminateCalls.Add((packageFullName, processId)); - } } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs deleted file mode 100644 index 505ef5a4..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -///

-/// Fake debug output service that records calls without actually attaching a debugger. -/// -internal class FakeDebugOutputService : IDebugOutputService -{ - public List AttachCalls { get; } = []; - public int FakeExitCode { get; set; } - - public Task RunDebugLoopAsync(uint processId, CancellationToken cancellationToken) - { - AttachCalls.Add(processId); - return Task.FromResult(FakeExitCode); - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs deleted file mode 100644 index 8e8c1fda..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -/// -/// Fake package registration service that records calls without actually -/// registering, unregistering, or installing MSIX packages. -/// -internal class FakePackageRegistrationService : IPackageRegistrationService -{ - public List RegisterLooseLayoutCalls { get; } = []; - public List<(string ManifestPath, string ExternalLocation)> RegisterSparseCalls { get; } = []; - public List UnregisterCalls { get; } = []; - public List InstallPackageCalls { get; } = []; - public List GetInstalledVersionCalls { get; } = []; - public List FindDevPackagesCalls { get; } = []; - - /// - /// When set, returns this value. - /// Defaults to false (no package found). - /// - public bool FakeUnregisterResult { get; set; } - - /// - /// When set, returns this value. - /// Defaults to null (package not installed). - /// - public string? FakeInstalledVersion { get; set; } - - /// - /// When set, returns these values. - /// Defaults to empty list. - /// - public List FakeDevPackages { get; set; } = []; - - public Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default) - { - RegisterLooseLayoutCalls.Add(manifestPath); - return Task.CompletedTask; - } - - public Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default) - { - RegisterSparseCalls.Add((manifestPath, externalLocation)); - return Task.CompletedTask; - } - - public Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) - { - UnregisterCalls.Add(packageName); - return Task.FromResult(FakeUnregisterResult); - } - - public Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default) - { - InstallPackageCalls.Add(packagePath); - return Task.CompletedTask; - } - - public string? GetInstalledVersion(string packageName) - { - GetInstalledVersionCalls.Add(packageName); - return FakeInstalledVersion; - } - - public List FindDevPackages(string packageName) - { - FindDevPackagesCalls.Add(packageName); - return FakeDevPackages; - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakePowerShellService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakePowerShellService.cs new file mode 100644 index 00000000..a0bc9ec4 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli.Tests/FakePowerShellService.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using WinApp.Cli.ConsoleTasks; +using WinApp.Cli.Services; + +namespace WinApp.Cli.Tests; + +internal class FakePowerShellService : IPowerShellService +{ + public Task<(int exitCode, string output, string error)> RunCommandAsync(string command, TaskContext taskContext, bool elevated = false, Dictionary? environmentVariables = null, CancellationToken cancellationToken = default) + { + return Task.FromResult((0, "Fake PowerShell command executed successfully.", string.Empty)); + } +} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs deleted file mode 100644 index 19044314..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class IncrementalCopyHelperTests -{ - private DirectoryInfo _tempDir = null!; - - [TestInitialize] - public void Setup() - { - _tempDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"IncrementalCopyTest_{Guid.NewGuid():N}")); - _tempDir.Create(); - } - - [TestCleanup] - public void Cleanup() - { - if (_tempDir.Exists) - { - _tempDir.Delete(recursive: true); - } - } - - private DirectoryInfo CreateSubDir(string name) - { - var dir = new DirectoryInfo(Path.Combine(_tempDir.FullName, name)); - dir.Create(); - return dir; - } - - private static FileInfo WriteFile(DirectoryInfo dir, string relativePath, string content) - { - var path = Path.Combine(dir.FullName, relativePath); - var file = new FileInfo(path); - file.Directory?.Create(); - File.WriteAllText(path, content); - return file; - } - - #region SyncDirectory Tests - - [TestMethod] - public void SyncDirectory_FirstSync_CopiesAllFiles() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe-content"); - WriteFile(source, "app.dll", "dll-content"); - WriteFile(source, "sub\\lib.dll", "lib-content"); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(3, result.Copied); - Assert.AreEqual(0, result.Skipped); - Assert.AreEqual(0, result.Deleted); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.exe"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.dll"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "sub", "lib.dll"))); - } - - [TestMethod] - public void SyncDirectory_UnchangedFiles_AreSkipped() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe-content"); - WriteFile(source, "app.dll", "dll-content"); - - // First sync copies everything - IncrementalCopyHelper.SyncDirectory(source, dest); - - // Second sync should skip everything (no changes) - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(0, result.Copied); - Assert.AreEqual(2, result.Skipped); - Assert.AreEqual(0, result.Deleted); - } - - [TestMethod] - public void SyncDirectory_ModifiedFile_IsCopied() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "original"); - - // First sync - IncrementalCopyHelper.SyncDirectory(source, dest); - - // Modify the source file (different content = different size) - Thread.Sleep(50); // ensure timestamp differs - WriteFile(source, "app.exe", "modified-content-longer"); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(1, result.Copied); - Assert.AreEqual(0, result.Skipped); - Assert.AreEqual(0, result.Deleted); - Assert.AreEqual("modified-content-longer", File.ReadAllText(Path.Combine(dest.FullName, "app.exe"))); - } - - [TestMethod] - public void SyncDirectory_StaleFile_IsDeleted() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe"); - WriteFile(source, "old.dll", "old"); - - // First sync copies both - IncrementalCopyHelper.SyncDirectory(source, dest); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "old.dll"))); - - // Remove old.dll from source - File.Delete(Path.Combine(source.FullName, "old.dll")); - - // Second sync should delete old.dll from dest - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(0, result.Copied); - Assert.AreEqual(1, result.Skipped); - Assert.AreEqual(1, result.Deleted); - Assert.IsFalse(File.Exists(Path.Combine(dest.FullName, "old.dll"))); - } - - [TestMethod] - public void SyncDirectory_ProtectedFiles_AreNotDeleted() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe"); - - // First sync - IncrementalCopyHelper.SyncDirectory(source, dest); - - // Manually create protected files in dest that don't exist in source - WriteFile(dest, "appxmanifest.xml", ""); - WriteFile(dest, "resources.pri", "pri-data"); - WriteFile(dest, "stale.dll", "stale"); - - var protectedFiles = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "appxmanifest.xml", - "resources.pri" - }; - - var result = IncrementalCopyHelper.SyncDirectory(source, dest, protectedFiles); - - // stale.dll should be deleted, but protected files should survive - Assert.AreEqual(1, result.Deleted); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "appxmanifest.xml"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "resources.pri"))); - Assert.IsFalse(File.Exists(Path.Combine(dest.FullName, "stale.dll"))); - } - - [TestMethod] - public void SyncDirectory_NestedOutputInsideSource_IsExcluded() - { - var source = CreateSubDir("source"); - var dest = new DirectoryInfo(Path.Combine(source.FullName, "AppX")); - dest.Create(); - - WriteFile(source, "app.exe", "exe"); - // This file is inside the dest folder (nested inside source) - WriteFile(dest, "should-not-recurse.txt", "nested"); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - // Should only copy app.exe, not recurse into dest - Assert.AreEqual(1, result.Copied); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.exe"))); - } - - [TestMethod] - public void SyncDirectory_CreatesDestDirectory_IfNotExists() - { - var source = CreateSubDir("source"); - var dest = new DirectoryInfo(Path.Combine(_tempDir.FullName, "nonexistent_dest")); - WriteFile(source, "app.exe", "exe"); - - Assert.IsFalse(dest.Exists); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(1, result.Copied); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.exe"))); - } - - [TestMethod] - public void SyncDirectory_SubdirectoriesInSource_AreCopied() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "root.dll", "root"); - WriteFile(source, "runtimes\\win-x64\\native\\lib.dll", "native-lib"); - WriteFile(source, "wwwroot\\index.html", ""); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(3, result.Copied); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "runtimes", "win-x64", "native", "lib.dll"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "wwwroot", "index.html"))); - } - - #endregion - - #region CopyFiles Tests - - [TestMethod] - public void CopyFiles_FirstCopy_CopiesAll() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - var file1 = WriteFile(source, "icon.png", "icon-data"); - var file2 = WriteFile(source, "assets\\logo.png", "logo-data"); - - var files = new List<(FileInfo, string)> - { - (file1, "icon.png"), - (file2, "assets\\logo.png"), - }; - - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(files, dest); - - Assert.AreEqual(2, copied); - Assert.AreEqual(0, skipped); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "icon.png"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "assets", "logo.png"))); - } - - [TestMethod] - public void CopyFiles_UnchangedFiles_AreSkipped() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - var file1 = WriteFile(source, "icon.png", "icon-data"); - - var files = new List<(FileInfo, string)> { (file1, "icon.png") }; - - // First copy - IncrementalCopyHelper.CopyFiles(files, dest); - - // Second copy should skip - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(files, dest); - - Assert.AreEqual(0, copied); - Assert.AreEqual(1, skipped); - } - - [TestMethod] - public void CopyFiles_ModifiedFile_IsCopied() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - var file1 = WriteFile(source, "icon.png", "original"); - - var files = new List<(FileInfo, string)> { (file1, "icon.png") }; - - // First copy - IncrementalCopyHelper.CopyFiles(files, dest); - - // Modify the source - Thread.Sleep(50); - file1 = WriteFile(source, "icon.png", "modified-content-longer"); - files = [(file1, "icon.png")]; - - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(files, dest); - - Assert.AreEqual(1, copied); - Assert.AreEqual(0, skipped); - Assert.AreEqual("modified-content-longer", File.ReadAllText(Path.Combine(dest.FullName, "icon.png"))); - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/InitCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/InitCommandTests.cs index 27b7055e..ed252655 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/InitCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/InitCommandTests.cs @@ -15,7 +15,8 @@ public class InitCommandTests : BaseCommandTests { protected override IServiceCollection ConfigureServices(IServiceCollection services) { - return services; + return services + .AddSingleton(); } [TestMethod] diff --git a/src/winapp-CLI/WinApp.Cli.Tests/ManifestCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/ManifestCommandTests.cs index e750399d..245e9378 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/ManifestCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/ManifestCommandTests.cs @@ -160,9 +160,9 @@ public async Task ManifestGenerateCommandWithLogoShouldGenerateAssets() // Verify expected MSIX asset files were generated var expectedAssets = new[] { - "AppList.png", - "MedTile.png", - "WideTile.png", + "Square44x44Logo.png", + "Square150x150Logo.png", + "Wide310x150Logo.png", "StoreLogo.png" }; @@ -526,74 +526,4 @@ public async Task ManifestGenerateCommandWithEntrypointShouldUseVersionInfoFromE Assert.Contains("CN=Microsoft Corporation", manifestContent, "Manifest publisher should be set to Microsoft Corporation from executable"); } - - [TestMethod] - public void DetermineIcoOutputPathShouldDefaultToAppIcoWhenNoExistingIco() - { - var assetsDir = _tempDirectory.CreateSubdirectory("AssetsEmpty"); - - var result = ManifestService.DetermineIcoOutputPath(assetsDir, TestTaskContext); - - Assert.AreEqual(Path.Combine(assetsDir.FullName, "app.ico"), result); - } - - [TestMethod] - public void DetermineIcoOutputPathShouldDefaultToAppIcoWhenDirectoryDoesNotExist() - { - var nonExistentDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "NonExistent")); - - var result = ManifestService.DetermineIcoOutputPath(nonExistentDir, TestTaskContext); - - Assert.AreEqual(Path.Combine(nonExistentDir.FullName, "app.ico"), result); - } - - [TestMethod] - public void DetermineIcoOutputPathShouldReuseSingleExistingIco() - { - var assetsDir = _tempDirectory.CreateSubdirectory("AssetsSingle"); - var existingIco = Path.Combine(assetsDir.FullName, "AppIcon.ico"); - File.WriteAllBytes(existingIco, [0]); - - var result = ManifestService.DetermineIcoOutputPath(assetsDir, TestTaskContext); - - Assert.AreEqual(existingIco, result); - } - - [TestMethod] - public void DetermineIcoOutputPathShouldPreferAppIconWhenMultipleExist() - { - var assetsDir = _tempDirectory.CreateSubdirectory("AssetsMulti"); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "AppIcon.ico"), [0]); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "favicon.ico"), [0]); - - var result = ManifestService.DetermineIcoOutputPath(assetsDir, TestTaskContext); - - Assert.AreEqual(Path.Combine(assetsDir.FullName, "AppIcon.ico"), result); - } - - [TestMethod] - public void DetermineIcoOutputPathShouldPreferAppOverIconInName() - { - var assetsDir = _tempDirectory.CreateSubdirectory("AssetsHeuristic"); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "app.ico"), [0]); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "icon.ico"), [0]); - - // "app" is preferred over "icon" in the heuristic - var result = ManifestService.DetermineIcoOutputPath(assetsDir, TestTaskContext); - - Assert.AreEqual(Path.Combine(assetsDir.FullName, "app.ico"), result); - } - - [TestMethod] - public void DetermineIcoOutputPathShouldCreateAppIcoWhenNoHeuristicMatch() - { - var assetsDir = _tempDirectory.CreateSubdirectory("AssetsUnrelated"); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "tray.ico"), [0]); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "cursor.ico"), [0]); - - var result = ManifestService.DetermineIcoOutputPath(assetsDir, TestTaskContext); - - // Unrelated ICO files should not be touched; fall back to app.ico - Assert.AreEqual(Path.Combine(assetsDir.FullName, "app.ico"), result); - } } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/ManifestUpdateAssetsCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/ManifestUpdateAssetsCommandTests.cs index b5a8c56c..52a286e3 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/ManifestUpdateAssetsCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/ManifestUpdateAssetsCommandTests.cs @@ -10,19 +10,17 @@ public class ManifestUpdateAssetsCommandTests : BaseCommandTests { private string _testManifestPath = null!; private string _testImagePath = null!; - private string _testLightImagePath = null!; [TestInitialize] public void Setup() { + // Create a test manifest file _testManifestPath = Path.Combine(_tempDirectory.FullName, "appxmanifest.xml"); CreateTestManifest(_testManifestPath); + // Create a test image file _testImagePath = Path.Combine(_tempDirectory.FullName, "testlogo.png"); PngHelper.CreateTestImage(_testImagePath); - - _testLightImagePath = Path.Combine(_tempDirectory.FullName, "testlogo-light.png"); - PngHelper.CreateTestImage(_testLightImagePath); } private static void CreateTestManifest(string path) @@ -56,8 +54,10 @@ private static void CreateTestManifest(string path) [TestMethod] public void ManifestUpdateAssetsCommandShouldBeAvailable() { + // Arrange & Act var manifestCommand = GetRequiredService(); + // Assert Assert.IsNotNull(manifestCommand, "ManifestCommand should be created"); Assert.IsTrue(manifestCommand.Subcommands.Any(c => c.Name == "update-assets"), "Should have 'update-assets' subcommand"); @@ -66,28 +66,33 @@ public void ManifestUpdateAssetsCommandShouldBeAvailable() [TestMethod] public async Task ManifestUpdateAssetsCommandShouldGenerateAssets() { + // Arrange var updateAssetsCommand = GetRequiredService(); var args = new[] { _testImagePath, - "--manifest", _testManifestPath, + "--manifest", _testManifestPath }; + // Act var parseResult = updateAssetsCommand.Parse(args); var exitCode = await parseResult.InvokeAsync(); + // Assert Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully"); + // Verify Assets directory was created var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); Assert.IsTrue(Directory.Exists(assetsDir), "Assets directory should be created"); + // Verify assets referenced in manifest were generated + // The test manifest references: StoreLogo.png, Square150x150Logo.png, Square44x44Logo.png, Wide310x150Logo.png var expectedAssets = new[] { "Square44x44Logo.png", "Square150x150Logo.png", "Wide310x150Logo.png", - "StoreLogo.png", - "app.ico", + "StoreLogo.png" }; foreach (var asset in expectedAssets) @@ -100,125 +105,77 @@ public async Task ManifestUpdateAssetsCommandShouldGenerateAssets() [TestMethod] public void ManifestUpdateAssetsCommandShouldFailWithNonExistentManifest() { + // Arrange var updateAssetsCommand = GetRequiredService(); var nonExistentManifest = Path.Combine(_tempDirectory.FullName, "nonexistent.xml"); var args = new[] { _testImagePath, - "--manifest", nonExistentManifest, + "--manifest", nonExistentManifest }; + // Act var parseResult = updateAssetsCommand.Parse(args); + // Assert Assert.IsNotEmpty(parseResult.Errors, "Should have parse errors for non-existent manifest"); } [TestMethod] public void ManifestUpdateAssetsCommandShouldFailWithNonExistentImage() { + // Arrange var updateAssetsCommand = GetRequiredService(); var nonExistentImage = Path.Combine(_tempDirectory.FullName, "nonexistent.png"); var args = new[] { nonExistentImage, - "--manifest", _testManifestPath, + "--manifest", _testManifestPath }; + // Act var parseResult = updateAssetsCommand.Parse(args); + // Assert Assert.IsNotEmpty(parseResult.Errors, "Should have parse errors for non-existent image"); } [TestMethod] public async Task ManifestUpdateAssetsCommandShouldGenerateCorrectSizes() { + // Arrange var updateAssetsCommand = GetRequiredService(); var args = new[] { _testImagePath, - "--manifest", _testManifestPath, + "--manifest", _testManifestPath }; + // Act var parseResult = updateAssetsCommand.Parse(args); var exitCode = await parseResult.InvokeAsync(); + // Assert Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully"); + // Verify specific asset sizes var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.scale-125.png"), 55, 55); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.scale-150.png"), 66, 66); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.scale-200.png"), 88, 88); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.scale-400.png"), 176, 176); - AssertImageDimensions(Path.Combine(assetsDir, "Square150x150Logo.scale-200.png"), 300, 300); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.targetsize-20.png"), 20, 20); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.targetsize-20_altform-unplated.png"), 20, 20); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.targetsize-256.png"), 256, 256); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.targetsize-256_altform-unplated.png"), 256, 256); - } - - [TestMethod] - public async Task ManifestUpdateAssetsCommandShouldGenerateLightThemeAssets() - { - var updateAssetsCommand = GetRequiredService(); - var args = new[] - { - _testImagePath, - "--manifest", _testManifestPath, - "--light-image", _testLightImagePath, - }; - - var parseResult = updateAssetsCommand.Parse(args); - var exitCode = await parseResult.InvokeAsync(); - - Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully with light image"); - - var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); - var expectedAssets = new[] - { - "Square44x44Logo.scale-100_altform-colorful_theme-light.png", - "Square44x44Logo.scale-200_altform-colorful_theme-light.png", - "StoreLogo.scale-400_altform-colorful_theme-light.png", - "Square44x44Logo.targetsize-20_altform-lightunplated.png", - "Square44x44Logo.targetsize-256_altform-lightunplated.png", - }; - - foreach (var asset in expectedAssets) - { - Assert.IsTrue(File.Exists(Path.Combine(assetsDir, asset)), $"Asset {asset} should be generated"); - } - } - - [TestMethod] - public async Task ManifestUpdateAssetsCommandShouldGenerateIcoWithExpectedFrames() - { - var updateAssetsCommand = GetRequiredService(); - var args = new[] - { - _testImagePath, - "--manifest", _testManifestPath, - }; - - var parseResult = updateAssetsCommand.Parse(args); - var exitCode = await parseResult.InvokeAsync(); - Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully"); - - var icoPath = Path.Combine(_tempDirectory.FullName, "Assets", "app.ico"); - Assert.IsTrue(File.Exists(icoPath), "app.ico should be generated"); - - using var stream = File.OpenRead(icoPath); - using var reader = new BinaryReader(stream); - Assert.AreEqual((ushort)0, reader.ReadUInt16(), "ICO reserved field should be 0"); - Assert.AreEqual((ushort)1, reader.ReadUInt16(), "ICO type should be 1"); - Assert.AreEqual((ushort)5, reader.ReadUInt16(), "ICO should contain 5 image frames"); + // Check that scale-200 assets exist (which should be 2x the base size) + Assert.IsTrue(File.Exists(Path.Combine(assetsDir, "Square44x44Logo.scale-200.png")), + "Square44x44Logo.scale-200.png should exist"); + Assert.IsTrue(File.Exists(Path.Combine(assetsDir, "Square150x150Logo.scale-200.png")), + "Square150x150Logo.scale-200.png should exist"); } [TestMethod] public async Task ManifestUpdateAssetsCommandShouldOverwriteExistingAssets() { + // Arrange var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); Directory.CreateDirectory(assetsDir); + // Create a dummy existing asset var existingAssetPath = Path.Combine(assetsDir, "Square150x150Logo.png"); File.WriteAllText(existingAssetPath, "old content"); var oldLength = new FileInfo(existingAssetPath).Length; @@ -227,12 +184,14 @@ public async Task ManifestUpdateAssetsCommandShouldOverwriteExistingAssets() var args = new[] { _testImagePath, - "--manifest", _testManifestPath, + "--manifest", _testManifestPath }; + // Act var parseResult = updateAssetsCommand.Parse(args); var exitCode = await parseResult.InvokeAsync(); + // Assert Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully"); Assert.IsTrue(File.Exists(existingAssetPath), "Asset should still exist"); @@ -243,28 +202,36 @@ public async Task ManifestUpdateAssetsCommandShouldOverwriteExistingAssets() [TestMethod] public void ManifestUpdateAssetsCommandHelpShouldDisplayCorrectInformation() { + // Arrange var updateAssetsCommand = GetRequiredService(); var args = new[] { "--help" }; + // Act var parseResult = updateAssetsCommand.Parse(args); + // Assert Assert.IsNotNull(parseResult, "Parse result should not be null"); + // The help option should be recognized and not produce errors } [TestMethod] public async Task ManifestUpdateAssetsCommandShouldLogProgress() { + // Arrange var updateAssetsCommand = GetRequiredService(); var args = new[] { _testImagePath, "--manifest", _testManifestPath, - "--verbose", + "--verbose" }; + // Act var exitCode = await ParseAndInvokeWithCaptureAsync(updateAssetsCommand, args); + // Assert Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully"); + Assert.Contains("Updating assets for manifest", TestAnsiConsole.Output, "Should log update message"); Assert.Contains("generated", TestAnsiConsole.Output.ToLowerInvariant(), "Should log generation progress"); } @@ -272,190 +239,23 @@ public async Task ManifestUpdateAssetsCommandShouldLogProgress() [TestMethod] public async Task ManifestUpdateAssetsCommandShouldInferManifestFromCurrentDirectory() { + // Arrange var updateAssetsCommand = GetRequiredService(); + // Only provide the image path, manifest should be inferred var args = new[] { - _testImagePath, + _testImagePath }; + // Act var parseResult = updateAssetsCommand.Parse(args); var exitCode = await parseResult.InvokeAsync(); + // Assert Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully when manifest is inferred"); + // Verify Assets directory was created var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); Assert.IsTrue(Directory.Exists(assetsDir), "Assets directory should be created"); - Assert.IsTrue(File.Exists(Path.Combine(assetsDir, "app.ico")), "app.ico should be generated when manifest is inferred"); - } - - [TestMethod] - public async Task ManifestUpdateAssetsCommandShouldGenerateAssetsFromSvg() - { - var svgImagePath = Path.Combine(_tempDirectory.FullName, "testlogo.svg"); - PngHelper.CreateTestSvgImage(svgImagePath); - - var updateAssetsCommand = GetRequiredService(); - var args = new[] - { - svgImagePath, - "--manifest", _testManifestPath, - }; - - var parseResult = updateAssetsCommand.Parse(args); - var exitCode = await parseResult.InvokeAsync(); - - Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully with SVG source"); - - var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); - Assert.IsTrue(Directory.Exists(assetsDir), "Assets directory should be created"); - - var expectedAssets = new[] - { - "Square44x44Logo.png", - "Square150x150Logo.png", - "Wide310x150Logo.png", - "StoreLogo.png", - "app.ico", - }; - - foreach (var asset in expectedAssets) - { - var assetPath = Path.Combine(assetsDir, asset); - Assert.IsTrue(File.Exists(assetPath), $"Asset {asset} should be generated from SVG source"); - } - } - - [TestMethod] - public async Task ManifestUpdateAssetsCommandShouldGenerateCorrectSizesFromSvg() - { - var svgImagePath = Path.Combine(_tempDirectory.FullName, "testlogo.svg"); - PngHelper.CreateTestSvgImage(svgImagePath); - - var updateAssetsCommand = GetRequiredService(); - var args = new[] - { - svgImagePath, - "--manifest", _testManifestPath, - }; - - var parseResult = updateAssetsCommand.Parse(args); - var exitCode = await parseResult.InvokeAsync(); - - Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully with SVG source"); - - var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.scale-125.png"), 55, 55); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.scale-200.png"), 88, 88); - AssertImageDimensions(Path.Combine(assetsDir, "Square150x150Logo.scale-200.png"), 300, 300); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.targetsize-32.png"), 32, 32); - AssertImageDimensions(Path.Combine(assetsDir, "Square44x44Logo.targetsize-32_altform-unplated.png"), 32, 32); - } - - private static void AssertImageDimensions(string imagePath, int expectedWidth, int expectedHeight) - { - Assert.IsTrue(File.Exists(imagePath), $"Asset {Path.GetFileName(imagePath)} should exist"); - using var bitmap = new System.Drawing.Bitmap(imagePath); - Assert.AreEqual(expectedWidth, bitmap.Width, $"{Path.GetFileName(imagePath)} width mismatch"); - Assert.AreEqual(expectedHeight, bitmap.Height, $"{Path.GetFileName(imagePath)} height mismatch"); - } - - private static void CreateNewNamingManifest(string path) - { - var manifestContent = @" - - - - TestPackage - TestPublisher - Assets\StoreLogo.png - - - - - - - - -"; - File.WriteAllText(path, manifestContent); - } - - [TestMethod] - public async Task ManifestUpdateAssetsCommandShouldGenerateAssetsWithNewNaming() - { - var newNamingManifest = Path.Combine(_tempDirectory.FullName, "appxmanifest-new.xml"); - CreateNewNamingManifest(newNamingManifest); - - var updateAssetsCommand = GetRequiredService(); - var args = new[] - { - _testImagePath, - "--manifest", newNamingManifest, - }; - - var parseResult = updateAssetsCommand.Parse(args); - var exitCode = await parseResult.InvokeAsync(); - - Assert.AreEqual(0, exitCode, "Update-assets command should succeed with new naming manifest"); - - var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); - - // Base assets use new names - var expectedBaseAssets = new[] { "AppList.png", "MedTile.png", "WideTile.png", "StoreLogo.png", "app.ico" }; - foreach (var asset in expectedBaseAssets) - { - Assert.IsTrue(File.Exists(Path.Combine(assetsDir, asset)), $"Asset {asset} should be generated"); - } - - // Scale variants use new names - AssertImageDimensions(Path.Combine(assetsDir, "AppList.scale-200.png"), 88, 88); - AssertImageDimensions(Path.Combine(assetsDir, "MedTile.scale-200.png"), 300, 300); - AssertImageDimensions(Path.Combine(assetsDir, "WideTile.scale-200.png"), 620, 300); - - // Targetsize variants generated for AppList (44x44 app icon) - AssertImageDimensions(Path.Combine(assetsDir, "AppList.targetsize-48.png"), 48, 48); - AssertImageDimensions(Path.Combine(assetsDir, "AppList.targetsize-48_altform-unplated.png"), 48, 48); - AssertImageDimensions(Path.Combine(assetsDir, "AppList.targetsize-256.png"), 256, 256); - - // No targetsize variants for non-app-icon assets - Assert.IsFalse(File.Exists(Path.Combine(assetsDir, "MedTile.targetsize-48.png")), - "MedTile should not have targetsize variants"); - } - - [TestMethod] - public async Task ManifestUpdateAssetsCommandShouldReplaceExistingIcoByName() - { - // Pre-create an Assets directory with an existing ICO file (simulating a project template) - var assetsDir = Path.Combine(_tempDirectory.FullName, "Assets"); - Directory.CreateDirectory(assetsDir); - File.WriteAllBytes(Path.Combine(assetsDir, "AppIcon.ico"), [0x00]); - - var updateAssetsCommand = GetRequiredService(); - var args = new[] - { - _testImagePath, - "--manifest", _testManifestPath, - }; - - var parseResult = updateAssetsCommand.Parse(args); - var exitCode = await parseResult.InvokeAsync(); - - Assert.AreEqual(0, exitCode, "Update-assets command should complete successfully"); - - // The existing AppIcon.ico should be replaced (size > 1 byte placeholder) - var replacedIco = Path.Combine(assetsDir, "AppIcon.ico"); - Assert.IsTrue(File.Exists(replacedIco), "AppIcon.ico should still exist"); - Assert.IsTrue(new FileInfo(replacedIco).Length > 1, "AppIcon.ico should be regenerated with real content"); - - // No duplicate app.ico should be created - Assert.IsFalse(File.Exists(Path.Combine(assetsDir, "app.ico")), - "app.ico should NOT be created when an existing ICO file is present"); } } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs deleted file mode 100644 index aaf74eca..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class MrtAssetHelperTests -{ - private DirectoryInfo _tempDir = null!; - - [TestInitialize] - public void Setup() - { - _tempDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"MrtTest_{Guid.NewGuid():N}")); - _tempDir.Create(); - } - - [TestCleanup] - public void Cleanup() - { - if (_tempDir.Exists) - { - _tempDir.Delete(recursive: true); - } - } - - #region IsSingleQualifierToken - - [TestMethod] - [DataRow("scale-100", DisplayName = "scale-100")] - [DataRow("scale-200", DisplayName = "scale-200")] - [DataRow("scale-400", DisplayName = "scale-400")] - [DataRow("theme-dark", DisplayName = "theme-dark")] - [DataRow("theme-light", DisplayName = "theme-light")] - [DataRow("contrast-standard", DisplayName = "contrast-standard")] - [DataRow("contrast-high", DisplayName = "contrast-high")] - [DataRow("targetsize-16", DisplayName = "targetsize-16")] - [DataRow("targetsize-256", DisplayName = "targetsize-256")] - [DataRow("altform-unplated", DisplayName = "altform-unplated")] - [DataRow("altform-lightunplated", DisplayName = "altform-lightunplated")] - [DataRow("dxfeaturelevel-9", DisplayName = "dxfeaturelevel-9")] - [DataRow("dxfeaturelevel-11", DisplayName = "dxfeaturelevel-11")] - [DataRow("device-family-desktop", DisplayName = "device-family-desktop")] - [DataRow("device-family-xbox", DisplayName = "device-family-xbox")] - [DataRow("homeregion-US", DisplayName = "homeregion-US")] - [DataRow("homeregion-JP", DisplayName = "homeregion-JP")] - [DataRow("configuration-debug", DisplayName = "configuration-debug")] - [DataRow("configuration-retail", DisplayName = "configuration-retail")] - [DataRow("en-US", DisplayName = "en-US (language)")] - [DataRow("fr", DisplayName = "fr (language)")] - [DataRow("zh-Hans", DisplayName = "zh-Hans (language)")] - [DataRow("pt-BR", DisplayName = "pt-BR (language)")] - [DataRow("ltr", DisplayName = "ltr (layout direction)")] - [DataRow("rtl", DisplayName = "rtl (layout direction)")] - public void IsSingleQualifierToken_ReturnsTrue_ForValidTokens(string token) - { - Assert.IsTrue(MrtAssetHelper.IsSingleQualifierToken(token)); - } - - [TestMethod] - [DataRow("", DisplayName = "empty string")] - [DataRow("Logo", DisplayName = "plain name")] - [DataRow("foo-barbaz1234", DisplayName = "arbitrary hyphenated (subtag too long)")] - [DataRow("scale-abc", DisplayName = "scale with non-numeric")] - [DataRow("theme-blue", DisplayName = "invalid theme")] - [DataRow("contrast-low", DisplayName = "invalid contrast")] - [DataRow("device-family-phone", DisplayName = "invalid device family")] - public void IsSingleQualifierToken_ReturnsFalse_ForInvalidTokens(string token) - { - Assert.IsFalse(MrtAssetHelper.IsSingleQualifierToken(token)); - } - - [TestMethod] - public void IsSingleQualifierToken_ReturnsFalse_ForNull() - { - Assert.IsFalse(MrtAssetHelper.IsSingleQualifierToken(null!)); - } - - #endregion - - #region IsQualifierToken (compound) - - [TestMethod] - [DataRow("scale-200", DisplayName = "single qualifier")] - [DataRow("scale-200_theme-dark", DisplayName = "compound: scale + theme")] - [DataRow("targetsize-24_altform-unplated", DisplayName = "compound: targetsize + altform")] - [DataRow("en-US", DisplayName = "language alone")] - public void IsQualifierToken_ReturnsTrue_ForValidTokens(string token) - { - Assert.IsTrue(MrtAssetHelper.IsQualifierToken(token)); - } - - [TestMethod] - [DataRow("", DisplayName = "empty string")] - [DataRow("Logo", DisplayName = "plain name")] - [DataRow("scale-200_Logo", DisplayName = "qualifier + non-qualifier")] - [DataRow("Logo_scale-200", DisplayName = "non-qualifier + qualifier")] - public void IsQualifierToken_ReturnsFalse_ForInvalidTokens(string token) - { - Assert.IsFalse(MrtAssetHelper.IsQualifierToken(token)); - } - - #endregion - - #region IsMrtVariantName - - [TestMethod] - public void IsMrtVariantName_ExactMatch_ReturnsTrue() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo")); - } - - [TestMethod] - public void IsMrtVariantName_CaseInsensitive() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "logo")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("logo", "Logo")); - } - - [TestMethod] - public void IsMrtVariantName_WithSingleQualifier() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.scale-200")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.targetsize-48")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.theme-dark")); - } - - [TestMethod] - public void IsMrtVariantName_WithMultipleQualifiers() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.scale-200.theme-dark")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.targetsize-48.altform-unplated")); - } - - [TestMethod] - public void IsMrtVariantName_WithCompoundQualifier() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.targetsize-24_altform-unplated")); - } - - [TestMethod] - public void IsMrtVariantName_ReturnsFalse_ForNonQualifierSuffix() - { - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.99invalid")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "LogoExtra")); - } - - [TestMethod] - public void IsMrtVariantName_ReturnsFalse_ForDifferentBaseName() - { - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "Icon.scale-200")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "Banner")); - } - - [TestMethod] - public void IsMrtVariantName_ReturnsFalse_ForEmptyInputs() - { - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("", "Logo")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("", "")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName(null!, "Logo")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", null!)); - } - - #endregion - - #region GetMrtVariantBaseName - - [TestMethod] - public void GetMrtVariantBaseName_UnqualifiedName_ReturnsSame() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo")); - } - - [TestMethod] - public void GetMrtVariantBaseName_SingleQualifier_ReturnsBase() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.scale-100")); - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.targetsize-48")); - } - - [TestMethod] - public void GetMrtVariantBaseName_MultipleQualifiers_ReturnsBase() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.scale-200.theme-dark")); - } - - [TestMethod] - public void GetMrtVariantBaseName_CompoundQualifier_ReturnsBase() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.targetsize-24_altform-unplated")); - } - - [TestMethod] - public void GetMrtVariantBaseName_DottedBaseName_PreservesNonQualifierDots() - { - // "Assets.Logo.scale-200" → base should be "Assets.Logo" - Assert.AreEqual("Assets.Logo", MrtAssetHelper.GetMrtVariantBaseName("Assets.Logo.scale-200")); - } - - [TestMethod] - public void GetMrtVariantBaseName_NoQualifiers_ReturnsOriginal() - { - Assert.AreEqual("SomeFile.backup", MrtAssetHelper.GetMrtVariantBaseName("SomeFile.backup")); - } - - [TestMethod] - public void GetMrtVariantBaseName_ThrowsForNull() - { - Assert.ThrowsExactly(() => MrtAssetHelper.GetMrtVariantBaseName(null!)); - } - - [TestMethod] - public void GetMrtVariantBaseName_ThrowsForEmpty() - { - Assert.ThrowsExactly(() => MrtAssetHelper.GetMrtVariantBaseName("")); - } - - [TestMethod] - public void GetMrtVariantBaseName_ThrowsForWhitespace() - { - Assert.ThrowsExactly(() => MrtAssetHelper.GetMrtVariantBaseName(" ")); - } - - #endregion - - #region ExpandManifestReferencedFiles - - [TestMethod] - public void ExpandManifestReferencedFiles_FindsMrtVariants() - { - // Create files: Logo.png, Logo.scale-100.png, Logo.scale-200.png - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-100.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png"], - taskContext: null); - - Assert.AreEqual(3, result.Count); - Assert.IsTrue(result.Any(f => f.RelativePath == "Logo.png")); - Assert.IsTrue(result.Any(f => f.RelativePath == "Logo.scale-100.png")); - Assert.IsTrue(result.Any(f => f.RelativePath == "Logo.scale-200.png")); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_FallsBackToExactFile_WhenNoVariants() - { - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Icon.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Icon.png"], - taskContext: null); - - Assert.AreEqual(1, result.Count); - Assert.AreEqual("Icon.png", result[0].RelativePath); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_MissingDirectory_ReturnsEmpty() - { - // Reference a file in a non-existent subdirectory - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["nonexistent\\Logo.png"], - taskContext: null); - - Assert.AreEqual(0, result.Count); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_MissingFile_NoVariants_ReturnsEmpty() - { - // No files exist at all - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Missing.png"], - taskContext: null); - - Assert.AreEqual(0, result.Count); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_ExcludesNonVariantFiles() - { - // Logo.png, Logo.backup.png (not a qualifier), Logo.scale-200.png - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.backup.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png"], - taskContext: null); - - // Should include Logo.png and Logo.scale-200.png, but NOT Logo.backup.png - Assert.AreEqual(2, result.Count); - Assert.IsFalse(result.Any(f => f.RelativePath.Contains("backup"))); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_HandlesSubdirectories() - { - var assetsDir = Directory.CreateDirectory(Path.Combine(_tempDir.FullName, "Assets")); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "Logo.scale-200.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Assets\\Logo.png"], - taskContext: null); - - Assert.AreEqual(2, result.Count); - Assert.IsTrue(result.All(f => f.RelativePath.StartsWith("Assets\\", StringComparison.Ordinal))); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_WithIncludeFilter() - { - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-100.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - // Only include files that end with scale-200 - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png"], - taskContext: null, - includeFile: f => f.Name.Contains("scale-200")); - - Assert.AreEqual(1, result.Count); - Assert.AreEqual("Logo.scale-200.png", result[0].RelativePath); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_EmptyReferencedFiles_ReturnsEmpty() - { - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - [], - taskContext: null); - - Assert.AreEqual(0, result.Count); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_DeduplicatesAcrossReferences() - { - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - // Reference the same logical file twice - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png", "Logo.png"], - taskContext: null); - - // Dictionary-based dedup should prevent duplicates - Assert.AreEqual(2, result.Count); // Logo.png + Logo.scale-200.png - } - - #endregion - - #region PriIncludedExtensions - - [TestMethod] - [DataRow(".png")] - [DataRow(".jpg")] - [DataRow(".jpeg")] - [DataRow(".gif")] - [DataRow(".bmp")] - [DataRow(".ico")] - [DataRow(".svg")] - public void PriIncludedExtensions_ContainsExpected(string ext) - { - Assert.IsTrue(MrtAssetHelper.PriIncludedExtensions.Contains(ext)); - } - - [TestMethod] - [DataRow(".exe")] - [DataRow(".dll")] - [DataRow(".xml")] - [DataRow(".txt")] - public void PriIncludedExtensions_ExcludesNonImage(string ext) - { - Assert.IsFalse(MrtAssetHelper.PriIncludedExtensions.Contains(ext)); - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs index 57662258..15fcc645 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs @@ -3,9 +3,7 @@ using System.Reflection; using System.Text; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using WinApp.Cli.ConsoleTasks; using WinApp.Cli.Services; namespace WinApp.Cli.Tests; @@ -742,7 +740,7 @@ public void FindManifestInDirectory_ReturnsNull_WhenNoManifest() public void DetectPeArchitecture_ReturnsNull_ForNonExistentFile() { // Act - var result = PeHelper.DetectPeArchitecture(Path.Combine(_tempDir.FullName, "nonexistent.exe")); + var result = MsixService.DetectPeArchitecture(Path.Combine(_tempDir.FullName, "nonexistent.exe")); // Assert Assert.IsNull(result); @@ -756,7 +754,7 @@ public void DetectPeArchitecture_ReturnsNull_ForNonPeFile() File.WriteAllText(path, "This is not a PE file"); // Act - var result = PeHelper.DetectPeArchitecture(path); + var result = MsixService.DetectPeArchitecture(path); // Assert Assert.IsNull(result); @@ -770,7 +768,7 @@ public void DetectPeArchitecture_ReturnsNull_ForTruncatedFile() File.WriteAllBytes(path, [0x4D, 0x5A]); // Just MZ header, nothing else // Act - var result = PeHelper.DetectPeArchitecture(path); + var result = MsixService.DetectPeArchitecture(path); // Assert Assert.IsNull(result); @@ -788,7 +786,7 @@ public void DetectPeArchitecture_ReturnsExpected_ForValidPeHeader(ushort machine File.WriteAllBytes(path, pe); // Act - var result = PeHelper.DetectPeArchitecture(path); + var result = MsixService.DetectPeArchitecture(path); // Assert Assert.AreEqual(expected, result); @@ -803,7 +801,7 @@ public void DetectPeArchitecture_ReturnsNull_ForUnknownMachineType() File.WriteAllBytes(path, pe); // Act - var result = PeHelper.DetectPeArchitecture(path); + var result = MsixService.DetectPeArchitecture(path); // Assert Assert.IsNull(result); @@ -847,161 +845,14 @@ private static byte[] BuildMinimalNativePe(ushort machineType) #endregion - #region AutoDetectProcessorArchitecture Tests - - private const string ManifestWithoutArch = """ - - - - - """; - - private const string ManifestWithX86Arch = """ - - - - - """; - - private const string ManifestWithNeutralArch = """ - - - - - """; - - private static TaskContext CreateTestTaskContext() - { - var task = new GroupableTask("test", null); - var console = new Spectre.Console.Testing.TestConsole(); - var logger = NullLogger.Instance; - var renderLock = new Lock(); - return new TaskContext(task, null, console, logger, renderLock); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_SetsArch_WhenMissingFromManifest() - { - // Arrange — x64 PE, manifest without ProcessorArchitecture - var exePath = Path.Combine(_tempDir.FullName, "test.exe"); - File.WriteAllBytes(exePath, BuildMinimalNativePe(0x8664)); // x64 - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithoutArch, exePath, taskContext); - - // Assert - Assert.AreEqual("x64", arch); - StringAssert.Contains(content, "ProcessorArchitecture=\"x64\""); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_SetsArm64_WhenMissingFromManifest() - { - // Arrange — arm64 PE, manifest without ProcessorArchitecture - var exePath = Path.Combine(_tempDir.FullName, "test.exe"); - File.WriteAllBytes(exePath, BuildMinimalNativePe(0xAA64)); // arm64 - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithoutArch, exePath, taskContext); - - // Assert - Assert.AreEqual("arm64", arch); - StringAssert.Contains(content, "ProcessorArchitecture=\"arm64\""); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenMatchesExe() - { - // Arrange — x86 PE, manifest already has x86 - var exePath = Path.Combine(_tempDir.FullName, "test.exe"); - File.WriteAllBytes(exePath, BuildMinimalNativePe(0x014C)); // x86 - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithX86Arch, exePath, taskContext); - - // Assert — manifest unchanged, architecture returned - Assert.AreEqual("x86", arch); - Assert.AreEqual(ManifestWithX86Arch, content, "Manifest should not be modified when arch matches"); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenMismatch() - { - // Arrange — x64 PE, manifest says x86 → should warn but keep existing - var exePath = Path.Combine(_tempDir.FullName, "test.exe"); - File.WriteAllBytes(exePath, BuildMinimalNativePe(0x8664)); // x64 - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithX86Arch, exePath, taskContext); - - // Assert — returns existing arch, does not modify manifest - Assert.AreEqual("x86", arch); - Assert.AreEqual(ManifestWithX86Arch, content, "Manifest should not be modified on mismatch"); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_SkipsWarning_WhenNeutral() - { - // Arrange — x64 PE, manifest says "neutral" → should not warn - var exePath = Path.Combine(_tempDir.FullName, "test.exe"); - File.WriteAllBytes(exePath, BuildMinimalNativePe(0x8664)); // x64 - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithNeutralArch, exePath, taskContext); - - // Assert — returns "neutral", no modification - Assert.AreEqual("neutral", arch); - Assert.AreEqual(ManifestWithNeutralArch, content); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenExeNotFound() - { - // Arrange — non-existent exe path, manifest has no arch - var exePath = Path.Combine(_tempDir.FullName, "nonexistent.exe"); - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithoutArch, exePath, taskContext); - - // Assert — returns null arch (can't detect), manifest unchanged - Assert.IsNull(arch); - Assert.AreEqual(ManifestWithoutArch, content); - } - - [TestMethod] - public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenExeIsNotPe() - { - // Arrange — non-PE file, manifest already has arch - var exePath = Path.Combine(_tempDir.FullName, "notape.exe"); - File.WriteAllText(exePath, "not a PE file"); - var taskContext = CreateTestTaskContext(); - - // Act - var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithX86Arch, exePath, taskContext); - - // Assert — returns existing manifest arch when PE detection fails - Assert.AreEqual("x86", arch); - Assert.AreEqual(ManifestWithX86Arch, content); - } - - #endregion - #region ContainsXGenerateLanguage / ReplaceXGenerateLanguage Tests [TestMethod] public void ContainsXGenerateLanguage_ReturnsTrueForXGenerateManifest() { - var manifest = @" - + var manifest = @" - -"; + "; Assert.IsTrue(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -1009,11 +860,9 @@ public void ContainsXGenerateLanguage_ReturnsTrueForXGenerateManifest() [TestMethod] public void ContainsXGenerateLanguage_ReturnsFalseForConcreteLanguage() { - var manifest = @" - + var manifest = @" - -"; + "; Assert.IsFalse(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -1021,7 +870,7 @@ public void ContainsXGenerateLanguage_ReturnsFalseForConcreteLanguage() [TestMethod] public void ContainsXGenerateLanguage_ReturnsFalseForNoResources() { - var manifest = @""; + var manifest = @""; Assert.IsFalse(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -1029,32 +878,28 @@ public void ContainsXGenerateLanguage_ReturnsFalseForNoResources() [TestMethod] public void ReplaceXGenerateLanguage_ReplacesSingleLanguage() { - var manifest = @" - + var manifest = @" - -"; + "; var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US"]); - Assert.Contains(@"Language=""en-US""", result); + Assert.Contains(@"", result); Assert.DoesNotContain("x-generate", result); } [TestMethod] public void ReplaceXGenerateLanguage_ReplacesMultipleLanguages() { - var manifest = @" - + var manifest = @" - -"; + "; var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US", "fr-FR", "de-DE"]); - Assert.Contains(@"Language=""en-US""", result); - Assert.Contains(@"Language=""fr-FR""", result); - Assert.Contains(@"Language=""de-DE""", result); + Assert.Contains(@"", result); + Assert.Contains(@"", result); + Assert.Contains(@"", result); Assert.DoesNotContain("x-generate", result); } @@ -1062,7 +907,7 @@ public void ReplaceXGenerateLanguage_ReplacesMultipleLanguages() public void ReplaceXGenerateLanguage_PreservesRestOfManifest() { var manifest = @" - + @@ -1073,30 +918,29 @@ public void ReplaceXGenerateLanguage_PreservesRestOfManifest() var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US"]); Assert.Contains(@"", result); + Assert.Contains(@"", result); Assert.DoesNotContain("x-generate", result); } [TestMethod] public void ReplaceXGenerateLanguage_HandlesVariousWhitespace() { - var manifest = "\n\n\n\n"; + // x-generate with different whitespace patterns + var manifest = "\n\n"; var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US"]); - Assert.Contains(@"Language=""en-US""", result); + Assert.Contains(@"", result); Assert.DoesNotContain("x-generate", result); } [TestMethod] public void ContainsXGenerateLanguage_HandlesSingleQuotes() { - var manifest = @" - + var manifest = @" - -"; + "; Assert.IsTrue(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -1182,10 +1026,9 @@ private MsixService CreateMsixServiceForManifestRewriteTests() null!, null!, null!, - null!, - null!, NullLogger.Instance, - new CurrentDirectoryProvider(_tempDir.FullName)); + new CurrentDirectoryProvider(_tempDir.FullName), + null!); } private static async Task InvokeUpdateAppxManifestContentAsync(MsixService service, string manifest) @@ -1194,24 +1037,20 @@ private static async Task InvokeUpdateAppxManifestContentAsync(MsixServi Assert.IsNotNull(updateMethod, "Could not locate UpdateAppxManifestContentAsync via reflection"); // selfContained=true and executable=.dll avoid dependency mutation paths, keeping this test focused - // exePath=null skips ProcessorArchitecture detection var resultTask = updateMethod.Invoke(service, [ manifest, null, "TestApp.dll", - null, true, true, null, null!, CancellationToken.None - ]) as dynamic; + ]) as Task; - Assert.IsNotNull(resultTask, "Reflection call did not return a Task"); - var result = await resultTask; - // Named tuple members aren't available via reflection; use Item1 (Content) - return result.Item1; + Assert.IsNotNull(resultTask, "Reflection call did not return Task"); + return await resultTask; } #endregion @@ -1337,11 +1176,11 @@ public void ExtractExecutionAliases_NoAliases_ReturnsEmptyList() public void InsertPackageLevelExtensions_WithExistingPackageLevelExtensions_InsertsBeforeClose() { // Arrange — manifest has both Application-level and Package-level - var manifest = @" + var manifest = @" - + @@ -1349,7 +1188,7 @@ public void InsertPackageLevelExtensions_WithExistingPackageLevelExtensions_Inse "; - var newEntry = @""; + var newEntry = " \n"; // Act var result = MsixService.InsertPackageLevelExtensions(manifest, newEntry); @@ -1367,16 +1206,16 @@ public void InsertPackageLevelExtensions_WithExistingPackageLevelExtensions_Inse public void InsertPackageLevelExtensions_WithOnlyApplicationLevelExtensions_CreatesNewPackageLevelBlock() { // Arrange — manifest has ONLY Application-level (the regression scenario) - var manifest = @" + var manifest = @" - + "; - var newEntry = @""; + var newEntry = " \n"; // Act var result = MsixService.InsertPackageLevelExtensions(manifest, newEntry); @@ -1391,7 +1230,7 @@ public void InsertPackageLevelExtensions_WithOnlyApplicationLevelExtensions_Crea "InProcessServer must be outside (Package-level), not inside Application-level Extensions"); // Should have two separate blocks - Assert.AreEqual(2, CountOccurrences(result, ""), "Should have Application-level + new Package-level blocks"); } @@ -1399,12 +1238,12 @@ public void InsertPackageLevelExtensions_WithOnlyApplicationLevelExtensions_Crea public void InsertPackageLevelExtensions_WithNoExtensions_CreatesNewPackageLevelBlock() { // Arrange — manifest has no at all - var manifest = @" + var manifest = @" "; - var newEntry = @""; + var newEntry = " \n"; // Act var result = MsixService.InsertPackageLevelExtensions(manifest, newEntry); @@ -1412,7 +1251,7 @@ public void InsertPackageLevelExtensions_WithNoExtensions_CreatesNewPackageLevel // Assert Assert.IsTrue(result.Contains("inProcessServer"), "Should contain the new entry"); var packageCloseIndex = result.IndexOf("", StringComparison.Ordinal); - var extensionsIndex = result.IndexOf("", StringComparison.Ordinal); Assert.IsTrue(extensionsIndex > result.IndexOf("", StringComparison.Ordinal), "New block should be after "); Assert.IsTrue(extensionsIndex < packageCloseIndex, diff --git a/src/winapp-CLI/WinApp.Cli.Tests/PackageCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/PackageCommandTests.cs index 12dda16b..510edb53 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/PackageCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/PackageCommandTests.cs @@ -1617,276 +1617,4 @@ private async Task EnsureWinAppSdkRuntimeInTestCacheAsync(string winAppSdkVersio } } } - - #region AppxRecipe helpers - - /// - /// Manifest content that includes build:Metadata with makepri.exe entry, - /// mimicking what MSBuild generates during dotnet build. - /// - private const string MSBuildGeneratedManifestContent = @" - - - - Recipe Test Package - Test Publisher - Test package with MSBuild metadata - Assets\Logo.png - - - - - - - - - - - - - - - -"; - - /// - /// Creates a .build.appxrecipe XML file that maps source files to their package paths. - /// - private static string CreateAppxRecipeContent(string inputDir, (string relativeSource, string packagePath)[] files, string? manifestRelativePath = null) - { - var sb = new System.Text.StringBuilder(); - sb.AppendLine(@""); - sb.AppendLine(@""); - sb.AppendLine(@" "); - - // Add manifest entry - var manifestSource = manifestRelativePath ?? "AppxManifest.xml"; - sb.AppendLine($@" "); - sb.AppendLine(@" AppxManifest.xml"); - sb.AppendLine(@" "); - - // Add file entries - foreach (var (relativeSource, packagePath) in files) - { - sb.AppendLine($@" "); - sb.AppendLine($@" {packagePath}"); - sb.AppendLine(@" "); - } - - sb.AppendLine(@" "); - sb.AppendLine(@""); - return sb.ToString(); - } - - #endregion - - #region AppxRecipe packaging tests - - [TestMethod] - public async Task CreateMsixPackageAsync_WithAppxRecipe_OnlyIncludesRecipeFiles() - { - // Arrange — create an input folder with an MSBuild-generated manifest, - // a .build.appxrecipe, and some files that should NOT end up in the package. - var packageDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "RecipeTestPackage")); - packageDir.Create(); - - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "AppxManifest.xml"), MSBuildGeneratedManifestContent, TestContext.CancellationToken); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.exe"), "fake exe content", TestContext.CancellationToken); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.dll"), "fake dll content", TestContext.CancellationToken); - - var assetsDir = Path.Combine(packageDir.FullName, "Assets"); - Directory.CreateDirectory(assetsDir); - await File.WriteAllTextAsync(Path.Combine(assetsDir, "Logo.png"), "fake logo content", TestContext.CancellationToken); - - // Files that should NOT be in the package (not in recipe) - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.pdb"), "debug symbols", TestContext.CancellationToken); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.deps.json"), "{}", TestContext.CancellationToken); - - // Create the .build.appxrecipe — only lists the files that belong in the package - var recipeContent = CreateAppxRecipeContent(packageDir.FullName, - [ - ("TestApp.exe", "TestApp.exe"), - ("TestApp.dll", "TestApp.dll"), - (@"Assets\Logo.png", @"Assets\Logo.png"), - ]); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.build.appxrecipe"), recipeContent, TestContext.CancellationToken); - - await File.WriteAllTextAsync(_configService.ConfigPath.FullName, "packages: []", TestContext.CancellationToken); - - // Act - var result = await _msixService.CreateMsixPackageAsync( - inputFolder: packageDir, - outputPath: _tempDirectory, - TestTaskContext, - packageName: "RecipeTestPackage", - skipPri: true, - autoSign: false, - cancellationToken: CancellationToken.None - ); - - // Assert - Assert.IsTrue(result.MsixPath.Exists, "MSIX package should exist"); - - using var archive = ZipFile.OpenRead(result.MsixPath.FullName); - var entryNames = archive.Entries.Select(e => e.FullName).ToList(); - - // Files listed in the recipe should be present - Assert.IsTrue(entryNames.Any(e => e.Equals("TestApp.exe", StringComparison.OrdinalIgnoreCase)), - "MSIX should include TestApp.exe from recipe"); - Assert.IsTrue(entryNames.Any(e => e.Equals("TestApp.dll", StringComparison.OrdinalIgnoreCase)), - "MSIX should include TestApp.dll from recipe"); - Assert.IsTrue(entryNames.Any(e => e.Equals("Assets/Logo.png", StringComparison.OrdinalIgnoreCase)), - "MSIX should include Assets/Logo.png from recipe"); - - // Files NOT in the recipe should be excluded - Assert.IsFalse(entryNames.Any(e => e.Equals("TestApp.pdb", StringComparison.OrdinalIgnoreCase)), - "MSIX should NOT include TestApp.pdb (not in recipe)"); - Assert.IsFalse(entryNames.Any(e => e.Equals("TestApp.deps.json", StringComparison.OrdinalIgnoreCase)), - "MSIX should NOT include TestApp.deps.json (not in recipe)"); - Assert.IsFalse(entryNames.Any(e => e.Contains(".appxrecipe", StringComparison.OrdinalIgnoreCase)), - "MSIX should NOT include the .appxrecipe file itself"); - } - - [TestMethod] - public async Task CreateMsixPackageAsync_MSBuildManifestWithoutRecipe_FallsBackToFullCopy() - { - // Arrange — MSBuild-generated manifest but no .build.appxrecipe file - var packageDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "NoRecipeTestPackage")); - packageDir.Create(); - - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "AppxManifest.xml"), MSBuildGeneratedManifestContent, TestContext.CancellationToken); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.exe"), "fake exe content", TestContext.CancellationToken); - - var assetsDir = Path.Combine(packageDir.FullName, "Assets"); - Directory.CreateDirectory(assetsDir); - await File.WriteAllTextAsync(Path.Combine(assetsDir, "Logo.png"), "fake logo content", TestContext.CancellationToken); - - // Extra file — without a recipe, full copy should include it - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.pdb"), "debug symbols", TestContext.CancellationToken); - - await File.WriteAllTextAsync(_configService.ConfigPath.FullName, "packages: []", TestContext.CancellationToken); - - // Act - var result = await _msixService.CreateMsixPackageAsync( - inputFolder: packageDir, - outputPath: _tempDirectory, - TestTaskContext, - packageName: "NoRecipeTestPackage", - skipPri: true, - autoSign: false, - cancellationToken: CancellationToken.None - ); - - // Assert — all files should be present because we fell back to full copy - Assert.IsTrue(result.MsixPath.Exists, "MSIX package should exist"); - - using var archive = ZipFile.OpenRead(result.MsixPath.FullName); - var entryNames = archive.Entries.Select(e => e.FullName).ToList(); - - Assert.IsTrue(entryNames.Any(e => e.Equals("TestApp.exe", StringComparison.OrdinalIgnoreCase)), - "MSIX should include TestApp.exe"); - Assert.IsTrue(entryNames.Any(e => e.Equals("TestApp.pdb", StringComparison.OrdinalIgnoreCase)), - "MSIX should include TestApp.pdb (full copy fallback)"); - } - - [TestMethod] - public async Task CreateMsixPackageAsync_NonMSBuildManifest_UsesFullCopy() - { - // Arrange — standard manifest without build:Metadata - var packageDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "NonMSBuildTestPackage")); - CreateTestPackageStructure(packageDir); - - // Add extra file — should be included since it's a non-MSBuild manifest - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "extra.dll"), "extra dll content", TestContext.CancellationToken); - - // Even though there's a .appxrecipe, it should be ignored for non-MSBuild manifests - var recipeContent = CreateAppxRecipeContent(packageDir.FullName, - [ - ("TestApp.exe", "TestApp.exe"), - ]); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.build.appxrecipe"), recipeContent, TestContext.CancellationToken); - - await File.WriteAllTextAsync(_configService.ConfigPath.FullName, "packages: []", TestContext.CancellationToken); - - // Act - var result = await _msixService.CreateMsixPackageAsync( - inputFolder: packageDir, - outputPath: _tempDirectory, - TestTaskContext, - packageName: "NonMSBuildTestPackage", - skipPri: true, - autoSign: false, - cancellationToken: CancellationToken.None - ); - - // Assert — all files should be present (non-MSBuild manifests always use full copy) - Assert.IsTrue(result.MsixPath.Exists, "MSIX package should exist"); - - using var archive = ZipFile.OpenRead(result.MsixPath.FullName); - var entryNames = archive.Entries.Select(e => e.FullName).ToList(); - - Assert.IsTrue(entryNames.Any(e => e.Equals("TestApp.exe", StringComparison.OrdinalIgnoreCase)), - "MSIX should include TestApp.exe"); - Assert.IsTrue(entryNames.Any(e => e.Equals("extra.dll", StringComparison.OrdinalIgnoreCase)), - "MSIX should include extra.dll (full copy for non-MSBuild manifest)"); - } - - [TestMethod] - public async Task CreateMsixPackageAsync_WithAppxRecipe_MapsPackagePathsCorrectly() - { - // Arrange — recipe maps a file from a flat source to a nested package path - var packageDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "RecipePathMappingPackage")); - packageDir.Create(); - - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "AppxManifest.xml"), MSBuildGeneratedManifestContent, TestContext.CancellationToken); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.exe"), "fake exe content", TestContext.CancellationToken); - - // File at root that the recipe says should go to a subdirectory - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "logo.png"), "logo content", TestContext.CancellationToken); - - var assetsDir = Path.Combine(packageDir.FullName, "Assets"); - Directory.CreateDirectory(assetsDir); - await File.WriteAllTextAsync(Path.Combine(assetsDir, "Logo.png"), "asset logo content", TestContext.CancellationToken); - - var recipeContent = CreateAppxRecipeContent(packageDir.FullName, - [ - ("TestApp.exe", "TestApp.exe"), - ("logo.png", @"Assets\Logo.png"), // remap: root → Assets subdirectory - ]); - await File.WriteAllTextAsync(Path.Combine(packageDir.FullName, "TestApp.build.appxrecipe"), recipeContent, TestContext.CancellationToken); - - await File.WriteAllTextAsync(_configService.ConfigPath.FullName, "packages: []", TestContext.CancellationToken); - - // Act - var result = await _msixService.CreateMsixPackageAsync( - inputFolder: packageDir, - outputPath: _tempDirectory, - TestTaskContext, - packageName: "RecipePathMappingPackage", - skipPri: true, - autoSign: false, - cancellationToken: CancellationToken.None - ); - - // Assert - Assert.IsTrue(result.MsixPath.Exists, "MSIX package should exist"); - - using var archive = ZipFile.OpenRead(result.MsixPath.FullName); - var entryNames = archive.Entries.Select(e => e.FullName).ToList(); - - // The logo.png from root should appear at Assets/Logo.png per recipe mapping - Assert.IsTrue(entryNames.Any(e => e.Equals("Assets/Logo.png", StringComparison.OrdinalIgnoreCase)), - "MSIX should include Assets/Logo.png mapped from root logo.png by recipe"); - Assert.IsTrue(entryNames.Any(e => e.Equals("TestApp.exe", StringComparison.OrdinalIgnoreCase)), - "MSIX should include TestApp.exe"); - } - - #endregion } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/PngHelper.cs b/src/winapp-CLI/WinApp.Cli.Tests/PngHelper.cs index 8b2f8742..e2edbd2b 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/PngHelper.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/PngHelper.cs @@ -25,16 +25,6 @@ internal static void CreateTestImage(string path) File.WriteAllBytes(path, pngData); } - internal static void CreateTestSvgImage(string path) - { - var svgContent = """ - - - - """; - File.WriteAllText(path, svgContent); - } - /// /// Verifies that all pixels in the image are fully transparent (alpha = 0). /// diff --git a/src/winapp-CLI/WinApp.Cli.Tests/PowerShellServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/PowerShellServiceTests.cs new file mode 100644 index 00000000..56d4d2a3 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli.Tests/PowerShellServiceTests.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using WinApp.Cli.Services; + +namespace WinApp.Cli.Tests; + +[TestClass] +[DoNotParallelize] +public class PowerShellServiceTests() : BaseCommandTests(configPaths: false) +{ + [TestMethod] + public async Task RunCommandAsync_WithRestoreStyleStdOut_ShouldReturnStdOut() + { + var service = GetRequiredService(); + + var (exitCode, output, error) = await service.RunCommandAsync( + "Write-Output 'SKIP|Microsoft.WindowsAppRuntime.1.8.msix|Already installed'; Write-Output 'INSTALLING|2 packages will be installed'", + TestTaskContext, + cancellationToken: TestContext.CancellationToken); + + Assert.AreEqual(0, exitCode); + Assert.IsTrue( + output.Contains("SKIP|Microsoft.WindowsAppRuntime.1.8.msix|Already installed", StringComparison.Ordinal), + $"Expected SKIP marker in output. Captured output:\n{output}"); + Assert.IsTrue( + output.Contains("INSTALLING|2 packages will be installed", StringComparison.Ordinal), + $"Expected INSTALLING marker in output. Captured output:\n{output}"); + Assert.IsTrue(string.IsNullOrWhiteSpace(error)); + } + + [TestMethod] + public async Task RunCommandAsync_WithRestoreStyleStdErr_ShouldReturnStdErr() + { + var service = GetRequiredService(); + + var (exitCode, output, error) = await service.RunCommandAsync( + "[Console]::Error.WriteLine('ERROR|Microsoft.WindowsAppRuntime.1.8.msix|Installation failed'); exit 1", + TestTaskContext, + cancellationToken: TestContext.CancellationToken); + + Assert.AreNotEqual(0, exitCode); + Assert.IsTrue(string.IsNullOrWhiteSpace(output)); + Assert.IsTrue( + error.Contains("ERROR|Microsoft.WindowsAppRuntime.1.8.msix|Installation failed", StringComparison.Ordinal), + $"Expected ERROR marker in stderr. Captured error:\n{error}"); + } +} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs index 86c18b1a..cb15b4ac 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs @@ -13,7 +13,6 @@ public class RunCommandTests : BaseCommandTests { private FakeMsixService _fakeMsixService = null!; private FakeAppLauncherService _fakeAppLauncherService = null!; - private FakeDebugOutputService _fakeDebugOutputService = null!; private const string TestManifestContent = """ @@ -49,11 +48,9 @@ protected override IServiceCollection ConfigureServices(IServiceCollection servi { _fakeMsixService = new FakeMsixService(); _fakeAppLauncherService = new FakeAppLauncherService(); - _fakeDebugOutputService = new FakeDebugOutputService(); return services .AddSingleton(_fakeMsixService) .AddSingleton(_fakeAppLauncherService) - .AddSingleton(_fakeDebugOutputService) .AddSingleton(); } @@ -412,189 +409,4 @@ public async Task RunCommand_WithAlias_RegistersIdentityButDoesNotLaunchByAumid( } #endregion - - #region --debug-output option tests - - [TestMethod] - public void ParseOptions_DebugOutput_IsParsedCorrectly() - { - // Arrange - var command = GetRequiredService(); - - // Act - var parseResult = command.Parse([_tempDirectory.FullName, "--debug-output"]); - - // Assert - Assert.IsEmpty(parseResult.Errors, "There should be no parsing errors"); - Assert.IsTrue(parseResult.GetValue(RunCommand.DebugOutputOption)); - } - - [TestMethod] - public void ParseOptions_DebugOutputNotSpecified_DefaultsToFalse() - { - // Arrange - var command = GetRequiredService(); - - // Act - var parseResult = command.Parse([_tempDirectory.FullName]); - - // Assert - Assert.IsEmpty(parseResult.Errors, "There should be no parsing errors"); - Assert.IsFalse(parseResult.GetValue(RunCommand.DebugOutputOption)); - } - - [TestMethod] - public async Task RunCommand_DebugOutputAndNoLaunch_ReturnsError() - { - // Arrange - --debug-output and --no-launch are mutually exclusive - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--debug-output", "--no-launch"]); - - // Assert - Assert.AreEqual(1, exitCode, "Command should fail when both --debug-output and --no-launch are specified"); - Assert.AreEqual(0, _fakeMsixService.AddLooseLayoutCalls.Count, "No identity should be created"); - Assert.AreEqual(0, _fakeAppLauncherService.LaunchCalls.Count, "No application should be launched"); - Assert.AreEqual(0, _fakeDebugOutputService.AttachCalls.Count, "Debug loop should not run"); - } - - [TestMethod] - public async Task RunCommand_DebugOutput_LaunchesByAumidAndCallsDebugService() - { - // Arrange - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--debug-output"]); - - // Assert - Assert.AreEqual(0, exitCode, "Command should succeed"); - Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); - Assert.AreEqual(1, _fakeAppLauncherService.LaunchCalls.Count, "Application should be launched via AUMID"); - Assert.AreEqual(1, _fakeDebugOutputService.AttachCalls.Count, "Debug service should be called"); - Assert.AreEqual(_fakeAppLauncherService.FakeProcessId, _fakeDebugOutputService.AttachCalls[0], - "Debug service should receive the launched process ID"); - } - - [TestMethod] - public async Task RunCommand_DebugOutputWithAlias_SkipsAumidLaunch() - { - // Arrange - with both --debug-output and --with-alias, the execution alias path is used. - // LaunchViaExecutionAliasAsync will fail because there's no processed manifest in AppX output, - // but verify that AUMID launch is not used. - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--debug-output", "--with-alias"]); - - // Assert - identity should be created but AUMID launch should NOT be used - Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); - Assert.AreEqual(0, _fakeAppLauncherService.LaunchCalls.Count, - "Application should NOT be launched via AUMID when --with-alias is specified"); - } - - [TestMethod] - public async Task RunCommand_DebugOutput_UsesDebugServiceExitCode() - { - // Arrange - await CreateTestManifestAsync(); - _fakeDebugOutputService.FakeExitCode = 42; - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--debug-output"]); - - // Assert - Assert.AreEqual(42, exitCode, "Exit code should come from the debug service"); - } - - [TestMethod] - public async Task RunCommand_JsonAndDebugOutput_ReturnsError() - { - // Arrange - --json and --debug-output are mutually exclusive - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--debug-output", "--json"]); - - // Assert - Assert.AreEqual(1, exitCode, "Command should fail when both --json and --debug-output are specified"); - Assert.AreEqual(0, _fakeMsixService.AddLooseLayoutCalls.Count, "No identity should be created"); - Assert.AreEqual(0, _fakeAppLauncherService.LaunchCalls.Count, "No application should be launched"); - Assert.AreEqual(0, _fakeDebugOutputService.AttachCalls.Count, "Debug loop should not run"); - } - - [TestMethod] - public async Task RunCommand_JsonAndWithAlias_ReturnsError() - { - // Arrange - --json and --with-alias are mutually exclusive - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--with-alias", "--json"]); - - // Assert - Assert.AreEqual(1, exitCode, "Command should fail when both --json and --with-alias are specified"); - Assert.AreEqual(0, _fakeMsixService.AddLooseLayoutCalls.Count, "No identity should be created"); - Assert.AreEqual(0, _fakeAppLauncherService.LaunchCalls.Count, "No application should be launched"); - } - - [TestMethod] - public async Task RunCommand_DebugOutput_PropagatesFailureExitCode() - { - // Arrange — debug service returns -1 (e.g., DebugActiveProcess failed) - await CreateTestManifestAsync(); - _fakeDebugOutputService.FakeExitCode = -1; - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--debug-output"]); - - // Assert - Assert.AreEqual(-1, exitCode, "Failure exit code from the debug service should propagate"); - } - - [TestMethod] - public async Task RunCommand_DebugOutputWithAliasAndNoLaunch_ReturnsError() - { - // Arrange — all three flags conflict; --with-alias + --no-launch is caught first - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, - [_tempDirectory.FullName, "--debug-output", "--with-alias", "--no-launch"]); - - // Assert - Assert.AreEqual(1, exitCode, "Command should fail with conflicting flags"); - Assert.AreEqual(0, _fakeMsixService.AddLooseLayoutCalls.Count, "No identity should be created"); - Assert.AreEqual(0, _fakeDebugOutputService.AttachCalls.Count, "Debug loop should not run"); - } - - [TestMethod] - public async Task RunCommand_DebugOutputWithArgs_ForwardsArgsToLauncher() - { - // Arrange - await CreateTestManifestAsync(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, - [_tempDirectory.FullName, "--debug-output", "--args", "--my-flag value"]); - - // Assert - Assert.AreEqual(0, exitCode, "Command should succeed"); - Assert.AreEqual(1, _fakeAppLauncherService.LaunchCalls.Count, "Application should be launched"); - Assert.AreEqual("--my-flag value", _fakeAppLauncherService.LaunchCalls[0].Arguments, - "Arguments should be forwarded to the launcher"); - Assert.AreEqual(1, _fakeDebugOutputService.AttachCalls.Count, "Debug service should be called"); - } - - #endregion } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs deleted file mode 100644 index 21aeca83..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.DependencyInjection; -using WinApp.Cli.Commands; -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class UnregisterCommandTests : BaseCommandTests -{ - private FakePackageRegistrationService _fakePackageRegistrationService = null!; - - private const string TestManifestContent = """ - - - - - Test Package - Test Publisher - Test package - Assets\Logo.png - - - - - - - - - - - - - - """; - - protected override IServiceCollection ConfigureServices(IServiceCollection services) - { - _fakePackageRegistrationService = new FakePackageRegistrationService(); - return services - .AddSingleton(_fakePackageRegistrationService); - } - - private async Task CreateTestManifestAsync(string? directory = null) - { - directory ??= _tempDirectory.FullName; - var manifestPath = Path.Combine(directory, "appxmanifest.xml"); - await File.WriteAllTextAsync(manifestPath, TestManifestContent, TestContext.CancellationToken); - return new FileInfo(manifestPath); - } - - [TestMethod] - public async Task UnregisterCommand_WithManifest_UnregistersDevPackages() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - _tempDirectory.FullName, IsDevelopmentMode: true) - ]; - _fakePackageRegistrationService.FakeUnregisterResult = true; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage")); - Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Contains("TestPackage")); - } - - [TestMethod] - public async Task UnregisterCommand_ChecksBothNameAndDebugVariant() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - _fakePackageRegistrationService.FakeDevPackages = []; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage")); - Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage.debug")); - } - - [TestMethod] - public async Task UnregisterCommand_SkipsNonDevModePackages() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - _tempDirectory.FullName, IsDevelopmentMode: false) - ]; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.AreEqual(0, _fakePackageRegistrationService.UnregisterCalls.Count); - } - - [TestMethod] - public async Task UnregisterCommand_SkipsPackagesFromDifferentTree() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - @"C:\OtherProject\bin\Debug\AppX", IsDevelopmentMode: true) - ]; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.AreEqual(0, _fakePackageRegistrationService.UnregisterCalls.Count); - } - - [TestMethod] - public async Task UnregisterCommand_WithForce_SkipsLocationCheck() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - @"C:\OtherProject\bin\Debug\AppX", IsDevelopmentMode: true) - ]; - _fakePackageRegistrationService.FakeUnregisterResult = true; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName, "--force"]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Contains("TestPackage")); - } - - [TestMethod] - public async Task UnregisterCommand_WithJson_ReturnsJsonOutput() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - _tempDirectory.FullName, IsDevelopmentMode: true) - ]; - _fakePackageRegistrationService.FakeUnregisterResult = true; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName, "--json"]); - - // Assert - Assert.AreEqual(0, exitCode); - var output = TestAnsiConsole.Output; - Assert.IsTrue(output.Contains("TestPackage_1.0.0.0_x64__abc123")); - } - - [TestMethod] - public async Task UnregisterCommand_NoManifest_ReturnsError() - { - // Arrange — empty temp directory with no manifest - var emptyDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "empty")); - emptyDir.Create(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, []); - - // Assert - Assert.AreEqual(1, exitCode); - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj b/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj index bc981218..6d15b9bf 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj +++ b/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj @@ -1,7 +1,7 @@  - net10.0-windows10.0.19041.0 + net10.0-windows enable enable diff --git a/src/winapp-CLI/WinApp.Cli.Tests/WorkspaceSetupServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/WorkspaceSetupServiceTests.cs index cf44bcc6..97093329 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/WorkspaceSetupServiceTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/WorkspaceSetupServiceTests.cs @@ -15,7 +15,8 @@ public class WorkspaceSetupServiceTests : BaseCommandTests { protected override IServiceCollection ConfigureServices(IServiceCollection services) { - return services; + return services + .AddSingleton(); } #region Helper methods @@ -207,6 +208,7 @@ protected override IServiceCollection ConfigureServices(IServiceCollection servi _fakeDotNetService = new FakeDotNetService(); return services + .AddSingleton() .AddSingleton() .AddSingleton(_fakeNugetService) .AddSingleton(_fakeDotNetService); diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.png deleted file mode 100644 index 6135405f8f13c518341e3995b41dfc0fbf86e5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^Iv~u#3?$#IayJ4}iUB?$uI>ds`|$#+%Dw(T#oQ%9 ze!&dV&*qE#sjjJ9ekxDB{uogBq^FBxh{pM;ldbs<8Su0%Z(C}Uo!o}p|QcIwa_ zzr75`QswsiYB9gAFEbIg{Dl><3nv3A}sm=EZ)#l3`NR zpZda3^rNox*D1%NC98Z~L*6zipLw~Gxn&(Y-;KmJ+aR6eLabU-L#y8HW%7P-E_-VlLqIabbHPHKT*)fT@9iWJ7iWgOT9%0}Lrj>lztPxWq6sPw3pi z#-<=#$jjrP_DD*i!RLsn0mIA=>4~N)IMYWIf=j%-zuKCdMG%tHYot70D1| zvWa0wMhauW#S>1CnI_;>!1Q3zMA17@DOVq{MQ+{U7^a&yA+%dMCG;WNPV0i;w$tu; zX^b}UKziPM)(<;)ruW;-`)bBN+rQNM*Zs_>?n$|FVFo-e*PZb*@U7VAd+tHb4e?=Blc~}S6K)wL}r*Gf`BM#QB z+y>N$mCswb4d{^{S9v_!eQj4fTRMOwOCi?lSk9%<=vAz}jM-*PQtH@Odn1LZcd^j#o> hW$4xn+CT+ep9lJ{OAO?njobhL002ovPDHLkV1nYebbkN< diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.png deleted file mode 100644 index 9c81c0fc0fe12b924da8d6319b04b7957a6e3b0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 733 zcmV<30wVp1P)-A$+fw5jbX5||j z)CE?yu|Zs5 z$xohSNqNeHr&&Ux@`t2Zai;d7I9O4t_5wRtVXpfAdRRfS`dxWg%WVC7cd-`f`a0xd z&GU_A$j6!ijOEeCnh4gi?PQGzYddkW28Owtcv(Hf+~vHij`C*izHU})?A5Fa+^qg| zZWRT7R@XJKiWNVrH)a%38LZZrRowj%3w%cMw+G(|@?8bLA@bV>*Bo*!1lMSC?*R8S za<2tz0J1g#YbLUm18YpO_62Kl@~i-!VaT%;c;+L|qTm^sJiCKE1+v!x_F%}~5ZJRK zdud>gkL*2yJyEh(3-*x7-Zt2CCuafRjDnnvyv%aSq0 z)njao1dV0XNw&c@qmj1e*jgQ$l@_urW5G4RSY#rT1z`#%3;{EB`aJK|TH^lb_3nAT z-_Q4X-(K&IS8UyqsnjYdippfmN-HT!X2MT;Dpcy~-#$k6V z|MR4vU#O&p7TC46pTflb3 zoUJ;ZRf#&8&EwXy5s%!&(q6cN62swD#FH%O-RJsjWPZN3^^@FCIQ&MxXIFo7!I#VI zkpIstuWqUV5uhgs07?k$*!`uiZ=5b#$lI|0c+XJvj(}zSE3MN#EyOK zql(#yA}~Ibl*r(s1}Z^5mmn*-n93g?-ccM+^PN?6HH~h0hjy6@XY*^i<-V)+OZ;p7 z7j`p_sT55xnYsedNIIel^QIIg7i@`2Qi}x5$!tk29$2OQI zs^kQXAKE}5ZJu$)2@Dxn?}}O@f@6@^!%9Tj+o>=jd!^ZuvBE4jb4g}Z5WMBtcmy^~ zoFGVS5|0FA!(1Q%fL?Bj*L+9ZL{mjSO8lzqrQ0UCZ)X zPwk$1HNFgaK%NxGpuXz}#ywXvf2JQ?BQ5uOZM2up4S#ieaxS$!o9o6Z=czNQb} zwAh|xLZ>+WyN%o?^uCAQw&&4o?S$DJ`WP(Hr*grL*qNXlqU0osCQ(Up5F(^$Z5;n&oJIO4uF`k&QL*j{f zU=;#MZ5{@b%qMbjTB3dh-5#mqY>%{0jgS+WdHyG diff --git a/samples/cpp-app-winui/Assets/Square150x150Logo.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square150x150Logo.png similarity index 100% rename from samples/cpp-app-winui/Assets/Square150x150Logo.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square150x150Logo.png diff --git a/samples/cpp-app-winui/Assets/Square150x150Logo.scale-200.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square150x150Logo.scale-200.png similarity index 100% rename from samples/cpp-app-winui/Assets/Square150x150Logo.scale-200.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square150x150Logo.scale-200.png diff --git a/samples/cpp-app-winui/Assets/Square44x44Logo.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square44x44Logo.png similarity index 100% rename from samples/cpp-app-winui/Assets/Square44x44Logo.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square44x44Logo.png diff --git a/samples/cpp-app-winui/Assets/Square44x44Logo.scale-200.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square44x44Logo.scale-200.png similarity index 100% rename from samples/cpp-app-winui/Assets/Square44x44Logo.scale-200.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square44x44Logo.scale-200.png diff --git a/samples/cpp-app-winui/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square44x44Logo.targetsize-24_altform-unplated.png similarity index 100% rename from samples/cpp-app-winui/Assets/Square44x44Logo.targetsize-24_altform-unplated.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/samples/cpp-app-winui/Assets/Wide310x150Logo.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Wide310x150Logo.png similarity index 100% rename from samples/cpp-app-winui/Assets/Wide310x150Logo.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Wide310x150Logo.png diff --git a/samples/cpp-app-winui/Assets/Wide310x150Logo.scale-200.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Wide310x150Logo.scale-200.png similarity index 100% rename from samples/cpp-app-winui/Assets/Wide310x150Logo.scale-200.png rename to src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/Wide310x150Logo.scale-200.png diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.png deleted file mode 100644 index 49a431d6c94028d821bd2b027ac0d1917b38f078..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806 zcmV+>1KIqEP)`#j%umtt)f(_Qx@)^@hpb;sJa4{hxKS}ERE zEv}h+ly_S)f4r7=+x)z;emj|q|KCb3T*D^XR^2h-YPOS7w(aJH2e)Ob7Q$^;?<}~6 z?Mu4thy_=(Jse866&$#RZ6)8vfos?b`8Ebz%Z6x6!?kRPwj^A~25C#db!?Ef1YFOC zX^X@4Y?!tv+`tBEi@^C^(A^*hawFY{)hr&Spcld2kLJw9SQc*r06=oXdu7v*BDeY?}q=v4Pu6IFAk7 zX2AJu=(Z8gXG6CQa0DB?t%oDn;B6fo#fEQd;V3qITLbq&v1QO!!#z`M8MM`K8;UK5 zwi=FN%b~4-`(9<)Hd$}mPK!L-=7G0d8ML);u9i8r)ofD|4&PQ^67I=EKD@2Hl9EqH z;BD=dlzcjZZtHJAC^&Rme*;3nf!oIWHUbXZHr}@paM(8UcGri)wwbrPJ{+{oJ|%c? z&^G&&;K3o=+{2X%hir2XS1uf|%|DYmaKJYIOzObl+Nh(v4To!^j`B7ftc`pEV#C4O z$R{8+9IB0Gv*A!}G@A_vY74PhaG8n{aQTOJl$_m)e#jmOr#9u#ilemgcRGydjIzg5(>zb)r)iVrgW7F(y& k>2x}sPN&o9bh>}uFDMS%SxTob*#H0l07*qoM6N<$f=8c%l>h($ diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.scale-200.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.scale-200.png deleted file mode 100644 index 8b4a5d0dd5f6c6ab408e1edf04a07888859a9eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2097 zcmc&$ZA@EL7(Q+3r2+3&!t4k)FxZ4;Vj0QoMdu<_3Kciz7YR4=A(qT^w)Jxi;?x!9 zuyq6dMF?b>c0sq%A~kJcD9FY~qQRMt?ZR3YyDZt}Od;|mgpc{2dv9AHF){kXU%k({ z=Y8JidEayHTkG@twPZ|U3_^%3ct-OgLSiFAqDN!|tbCX@c@?4P`2x*TMK!+Q4b?k0 ziW7!!KF6dPWcF<%I|iznM~`QJ_V7sHGV_D`dhgpA9Vd@&X}ErK+j~_rdv;Bp?OA@a zFXOk7eWOJe5NcK;6h$FaM&7JxNc#-@QTwzW6x#d_zmQNkz5) zPI;kh;3d;5UCJU+9a(cOxX(|edWoOiAEdGU#kPJ&xnc2||3vDbuhBCkj-pb0as$Zl z5;}4n=**n6(1g`JEtSy;SG6X;#-F~Oz3lESG2b5`j@wAwY4Yp<=4Xeb>iH=6aicF?DxD&q{`!&}ct zBI)aycwuobQAf&678Uf+Mmh-@9RUhyH~>?w0dixO0#jZjEc9R^=5NZw=|a(kcB?9^ zfnTiEFXp-q#B;Tn>(O%$A*ud^Rg&eVH6Y_5Y%!E39RR&s?XpG`gKwU!6FE1 z7X)DC7)*(5g}lh`4`{i~DZcWupZI`K)_4P)VE{@gc7@Xsd^86zl~_mOYH?I4!aGeX z^E(_=L6?PgveDQ+r%P@UISEXrkn`LHJZ##+!-anV>6h)IkKp;E@p8+3&(5%kS2)ld*J*rJccZM0iyaAx7+F~GW1UWFK&3X$PE1^}NH zgAG9ck5K!{07OwU@j@Do>TbH=CDEo#4m0cEyAuXy_<&jlzJVcKweSJ5 z&=q~iIn18$w8yb=rmEmHxVEUA^?RwnB?6Qlp1os8@*dWTGL2bhzZ!s*xqScR?EPL` zo(JwNdKUUYy7GtvZ3asXm)cgFvCx9EmAi;|w=a0iGiv%%VYKh`P0Wma4y`Xyx|T~( zAmfGbgbEEC7)j8b@WA@+5W3a61HJXC1dX@6_T|Czk0I0zBk%tnW~()VWITGI!`$c< gARL?UBrYYkwoDw4eo*CrzXGTrZ@;GF>596)00d&n@&Et; diff --git a/src/winapp-CLI/WinApp.Cli/Commands/CreateDebugIdentityCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/CreateDebugIdentityCommand.cs index ee8be829..c4e9cb77 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/CreateDebugIdentityCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/CreateDebugIdentityCommand.cs @@ -76,13 +76,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio } catch (Exception error) { - var baseEx = error.GetBaseException(); - var message = string.IsNullOrWhiteSpace(baseEx.Message) ? error.Message : baseEx.Message; - if (baseEx.HResult != 0) - { - message += $" (0x{baseEx.HResult:X8})"; - } - return (1, $"{UiSymbols.Error} Failed to add package identity: {message}"); + return (1, $"{UiSymbols.Error} Failed to add package identity: {error.GetBaseException().Message}"); } return (0, "Package identity created successfully."); diff --git a/src/winapp-CLI/WinApp.Cli/Commands/ManifestUpdateAssetsCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/ManifestUpdateAssetsCommand.cs index cbacfb00..d9a8d01c 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/ManifestUpdateAssetsCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/ManifestUpdateAssetsCommand.cs @@ -15,13 +15,12 @@ internal class ManifestUpdateAssetsCommand : Command, IShortDescription public static Argument ImageArgument { get; } public static Option ManifestOption { get; } - public static Option LightImageOption { get; } static ManifestUpdateAssetsCommand() { ImageArgument = new Argument("image-path") { - Description = "Path to source image file (SVG, PNG, ICO, JPG, BMP, GIF)" + Description = "Path to source image file" }; ImageArgument.AcceptExistingOnly(); @@ -30,19 +29,12 @@ static ManifestUpdateAssetsCommand() Description = "Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory)" }; ManifestOption.AcceptExistingOnly(); - - LightImageOption = new Option("--light-image") - { - Description = "Path to source image for light theme variants (SVG, PNG, ICO, JPG, BMP, GIF)" - }; - LightImageOption.AcceptExistingOnly(); } public ManifestUpdateAssetsCommand() : base("update-assets", "Generate new assets for images referenced in an appxmanifest.xml from a single source image. Source image should be at least 400x400 pixels.") { Arguments.Add(ImageArgument); Options.Add(ManifestOption); - Options.Add(LightImageOption); } public class Handler(IManifestService manifestService, ICurrentDirectoryProvider currentDirectoryProvider, IStatusService statusService, ILogger logger) : AsynchronousCommandLineAction @@ -51,7 +43,6 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio { var imagePath = parseResult.GetValue(ImageArgument); var manifestPath = parseResult.GetValue(ManifestOption); - var lightImagePath = parseResult.GetValue(LightImageOption); // If manifest path is not provided, try to find it in the current directory if (manifestPath == null) @@ -62,7 +53,6 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio logger.LogError("{UISymbol} Could not find AppxManifest.xml/Package.appxmanifest in current directory or parent directories", UiSymbols.Error); return 1; } - logger.LogDebug("Found manifest at: {ManifestPath}", manifestPath.FullName); } @@ -76,7 +66,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio { try { - await manifestService.UpdateManifestAssetsAsync(manifestPath, imagePath, taskContext, lightImagePath, cancellationToken); + await manifestService.UpdateManifestAssetsAsync(manifestPath, imagePath, taskContext, cancellationToken); return (0, "Successfully updated assets for manifest."); } catch (Exception ex) diff --git a/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs index c6517879..f2fe9bc2 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs @@ -24,8 +24,6 @@ internal partial class RunCommand : Command, IShortDescription public static Option ArgsOption { get; } public static Option NoLaunchOption { get; } public static Option WithAliasOption { get; } - public static Option DebugOutputOption { get; } - public static Option UnregisterOnExitOption { get; } static RunCommand() { @@ -61,16 +59,6 @@ static RunCommand() { Description = "Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a uap5:ExecutionAlias in the manifest. Use \"winapp manifest add-alias\" to add an execution alias to the manifest." }; - - DebugOutputOption = new Option("--debug-output") - { - Description = "Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json." - }; - - UnregisterOnExitOption = new Option("--unregister-on-exit") - { - Description = "Unregister the development package after the application exits. Only removes packages registered in development mode." - }; } public RunCommand() : base("run", "Creates packaged layout, registers the Application, and launches the packaged application.") @@ -81,16 +69,12 @@ public RunCommand() : base("run", "Creates packaged layout, registers the Applic Options.Add(ArgsOption); Options.Add(NoLaunchOption); Options.Add(WithAliasOption); - Options.Add(DebugOutputOption); - Options.Add(UnregisterOnExitOption); Options.Add(WinAppRootCommand.JsonOption); } public class Handler( IMsixService msixService, IAppLauncherService appLauncherService, - IPackageRegistrationService packageRegistrationService, - IDebugOutputService debugOutputService, ICurrentDirectoryProvider currentDirectoryProvider, IAnsiConsole ansiConsole, IStatusService statusService, @@ -104,8 +88,6 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio var appArgs = parseResult.GetValue(ArgsOption); var noLaunch = parseResult.GetValue(NoLaunchOption); var withAlias = parseResult.GetValue(WithAliasOption); - var debugOutput = parseResult.GetValue(DebugOutputOption); - var unregisterOnExit = parseResult.GetValue(UnregisterOnExitOption); var isJson = parseResult.GetValue(WinAppRootCommand.JsonOption); // Validate mutually exclusive options @@ -115,34 +97,8 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio return 1; } - if (debugOutput && noLaunch) - { - logger.LogError("{UISymbol} --debug-output and --no-launch cannot be used together.", UiSymbols.Error); - return 1; - } - - if (isJson && debugOutput) - { - logger.LogError("{UISymbol} --json and --debug-output cannot be used together.", UiSymbols.Error); - return 1; - } - - if (isJson && withAlias) - { - logger.LogError("{UISymbol} --json and --with-alias cannot be used together.", UiSymbols.Error); - return 1; - } - - if (unregisterOnExit && noLaunch) - { - logger.LogError("{UISymbol} --unregister-on-exit and --no-launch cannot be used together.", UiSymbols.Error); - return 1; - } - uint processId = 0; string? packageFamilyName = null; - string? packageFullName = null; - string? packageName = null; string? publisher = null; string? applicationId = null; string? aumid = null; @@ -199,8 +155,6 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio packageFamilyName = appLauncherService.ComputePackageFamilyName( identityResult.PackageName, identityResult.Publisher); - packageFullName = appLauncherService.GetPackageFullName(packageFamilyName); - packageName = identityResult.PackageName; publisher = identityResult.Publisher; applicationId = identityResult.ApplicationId; aumid = $"{packageFamilyName}!{applicationId}"; @@ -256,12 +210,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio // --with-alias: launch via execution alias with inherited stdio if (withAlias) { - var aliasExitCode = await LaunchViaExecutionAliasAsync(resolvedOutputDir!, appArgs, debugOutput, packageFullName, cancellationToken); - if (unregisterOnExit && packageName != null) - { - await UnregisterDevPackageAsync(packageName, cancellationToken); - } - return aliasExitCode; + return await LaunchViaExecutionAliasAsync(resolvedOutputDir!, appArgs, aumid, isJson, cancellationToken); } if (isJson) @@ -269,58 +218,27 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio PrintJson(aumid, processId, errorMessage: null); } - // --debug-output: run the debug event loop instead of plain WaitForExit. - // DebugSetProcessKillOnExit(true) in the debug service handles crash cleanup. - if (debugOutput) - { - var exitCode = await debugOutputService.RunDebugLoopAsync(processId, cancellationToken); - if (cancellationToken.IsCancellationRequested) - { - appLauncherService.TerminatePackageProcesses(packageFullName, processId); - } - if (unregisterOnExit && packageName != null) - { - await UnregisterDevPackageAsync(packageName, cancellationToken); - } - return exitCode; - } - // Wait for the launched process to exit before returning. // The process may have already exited by the time we get here (common for // fast-starting apps), in which case GetProcessById throws ArgumentException. // PIDs above int.MaxValue cannot be tracked via Process.GetProcessById. - int appExitCode; if (processId > int.MaxValue) { - appExitCode = 0; + return 0; } - else + + try { - try - { - using var process = Process.GetProcessById(unchecked((int)processId)); - await process.WaitForExitAsync(cancellationToken); - appExitCode = process.ExitCode; - } - catch (ArgumentException) - { - // Process already exited before we could attach — treat as success. - appExitCode = 0; - } - catch (OperationCanceledException) - { - // Ctrl+C — terminate all processes belonging to the package before exiting. - appLauncherService.TerminatePackageProcesses(packageFullName, processId); - appExitCode = -1; - } + using var process = Process.GetProcessById(unchecked((int)processId)); + await process.WaitForExitAsync(cancellationToken); + + return process.ExitCode; } - - if (unregisterOnExit && packageName != null) + catch (ArgumentException) { - await UnregisterDevPackageAsync(packageName, cancellationToken); + // Process already exited before we could attach — treat as success. + return 0; } - - return appExitCode; } void PrintJson(string? aumid, uint? processId, string? errorMessage) @@ -336,32 +254,15 @@ void PrintJson(string? aumid, uint? processId, string? errorMessage) ansiConsole.WriteLine(json); } - private static FileInfo FindManifest(string directory) => ManifestHelper.FindManifest(directory); - - /// - /// Unregisters dev-mode packages matching the given name. - /// Only removes packages where IsDevelopmentMode == true. - /// - private async Task UnregisterDevPackageAsync(string packageName, CancellationToken cancellationToken) + private static FileInfo FindManifest(string directory) { - try - { - var packages = packageRegistrationService.FindDevPackages(packageName); - foreach (var pkg in packages) - { - if (!pkg.IsDevelopmentMode) - { - continue; - } - - await packageRegistrationService.UnregisterAsync(pkg.Name, cancellationToken); - logger.LogDebug("Unregistered package {FullName} on exit.", pkg.FullName); - } - } - catch (Exception ex) + var manifestPath = Path.Combine(directory, "appxmanifest.xml"); + if (File.Exists(manifestPath)) { - logger.LogDebug("Failed to unregister package on exit: {Message}", ex.Message); + return new FileInfo(manifestPath); } + manifestPath = Path.Combine(directory, "Package.appxmanifest"); + return new FileInfo(manifestPath); } /// @@ -371,8 +272,8 @@ private async Task UnregisterDevPackageAsync(string packageName, CancellationTok private async Task LaunchViaExecutionAliasAsync( DirectoryInfo outputAppXDirectory, string? appArgs, - bool debugOutput, - string? packageFullName, + string? aumid, + bool isJson, CancellationToken cancellationToken) { // Read the processed manifest from the AppX output directory (placeholders already resolved) @@ -394,6 +295,11 @@ private async Task LaunchViaExecutionAliasAsync( var alias = aliases[0]; // Use the first alias + if (isJson) + { + PrintJson(aumid, processId: null, errorMessage: null); + } + // Launch the execution alias process with inherited stdio var psi = new ProcessStartInfo { @@ -418,27 +324,8 @@ private async Task LaunchViaExecutionAliasAsync( return 1; } - if (debugOutput) - { - var exitCode = await debugOutputService.RunDebugLoopAsync(unchecked((uint)process.Id), cancellationToken); - if (cancellationToken.IsCancellationRequested) - { - appLauncherService.TerminatePackageProcesses(packageFullName, unchecked((uint)process.Id)); - } - return exitCode; - } - - try - { - await process.WaitForExitAsync(cancellationToken); - return process.ExitCode; - } - catch (OperationCanceledException) - { - // Ctrl+C — terminate all processes belonging to the package before exiting. - appLauncherService.TerminatePackageProcesses(packageFullName, unchecked((uint)process.Id)); - return -1; - } + await process.WaitForExitAsync(cancellationToken); + return process.ExitCode; } catch (Exception ex) { diff --git a/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs deleted file mode 100644 index a9817cee..00000000 --- a/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.Logging; -using Spectre.Console; -using System.CommandLine; -using System.CommandLine.Invocation; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using WinApp.Cli.Helpers; -using WinApp.Cli.Services; - -namespace WinApp.Cli.Commands; - -internal class UnregisterCommand : Command, IShortDescription -{ - public string ShortDescription => "Unregister a sideloaded development package."; - - public static Option ManifestOption { get; } - public static Option ForceOption { get; } - - static UnregisterCommand() - { - ManifestOption = new Option("--manifest") - { - Description = "Path to the appxmanifest.xml (default: auto-detect from current directory)" - }; - ManifestOption.AcceptExistingOnly(); - - ForceOption = new Option("--force") - { - Description = "Skip the install-location directory check and unregister even if the package was registered from a different project tree" - }; - } - - public UnregisterCommand() : base("unregister", "Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity').") - { - Options.Add(ManifestOption); - Options.Add(ForceOption); - Options.Add(WinAppRootCommand.JsonOption); - } - - public class Handler( - IPackageRegistrationService packageRegistrationService, - ICurrentDirectoryProvider currentDirectoryProvider, - IAnsiConsole ansiConsole, - ILogger logger) : AsynchronousCommandLineAction - { - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) - { - var manifest = parseResult.GetValue(ManifestOption); - var force = parseResult.GetValue(ForceOption); - var isJson = parseResult.GetValue(WinAppRootCommand.JsonOption); - - // Resolve manifest - FileInfo resolvedManifest; - if (manifest != null && manifest.Exists) - { - resolvedManifest = manifest; - } - else - { - resolvedManifest = ManifestHelper.FindManifest(currentDirectoryProvider.GetCurrentDirectory()); - if (!resolvedManifest.Exists) - { - var message = "No manifest found in the current directory. Use --manifest to specify the path."; - if (isJson) - { - PrintJson([], [], message); - } - else - { - logger.LogError("{UISymbol} {Message}", UiSymbols.Error, message); - } - return 1; - } - } - - // Parse package name from manifest - var manifestContent = await File.ReadAllTextAsync(resolvedManifest.FullName, Encoding.UTF8, cancellationToken); - var identity = MsixService.ParseAppxManifestAsync(manifestContent); - var packageName = identity.PackageName; - - // Search for both the exact name and the .debug variant - var namesToCheck = new[] { packageName, $"{packageName}.debug" }; - var cwd = Path.GetFullPath(currentDirectoryProvider.GetCurrentDirectory()); - - var unregistered = new List(); - var skipped = new List(); - - foreach (var name in namesToCheck) - { - var packages = packageRegistrationService.FindDevPackages(name); - - foreach (var pkg in packages) - { - if (!pkg.IsDevelopmentMode) - { - if (!isJson) - { - logger.LogInformation("{UISymbol} {FullName}: installed via MSIX/Store, skipping.", UiSymbols.Note, pkg.FullName); - } - skipped.Add(pkg.FullName); - continue; - } - - // Check install location is under current directory tree - if (!force && !string.IsNullOrEmpty(pkg.InstallLocation)) - { - var installPath = Path.GetFullPath(pkg.InstallLocation); - if (!installPath.StartsWith(cwd, StringComparison.OrdinalIgnoreCase)) - { - if (!isJson) - { - logger.LogWarning("{UISymbol} {FullName}: registered from a different project tree ({Location}). Use --force to override.", - UiSymbols.Warning, pkg.FullName, pkg.InstallLocation); - } - skipped.Add(pkg.FullName); - continue; - } - } - - // Unregister - await packageRegistrationService.UnregisterAsync(name, cancellationToken); - - if (!isJson) - { - ansiConsole.MarkupLineInterpolated($"{UiSymbols.Check} Unregistered {pkg.FullName}"); - } - unregistered.Add(pkg.FullName); - } - } - - if (isJson) - { - PrintJson(unregistered, skipped, errorMessage: null); - } - else if (unregistered.Count == 0 && skipped.Count == 0) - { - logger.LogInformation("{UISymbol} No dev-registered package found for '{PackageName}'.", UiSymbols.Note, packageName); - } - - return 0; - } - - private void PrintJson(List unregistered, List skipped, string? errorMessage) - { - var result = new UnregisterResult - { - Unregistered = unregistered.Count > 0 ? unregistered : null, - Skipped = skipped.Count > 0 ? skipped : null, - Error = errorMessage - }; - - var json = JsonSerializer.Serialize(result, UnregisterJsonContext.Default.UnregisterResult); - ansiConsole.WriteLine(json); - } - } -} - -internal sealed class UnregisterResult -{ - public List? Unregistered { get; set; } - public List? Skipped { get; set; } - public string? Error { get; set; } -} - -[JsonSerializable(typeof(UnregisterResult))] -[JsonSourceGenerationOptions( - WriteIndented = true, - NewLine = "\n", - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] -internal partial class UnregisterJsonContext : JsonSerializerContext; diff --git a/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs index 023084a6..04898ecb 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs @@ -36,13 +36,6 @@ internal class WinAppRootCommand : RootCommand, IShortDescription Action = new PrintCliSchemaAction() }; - internal static readonly Option CallerOption = new("--caller") - { - Description = "Identifies the caller (e.g., nuget-package, npm). Used for telemetry.", - Recursive = true, - Hidden = true - }; - private class PrintCliSchemaAction : SynchronousCommandLineAction { public override bool Terminating => true; @@ -62,7 +55,6 @@ public WinAppRootCommand( UpdateCommand updateCommand, CreateDebugIdentityCommand createDebugIdentityCommand, RunCommand runCommand, - UnregisterCommand unregisterCommand, GetWinappPathCommand getWinappPathCommand, CertCommand certCommand, SignCommand signCommand, @@ -78,7 +70,6 @@ public WinAppRootCommand( Subcommands.Add(updateCommand); Subcommands.Add(createDebugIdentityCommand); Subcommands.Add(runCommand); - Subcommands.Add(unregisterCommand); Subcommands.Add(getWinappPathCommand); Subcommands.Add(certCommand); Subcommands.Add(signCommand); @@ -87,14 +78,13 @@ public WinAppRootCommand( Subcommands.Add(createExternalCatalogCommand); Options.Add(CliSchemaOption); - Options.Add(CallerOption); // Replace the default help with a custom categorized help screen var helpOption = Options.OfType().First(); helpOption.Action = new CustomHelpAction(this, ansiConsole, ("Setup", [typeof(InitCommand), typeof(RestoreCommand), typeof(UpdateCommand)]), ("Packaging & Signing", [typeof(PackageCommand), typeof(SignCommand), typeof(CertCommand), typeof(ManifestCommand), typeof(CreateExternalCatalogCommand)]), - ("Development Tools", [typeof(CreateDebugIdentityCommand), typeof(MSStoreCommand), typeof(ToolCommand), typeof(GetWinappPathCommand), typeof(RunCommand), typeof(UnregisterCommand)]) + ("Development Tools", [typeof(CreateDebugIdentityCommand), typeof(MSStoreCommand), typeof(ToolCommand), typeof(GetWinappPathCommand), typeof(RunCommand)]) ); } } diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/CliSchema.cs b/src/winapp-CLI/WinApp.Cli/Helpers/CliSchema.cs index d9719a37..3f16d2da 100644 --- a/src/winapp-CLI/WinApp.Cli/Helpers/CliSchema.cs +++ b/src/winapp-CLI/WinApp.Cli/Helpers/CliSchema.cs @@ -143,11 +143,11 @@ private static RootCommandDetails CreateRootCommandDetails(Command command) return null; } var dict = new Dictionary(); - foreach (var option in options.Where(o => !o.Hidden).OrderBy(o => o.Name, StringComparer.OrdinalIgnoreCase)) + foreach (var option in options.OrderBy(o => o.Name, StringComparer.OrdinalIgnoreCase)) { dict[option.Name] = CreateOptionDetails(option); } - return dict.Count > 0 ? dict : null; + return dict; } private static Dictionary? CreateSubcommandsDictionary(IList subcommands) diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs index 6c1ef84b..ef537229 100644 --- a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs +++ b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs @@ -28,10 +28,10 @@ public static IServiceCollection ConfigureServices(this IServiceCollection servi .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -39,8 +39,6 @@ public static IServiceCollection ConfigureServices(this IServiceCollection servi .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton() .AddSingleton(AnsiConsole.Console) .AddSingleton() .AddSingleton(); @@ -60,7 +58,6 @@ public static IServiceCollection ConfigureCommands(this IServiceCollection servi .UseCommandHandler() .UseCommandHandler() .UseCommandHandler() - .UseCommandHandler() .UseCommandHandler() .ConfigureCommand() .UseCommandHandler() diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs b/src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs deleted file mode 100644 index 15919bda..00000000 --- a/src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Helpers; - -/// -/// Shared helper for locating appxmanifest.xml files. -/// -internal static class ManifestHelper -{ - private static readonly string[] ManifestNames = ["appxmanifest.xml", "Package.appxmanifest"]; - - /// - /// Finds an appxmanifest file in the specified directory. - /// Checks for appxmanifest.xml first, then Package.appxmanifest. - /// - /// A for the manifest. Check before using. - public static FileInfo FindManifest(string directory) - { - foreach (var name in ManifestNames) - { - var path = Path.Combine(directory, name); - if (File.Exists(path)) - { - return new FileInfo(path); - } - } - - // Return a non-existent FileInfo for the primary name so callers can check .Exists - return new FileInfo(Path.Combine(directory, ManifestNames[0])); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/NativeMethods.txt b/src/winapp-CLI/WinApp.Cli/NativeMethods.txt index a896356e..f54eaf90 100644 --- a/src/winapp-CLI/WinApp.Cli/NativeMethods.txt +++ b/src/winapp-CLI/WinApp.Cli/NativeMethods.txt @@ -31,17 +31,3 @@ CryptCATCDFEnumMembersByCDFTagEx CryptCATCDFEnumAttributesWithCDFTag IApplicationActivationManager ApplicationActivationManager -IPackageDebugSettings -PackageDebugSettings -DebugActiveProcess -WaitForDebugEventEx -ContinueDebugEvent -DebugActiveProcessStop -DebugSetProcessKillOnExit -DEBUG_EVENT -ReadProcessMemory -OpenProcess -CloseHandle -PROCESS_ACCESS_RIGHTS -DBG_CONTINUE -DBG_EXCEPTION_NOT_HANDLED diff --git a/src/winapp-CLI/WinApp.Cli/Program.cs b/src/winapp-CLI/WinApp.Cli/Program.cs index d6591e04..800d0054 100644 --- a/src/winapp-CLI/WinApp.Cli/Program.cs +++ b/src/winapp-CLI/WinApp.Cli/Program.cs @@ -106,13 +106,6 @@ static async Task Main(string[] args) var parseResult = rootCommand.Parse(args); - // Set WINAPP_CLI_CALLER env var from --caller option so telemetry picks it up - var caller = parseResult.GetValue(WinAppRootCommand.CallerOption); - if (!string.IsNullOrWhiteSpace(caller)) - { - Environment.SetEnvironmentVariable("WINAPP_CLI_CALLER", caller); - } - try { CommandInvokedEvent.Log(parseResult.CommandResult); diff --git a/src/winapp-CLI/WinApp.Cli/Services/AppLauncherService.cs b/src/winapp-CLI/WinApp.Cli/Services/AppLauncherService.cs index 3743958c..31ef321e 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/AppLauncherService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/AppLauncherService.cs @@ -1,18 +1,15 @@ // Copyright (c) Microsoft Corporation and Contributors. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; -using Microsoft.Extensions.Logging; -using Windows.Management.Deployment; using Windows.Win32; using Windows.Win32.UI.Shell; namespace WinApp.Cli.Services; -internal class AppLauncherService(ILogger logger) : IAppLauncherService +internal class AppLauncherService : IAppLauncherService { // Crockford's Base32 alphabet (used by Windows for publisher ID) private static readonly char[] Base32Chars = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".ToCharArray(); @@ -35,21 +32,6 @@ public string ComputePackageFamilyName(string packageName, string publisher) return $"{packageName}_{publisherId}"; } - /// - public string? GetPackageFullName(string packageFamilyName) - { - try - { - var pm = new PackageManager(); - var packages = pm.FindPackages(packageFamilyName); - return packages.FirstOrDefault()?.Id.FullName; - } - catch - { - return null; - } - } - /// /// Computes the publisher ID from the publisher DN. /// The publisher ID is a 13-character Crockford Base32 encoding @@ -107,45 +89,4 @@ private static string EncodeBase32Crockford(ReadOnlySpan data) return new string(result).ToLowerInvariant(); } - - /// - [SupportedOSPlatform("windows8.0")] - public void TerminatePackageProcesses(string? packageFullName, uint processId) - { - if (packageFullName is not null) - { - try - { - var debugSettings = PackageDebugSettings.CreateInstance(); - debugSettings.TerminateAllProcesses(packageFullName); - logger.LogDebug("Terminated all processes for package {PackageFullName}.", packageFullName); - return; - } - catch (Exception ex) - { - logger.LogDebug("IPackageDebugSettings.TerminateAllProcesses failed: {Message}. Falling back to PID-based kill.", ex.Message); - } - } - - // Fallback: kill the specific process by PID - if (processId == 0 || processId > int.MaxValue) - { - return; - } - - try - { - using var process = Process.GetProcessById(unchecked((int)processId)); - process.Kill(entireProcessTree: true); - logger.LogDebug("Terminated process tree for PID {PID}.", processId); - } - catch (ArgumentException) - { - // Process already exited. - } - catch (InvalidOperationException) - { - // Process already exited. - } - } } diff --git a/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs b/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs deleted file mode 100644 index 90f48784..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Text; -using System.Xml; -using System.Xml.Linq; - -namespace WinApp.Cli.Services; - -/// -/// XDocument-based wrapper for reading and manipulating AppxManifest.xml files. -/// This is a pure data class with no DI dependencies. -/// -internal class AppxManifestDocument -{ - // AppxManifest XML namespaces - public static readonly XNamespace DefaultNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10"; - public static readonly XNamespace UapNs = "http://schemas.microsoft.com/appx/manifest/uap/windows10"; - public static readonly XNamespace Uap5Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/5"; - public static readonly XNamespace Uap10Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/10"; - public static readonly XNamespace RescapNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"; - public static readonly XNamespace BuildNs = "http://schemas.microsoft.com/developer/appx/2015/build"; - public static readonly XNamespace DesktopNs = "http://schemas.microsoft.com/appx/manifest/desktop/windows10"; - public static readonly XNamespace Desktop6Ns = "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"; - - private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false); - - private readonly XDocument _document; - - private AppxManifestDocument(XDocument document) - { - _document = document ?? throw new ArgumentNullException(nameof(document)); - } - - /// - /// Direct access to the underlying XDocument for advanced operations. - /// - public XDocument Document => _document; - - #region Static Factory Methods - - /// - /// Loads an AppxManifest from a file path. - /// - public static AppxManifestDocument Load(string path) - { - var doc = XDocument.Load(path); - return new AppxManifestDocument(doc); - } - - /// - /// Loads an AppxManifest from a stream. - /// - public static AppxManifestDocument Load(Stream stream) - { - var doc = XDocument.Load(stream); - return new AppxManifestDocument(doc); - } - - /// - /// Parses an AppxManifest from an XML string. - /// - public static AppxManifestDocument Parse(string xml) - { - var doc = XDocument.Parse(xml); - return new AppxManifestDocument(doc); - } - - #endregion - - #region Save / Serialize - - /// - /// Saves the manifest to a file with UTF-8 (no BOM) encoding. - /// - public void Save(string path) - { - var settings = new XmlWriterSettings - { - Indent = true, - IndentChars = " ", - Encoding = Utf8NoBom, - OmitXmlDeclaration = _document.Declaration == null, - }; - - using var memoryStream = new MemoryStream(); - using (var writer = XmlWriter.Create(memoryStream, settings)) - { - _document.Save(writer); - } - - File.WriteAllBytes(path, memoryStream.ToArray()); - } - - /// - /// Serializes the manifest to an XML string. - /// - public string ToXml() - { - var settings = new XmlWriterSettings - { - Indent = true, - IndentChars = " ", - Encoding = Utf8NoBom, - OmitXmlDeclaration = _document.Declaration == null, - }; - - using var memoryStream = new MemoryStream(); - using (var writer = XmlWriter.Create(memoryStream, settings)) - { - _document.Save(writer); - } - - return Utf8NoBom.GetString(memoryStream.ToArray()); - } - - #endregion - - #region Element Accessors - - /// - /// Gets the Identity element. - /// - public XElement? GetIdentityElement() => - _document.Root?.Element(DefaultNs + "Identity"); - - /// - /// Gets the first Application element. - /// - public XElement? GetFirstApplicationElement() => - _document.Root?.Element(DefaultNs + "Applications")?.Element(DefaultNs + "Application"); - - /// - /// Gets the uap:VisualElements element from the first Application. - /// - public XElement? GetVisualElements() => - GetFirstApplicationElement()?.Element(UapNs + "VisualElements"); - - /// - /// Gets the Resources element. - /// - public XElement? GetResourcesElement() => - _document.Root?.Element(DefaultNs + "Resources"); - - /// - /// Gets the Dependencies element. - /// - public XElement? GetDependenciesElement() => - _document.Root?.Element(DefaultNs + "Dependencies"); - - /// - /// Gets the package-level Extensions element (child of Package, after Applications). - /// - public XElement? GetExtensionsElement() => - _document.Root?.Element(DefaultNs + "Extensions"); - - /// - /// Gets the Capabilities element. - /// - public XElement? GetCapabilitiesElement() => - _document.Root?.Element(DefaultNs + "Capabilities"); - - #endregion - - #region Identity Properties - - /// - /// Gets or sets the Identity Name attribute. - /// - public string? IdentityName - { - get => GetIdentityElement()?.Attribute("Name")?.Value; - set => SetIdentityAttribute("Name", value); - } - - /// - /// Gets or sets the Identity Publisher attribute. - /// - public string? IdentityPublisher - { - get => GetIdentityElement()?.Attribute("Publisher")?.Value; - set => SetIdentityAttribute("Publisher", value); - } - - /// - /// Gets or sets the Identity Version attribute. - /// - public string? IdentityVersion - { - get => GetIdentityElement()?.Attribute("Version")?.Value; - set => SetIdentityAttribute("Version", value); - } - - /// - /// Gets or sets the Identity ProcessorArchitecture attribute. - /// - public string? IdentityProcessorArchitecture - { - get => GetIdentityElement()?.Attribute("ProcessorArchitecture")?.Value; - set => SetIdentityAttribute("ProcessorArchitecture", value); - } - - private void SetIdentityAttribute(string attributeName, string? value) - { - var identity = GetIdentityElement(); - if (identity == null) - { - if (value == null) - { - return; - } - - identity = new XElement(DefaultNs + "Identity"); - _document.Root?.AddFirst(identity); - } - - if (value == null) - { - identity.Attribute(attributeName)?.Remove(); - } - else - { - identity.SetAttributeValue(attributeName, value); - } - } - - #endregion - - #region Application Properties - - /// - /// Gets or sets the first Application's Id attribute. - /// - public string? ApplicationId - { - get => GetFirstApplicationElement()?.Attribute("Id")?.Value; - set => SetApplicationAttribute("Id", value); - } - - /// - /// Gets or sets the first Application's Executable attribute. - /// - public string? ApplicationExecutable - { - get => GetFirstApplicationElement()?.Attribute("Executable")?.Value; - set => SetApplicationAttribute("Executable", value); - } - - /// - /// Gets or sets the first Application's EntryPoint attribute. - /// - public string? ApplicationEntryPoint - { - get => GetFirstApplicationElement()?.Attribute("EntryPoint")?.Value; - set => SetApplicationAttribute("EntryPoint", value); - } - - private void SetApplicationAttribute(string attributeName, string? value) - { - var app = GetFirstApplicationElement(); - if (app == null) - { - return; - } - - if (value == null) - { - app.Attribute(attributeName)?.Remove(); - } - else - { - app.SetAttributeValue(attributeName, value); - } - } - - #endregion - - #region VisualElements Properties - - /// - /// Gets or sets the uap:VisualElements DisplayName attribute. - /// - public string? VisualElementsDisplayName - { - get => GetVisualElements()?.Attribute("DisplayName")?.Value; - set - { - var ve = GetVisualElements(); - if (ve == null) - { - return; - } - - if (value == null) - { - ve.Attribute("DisplayName")?.Remove(); - } - else - { - ve.SetAttributeValue("DisplayName", value); - } - } - } - - #endregion - - #region Resource Languages - - /// - /// Extracts all Resource Language values. - /// - public List GetResourceLanguages() - { - var resources = GetResourcesElement(); - if (resources == null) - { - return []; - } - - return resources.Elements(DefaultNs + "Resource") - .Select(r => r.Attribute("Language")?.Value) - .Where(lang => lang != null) - .Cast() - .ToList(); - } - - /// - /// Replaces the Resources block with the given languages. - /// - public void SetResourceLanguages(IList languages) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var resources = GetResourcesElement(); - if (resources == null) - { - resources = new XElement(DefaultNs + "Resources"); - // Insert after Dependencies if present, otherwise after Identity - var dependencies = GetDependenciesElement(); - if (dependencies != null) - { - dependencies.AddAfterSelf(resources); - } - else - { - var identity = GetIdentityElement(); - if (identity != null) - { - identity.AddAfterSelf(resources); - } - else - { - root.Add(resources); - } - } - } - else - { - resources.RemoveAll(); - } - - foreach (var lang in languages) - { - resources.Add(new XElement(DefaultNs + "Resource", new XAttribute("Language", lang))); - } - } - - #endregion - - #region Namespace Management - - /// - /// Adds a prefix to the IgnorableNamespaces attribute on the Package element if not already present. - /// - public void AddIgnorableNamespace(string prefix) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var ignorableAttr = root.Attribute("IgnorableNamespaces"); - if (ignorableAttr != null) - { - var namespaces = ignorableAttr.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (!namespaces.Contains(prefix, StringComparer.OrdinalIgnoreCase)) - { - ignorableAttr.Value = ignorableAttr.Value + " " + prefix; - } - } - else - { - root.SetAttributeValue("IgnorableNamespaces", prefix); - } - } - - /// - /// Adds an xmlns:prefix declaration to the Package element if not already present. - /// - public void EnsureNamespace(string prefix, XNamespace ns) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var existing = root.Attribute(XNamespace.Xmlns + prefix); - if (existing == null) - { - root.Add(new XAttribute(XNamespace.Xmlns + prefix, ns.NamespaceName)); - } - } - - #endregion - - #region Capabilities - - /// - /// Adds a capability if not already present. Uses the default namespace unless a specific namespace is provided. - /// - public void EnsureCapability(string capabilityName, XNamespace? ns = null) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var capabilities = GetCapabilitiesElement(); - if (capabilities == null) - { - capabilities = new XElement(DefaultNs + "Capabilities"); - root.Add(capabilities); - } - - var targetNs = ns ?? DefaultNs; - - // Check all child elements for a matching Name attribute regardless of namespace - var existing = capabilities.Elements() - .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, capabilityName, StringComparison.OrdinalIgnoreCase)); - - if (existing == null) - { - capabilities.Add(new XElement(targetNs + "Capability", new XAttribute("Name", capabilityName))); - } - } - - #endregion - - #region Build Metadata - - /// - /// Adds or updates a build:Item entry in the build:Metadata section. - /// - public void SetBuildMetadata(string toolName, string version) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var metadata = root.Element(BuildNs + "Metadata"); - if (metadata == null) - { - metadata = new XElement(BuildNs + "Metadata"); - root.Add(metadata); - } - - var existingItem = metadata.Elements(BuildNs + "Item") - .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, toolName, StringComparison.OrdinalIgnoreCase)); - - if (existingItem != null) - { - existingItem.SetAttributeValue("Version", version); - } - else - { - metadata.Add(new XElement(BuildNs + "Item", - new XAttribute("Name", toolName), - new XAttribute("Version", version))); - } - } - - #endregion - - #region Package-level Extensions - - /// - /// Gets or creates the Package-level Extensions element (direct child of Package root). - /// This is distinct from Application-level Extensions which live inside Application elements. - /// - public XElement GetOrCreatePackageLevelExtensionsElement() - { - var root = _document.Root ?? throw new InvalidOperationException("Document has no root element"); - - // root.Element() only returns direct children, so this correctly gets - // Package-level Extensions (not Application > Extensions) - var extensions = root.Element(DefaultNs + "Extensions"); - if (extensions != null) - { - return extensions; - } - - extensions = new XElement(DefaultNs + "Extensions"); - - // Insert after Applications (standard AppxManifest element order) - var applications = root.Element(DefaultNs + "Applications"); - if (applications != null) - { - applications.AddAfterSelf(extensions); - } - else - { - root.Add(extensions); - } - - return extensions; - } - - /// - /// Collects all DLL paths registered in InProcessServer or ProxyStub extensions - /// (from Package-level <Path> elements). Used for dedup when adding new entries. - /// - public HashSet GetRegisteredExtensionDllPaths() - { - var result = new HashSet(StringComparer.OrdinalIgnoreCase); - var root = _document.Root; - if (root == null) - { - return result; - } - - foreach (var path in root.Descendants(DefaultNs + "Path")) - { - var text = path.Value?.Trim(); - if (!string.IsNullOrEmpty(text)) - { - result.Add(text); - } - } - - return result; - } - - /// - /// Adds an InProcessServer extension entry to the Package-level Extensions element. - /// - public void AddInProcessServerExtension(string dllPath, IEnumerable activatableClasses) - { - var extensions = GetOrCreatePackageLevelExtensionsElement(); - - var extension = new XElement(DefaultNs + "Extension", - new XAttribute("Category", "windows.activatableClass.inProcessServer"), - new XElement(DefaultNs + "InProcessServer", - new XElement(DefaultNs + "Path", dllPath), - activatableClasses.Select(cls => - new XElement(DefaultNs + "ActivatableClass", - new XAttribute("ActivatableClassId", cls), - new XAttribute("ThreadingModel", "both"))))); - - extensions.Add(extension); - } - - #endregion - - #region Package Dependencies - - /// - /// Checks if a PackageDependency with the given name prefix exists. - /// - public bool HasPackageDependency(string namePrefix) - { - var dependencies = GetDependenciesElement(); - if (dependencies == null) - { - return false; - } - - return dependencies.Elements(DefaultNs + "PackageDependency") - .Any(e => e.Attribute("Name")?.Value?.StartsWith(namePrefix, StringComparison.OrdinalIgnoreCase) == true); - } - - /// - /// Adds or updates a PackageDependency element. - /// - public void SetPackageDependency(string name, string minVersion, string publisher) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var dependencies = GetDependenciesElement(); - if (dependencies == null) - { - dependencies = new XElement(DefaultNs + "Dependencies"); - root.Add(dependencies); - } - - var existing = dependencies.Elements(DefaultNs + "PackageDependency") - .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, name, StringComparison.Ordinal)); - - if (existing != null) - { - existing.SetAttributeValue("MinVersion", minVersion); - existing.SetAttributeValue("Publisher", publisher); - } - else - { - dependencies.Add(new XElement(DefaultNs + "PackageDependency", - new XAttribute("Name", name), - new XAttribute("MinVersion", minVersion), - new XAttribute("Publisher", publisher))); - } - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs b/src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs deleted file mode 100644 index feacd86c..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -// The CLI requires Windows 10+; suppress platform compat warnings for Debug APIs. -#pragma warning disable CA1416 - -using Microsoft.Extensions.Logging; -using Spectre.Console; -using System.Text; -using Windows.Win32; -using Windows.Win32.Foundation; -using Windows.Win32.System.Diagnostics.Debug; -using Windows.Win32.System.Threading; - -namespace WinApp.Cli.Services; - -/// -/// Attaches to a running process via the Win32 Debug API and streams -/// OutputDebugString messages and first-chance exceptions to the console. -/// Only one debugger can attach to a process at a time — using this service -/// prevents other debuggers (Visual Studio, VS Code) from attaching. -/// The debugged process is terminated when the debug session ends (e.g., Ctrl+C) -/// or if the winapp process exits unexpectedly. -/// -internal sealed class DebugOutputService(IAnsiConsole console, ILogger logger) : IDebugOutputService -{ - // Well-known NTSTATUS / exception codes - private const uint STATUS_BREAKPOINT = 0x80000003; - private const uint STATUS_SINGLE_STEP = 0x80000004; - private const uint STATUS_WX86_BREAKPOINT = 0x4000001F; - private const uint THREAD_NAME_EXCEPTION = 0x406D1388; - - /// - public Task RunDebugLoopAsync(uint processId, CancellationToken cancellationToken) - { - // DebugActiveProcess + WaitForDebugEventEx must be called from the same thread, - // so spin up a dedicated thread via Task.Run. - return Task.Run(() => RunDebugLoop(processId, cancellationToken), cancellationToken); - } - - private int RunDebugLoop(uint processId, CancellationToken cancellationToken) - { - // If winapp crashes without cleanup, the OS terminates the debuggee. - PInvoke.DebugSetProcessKillOnExit(true); - - if (!PInvoke.DebugActiveProcess(processId)) - { - logger.LogError( - "Failed to attach debugger to process {PID}. The process may have exited before the debugger could attach. " + - "For short-lived apps, consider using --with-alias instead.", - processId); - return -1; - } - - logger.LogDebug("Attached debugger to process {PID}.", processId); - - try - { - return DebugEventLoop(processId, cancellationToken); - } - finally - { - PInvoke.DebugActiveProcessStop(processId); - logger.LogDebug("Detached debugger from process {PID}.", processId); - } - } - - private int DebugEventLoop(uint processId, CancellationToken cancellationToken) - { - int exitCode = -1; - bool initialBreakpointSeen = false; - - while (!cancellationToken.IsCancellationRequested) - { - // Poll with a short timeout so we can check the cancellation token. - if (!PInvoke.WaitForDebugEventEx(out var debugEvent, 100)) - { - continue; - } - - var continueStatus = NTSTATUS.DBG_CONTINUE; - - switch (debugEvent.dwDebugEventCode) - { - case DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT: - HandleOutputDebugString(in debugEvent); - break; - - case DEBUG_EVENT_CODE.EXCEPTION_DEBUG_EVENT: - HandleException(in debugEvent, ref initialBreakpointSeen, ref continueStatus); - break; - - case DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT: - exitCode = unchecked((int)debugEvent.u.ExitProcess.dwExitCode); - PInvoke.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus); - return exitCode; - - case DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT: - CloseHandleSafe(debugEvent.u.CreateProcessInfo.hFile); - break; - - case DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT: - CloseHandleSafe(debugEvent.u.LoadDll.hFile); - break; - } - - PInvoke.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus); - } - - return exitCode; - } - - private unsafe void HandleOutputDebugString(in DEBUG_EVENT debugEvent) - { - var info = debugEvent.u.DebugString; - int length = Math.Min((int)info.nDebugStringLength, 65534); - if (length == 0) - { - return; - } - - using var processHandle = PInvoke.OpenProcess_SafeHandle( - PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ, false, debugEvent.dwProcessId); - - if (processHandle.IsInvalid) - { - return; - } - - Span buffer = length <= 4096 ? stackalloc byte[length] : new byte[length]; - - if (!PInvoke.ReadProcessMemory(processHandle, info.lpDebugStringData, buffer, out var bytesRead) || bytesRead == 0) - { - return; - } - - var usable = buffer[..(int)bytesRead]; - string message = info.fUnicode != 0 - ? Encoding.Unicode.GetString(usable) - : Encoding.Default.GetString(usable); - - message = message.TrimEnd('\0'); - - if (!string.IsNullOrWhiteSpace(message)) - { - // Trim trailing newline so Spectre doesn't double-space the output. - message = message.TrimEnd('\r', '\n'); - console.MarkupLine($"[dim][[Debug]][/] {message.EscapeMarkup()}"); - } - } - - private unsafe void HandleException( - in DEBUG_EVENT debugEvent, - ref bool initialBreakpointSeen, - ref NTSTATUS continueStatus) - { - var exInfo = debugEvent.u.Exception; - uint code = unchecked((uint)exInfo.ExceptionRecord.ExceptionCode.Value); - bool firstChance = exInfo.dwFirstChance != 0; - - // Suppress the initial breakpoint that the OS sends when we attach. - if (!initialBreakpointSeen && (code is STATUS_BREAKPOINT or STATUS_WX86_BREAKPOINT)) - { - initialBreakpointSeen = true; - continueStatus = NTSTATUS.DBG_CONTINUE; - return; - } - - // Suppress single-step and thread-name exceptions — they are noise. - if (code is STATUS_SINGLE_STEP or THREAD_NAME_EXCEPTION) - { - continueStatus = NTSTATUS.DBG_CONTINUE; - return; - } - - if (firstChance) - { - var name = GetExceptionName(code); - var address = (nuint)exInfo.ExceptionRecord.ExceptionAddress; - console.MarkupLine($"[yellow]First-chance exception:[/] {name} (0x{code:X8}) at 0x{address:X}"); - } - - // Let the target's own exception handling run. For second-chance exceptions - // (firstChance == false), this causes the OS to terminate the process — correct - // behavior for a passive listener that doesn't handle exceptions itself. - continueStatus = NTSTATUS.DBG_EXCEPTION_NOT_HANDLED; - } - - private static unsafe void CloseHandleSafe(HANDLE handle) - { - if (!handle.IsNull && handle.Value != (void*)-1) - { - PInvoke.CloseHandle(handle); - } - } - - private static string GetExceptionName(uint code) => code switch - { - 0xC0000005 => "Access Violation", - 0xC00000FD => "Stack Overflow", - 0xC0000094 => "Integer Division By Zero", - 0xC0000017 => "No Memory", - 0xC000001D => "Illegal Instruction", - 0xC0000025 => "Non-Continuable Exception", - 0xC000008C => "Array Bounds Exceeded", - 0xC0000135 => "DLL Not Found", - 0xC0000142 => "DLL Initialization Failed", - STATUS_BREAKPOINT => "Breakpoint", - STATUS_SINGLE_STEP => "Single Step", - 0xE06D7363 => "C++ Exception", - 0xE0434352 => "CLR Exception", - _ => "Exception", - }; -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IAppLauncherService.cs b/src/winapp-CLI/WinApp.Cli/Services/IAppLauncherService.cs index c9331391..fa1eab02 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/IAppLauncherService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/IAppLauncherService.cs @@ -17,15 +17,6 @@ internal interface IAppLauncherService /// The process ID of the launched application. uint LaunchByAumid(string aumid, string? arguments = null); - /// - /// Terminates all processes belonging to a packaged application using - /// IPackageDebugSettings.TerminateAllProcesses. Falls back to killing a - /// single process by PID when the package-level termination fails. - /// - /// The full name of the package whose processes should be terminated, or null to skip package-level termination. - /// Fallback process ID to kill if package-level termination fails or is null. - void TerminatePackageProcesses(string? packageFullName, uint processId); - /// /// Computes the package family name from a package name and publisher distinguished name. /// The result follows the Windows format: {packageName}_{publisherId}, where the @@ -35,12 +26,4 @@ internal interface IAppLauncherService /// The publisher distinguished name (e.g. CN=MyCompany). /// The computed package family name. string ComputePackageFamilyName(string packageName, string publisher); - - /// - /// Resolves the package full name from a package family name by querying - /// the system's package inventory. - /// - /// The package family name (e.g. MyApp_abc123def). - /// The package full name, or null if the package is not installed. - string? GetPackageFullName(string packageFamilyName); } diff --git a/src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs b/src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs deleted file mode 100644 index f31c0312..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Attaches to a running process as a debugger to capture OutputDebugString -/// messages and first-chance exceptions, similar to Visual Studio's Output window. -/// Only one debugger can attach to a process at a time — using this service -/// prevents other debuggers (Visual Studio, VS Code) from attaching. -/// -internal interface IDebugOutputService -{ - /// - /// Attaches to the specified process using the Win32 Debug API and writes - /// captured debug output and exception information to the console until - /// the process exits or the is signaled. - /// When cancelled, the debugged process is terminated before returning. - /// - /// The ID of the process to attach to. - /// Token to stop the debug loop (e.g. Ctrl+C). The debugged process is terminated when this token is signaled. - /// The exit code of the debugged process, or -1 if terminated early. - Task RunDebugLoopAsync(uint processId, CancellationToken cancellationToken); -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IImageAssetService.cs b/src/winapp-CLI/WinApp.Cli/Services/IImageAssetService.cs index c13fddce..43a8ee84 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/IImageAssetService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/IImageAssetService.cs @@ -17,30 +17,23 @@ internal interface IImageAssetService { /// /// Generates MSIX image assets from a source image and saves them to the specified directory. - /// Uses the default manifest asset references to generate the standard MSIX asset set. + /// Uses a hardcoded list of standard MSIX asset specifications. /// /// Path to the source image file /// Directory where generated assets will be saved /// Task context for status messages - /// Optional path to the source image for light theme variants /// Cancellation token /// Task that completes when all assets are generated - Task GenerateAssetsAsync( - FileInfo sourceImagePath, - DirectoryInfo outputDirectory, - TaskContext taskContext, - FileInfo? lightImagePath = null, - CancellationToken cancellationToken = default); + Task GenerateAssetsAsync(FileInfo sourceImagePath, DirectoryInfo outputDirectory, TaskContext taskContext, CancellationToken cancellationToken = default); /// /// Generates MSIX image assets from a source image based on asset references from the manifest. - /// Creates the base asset, scale variants, targetsize variants, and optional light-theme variants. + /// Creates the base asset and scaled variants (scale-200, targetsize variants) matching the aspect ratio. /// /// Path to the source image file /// Directory where the manifest is located (assets are relative to this) /// Asset references extracted from the manifest /// Task context for status messages - /// Optional path to the source image for light theme variants /// Cancellation token /// Task that completes when all assets are generated Task GenerateAssetsFromManifestAsync( @@ -48,16 +41,5 @@ Task GenerateAssetsFromManifestAsync( DirectoryInfo manifestDirectory, IReadOnlyList assetReferences, TaskContext taskContext, - FileInfo? lightImagePath = null, CancellationToken cancellationToken = default); - - /// - /// Generates a multi-resolution ICO file from the source image. - /// - /// Path to the source image file - /// Output path for the generated ICO file - /// Task context for status messages - /// Cancellation token - /// Task that completes when the ICO file is generated - Task GenerateIcoAsync(FileInfo sourceImagePath, string outputPath, TaskContext taskContext, CancellationToken cancellationToken = default); } diff --git a/src/winapp-CLI/WinApp.Cli/Services/IManifestService.cs b/src/winapp-CLI/WinApp.Cli/Services/IManifestService.cs index 3f31c29e..0ef6ca4a 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/IManifestService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/IManifestService.cs @@ -42,7 +42,6 @@ public Task UpdateManifestAssetsAsync( FileInfo manifestPath, FileInfo imagePath, TaskContext taskContext, - FileInfo? lightImagePath = null, CancellationToken cancellationToken = default); public Task AddExecutionAliasAsync( diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs deleted file mode 100644 index 1e94be56..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Provides methods for registering, unregistering, and querying MSIX packages -/// using the Windows PackageManager API. -/// -internal interface IPackageRegistrationService -{ - /// - /// Registers a loose-layout MSIX package from an AppxManifest.xml path. - /// Uses DevelopmentMode to allow registration without signing. - /// - /// Path to the AppxManifest.xml in the loose layout. - /// Cancellation token. - Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default); - - /// - /// Registers a sparse MSIX package with an external location. - /// The package references files at the external location rather than containing them. - /// - /// Path to the AppxManifest.xml file. - /// External directory containing the app files. - /// Cancellation token. - Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default); - - /// - /// Unregisters an installed package by name. Returns true if a package was found and removed. - /// - /// The package identity name (e.g. MyCompany.MyApp). - /// Cancellation token. - /// True if a package was unregistered, false if no matching package was found. - Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default); - - /// - /// Installs an MSIX/APPX package file, optionally forcing application shutdown. - /// Used for installing framework dependencies like Windows App Runtime. - /// - /// Path to the .msix or .appx file. - /// Cancellation token. - Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default); - - /// - /// Checks if a package with the given name is installed and returns its version, - /// or null if not found. - /// - /// The package identity name. - /// The installed version, or null if not found. - string? GetInstalledVersion(string packageName); - - /// - /// Finds all installed packages matching the given name that were registered in - /// development mode (sideloaded). Returns package metadata including the full name - /// and install location for safety checks. - /// - /// The package identity name to search for. - /// A list of matching dev-mode packages. - List FindDevPackages(string packageName); -} - -/// -/// Information about a development-mode registered package. -/// -internal sealed record DevPackageInfo( - string FullName, - string Name, - string Version, - string? InstallLocation, - bool IsDevelopmentMode); diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPowerShellService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPowerShellService.cs new file mode 100644 index 00000000..0d894679 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/IPowerShellService.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using WinApp.Cli.ConsoleTasks; + +namespace WinApp.Cli.Services; + +internal interface IPowerShellService +{ + public Task<(int exitCode, string output, string error)> RunCommandAsync( + string command, + TaskContext taskContext, + bool elevated = false, + Dictionary? environmentVariables = null, + CancellationToken cancellationToken = default); +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs deleted file mode 100644 index 9205f534..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.ConsoleTasks; - -namespace WinApp.Cli.Services; - -internal interface IPriService -{ - Task CreatePriConfigAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - IEnumerable precomputedPriResourceCandidates, - string language = "en-US", - string platformVersion = "10.0.0", - CancellationToken cancellationToken = default); - - Task> GeneratePriFileAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - FileInfo? configPath = null, - FileInfo? outputPath = null, - CancellationToken cancellationToken = default); - - Task> ExtractLanguagesFromPriAsync( - FileInfo priFile, - TaskContext taskContext, - CancellationToken cancellationToken); -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/ImageAssetService.cs b/src/winapp-CLI/WinApp.Cli/Services/ImageAssetService.cs index d703fc10..ca23ba26 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/ImageAssetService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/ImageAssetService.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation and Contributors. All rights reserved. // Licensed under the MIT License. -using SkiaSharp; -using Svg.Skia; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; @@ -11,235 +9,101 @@ namespace WinApp.Cli.Services; -/// -/// Wraps either a raster Bitmap or an SVG SKPicture so that SVG sources can be -/// rendered directly at each target size without an intermediate rasterization step. -/// -internal sealed class ImageSource : IDisposable +internal class ImageAssetService : IImageAssetService { - private readonly Bitmap? bitmap; - private readonly SKSvg? svg; - private readonly SKPicture? svgPicture; - private readonly SKRect svgBounds; - - private ImageSource(Bitmap bitmap) - { - this.bitmap = bitmap; - AspectRatio = (float)bitmap.Width / bitmap.Height; - } - - private ImageSource(SKSvg svg, SKPicture picture, SKRect bounds) - { - this.svg = svg; - svgPicture = picture; - svgBounds = bounds; - - var width = Math.Max(1, (int)Math.Ceiling(bounds.Width)); - var height = Math.Max(1, (int)Math.Ceiling(bounds.Height)); - AspectRatio = (float)width / height; - } - - public float AspectRatio { get; } + // Define the required asset specifications for MSIX packages (used for fallback/default generation) + private static readonly (string FileName, int Width, int Height)[] AssetSpecifications = + [ + ("Square44x44Logo.png", 44, 44), + ("Square44x44Logo.scale-200.png", 88, 88), + ("Square44x44Logo.targetsize-24_altform-unplated.png", 24, 24), + ("Square150x150Logo.png", 150, 150), + ("Square150x150Logo.scale-200.png", 300, 300), + ("Wide310x150Logo.png", 310, 150), + ("Wide310x150Logo.scale-200.png", 620, 300), + ("StoreLogo.png", 50, 50), + ]; - public bool IsSvg => svgPicture != null; + // Scale factors to generate for each asset + private static readonly (string Suffix, float Scale)[] ScaleVariants = + [ + ("", 1.0f), // Base (scale-100) + (".scale-200", 2.0f), // scale-200 + ]; - public string DimensionsLabel => bitmap != null - ? $"{bitmap.Width}x{bitmap.Height}" - : $"{(int)Math.Ceiling(svgBounds.Width)}x{(int)Math.Ceiling(svgBounds.Height)} (SVG)"; + // Target size variants for square assets (for taskbar, Start menu, etc.) + private static readonly int[] TargetSizes = [16, 24, 32, 48, 256]; - /// - /// Renders the source at the exact target size and returns PNG bytes. - /// SVG sources are rasterized via SkiaSharp at the target resolution. - /// Raster sources are scaled via GDI+ high-quality bicubic. - /// - public byte[] RenderToPng(int targetWidth, int targetHeight) + public async Task GenerateAssetsAsync(FileInfo sourceImagePath, DirectoryInfo outputDirectory, TaskContext taskContext, CancellationToken cancellationToken = default) { - if (svgPicture != null) + if (!sourceImagePath.Exists) { - return RenderSvgToPng(targetWidth, targetHeight); + throw new FileNotFoundException($"Source image not found: {sourceImagePath.FullName}"); } - return RenderBitmapToPng(bitmap!, targetWidth, targetHeight); - } + taskContext.AddStatusMessage($"{UiSymbols.Info} Generating MSIX image assets from: {sourceImagePath.FullName}"); - private byte[] RenderSvgToPng(int targetWidth, int targetHeight) - { - using var skBitmap = new SKBitmap(targetWidth, targetHeight); - using (var canvas = new SKCanvas(skBitmap)) + // Load the source image + Bitmap sourceImage; + try { - canvas.Clear(SKColors.Transparent); - - // Fit SVG into target maintaining aspect ratio, centered - var svgWidth = svgBounds.Width; - var svgHeight = svgBounds.Height; - var svgAspect = svgWidth / svgHeight; - var targetAspect = (float)targetWidth / targetHeight; - - float scale; - float offsetX, offsetY; - if (svgAspect > targetAspect) + if (sourceImagePath.Extension.Equals(".ico", StringComparison.OrdinalIgnoreCase)) { - scale = targetWidth / svgWidth; - offsetX = 0; - offsetY = (targetHeight - svgHeight * scale) / 2f; + using var icon = new Icon(sourceImagePath.FullName); + sourceImage = icon.ToBitmap(); } else { - scale = targetHeight / svgHeight; - offsetX = (targetWidth - svgWidth * scale) / 2f; - offsetY = 0; + sourceImage = new Bitmap(sourceImagePath.FullName); } - - canvas.Translate(offsetX - svgBounds.Left * scale, offsetY - svgBounds.Top * scale); - canvas.Scale(scale); - canvas.DrawPicture(svgPicture); - canvas.Flush(); - } - - using var image = SKImage.FromBitmap(skBitmap); - using var data = image.Encode(SKEncodedImageFormat.Png, 100); - return data.ToArray(); - } - - private static byte[] RenderBitmapToPng(Bitmap source, int targetWidth, int targetHeight) - { - var sourceAspect = (float)source.Width / source.Height; - var targetAspect = (float)targetWidth / targetHeight; - - int scaledWidth; - int scaledHeight; - if (sourceAspect > targetAspect) - { - scaledWidth = targetWidth; - scaledHeight = (int)(targetWidth / sourceAspect); - } - else - { - scaledHeight = targetHeight; - scaledWidth = (int)(targetHeight * sourceAspect); - } - - using var targetBitmap = new Bitmap(targetWidth, targetHeight, PixelFormat.Format32bppArgb); - using var graphics = Graphics.FromImage(targetBitmap); - - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.CompositingMode = CompositingMode.SourceOver; - graphics.Clear(Color.Transparent); - - var x = (targetWidth - scaledWidth) / 2f; - var y = (targetHeight - scaledHeight) / 2f; - graphics.DrawImage(source, new RectangleF(x, y, scaledWidth, scaledHeight)); - - using var ms = new MemoryStream(); - targetBitmap.Save(ms, ImageFormat.Png); - return ms.ToArray(); - } - - public static ImageSource FromFile(FileInfo path) - { - if (path.Extension.Equals(".svg", StringComparison.OrdinalIgnoreCase)) - { - return FromSvgFile(path); } - - if (path.Extension.Equals(".ico", StringComparison.OrdinalIgnoreCase)) + catch (Exception ex) { - using var icon = new Icon(path.FullName); - return new ImageSource(icon.ToBitmap()); + throw new InvalidOperationException($"Failed to decode image: {sourceImagePath.FullName}. Please ensure the file is a valid image format.", ex); } - return new ImageSource(new Bitmap(path.FullName)); - } - - private static ImageSource FromSvgFile(FileInfo path) - { - var svg = new SKSvg(); - try + using (sourceImage) { - using var stream = File.OpenRead(path.FullName); - svg.Load(stream); + taskContext.AddDebugMessage($"Source image size: {sourceImage.Width}x{sourceImage.Height}"); - var picture = svg.Picture; - if (picture == null) + // Ensure output directory exists + if (!outputDirectory.Exists) { - throw new InvalidOperationException( - $"Failed to render SVG image: {path.FullName}. The file may be corrupted or contain unsupported SVG features."); + outputDirectory.Create(); } - var bounds = picture.CullRect; - var width = (int)Math.Ceiling(bounds.Width); - var height = (int)Math.Ceiling(bounds.Height); - - if (width <= 0 || height <= 0) + // Generate each required asset + var successCount = 0; + foreach (var (fileName, width, height) in AssetSpecifications) { - throw new InvalidOperationException( - $"SVG image has invalid dimensions ({width}x{height}): {path.FullName}. Ensure the SVG has a valid viewBox or width/height attributes."); + try + { + var outputPath = Path.Combine(outputDirectory.FullName, fileName); + await GenerateAssetAsync(sourceImage, outputPath, width, height, cancellationToken); + successCount++; + taskContext.AddDebugMessage($" {UiSymbols.Check} Generated: {fileName} ({width}x{height})"); + } + catch (Exception ex) + { + taskContext.AddDebugMessage($" {UiSymbols.Warning} Failed to generate {fileName}: {ex.Message}"); + } + } + if (successCount == AssetSpecifications.Length) + { + taskContext.AddStatusMessage($"{UiSymbols.Info} Successfully generated {AssetSpecifications.Length} image assets in: {outputDirectory.FullName}"); + } + else + { + taskContext.AddStatusMessage($"{UiSymbols.Info} Successfully generated {successCount} of {AssetSpecifications.Length} image assets in: {outputDirectory.FullName}"); } - - return new ImageSource(svg, picture, bounds); - } - catch - { - svg.Dispose(); - throw; } } - public void Dispose() - { - bitmap?.Dispose(); - svg?.Dispose(); // Deterministically frees the SKPicture it owns - } -} - -internal class ImageAssetService : IImageAssetService -{ - private static readonly ManifestAssetReference[] DefaultAssetReferences = - [ - new("AppList.png", 44, 44), - new("MedTile.png", 150, 150), - new("WideTile.png", 310, 150), - new("StoreLogo.png", 50, 50), - ]; - - private static readonly (string Suffix, float Scale)[] ScaleVariants = - [ - ("", 1.0f), - (".scale-125", 1.25f), - (".scale-150", 1.5f), - (".scale-200", 2.0f), - (".scale-400", 4.0f), - ]; - - private static readonly int[] TargetSizes = [16, 20, 24, 30, 32, 36, 40, 48, 60, 64, 72, 80, 96, 256]; - - private static readonly int[] IcoSizes = [16, 24, 32, 48, 256]; - - public Task GenerateAssetsAsync( - FileInfo sourceImagePath, - DirectoryInfo outputDirectory, - TaskContext taskContext, - FileInfo? lightImagePath = null, - CancellationToken cancellationToken = default) - { - return GenerateAssetsFromManifestAsync( - sourceImagePath, - outputDirectory, - DefaultAssetReferences, - taskContext, - lightImagePath, - cancellationToken); - } - public async Task GenerateAssetsFromManifestAsync( FileInfo sourceImagePath, DirectoryInfo manifestDirectory, IReadOnlyList assetReferences, TaskContext taskContext, - FileInfo? lightImagePath = null, CancellationToken cancellationToken = default) { if (!sourceImagePath.Exists) @@ -247,140 +111,98 @@ public async Task GenerateAssetsFromManifestAsync( throw new FileNotFoundException($"Source image not found: {sourceImagePath.FullName}"); } - if (lightImagePath is { Exists: false }) - { - throw new FileNotFoundException($"Light theme source image not found: {lightImagePath.FullName}"); - } - if (assetReferences.Count == 0) { taskContext.AddStatusMessage($"{UiSymbols.Warning} No asset references found in manifest. No assets generated."); return; } - taskContext.AddStatusMessage($"{UiSymbols.Info} Generating MSIX image assets from: {sourceImagePath.FullName}"); + taskContext.AddStatusMessage($"{UiSymbols.Info} Generating MSIX image assets from manifest references: {sourceImagePath.FullName}"); - ImageSource source; + // Load the source image + Bitmap sourceImage; try { - source = ImageSource.FromFile(sourceImagePath); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to decode image: {sourceImagePath.FullName}. Please ensure the file is a valid image format.", ex); - } - - ImageSource? lightSource = null; - try - { - if (lightImagePath != null) + if (sourceImagePath.Extension.Equals(".ico", StringComparison.OrdinalIgnoreCase)) + { + using var icon = new Icon(sourceImagePath.FullName); + sourceImage = icon.ToBitmap(); + } + else { - lightSource = ImageSource.FromFile(lightImagePath); + sourceImage = new Bitmap(sourceImagePath.FullName); } } catch (Exception ex) { - source.Dispose(); - throw new InvalidOperationException($"Failed to decode image: {lightImagePath!.FullName}. Please ensure the file is a valid image format.", ex); + throw new InvalidOperationException($"Failed to decode image: {sourceImagePath.FullName}. Please ensure the file is a valid image format.", ex); } - using (source) - using (lightSource) + using (sourceImage) { - taskContext.AddDebugMessage($"Source image: {source.DimensionsLabel}"); - if (lightSource != null) - { - taskContext.AddDebugMessage($"Light image: {lightSource.DimensionsLabel}"); - } + taskContext.AddDebugMessage($"Source image size: {sourceImage.Width}x{sourceImage.Height}"); - var (successCount, totalCount) = await Task.Run(() => - { - var success = 0; - var total = 0; + var successCount = 0; + var totalCount = 0; - foreach (var assetReference in assetReferences) + foreach (var assetRef in assetReferences) { - var assetFullPath = Path.Combine(manifestDirectory.FullName, assetReference.RelativePath); - var assetDirectory = Path.GetDirectoryName(assetFullPath) ?? manifestDirectory.FullName; - var assetFileName = Path.GetFileNameWithoutExtension(assetReference.RelativePath); - var assetExtension = Path.GetExtension(assetReference.RelativePath); - - if (!Directory.Exists(assetDirectory)) + // Get the directory and base filename + var assetFullPath = Path.Combine(manifestDirectory.FullName, assetRef.RelativePath); + var assetDirectory = Path.GetDirectoryName(assetFullPath); + var assetFileName = Path.GetFileNameWithoutExtension(assetRef.RelativePath); + var assetExtension = Path.GetExtension(assetRef.RelativePath); + + // Ensure asset directory exists + if (!string.IsNullOrEmpty(assetDirectory) && !Directory.Exists(assetDirectory)) { Directory.CreateDirectory(assetDirectory); } + // Generate scale variants foreach (var (suffix, scale) in ScaleVariants) { - var scaledWidth = (int)Math.Round(assetReference.BaseWidth * scale, MidpointRounding.AwayFromZero); - var scaledHeight = (int)Math.Round(assetReference.BaseHeight * scale, MidpointRounding.AwayFromZero); + totalCount++; + var scaledWidth = (int)(assetRef.BaseWidth * scale); + var scaledHeight = (int)(assetRef.BaseHeight * scale); var scaledFileName = $"{assetFileName}{suffix}{assetExtension}"; - var scaledPath = Path.Combine(assetDirectory, scaledFileName); + var scaledPath = Path.Combine(assetDirectory ?? manifestDirectory.FullName, scaledFileName); - total++; - if (TryGenerateAsset(source, scaledPath, scaledFileName, scaledWidth, scaledHeight, taskContext)) + try { - success++; + await GenerateAssetAsync(sourceImage, scaledPath, scaledWidth, scaledHeight, cancellationToken); + successCount++; + taskContext.AddDebugMessage($" {UiSymbols.Check} Generated: {scaledFileName} ({scaledWidth}x{scaledHeight})"); } - - if (lightSource != null) + catch (Exception ex) { - var lightScaleFileName = $"{assetFileName}.scale-{GetScalePercentage(scale)}_altform-colorful_theme-light{assetExtension}"; - var lightScalePath = Path.Combine(assetDirectory, lightScaleFileName); - - total++; - if (TryGenerateAsset(lightSource, lightScalePath, lightScaleFileName, scaledWidth, scaledHeight, taskContext)) - { - success++; - } + taskContext.AddDebugMessage($" {UiSymbols.Warning} Failed to generate {scaledFileName}: {ex.Message}"); } - - cancellationToken.ThrowIfCancellationRequested(); } - if (IsTargetSizeAsset(assetReference)) + // Generate targetsize variants for square assets (used for taskbar icons, etc.) + if (assetRef.BaseWidth == assetRef.BaseHeight && assetFileName.Contains("44x44", StringComparison.OrdinalIgnoreCase)) { foreach (var targetSize in TargetSizes) { - var platedFileName = $"{assetFileName}.targetsize-{targetSize}{assetExtension}"; - var platedPath = Path.Combine(assetDirectory, platedFileName); + totalCount++; + var targetFileName = $"{assetFileName}.targetsize-{targetSize}_altform-unplated{assetExtension}"; + var targetPath = Path.Combine(assetDirectory ?? manifestDirectory.FullName, targetFileName); - total++; - if (TryGenerateAsset(source, platedPath, platedFileName, targetSize, targetSize, taskContext)) + try { - success++; + await GenerateAssetAsync(sourceImage, targetPath, targetSize, targetSize, cancellationToken); + successCount++; + taskContext.AddDebugMessage($" {UiSymbols.Check} Generated: {targetFileName} ({targetSize}x{targetSize})"); } - - var unplatedFileName = $"{assetFileName}.targetsize-{targetSize}_altform-unplated{assetExtension}"; - var unplatedPath = Path.Combine(assetDirectory, unplatedFileName); - - total++; - if (TryGenerateAsset(source, unplatedPath, unplatedFileName, targetSize, targetSize, taskContext)) - { - success++; - } - - if (lightSource != null) + catch (Exception ex) { - var lightTargetFileName = $"{assetFileName}.targetsize-{targetSize}_altform-lightunplated{assetExtension}"; - var lightTargetPath = Path.Combine(assetDirectory, lightTargetFileName); - - total++; - if (TryGenerateAsset(lightSource, lightTargetPath, lightTargetFileName, targetSize, targetSize, taskContext)) - { - success++; - } + taskContext.AddDebugMessage($" {UiSymbols.Warning} Failed to generate {targetFileName}: {ex.Message}"); } - - cancellationToken.ThrowIfCancellationRequested(); } } } - return (success, total); - }, - cancellationToken); - if (successCount == totalCount) { taskContext.AddStatusMessage($"{UiSymbols.Info} Successfully generated {totalCount} image assets"); @@ -392,125 +214,52 @@ public async Task GenerateAssetsFromManifestAsync( } } - public async Task GenerateIcoAsync(FileInfo sourceImagePath, string outputPath, TaskContext taskContext, CancellationToken cancellationToken = default) + private static async Task GenerateAssetAsync(Bitmap sourceImage, string outputPath, int targetWidth, int targetHeight, CancellationToken cancellationToken) { - if (!sourceImagePath.Exists) - { - throw new FileNotFoundException($"Source image not found: {sourceImagePath.FullName}"); - } - - taskContext.AddStatusMessage($"{UiSymbols.Info} Generating ICO file: {outputPath}"); - - ImageSource source; - try + await Task.Run(() => { - source = ImageSource.FromFile(sourceImagePath); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to decode image: {sourceImagePath.FullName}. Please ensure the file is a valid image format.", ex); - } + // Calculate scaling to fit target dimensions while maintaining aspect ratio + var sourceAspect = (float)sourceImage.Width / sourceImage.Height; + var targetAspect = (float)targetWidth / targetHeight; - using (source) - { - var outputDirectory = Path.GetDirectoryName(outputPath); - if (!string.IsNullOrEmpty(outputDirectory) && !Directory.Exists(outputDirectory)) + int scaledWidth, scaledHeight; + if (sourceAspect > targetAspect) { - Directory.CreateDirectory(outputDirectory); + // Source is wider - fit to width + scaledWidth = targetWidth; + scaledHeight = (int)(targetWidth / sourceAspect); } - - await Task.Run(() => + else { - var pngFrames = new List(IcoSizes.Length); - - foreach (var size in IcoSizes) - { - cancellationToken.ThrowIfCancellationRequested(); - pngFrames.Add(source.RenderToPng(size, size)); - } - - WriteIcoFile(outputPath, IcoSizes, pngFrames); - }, cancellationToken); - } - - taskContext.AddStatusMessage($"{UiSymbols.Info} Generated ICO file with {IcoSizes.Length} sizes"); - } - - private static int GetScalePercentage(float scale) - { - return (int)Math.Round(scale * 100, MidpointRounding.AwayFromZero); - } - - private static bool IsTargetSizeAsset(ManifestAssetReference assetReference) - { - // App icon assets (44x44) get targetsize variants regardless of naming convention - // Supports both old naming (Square44x44Logo) and new naming (AppList) - return assetReference.BaseWidth == 44 - && assetReference.BaseHeight == 44; - } - - private static bool TryGenerateAsset( - ImageSource source, - string outputPath, - string fileName, - int targetWidth, - int targetHeight, - TaskContext taskContext) - { - try - { - GenerateAsset(source, outputPath, targetWidth, targetHeight); - taskContext.AddDebugMessage($" {UiSymbols.Check} Generated: {fileName} ({targetWidth}x{targetHeight})"); - return true; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($" {UiSymbols.Warning} Failed to generate {fileName}: {ex.Message}"); - return false; - } - } + // Source is taller - fit to height + scaledHeight = targetHeight; + scaledWidth = (int)(targetHeight * sourceAspect); + } - private static void WriteIcoFile(string outputPath, int[] sizes, List pngFrames) - { - if (sizes.Length != pngFrames.Count) - { - throw new InvalidOperationException("ICO size and frame counts must match."); - } + // Create the target bitmap with the required dimensions + using var targetBitmap = new Bitmap(targetWidth, targetHeight, PixelFormat.Format32bppArgb); + using var graphics = Graphics.FromImage(targetBitmap); - using var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write); - using var writer = new BinaryWriter(fileStream); + // Set high-quality rendering options + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.CompositingMode = CompositingMode.SourceOver; - writer.Write((ushort)0); - writer.Write((ushort)1); - writer.Write((ushort)sizes.Length); + // Fill with transparent background + graphics.Clear(Color.Transparent); - var dataOffset = 6 + (16 * sizes.Length); - for (var i = 0; i < sizes.Length; i++) - { - var size = sizes[i]; - var pngData = pngFrames[i]; - - writer.Write((byte)(size >= 256 ? 0 : size)); - writer.Write((byte)(size >= 256 ? 0 : size)); - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write((ushort)1); - writer.Write((ushort)32); - writer.Write((uint)pngData.Length); - writer.Write((uint)dataOffset); - - dataOffset += pngData.Length; - } + // Calculate position to center the scaled image + var x = (targetWidth - scaledWidth) / 2f; + var y = (targetHeight - scaledHeight) / 2f; + var destRect = new RectangleF(x, y, scaledWidth, scaledHeight); - foreach (var pngData in pngFrames) - { - writer.Write(pngData); - } - } + // Draw the scaled image + graphics.DrawImage(sourceImage, destRect); - private static void GenerateAsset(ImageSource source, string outputPath, int targetWidth, int targetHeight) - { - var pngData = source.RenderToPng(targetWidth, targetHeight); - File.WriteAllBytes(outputPath, pngData); + // Save as PNG + targetBitmap.Save(outputPath, ImageFormat.Png); + }, cancellationToken); } } diff --git a/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs deleted file mode 100644 index d7b5b226..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Static helper for incremental file copy operations. -/// Compares source and destination by file size and last-write timestamp -/// to skip unchanged files and remove stale files. -/// -internal static class IncrementalCopyHelper -{ - internal record SyncResult(int Copied, int Skipped, int Deleted); - - /// - /// Synchronizes files from to incrementally. - /// Only copies files that are new or changed (by size or timestamp). - /// Removes stale files from that no longer exist in source, - /// except for files in . - /// - internal static SyncResult SyncDirectory( - DirectoryInfo sourceDir, - DirectoryInfo destDir, - HashSet? protectedFileNames = null) - { - if (!destDir.Exists) - { - destDir.Create(); - } - - var destFullPath = destDir.FullName.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; - var sourceRelativePaths = new HashSet(StringComparer.OrdinalIgnoreCase); - int copied = 0, skipped = 0; - - foreach (var file in sourceDir.EnumerateFiles("*", SearchOption.AllDirectories)) - { - // Skip files that are inside the dest folder (if dest is nested inside source) - if (file.FullName.StartsWith(destFullPath, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var relativePath = Path.GetRelativePath(sourceDir.FullName, file.FullName); - sourceRelativePaths.Add(relativePath); - var destFile = new FileInfo(Path.Combine(destDir.FullName, relativePath)); - - // Skip copy if destination exists with same size and timestamp - if (destFile.Exists && destFile.Length == file.Length && destFile.LastWriteTimeUtc == file.LastWriteTimeUtc) - { - skipped++; - continue; - } - - destFile.Directory?.Create(); - file.CopyTo(destFile.FullName, overwrite: true); - copied++; - } - - // Remove stale files in dest that no longer exist in source - int deleted = 0; - foreach (var destFile in destDir.EnumerateFiles("*", SearchOption.AllDirectories)) - { - var relativePath = Path.GetRelativePath(destDir.FullName, destFile.FullName); - - if (protectedFileNames != null && protectedFileNames.Contains(relativePath)) - { - continue; - } - - if (!sourceRelativePaths.Contains(relativePath)) - { - destFile.Delete(); - deleted++; - } - } - - return new SyncResult(copied, skipped, deleted); - } - - /// - /// Copies a list of files to a target directory incrementally, - /// skipping files that are unchanged (same size and timestamp). - /// - internal static (int Copied, int Skipped) CopyFiles( - List<(FileInfo SourceFile, string RelativePath)> files, - DirectoryInfo targetDir) - { - int copied = 0, skipped = 0; - - foreach (var (sourceFile, relativePath) in files) - { - var targetFile = new FileInfo(Path.Combine(targetDir.FullName, relativePath)); - - if (targetFile.Exists && targetFile.Length == sourceFile.Length && targetFile.LastWriteTimeUtc == sourceFile.LastWriteTimeUtc) - { - skipped++; - continue; - } - - targetFile.Directory?.Create(); - sourceFile.CopyTo(targetFile.FullName, overwrite: true); - copied++; - } - - return (copied, skipped); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/ManifestService.cs b/src/winapp-CLI/WinApp.Cli/Services/ManifestService.cs index 4d154d94..3146c754 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/ManifestService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/ManifestService.cs @@ -160,7 +160,7 @@ await manifestTemplateService.GenerateCompleteManifestAsync( } if (manifestPath.Exists) { - await UpdateManifestAssetsAsync(manifestPath, logoPath, taskContext, cancellationToken: cancellationToken); + await UpdateManifestAssetsAsync(manifestPath, logoPath, taskContext, cancellationToken); } } @@ -244,48 +244,32 @@ public async Task UpdateManifestAssetsAsync( FileInfo manifestPath, FileInfo imagePath, TaskContext taskContext, - FileInfo? lightImagePath = null, CancellationToken cancellationToken = default) { taskContext.AddStatusMessage($"{UiSymbols.Info} Updating assets for manifest: {manifestPath.FullName}"); + // Determine the manifest directory var manifestDir = manifestPath.Directory; if (manifestDir == null) { throw new InvalidOperationException("Could not determine manifest directory"); } + // Extract asset references from the manifest var assetReferences = ExtractAssetReferencesFromManifest(manifestPath, taskContext); - DirectoryInfo assetsDir; if (assetReferences.Count > 0) { - await imageAssetService.GenerateAssetsFromManifestAsync(imagePath, manifestDir, assetReferences, taskContext, lightImagePath, cancellationToken); - - // Place app.ico alongside the app icon asset (44x44), falling back to - // the most common asset directory so we don't depend on parse order. - var appIconRef = assetReferences.FirstOrDefault(r => r.BaseWidth == 44 && r.BaseHeight == 44); - var relativeAssetsDirectory = Path.GetDirectoryName( - appIconRef?.RelativePath ?? GetMostCommonAssetDirectory(assetReferences)); - var assetsDirectoryPath = string.IsNullOrWhiteSpace(relativeAssetsDirectory) - ? manifestDir.FullName - : Path.Combine(manifestDir.FullName, relativeAssetsDirectory); - assetsDir = new DirectoryInfo(assetsDirectoryPath); + // Generate assets based on manifest references + await imageAssetService.GenerateAssetsFromManifestAsync(imagePath, manifestDir, assetReferences, taskContext, cancellationToken); } else { + // Fallback to default behavior if no asset references found taskContext.AddStatusMessage($"{UiSymbols.Warning} No asset references found in manifest, generating default assets"); - assetsDir = manifestDir.CreateSubdirectory("Assets"); - await imageAssetService.GenerateAssetsAsync(imagePath, assetsDir, taskContext, lightImagePath, cancellationToken); - } - - if (!assetsDir.Exists) - { - assetsDir.Create(); + var assetsDir = manifestDir.CreateSubdirectory("Assets"); + await imageAssetService.GenerateAssetsAsync(imagePath, assetsDir, taskContext, cancellationToken); } - - var icoPath = DetermineIcoOutputPath(assetsDir, taskContext); - await imageAssetService.GenerateIcoAsync(imagePath, icoPath, taskContext, cancellationToken); } /// @@ -309,19 +293,13 @@ internal static List ExtractAssetReferencesFromManifest( // Known asset types and their base dimensions var assetTypeDimensions = new Dictionary(StringComparer.OrdinalIgnoreCase) { - // Square logos (old naming) + // Square logos { "Square44x44Logo", (44, 44) }, { "Square71x71Logo", (71, 71) }, { "Square150x150Logo", (150, 150) }, { "Square310x310Logo", (310, 310) }, - // Wide logos (old naming) + // Wide logos { "Wide310x150Logo", (310, 150) }, - // New naming convention - { "AppList", (44, 44) }, - { "SmallTile", (71, 71) }, - { "MedTile", (150, 150) }, - { "WideTile", (310, 150) }, - { "LargeTile", (310, 310) }, // Store logo (typically 50x50) { "Logo", (50, 50) }, { "StoreLogo", (50, 50) }, @@ -491,6 +469,9 @@ private static (int Width, int Height) GetDimensionsFromPath(string path, Dictio [GeneratedRegex(@"(\d+)x(\d+)", RegexOptions.IgnoreCase)] private static partial Regex DimensionRegex(); + private static readonly XNamespace AppxDefaultNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10"; + private static readonly XNamespace Uap5Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/5"; + [GeneratedRegex(@"^(\s*)<([\w:.-]+)((?:\s+[\w:.-]+\s*=\s*""[^""]*"")+)\s*(\/?>)\s*$")] private static partial Regex TagPattern(); @@ -518,7 +499,7 @@ public async Task AddExecutionAliasAsync( } // Find the target Application element - var applications = root.Descendants(AppxManifestDocument.DefaultNs + "Application").ToList(); + var applications = root.Descendants(AppxDefaultNs + "Application").ToList(); if (applications.Count == 0) { return new AddExecutionAliasResult(AddExecutionAliasStatus.NoApplicationElement); @@ -561,13 +542,13 @@ public async Task AddExecutionAliasAsync( } // Check if the target Application already has any execution alias - var targetExtensions = targetApp.Element(AppxManifestDocument.DefaultNs + "Extensions"); + var targetExtensions = targetApp.Element(AppxDefaultNs + "Extensions"); if (targetExtensions != null) { var existingAliasElements = targetExtensions - .Elements(AppxManifestDocument.Uap5Ns + "Extension") + .Elements(Uap5Ns + "Extension") .Where(e => string.Equals(e.Attribute("Category")?.Value, "windows.appExecutionAlias", StringComparison.OrdinalIgnoreCase)) - .Descendants(AppxManifestDocument.Uap5Ns + "ExecutionAlias") + .Descendants(Uap5Ns + "ExecutionAlias") .Select(e => e.Attribute("Alias")?.Value) .Where(v => v != null) .ToList(); @@ -589,7 +570,7 @@ public async Task AddExecutionAliasAsync( // Ensure uap5 namespace is declared on the Package element if (root.GetNamespaceOfPrefix("uap5") == null) { - root.Add(new XAttribute(XNamespace.Xmlns + "uap5", AppxManifestDocument.Uap5Ns)); + root.Add(new XAttribute(XNamespace.Xmlns + "uap5", Uap5Ns)); } // Ensure uap5 is in IgnorableNamespaces @@ -604,19 +585,19 @@ public async Task AddExecutionAliasAsync( } // Build the ExecutionAlias element - var aliasElement = new XElement(AppxManifestDocument.Uap5Ns + "ExecutionAlias", + var aliasElement = new XElement(Uap5Ns + "ExecutionAlias", new XAttribute("Alias", aliasName)); // Find or create the Extensions > uap5:Extension > uap5:AppExecutionAlias hierarchy - var extensions = targetApp.Element(AppxManifestDocument.DefaultNs + "Extensions"); + var extensions = targetApp.Element(AppxDefaultNs + "Extensions"); if (extensions == null) { - extensions = new XElement(AppxManifestDocument.DefaultNs + "Extensions"); + extensions = new XElement(AppxDefaultNs + "Extensions"); targetApp.Add(extensions); } // Look for an existing uap5:Extension with Category="windows.appExecutionAlias" - var aliasExtension = extensions.Elements(AppxManifestDocument.Uap5Ns + "Extension") + var aliasExtension = extensions.Elements(Uap5Ns + "Extension") .FirstOrDefault(e => string.Equals( e.Attribute("Category")?.Value, "windows.appExecutionAlias", @@ -625,23 +606,23 @@ public async Task AddExecutionAliasAsync( if (aliasExtension != null) { // Add to existing AppExecutionAlias block - var appExecAlias = aliasExtension.Element(AppxManifestDocument.Uap5Ns + "AppExecutionAlias"); + var appExecAlias = aliasExtension.Element(Uap5Ns + "AppExecutionAlias"); if (appExecAlias != null) { appExecAlias.Add(aliasElement); } else { - var newAppExecAlias = new XElement(AppxManifestDocument.Uap5Ns + "AppExecutionAlias", aliasElement); + var newAppExecAlias = new XElement(Uap5Ns + "AppExecutionAlias", aliasElement); aliasExtension.Add(newAppExecAlias); } } else { // Create new Extension block - var newExtension = new XElement(AppxManifestDocument.Uap5Ns + "Extension", + var newExtension = new XElement(Uap5Ns + "Extension", new XAttribute("Category", "windows.appExecutionAlias"), - new XElement(AppxManifestDocument.Uap5Ns + "AppExecutionAlias", aliasElement)); + new XElement(Uap5Ns + "AppExecutionAlias", aliasElement)); extensions.Add(newExtension); } @@ -727,65 +708,4 @@ internal static string FormatXmlAttributes(string xml) return result.ToString(); } - - /// - /// Determines the output path for the generated ICO file. - /// If the assets directory already contains an .ico file, reuses its name so that - /// project-template icons (e.g. AppIcon.ico) are replaced rather than duplicated. - /// When multiple .ico files exist, a name-based heuristic picks the most likely app icon. - /// Falls back to "app.ico" when no existing .ico file is found. - /// - internal static string DetermineIcoOutputPath(DirectoryInfo assetsDir, TaskContext taskContext) - { - if (!assetsDir.Exists) - { - return Path.Combine(assetsDir.FullName, "app.ico"); - } - - var existingIcoFiles = assetsDir.GetFiles("*.ico"); - - if (existingIcoFiles.Length == 0) - { - return Path.Combine(assetsDir.FullName, "app.ico"); - } - - if (existingIcoFiles.Length == 1) - { - taskContext.AddDebugMessage($"Found existing ICO file: {existingIcoFiles[0].Name}, will replace it"); - return existingIcoFiles[0].FullName; - } - - // Multiple .ico files — pick the best candidate by name heuristic - var preferredNames = new[] { "appicon", "app", "icon" }; - foreach (var preferred in preferredNames) - { - var match = existingIcoFiles.FirstOrDefault(f => - Path.GetFileNameWithoutExtension(f.Name) - .Contains(preferred, StringComparison.OrdinalIgnoreCase)); - if (match != null) - { - taskContext.AddDebugMessage($"Found multiple ICO files, replacing best match: {match.Name}"); - return match.FullName; - } - } - - // No name heuristic matched — existing ICO files are likely unrelated, - // so create app.ico rather than overwriting an unknown file. - taskContext.AddDebugMessage($"Found {existingIcoFiles.Length} ICO files but none matched app icon heuristics, creating app.ico"); - return Path.Combine(assetsDir.FullName, "app.ico"); - } - - /// - /// Returns the relative path of the asset whose parent directory appears most often, - /// so the ICO file lands in the majority directory even for non-standard manifests. - /// - private static string GetMostCommonAssetDirectory(IReadOnlyList assetReferences) - { - return assetReferences - .GroupBy(r => Path.GetDirectoryName(r.RelativePath) ?? string.Empty, StringComparer.OrdinalIgnoreCase) - .OrderByDescending(g => g.Count()) - .First() - .First() - .RelativePath; - } } diff --git a/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs deleted file mode 100644 index e48220c6..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Text.RegularExpressions; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; - -namespace WinApp.Cli.Services; - -/// -/// Static helper for MRT (Modern Resource Technology) asset qualification, -/// expansion, and file copying. -/// -internal static partial class MrtAssetHelper -{ - // Language (en, en-US, pt-BR, zh-Hans, etc.) – bare token - [GeneratedRegex(@"^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex LanguageQualifierRegex(); - - [GeneratedRegex(@"^scale-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - // scale-100, scale-200, etc. - private static partial Regex ScaleQualifierRegex(); - - // theme-dark, theme-light - [GeneratedRegex(@"^theme-(light|dark)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ThemeQualifierRegex(); - - // contrast-standard, contrast-high - [GeneratedRegex(@"^contrast-(standard|high)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ContrastQualifierRegex(); - - // dxfeaturelevel-9 / 10 / 11 - [GeneratedRegex(@"^dxfeaturelevel-(9|10|11)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex DxFeatureLevelQualifierRegex(); - - // device-family-desktop / xbox / team / iot / mobile - [GeneratedRegex(@"^device-family-(desktop|mobile|team|xbox|iot)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex DeviceFamilyQualifierRegex(); - - // homeregion-US, homeregion-JP, ... - [GeneratedRegex(@"^homeregion-[A-Za-z]{2}$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex HomeRegionQualifierRegex(); - - // configuration-debug, configuration-retail, etc. - [GeneratedRegex(@"^configuration-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ConfigurationQualifierRegex(); - - // targetsize-16, targetsize-24, targetsize-256, ... - [GeneratedRegex(@"^targetsize-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex TargetSizeQualifierRegex(); - - // altform-unplated, altform-lightunplated, etc. - [GeneratedRegex(@"^altform-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex AltFormQualifierRegex(); - - internal static readonly HashSet PriIncludedExtensions = new(StringComparer.OrdinalIgnoreCase) - { - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".svg" - }; - - internal static List<(FileInfo SourceFile, string RelativePath)> ExpandManifestReferencedFiles( - DirectoryInfo manifestDir, - IEnumerable referencedFiles, - TaskContext? taskContext, - Func? includeFile = null) - { - var expandedFilesByRelativePath = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var relativeFilePath in referencedFiles) - { - var logicalSourceFile = new FileInfo(Path.Combine(manifestDir.FullName, relativeFilePath)); - var sourceDir = logicalSourceFile.Directory; - - if (sourceDir is null || !sourceDir.Exists) - { - taskContext?.AddDebugMessage($"{UiSymbols.Warning} Source directory not found for referenced file: {relativeFilePath}"); - continue; - } - - var logicalBaseName = Path.GetFileNameWithoutExtension(logicalSourceFile.Name); - var variantBaseName = GetMrtVariantBaseName(logicalBaseName); - var extension = logicalSourceFile.Extension; - - var searchPattern = variantBaseName + "*" + extension; - var candidates = sourceDir.EnumerateFiles(searchPattern); - var anyIncludedForLogical = false; - - foreach (var candidateFile in candidates) - { - if (includeFile != null && !includeFile(candidateFile)) - { - continue; - } - - var candidateNameWithoutExtension = Path.GetFileNameWithoutExtension(candidateFile.Name); - if (!IsMrtVariantName(variantBaseName, candidateNameWithoutExtension)) - { - continue; - } - - var relativeDir = Path.GetDirectoryName(relativeFilePath); - var candidateRelativePath = string.IsNullOrEmpty(relativeDir) - ? candidateFile.Name - : Path.Combine(relativeDir, candidateFile.Name); - - expandedFilesByRelativePath[candidateRelativePath] = candidateFile; - anyIncludedForLogical = true; - } - - if (!anyIncludedForLogical && logicalSourceFile.Exists && (includeFile == null || includeFile(logicalSourceFile))) - { - expandedFilesByRelativePath[relativeFilePath] = logicalSourceFile; - } - else if (!anyIncludedForLogical && !logicalSourceFile.Exists) - { - taskContext?.AddDebugMessage($"{UiSymbols.Warning} Referenced file not found (no MRT variants): {logicalSourceFile}"); - } - } - - return [.. expandedFilesByRelativePath - .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase) - .Select(pair => (pair.Value, pair.Key))]; - } - - internal static List<(FileInfo SourceFile, string RelativePath)> GetExpandedManifestReferencedFiles( - FileInfo manifestPath, - TaskContext taskContext) - { - var manifestDir = manifestPath.Directory; - if (manifestDir == null) - { - taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest directory not found for: {manifestPath}"); - return []; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Reading manifest: {manifestPath}"); - - var assetReferences = ManifestService.ExtractAssetReferencesFromManifest(manifestPath, taskContext); - var referencedFiles = assetReferences.Select(a => a.RelativePath); - return ExpandManifestReferencedFiles(manifestDir, referencedFiles, taskContext); - } - - internal static void CopyAllAssets(List<(FileInfo SourceFile, string RelativePath)> expandedFiles, DirectoryInfo targetDir, TaskContext taskContext) - { - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(expandedFiles, targetDir); - taskContext.AddDebugMessage($"{UiSymbols.Note} Manifest resources: {copied} copied, {skipped} unchanged"); - } - - // ltr / rtl - private static bool IsLayoutDirectionQualifier(string token) - { - return token.Equals("ltr", StringComparison.OrdinalIgnoreCase) || - token.Equals("rtl", StringComparison.OrdinalIgnoreCase); - } - - internal static bool IsSingleQualifierToken(string token) - { - if (string.IsNullOrEmpty(token)) - { - return false; - } - - return LanguageQualifierRegex().IsMatch(token) - || ScaleQualifierRegex().IsMatch(token) - || ThemeQualifierRegex().IsMatch(token) - || ContrastQualifierRegex().IsMatch(token) - || DxFeatureLevelQualifierRegex().IsMatch(token) - || DeviceFamilyQualifierRegex().IsMatch(token) - || HomeRegionQualifierRegex().IsMatch(token) - || ConfigurationQualifierRegex().IsMatch(token) - || TargetSizeQualifierRegex().IsMatch(token) - || AltFormQualifierRegex().IsMatch(token) - || IsLayoutDirectionQualifier(token); - } - - internal static bool IsQualifierToken(string token) - { - if (string.IsNullOrEmpty(token)) - { - return false; - } - - var parts = token.Split('_'); - - foreach (var part in parts) - { - if (!IsSingleQualifierToken(part)) - { - return false; - } - } - - return true; - } - - /// - /// Returns true if is a valid MRT - /// variant of the logical base name (dots allowed in base name). - /// - internal static bool IsMrtVariantName(string logicalBaseName, string candidateNameWithoutExtension) - { - if (string.IsNullOrWhiteSpace(logicalBaseName) || string.IsNullOrWhiteSpace(candidateNameWithoutExtension)) - { - return false; - } - - // Split by '.'; "Logo.scale-200.theme-dark" -> ["Logo", "scale-200", "theme-dark"] - var parts = candidateNameWithoutExtension.Split('.'); - - if (parts.Length == 0) - { - return false; - } - - // First token must match logical base name (case-insensitive) - if (!parts[0].Equals(logicalBaseName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - // No qualifiers -> exact logical name, valid - if (parts.Length == 1) - { - return true; - } - - // All remaining tokens must be valid MRT qualifiers - for (int i = 1; i < parts.Length; i++) - { - if (!IsQualifierToken(parts[i])) - { - return false; - } - } - - return true; - } - - /// - /// For a qualified logical name like "Logo.scale-100" or "Logo.targetsize-24_altform-unplated", - /// returns the unqualified asset family base (e.g. "Logo"). - /// If the name has no trailing qualifier tokens, returns the original name unchanged. - /// - internal static string GetMrtVariantBaseName(string logicalBaseName) - { - ArgumentException.ThrowIfNullOrWhiteSpace(logicalBaseName); - - var parts = logicalBaseName.Split('.'); - if (parts.Length <= 1) - { - return logicalBaseName; - } - - // Find the earliest segment where every remaining segment is a valid qualifier token. - for (int i = 1; i < parts.Length; i++) - { - var allRemainingAreQualifiers = true; - for (int j = i; j < parts.Length; j++) - { - if (!IsQualifierToken(parts[j])) - { - allRemainingAreQualifiers = false; - break; - } - } - - if (allRemainingAreQualifiers) - { - return string.Join('.', parts[..i]); - } - } - - return logicalBaseName; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs deleted file mode 100644 index a737ccba..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs +++ /dev/null @@ -1,771 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Security; -using System.Text; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; -using WinApp.Cli.Models; -using WinApp.Cli.Tools; - -namespace WinApp.Cli.Services; - -internal partial class MsixService -{ - public async Task AddSparseIdentityAsync(string? entryPointPath, FileInfo appxManifestPath, bool noInstall, bool keepIdentity, TaskContext taskContext, CancellationToken cancellationToken = default) - { - // Validate inputs - if (!appxManifestPath.Exists) - { - throw new FileNotFoundException($"AppX manifest not found at: {appxManifestPath}. You can generate one using 'winapp manifest generate'."); - } - - if (!devModeService.IsEnabled() && noInstall == false) - { - throw new InvalidOperationException("Developer Mode is not enabled on this machine. Please enable Developer Mode and try again."); - } - - if (entryPointPath == null) - { - var manifestContent = await File.ReadAllTextAsync(appxManifestPath.FullName, Encoding.UTF8, cancellationToken); - - // Parse once to extract the executable path - var doc = AppxManifestDocument.Parse(manifestContent); - - if (PlaceholderHelper.ContainsPlaceholders(manifestContent)) - { - // Without an explicit entrypoint, we can't resolve $targetnametoken$ in the executable - if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) - { - throw new InvalidOperationException( - "The manifest contains a placeholder for the executable. " + - "Provide the entrypoint argument to specify the executable path."); - } - - // Resolve built-in tokens (e.g. $targetentrypoint$) in memory — the executable - // attribute itself has no placeholders, so its value from the initial parse is valid. - manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent); - } - - entryPointPath = doc.ApplicationExecutable ?? entryPointPath; - } - - // Validate inputs - if (!File.Exists(entryPointPath)) - { - throw new FileNotFoundException($"EntryPoint/Executable not found at: {entryPointPath}"); - } - - taskContext.AddDebugMessage($"Processing entryPoint/executable: {entryPointPath}"); - taskContext.AddDebugMessage($"Using AppX manifest: {appxManifestPath}"); - - // Generate sparse package structure - // Fetch dotnet package list once for all downstream operations - var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - - var (debugManifestPath, debugIdentity) = await GenerateSparsePackageStructureAsync( - appxManifestPath, - entryPointPath, - keepIdentity, - dotNetPackageList, - taskContext, - cancellationToken); - - // Update executable with debug identity - if (Path.HasExtension(entryPointPath) && string.Equals(Path.GetExtension(entryPointPath), ".exe", StringComparison.OrdinalIgnoreCase)) - { - var exePath = new FileInfo(entryPointPath); - await EmbedMsixIdentityToExeAsync(exePath, debugIdentity, taskContext, cancellationToken); - } - - if (noInstall) - { - taskContext.AddDebugMessage("Skipping package installation as per --no-install option."); - } - else - { - // Register the debug appxmanifest - var entryPointDir = Path.GetDirectoryName(entryPointPath); - var externalLocation = new DirectoryInfo(string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir); - - // Unregister any existing package first - await UnregisterExistingPackageAsync(debugIdentity.PackageName, taskContext, cancellationToken); - - // Register the new debug manifest with external location - await RegisterSparsePackageAsync(debugManifestPath, externalLocation, taskContext, cancellationToken); - } - - return new MsixIdentityResult(debugIdentity.PackageName, debugIdentity.Publisher, debugIdentity.ApplicationId); - } - - public async Task AddLooseLayoutIdentityAsync(FileInfo appxManifestPath, DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, CancellationToken cancellationToken = default) - { - // Validate inputs - if (!appxManifestPath.Exists) - { - throw new FileNotFoundException($"AppX manifest not found at: {appxManifestPath}. You can generate one using 'winapp manifest generate'."); - } - - if (!devModeService.IsEnabled()) - { - throw new InvalidOperationException("Developer Mode is not enabled on this machine. Please enable Developer Mode and try again."); - } - - taskContext.AddDebugMessage($"Using AppX manifest: {appxManifestPath}"); - - var manifestContent = await File.ReadAllTextAsync(appxManifestPath.FullName, Encoding.UTF8, cancellationToken); - - // Detect whether this manifest was generated by MSBuild (dotnet build). - // MSBuild-generated manifests have build:Metadata with a makepri.exe entry. - // When MSBuild-generated, the build output includes a .appxrecipe file that - // lists all files and their correct source paths for the AppX layout. - var doc = AppxManifestDocument.Parse(manifestContent); - var isMSBuildGenerated = doc.Document.Root? - .Element(AppxManifestDocument.BuildNs + "Metadata")? - .Elements(AppxManifestDocument.BuildNs + "Item") - .Any(e => string.Equals(e.Attribute("Name")?.Value, "makepri.exe", StringComparison.OrdinalIgnoreCase)) == true; - - if (isMSBuildGenerated) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} MSBuild-generated manifest detected"); - - // Look for a .build.appxrecipe file in the input directory - var recipeFile = inputDirectory.EnumerateFiles("*.build.appxrecipe", SearchOption.TopDirectoryOnly).FirstOrDefault(); - - if (recipeFile != null) - { - taskContext.AddDebugMessage($"{UiSymbols.Files} Using appxrecipe for layout: {recipeFile.Name}"); - await CopyFilesFromRecipeAsync(recipeFile, outputAppXDirectory, taskContext, cancellationToken); - } - else - { - // No recipe — fall back to incremental copy from input directory - taskContext.AddDebugMessage($"{UiSymbols.Warning} No .appxrecipe found, falling back to file copy"); - SyncFilesToOutputDirectory(inputDirectory, outputAppXDirectory, appxManifestPath, taskContext); - } - - var identity = ParseAppxManifestAsync(manifestContent); - - // Unregister any existing package first - await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); - - // Register from the AppX layout directory - var registrationManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "AppxManifest.xml")); - if (!registrationManifest.Exists) - { - registrationManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml")); - } - await RegisterLooseLayoutPackageAsync(registrationManifest, taskContext, cancellationToken); - - return new MsixIdentityResult(identity.PackageName, identity.Publisher, identity.ApplicationId); - } - - // --- Non-MSBuild manifest path (raw Package.appxmanifest with unresolved placeholders) --- - - if (!outputAppXDirectory.Exists) - { - outputAppXDirectory.Create(); - } - - SyncFilesToOutputDirectory(inputDirectory, outputAppXDirectory, appxManifestPath, taskContext); - - var copiedAppxManifestPath = new FileInfo(Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name)); - manifestContent = await File.ReadAllTextAsync(copiedAppxManifestPath.FullName, Encoding.UTF8, cancellationToken); - var executableMatch = outputAppXDirectory.EnumerateFiles("*", SearchOption.AllDirectories) - .FirstOrDefault(f => string.Equals(f.Extension, ".exe", StringComparison.OrdinalIgnoreCase)); - - if (executableMatch == null) - { - throw new FileNotFoundException("No executable (.exe) file found in the output directory for token replacement."); - } - - // Fetch dotnet package list once for all downstream operations - var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - - // If there is a pri file named after the executable, rename it to resources.pri - var priFilePath = Path.Combine(outputAppXDirectory.FullName, Path.GetFileNameWithoutExtension(executableMatch.Name) + ".pri"); - if (File.Exists(priFilePath)) - { - var resourcesPriPath = Path.Combine(outputAppXDirectory.FullName, "resources.pri"); - File.Move(priFilePath, resourcesPriPath, overwrite: true); - taskContext.AddDebugMessage($"{UiSymbols.Files} Renamed {Path.GetFileName(priFilePath)} to resources.pri"); - } - - // Generate resources.pri if not present (matches winapp package behavior) - var existingPri = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "resources.pri")); - if (!existingPri.Exists) - { - try - { - var stagingManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml")); - var priExpandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(stagingManifest, taskContext); - var priResourceCandidates = priExpandedFiles.Select(file => file.RelativePath); - await priService.CreatePriConfigAsync( - outputAppXDirectory, - taskContext, - precomputedPriResourceCandidates: priResourceCandidates, - cancellationToken: cancellationToken); - await priService.GeneratePriFileAsync(outputAppXDirectory, taskContext, cancellationToken: cancellationToken); - taskContext.AddDebugMessage($"{UiSymbols.Files} Generated resources.pri"); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to generate PRI: {ex.Message}"); - } - } - - // Resolve $targetnametoken$ and $targetentrypoint$ placeholders - var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - [PlaceholderHelper.TargetNameToken] = Path.GetFileNameWithoutExtension(executableMatch.Name) - }; - manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent, replacements); - - // Resolve — falls back to "en-US" if no PRI found - manifestContent = manifestContent.Replace("x-generate", "EN-US"); - - // Unified manifest processing: WinAppSDK dependency, third-party WinRT components, - // ProcessorArchitecture auto-detection, and build metadata - (manifestContent, _) = await UpdateAppxManifestContentAsync( - manifestContent, null, null, executableMatch.FullName, - sparse: false, selfContained: false, - dotNetPackageList, taskContext, cancellationToken); - - await File.WriteAllTextAsync(copiedAppxManifestPath.FullName, manifestContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), cancellationToken); - - // Copy all assets - var originalManifestDir = appxManifestPath.DirectoryName; - - if (!string.Equals(originalManifestDir, outputAppXDirectory.FullName, StringComparison.OrdinalIgnoreCase)) - { - var expandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(appxManifestPath, taskContext); - MrtAssetHelper.CopyAllAssets(expandedFiles, outputAppXDirectory, taskContext); - } - else - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Manifest directory and target directory are the same, skipping assets copy"); - } - - { - var identity = ParseAppxManifestAsync(manifestContent); - - // Install the Windows App Runtime framework packages if not already present - await EnsureWindowsAppRuntimeInstalledAsync(dotNetPackageList, taskContext, cancellationToken); - - // Unregister any existing package first - await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); - - // Register the new debug manifest with external location - await RegisterLooseLayoutPackageAsync(copiedAppxManifestPath, taskContext, cancellationToken); - - return new MsixIdentityResult(identity.PackageName, identity.Publisher, identity.ApplicationId); - } - } - - /// - /// Copies files to the AppX layout directory using the .build.appxrecipe file. - /// The recipe is generated by MSBuild and lists all files with their correct source - /// paths and target PackagePaths. This preserves file metadata that .NET's CopyTo may lose. - /// - private static async Task CopyFilesFromRecipeAsync(FileInfo recipeFile, DirectoryInfo outputDir, TaskContext taskContext, CancellationToken cancellationToken) - { - if (!outputDir.Exists) - { - outputDir.Create(); - } - - var recipeContent = await File.ReadAllTextAsync(recipeFile.FullName, Encoding.UTF8, cancellationToken); - var recipeDoc = System.Xml.Linq.XDocument.Parse(recipeContent); - System.Xml.Linq.XNamespace msbuildNs = "http://schemas.microsoft.com/developer/msbuild/2003"; - - int copied = 0, skipped = 0; - - // Copy the AppxManifest - var manifestEntries = recipeDoc.Descendants(msbuildNs + "AppXManifest"); - foreach (var entry in manifestEntries) - { - var sourcePath = entry.Attribute("Include")?.Value; - var packagePath = entry.Element(msbuildNs + "PackagePath")?.Value; - if (sourcePath != null && packagePath != null && File.Exists(sourcePath)) - { - var destPath = Path.Combine(outputDir.FullName, packagePath); - Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); - File.Copy(sourcePath, destPath, overwrite: true); - copied++; - } - } - - // Copy all AppxPackagedFile entries - var fileEntries = recipeDoc.Descendants(msbuildNs + "AppxPackagedFile"); - foreach (var entry in fileEntries) - { - var sourcePath = entry.Attribute("Include")?.Value; - var packagePath = entry.Element(msbuildNs + "PackagePath")?.Value; - if (sourcePath == null || packagePath == null || !File.Exists(sourcePath)) - { - continue; - } - - var destPath = Path.Combine(outputDir.FullName, packagePath); - var destFile = new FileInfo(destPath); - - // Skip unchanged files (same size and timestamp) - if (destFile.Exists) - { - var srcFile = new FileInfo(sourcePath); - if (destFile.Length == srcFile.Length && destFile.LastWriteTimeUtc == srcFile.LastWriteTimeUtc) - { - skipped++; - continue; - } - } - - Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); - File.Copy(sourcePath, destPath, overwrite: true); - copied++; - } - - taskContext.AddDebugMessage($"{UiSymbols.Check} AppX layout from recipe: {copied} copied, {skipped} unchanged"); - } - - /// - /// Syncs files from the input directory to the output AppX directory using IncrementalCopyHelper. - /// Also handles manifest copy and rename. - /// - private static void SyncFilesToOutputDirectory(DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, FileInfo appxManifestPath, TaskContext taskContext) - { - if (!outputAppXDirectory.Exists) - { - outputAppXDirectory.Create(); - } - - if (inputDirectory != null && !string.Equals(inputDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar), - outputAppXDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)) - { - var protectedFiles = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "appxmanifest.xml", - "Package.appxmanifest", - "resources.pri" - }; - - var result = IncrementalCopyHelper.SyncDirectory(inputDirectory, outputAppXDirectory, protectedFiles); - taskContext.AddDebugMessage($"{UiSymbols.Check} Sync to output directory: {result.Copied} copied, {result.Skipped} unchanged, {result.Deleted} deleted"); - } - - // Copy the appxmanifest to the output directory - appxManifestPath.CopyTo(Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name), overwrite: true); - - // If its Package.appxmanifest, rename to appxmanifest.xml - if (string.Equals(appxManifestPath.Name, "Package.appxmanifest", StringComparison.OrdinalIgnoreCase)) - { - var renamedPath = Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml"); - var originalPath = Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name); - File.Move(originalPath, renamedPath, true); - taskContext.AddDebugMessage($"{UiSymbols.Files} Renamed Package.appxmanifest to appxmanifest.xml"); - } - } - - /// - /// Ensures that the Windows App Runtime framework MSIX packages are installed on the machine. - /// Locates the runtime MSIX directory from the NuGet package cache and installs any - /// missing or outdated packages (Framework, DDLM, Singleton, Main) via Add-AppxPackage. - /// - private async Task EnsureWindowsAppRuntimeInstalledAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); - if (msixDir == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not locate Windows App Runtime MSIX packages. The runtime may need to be installed manually."); - return; - } - - var (installedCount, errorCount) = await workspaceSetupService.InstallWindowsAppRuntimeAsync(msixDir, taskContext, cancellationToken); - - if (errorCount > 0) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} {errorCount} runtime package(s) failed to install. The app may not launch correctly."); - } - else if (installedCount > 0) - { - taskContext.AddDebugMessage($"{UiSymbols.Check} Installed {installedCount} Windows App Runtime package(s)"); - } - } - - private async Task EmbedMsixIdentityToExeAsync(FileInfo exePath, MsixIdentityResult identityInfo, TaskContext taskContext, CancellationToken cancellationToken) - { - // Create the MSIX element for the win32 manifest - string assemblyIdentity = $@";"; - var existingManifestPath = new FileInfo(Path.Combine(exePath.DirectoryName!, "temp_extracted.manifest")); - - try - { - bool hasExistingManifest = await TryExtractManifestFromExeAsync(exePath, existingManifestPath, taskContext, cancellationToken); - if (!hasExistingManifest) - { - assemblyIdentity = string.Empty; - } - else - { - taskContext.AddDebugMessage("Existing manifest found in executable, checking for AssemblyIdentity..."); - var existingManifestContent = await File.ReadAllTextAsync(existingManifestPath.FullName, Encoding.UTF8, cancellationToken); - var assemblyIdentityMatch = AssemblyIdentityNameRegex().Match(existingManifestContent); - if (assemblyIdentityMatch.Success) - { - taskContext.AddDebugMessage("Existing AssemblyIdentity found in manifest, will not add a new one."); - assemblyIdentity = string.Empty; - } - } - } - finally - { - TryDeleteFile(existingManifestPath); - } - - var manifestContent = $@" - - - {assemblyIdentity} -"; - - // Create a temporary manifest file - var tempManifestPath = new FileInfo(Path.Combine(exePath.DirectoryName!, "msix_identity_temp.manifest")); - - try - { - await File.WriteAllTextAsync(tempManifestPath.FullName, manifestContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), cancellationToken); - - // Use mt.exe to merge manifests - await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - } - finally - { - TryDeleteFile(tempManifestPath); - } - } - - /// - /// Embeds a manifest file into the Win32 manifest of an executable using mt.exe for proper merging. - /// - /// Path to the executable to modify - /// Path to the manifest file to embed - /// Cancellation token - private async Task EmbedManifestFileToExeAsync( - FileInfo exePath, - FileInfo manifestPath, - TaskContext taskContext, - CancellationToken cancellationToken = default) - { - // Validate inputs - if (!exePath.Exists) - { - throw new FileNotFoundException($"Executable not found at: {exePath}"); - } - - if (!manifestPath.Exists) - { - throw new FileNotFoundException($"Manifest file not found at: {manifestPath}"); - } - - taskContext.AddDebugMessage($"Processing executable: {exePath}"); - taskContext.AddDebugMessage($"Embedding manifest: {manifestPath}"); - - var exeDir = exePath.DirectoryName!; - var tempManifestPath = new FileInfo(Path.Combine(exeDir, "temp_extracted.manifest")); - var mergedManifestPath = new FileInfo(Path.Combine(exeDir, "merged.manifest")); - - try - { - bool hasExistingManifest = await TryExtractManifestFromExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - - if (hasExistingManifest) - { - taskContext.AddDebugMessage("Merging with existing manifest using mt.exe..."); - - // Use mt.exe to merge existing manifest with new manifest - await RunMtToolAsync($@"-manifest ""{tempManifestPath}"" ""{manifestPath}"" -out:""{mergedManifestPath}""", true, taskContext, cancellationToken); - } - else - { - taskContext.AddDebugMessage("No existing manifest, using new manifest as-is"); - - // No existing manifest, use the new manifest directly - manifestPath.CopyTo(mergedManifestPath.FullName); - } - - taskContext.AddDebugMessage("Embedding merged manifest into executable..."); - - // Update the executable with merged manifest - await RunMtToolAsync($@"-manifest ""{mergedManifestPath}"" -outputresource:""{exePath}"";#1", true, taskContext, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Successfully embedded manifest into: {exePath}"); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to embed manifest into executable: {ex.Message}", ex); - } - finally - { - // Clean up temporary files - TryDeleteFile(tempManifestPath); - TryDeleteFile(mergedManifestPath); - } - } - - private async Task TryExtractManifestFromExeAsync(FileInfo exePath, FileInfo tempManifestPath, TaskContext taskContext, CancellationToken cancellationToken) - { - taskContext.AddDebugMessage("Extracting current manifest from executable..."); - - // Extract current manifest from the executable - bool hasExistingManifest = false; - try - { - await RunMtToolAsync($@"-inputresource:""{exePath}"";#1 -out:""{tempManifestPath}""", false, taskContext, cancellationToken); - tempManifestPath.Refresh(); - hasExistingManifest = tempManifestPath.Exists; - } - catch - { - taskContext.AddDebugMessage("No existing manifest found in executable"); - } - - return hasExistingManifest; - } - - private async Task RunMtToolAsync(string arguments, bool printErrors, TaskContext taskContext, CancellationToken cancellationToken = default) - { - // Use BuildToolsService to run mt.exe - await buildToolsService.RunBuildToolAsync(new GenericTool("mt.exe"), arguments, taskContext, printErrors, cancellationToken: cancellationToken); - } - - /// Path to the original appxmanifest.xml - /// Path to the entryPoint/executable that the manifest should reference - /// Cancellation token - /// Tuple containing the debug manifest path and modified identity info - public async Task<(FileInfo debugManifestPath, MsixIdentityResult debugIdentity)> GenerateSparsePackageStructureAsync( - FileInfo originalManifestPath, - string entryPointPath, - bool keepIdentity, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken = default) - { - var winappDir = winappDirectoryService.GetLocalWinappDirectory(); - var debugDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "debug")); - - taskContext.AddDebugMessage($"{UiSymbols.Note} Creating sparse package structure in: {debugDir.FullName}"); - - // Step 1: Create debug directory, removing existing one if present - if (debugDir.Exists) - { - taskContext.AddDebugMessage($"{UiSymbols.Trash} Removing existing debug directory..."); - debugDir.Delete(recursive: true); - } - - debugDir.Create(); - taskContext.AddDebugMessage($"{UiSymbols.Folder} Created debug directory"); - - // Step 2: Parse original manifest to get identity and assets - var originalManifestContent = await File.ReadAllTextAsync(originalManifestPath.FullName, Encoding.UTF8, cancellationToken); - - // Resolve placeholders in memory (never write back to the original manifest) - if (PlaceholderHelper.ContainsPlaceholders(originalManifestContent)) - { - var nameWithoutExtension = Path.GetFileNameWithoutExtension(entryPointPath); - var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - [PlaceholderHelper.TargetNameToken] = nameWithoutExtension - }; - - // Also replace the Executable attribute if it has a placeholder - var doc = AppxManifestDocument.Parse(originalManifestContent); - if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) - { - var exeName = Path.GetFileName(entryPointPath); - doc.ApplicationExecutable = exeName; - originalManifestContent = doc.ToXml(); - } - - originalManifestContent = PlaceholderHelper.ReplacePlaceholders(originalManifestContent, replacements); - PlaceholderHelper.ThrowIfUnresolvedPlaceholders(originalManifestContent); - - taskContext.AddDebugMessage($"{UiSymbols.Note} Resolved manifest placeholders for debug identity"); - } - - var originalIdentity = ParseAppxManifestAsync(originalManifestContent); - - // Step 3: Create debug identity (optionally with ".debug" suffix) - var debugIdentity = keepIdentity ? originalIdentity : CreateDebugIdentity(originalIdentity); - - // Step 4: Modify manifest for sparse packaging and debug identity - (var debugManifestContent, _) = await UpdateAppxManifestContentAsync( - originalManifestContent, - debugIdentity, - entryPointPath, - entryPointPath, - sparse: true, - selfContained: false, - dotNetPackageList, - taskContext, - cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Note} Modified manifest for sparse packaging and debug identity"); - - // Step 5: Write debug manifest - var debugManifestPath = new FileInfo(Path.Combine(debugDir.FullName, "appxmanifest.xml")); - await File.WriteAllTextAsync(debugManifestPath.FullName, debugManifestContent, Encoding.UTF8, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Files} Created debug manifest: {debugManifestPath.FullName}"); - - // Step 6: Copy all assets - var entryPointDir = Path.GetDirectoryName(entryPointPath); - if (!string.IsNullOrEmpty(entryPointDir)) - { - var entryPointDirInfo = new DirectoryInfo(entryPointDir); - var originalManifestDir = originalManifestPath.DirectoryName; - - if (!string.Equals(originalManifestDir, entryPointDirInfo.FullName, StringComparison.OrdinalIgnoreCase)) - { - var expandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(originalManifestPath, taskContext); - MrtAssetHelper.CopyAllAssets(expandedFiles, entryPointDirInfo, taskContext); - } - else - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Manifest directory and target directory are the same, skipping assets copy"); - } - } - - return (debugManifestPath, debugIdentity); - } - - /// - /// Auto-detects ProcessorArchitecture from the executable PE header and sets it in the manifest - /// if not already present. Mirrors the logic used by all three code paths (run, create-debug-identity, package). - /// Without this, ARM64 Windows resolves framework dependencies to ARM64 DLLs even for x64 apps. - /// - /// The effective architecture (detected or existing), or null if unknown. - internal static (string manifestContent, string? architecture) AutoDetectProcessorArchitecture(string manifestContent, string exePath, TaskContext taskContext) - { - var detectedArch = PeHelper.DetectPeArchitecture(exePath); - if (detectedArch == null) - { - // Can't detect — return whatever the manifest already has - var existingDoc = AppxManifestDocument.Parse(manifestContent); - return (manifestContent, existingDoc.IdentityProcessorArchitecture); - } - - var doc = AppxManifestDocument.Parse(manifestContent); - var existingArch = doc.IdentityProcessorArchitecture; - - if (existingArch == null) - { - doc.IdentityProcessorArchitecture = detectedArch; - taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-detected ProcessorArchitecture: {detectedArch}"); - return (doc.ToXml(), detectedArch); - } - - if (!string.Equals(existingArch, detectedArch, StringComparison.OrdinalIgnoreCase) - && !string.Equals(existingArch, "neutral", StringComparison.OrdinalIgnoreCase)) - { - taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest ProcessorArchitecture is '{existingArch}' but the executable is {detectedArch}. This may cause runtime failures."); - } - - return (manifestContent, existingArch); - } - - /// - /// Creates a debug version of the identity by appending ".debug" to package name and application ID - /// - private static MsixIdentityResult CreateDebugIdentity(MsixIdentityResult originalIdentity) - { - var debugPackageName = originalIdentity.PackageName.EndsWith(".debug") - ? originalIdentity.PackageName - : $"{originalIdentity.PackageName}.debug"; - - var debugApplicationId = originalIdentity.ApplicationId.EndsWith(".debug") - ? originalIdentity.ApplicationId - : $"{originalIdentity.ApplicationId}.debug"; - - return new MsixIdentityResult(debugPackageName, originalIdentity.Publisher, debugApplicationId); - } - - /// - /// Copies files referenced in the manifest to the target directory. - /// - /// - /// Checks if a package with the given name exists and unregisters it if found - /// - /// The name of the package to check and unregister - /// Cancellation token - /// True if package was found and unregistered, false if no package was found - public async Task UnregisterExistingPackageAsync(string packageName, TaskContext taskContext, CancellationToken cancellationToken = default) - { - taskContext.AddDebugMessage($"{UiSymbols.Trash} Checking for existing package..."); - - try - { - var removed = await packageRegistrationService.UnregisterAsync(packageName, cancellationToken); - - if (removed) - { - taskContext.AddDebugMessage($"{UiSymbols.Check} Existing package unregistered successfully"); - } - else - { - taskContext.AddDebugMessage($"{UiSymbols.Note} No existing package found"); - } - - return removed; - } - catch (Exception ex) - { - // If check fails, package likely doesn't exist or we don't have permission - taskContext.AddDebugMessage($"{UiSymbols.Note} Could not check for existing package: {ex.Message}"); - return false; - } - } - - /// - /// Registers a sparse package with external location using Add-AppxPackage - /// - /// Path to the appxmanifest.xml file - /// External location path (typically the working directory) - /// Cancellation token - public async Task RegisterSparsePackageAsync(FileInfo manifestPath, DirectoryInfo externalLocation, TaskContext taskContext, CancellationToken cancellationToken = default) - { - taskContext.AddDebugMessage($"{UiSymbols.Clipboard} Registering sparse package with external location..."); - - try - { - await packageRegistrationService.RegisterSparseAsync( - manifestPath.FullName, externalLocation.FullName, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Sparse package registered successfully"); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to register sparse package: {ex.Message}", ex); - } - } - - public async Task RegisterLooseLayoutPackageAsync(FileInfo manifestPath, TaskContext taskContext, CancellationToken cancellationToken = default) - { - taskContext.AddDebugMessage($"{UiSymbols.Clipboard} Registering loose layout package..."); - - try - { - await packageRegistrationService.RegisterLooseLayoutAsync( - manifestPath.FullName, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Package registered successfully"); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to register package: {ex.Message}", ex); - } - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs deleted file mode 100644 index 1da10336..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs +++ /dev/null @@ -1,881 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.IO.Compression; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Linq; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; -using WinApp.Cli.Models; -using WinApp.Cli.Tools; - -namespace WinApp.Cli.Services; - -internal partial class MsixService -{ - [GeneratedRegex(@"^Microsoft\.WindowsAppRuntime\.\d+\.\d+.*\.msix$", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex WindowsAppRuntimeMsixRegex(); - [GeneratedRegex(@"]*name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AssemblyIdentityNameRegex(); - - // DLL dedup regex — extract registered file names from SxS manifest StringBuilder - [GeneratedRegex(@" - /// Sets up Windows App SDK for self-contained deployment by extracting MSIX content - /// and preparing the necessary files for embedding in applications. - /// - public async Task SetupSelfContainedAsync(DirectoryInfo winappDir, string architecture, TaskContext taskContext, DotNetPackageListJson? dotNetPackageList = null, CancellationToken cancellationToken = default) - { - await taskContext.AddSubTaskAsync("Setting up Self Contained", async (taskContext, cancellationToken) => - { - // Look for the Runtime package which contains the MSIX files - var selfContainedDir = winappDir.CreateSubdirectory("self-contained"); - var archSelfContainedDir = selfContainedDir.CreateSubdirectory(architecture); - - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken) ?? throw new DirectoryNotFoundException("Windows App SDK Runtime MSIX directory not found. Ensure Windows App SDK is installed."); - - // Look for the MSIX file in the tools/MSIX folder - var msixToolsDir = new DirectoryInfo(Path.Combine(msixDir.FullName, $"win10-{architecture}")); - if (!msixToolsDir.Exists) - { - throw new DirectoryNotFoundException($"MSIX tools directory not found: {msixToolsDir}"); - } - - // Try to use inventory first for accurate file selection - FileInfo? msixPath = null; - try - { - var packageEntries = await WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken); - if (packageEntries != null) - { - // Look for the base Windows App Runtime package (not Framework, DDLM, or Singleton packages) - var mainRuntimeEntry = packageEntries.FirstOrDefault(entry => - entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && - !entry.PackageIdentity.Contains("Framework") && - !entry.FileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && - !entry.FileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase)); - - if (mainRuntimeEntry != null) - { - msixPath = new FileInfo(Path.Combine(msixToolsDir.FullName, mainRuntimeEntry.FileName)); - taskContext.AddDebugMessage($"{UiSymbols.Package} Found main runtime package from inventory: {mainRuntimeEntry.FileName}"); - } - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Could not parse inventory, falling back to file search: {ex.Message}"); - } - - // Fallback: search for files directly with pattern matching - if (msixPath == null || !msixPath.Exists) - { - var msixFiles = msixToolsDir.GetFiles("Microsoft.WindowsAppRuntime.*.msix"); - if (msixFiles.Length == 0) - { - throw new FileNotFoundException($"No MSIX files found in {msixToolsDir}"); - } - - // Look for the base runtime package (format: Microsoft.WindowsAppRuntime.{version}.msix) - // Exclude files with additional suffixes like DDLM, Singleton, Framework, etc. - msixPath = msixFiles.FirstOrDefault(f => - { - var fileName = f.Name; - return !fileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && - !fileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase) && - !fileName.Contains("Framework", StringComparison.OrdinalIgnoreCase) && - WindowsAppRuntimeMsixRegex().IsMatch(fileName); - }) ?? msixFiles[0]; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Extracting MSIX: {msixPath.FullName}"); - - // Extract MSIX content - var extractedDir = new DirectoryInfo(Path.Combine(archSelfContainedDir.FullName, "extracted")); - if (extractedDir.Exists) - { - extractedDir.Delete(recursive: true); - } - extractedDir.Refresh(); - extractedDir.Create(); - - using (var archive = await ZipFile.OpenReadAsync(msixPath.FullName, cancellationToken)) - { - await archive.ExtractToDirectoryAsync(extractedDir.FullName, cancellationToken); - } - - // Copy relevant files to deployment directory - var deploymentDir = archSelfContainedDir.CreateSubdirectory("deployment"); - - // Copy DLLs, WinMD files, and other runtime assets - await CopyRuntimeFilesAsync(extractedDir, deploymentDir, taskContext, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Self-contained files prepared in: {archSelfContainedDir.FullName}"); - - return 0; - }, cancellationToken); - } - - private async Task EmbedActivationManifestToExeAsync(FileInfo exePath, DirectoryInfo winAppSDKDeploymentDir, FileInfo windowsAppSDKAppXManifestPath, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - // Use applicationLocation for DLL content (where runtime files were copied by PrepareRuntimeForPackagingAsync) - var exeDir = exePath.Directory!; - - taskContext.AddDebugMessage($"{UiSymbols.Note} Generating activation manifest from: {windowsAppSDKAppXManifestPath}"); - taskContext.AddDebugMessage($"{UiSymbols.Package} Using DLL content from: {winAppSDKDeploymentDir}"); - - // Create a temporary manifest file - var tempManifestPath = new FileInfo(Path.Combine(exeDir.FullName, "WindowsAppSDK_temp.manifest")); - - try - { - // Build the entire manifest in memory, then write to disk once - var sb = new StringBuilder(); - sb.AppendLine(""); - sb.AppendLine(""); - - // Collect all AppX manifests (main package + component fragments) and their DLLs - (var packageDependencies, _) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); - if (packageDependencies == null || packageDependencies.Count == 0) - { - throw new InvalidOperationException("No Windows SDK packages found. Please install the Windows SDK or Windows App SDK."); - } - - var architecture = WorkspaceSetupService.GetSystemArchitecture(); - IEnumerable appxFragments = GetComponents(packageDependencies); - - // Combine all manifests: main AppxManifest.xml (Package root) + fragments (Fragment root) - var allManifests = new List { windowsAppSDKAppXManifestPath }; - allManifests.AddRange(appxFragments); - - // Combine all DLL file names from deployment dir and fragment native dirs - var allDllFiles = new List(winAppSDKDeploymentDir.EnumerateFiles("*.dll").Select(di => di.Name)); - allDllFiles.AddRange(appxFragments - .Select(fragment => Path.Combine(fragment.DirectoryName!, $"win-{architecture}\\native")) - .Where(Directory.Exists) - .SelectMany(dir => Directory.EnumerateFiles(dir, "*.dll")) - .Select(Path.GetFileName)!); - - // Single pass: process all AppX manifests (auto-detects Package vs Fragment root) - AppendAppManifestFromAppx( - sb, - redirectDlls: false, - inDllFiles: allDllFiles, - inAppxManifests: allManifests); - - // Phase 3: Discover and register third-party WinRT components (e.g., Win2D, WebView2) - // These packages ship .winmd files + native DLLs but no package.appxfragment - await AppendThirdPartyWinRTManifestEntriesAsync( - sb, architecture, dotNetPackageList, taskContext, cancellationToken); - - sb.AppendLine(""); - - // Single write to disk - await File.WriteAllTextAsync( - tempManifestPath.FullName, - sb.ToString(), - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), - cancellationToken); - - // Use mt.exe to merge manifests - await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - } - finally - { - TryDeleteFile(tempManifestPath); - } - } - - private IEnumerable GetComponents(Dictionary packageDependencies) - { - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - - // Find appx fragments in the NuGet global cache (lowercase-id/version/ layout) - var appxFragments = packageDependencies - .Select(package => new FileInfo(Path.Combine(nugetCacheDir.FullName, package.Key.ToLowerInvariant(), package.Value, "runtimes-framework", "package.appxfragment"))) - .Where(f => f.Exists); - return appxFragments; - } - - /// - /// Collects all user NuGet packages from winapp.yaml or .csproj. - /// Returns the full package dictionary (name → version) for WinRT component scanning. - /// - private async Task> GetAllUserPackagesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var packages = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Path 1: Try winapp.yaml - if (configService.Exists()) - { - var config = configService.Load(); - foreach (var pkg in config.Packages) - { - packages.TryAdd(pkg.Name, pkg.Version); - } - } - else - { - // Path 2: Try .csproj via `dotnet list package --format json` (cached) - try - { - var allPackages = dotNetPackageList?.Projects? - .SelectMany(p => p.Frameworks ?? []) - .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - - if (allPackages != null) - { - foreach (var pkg in allPackages) - { - if (!string.IsNullOrEmpty(pkg.Id) && !string.IsNullOrEmpty(pkg.ResolvedVersion)) - { - packages.TryAdd(pkg.Id, pkg.ResolvedVersion); - } - } - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not retrieve package list from .csproj: {ex.Message}"); - } - } - - return packages; - } - - /// - /// Discovers third-party WinRT components and appends their activatable class - /// entries to the in-memory SxS manifest (for self-contained deployment). - /// - private async Task AppendThirdPartyWinRTManifestEntriesAsync( - StringBuilder sb, - string architecture, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) - { - var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); - if (allPackages.Count == 0) - { - return; - } - - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - - // DiscoverWinRTComponents filters out packages that have a package.appxfragment - // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. - // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 - // are transitive WinAppSDK deps but need their own InProcessServer entries. - var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); - if (components.Count == 0) - { - return; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found {components.Count} third-party WinRT component(s) to register"); - - // Build a set of DLL names already registered in the manifest (from WinAppSDK fragments) - // so we can do exact-name dedup instead of substring matching. - var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in SxsFileNameRegex().Matches(sb.ToString())) - { - registeredDlls.Add(match.Groups[1].Value); - } - - foreach (var component in components) - { - var classes = winmdService.GetActivatableClasses(component.WinmdPath); - if (classes.Count == 0) - { - continue; - } - - // Skip components whose DLL is already in the manifest (from WinAppSDK fragments - // or a previous iteration) to avoid duplicate activatableClass entries. - if (!registeredDlls.Add(component.ImplementationDll)) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); - continue; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Registering {classes.Count} activatable class(es) from {component.ImplementationDll}"); - - sb.AppendLine($" "); - foreach (var className in classes) - { - sb.AppendLine($" "); - } - sb.AppendLine(" "); - } - } - - /// - /// Discovers third-party WinRT components and generates InProcessServer - /// extension entries for AppxManifest.xml (for packaged apps). - /// - private async Task AddThirdPartyWinRTExtensionsToAppxManifestAsync( - string manifestContent, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) - { - var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); - if (allPackages.Count == 0) - { - return manifestContent; - } - - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - var architecture = WorkspaceSetupService.GetSystemArchitecture(); - - // DiscoverWinRTComponents filters out packages that have a package.appxfragment - // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. - // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 - // are transitive WinAppSDK deps but need their own InProcessServer entries. - var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); - if (components.Count == 0) - { - return manifestContent; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Adding InProcessServer entries for {components.Count} third-party WinRT component(s)"); - - var doc = AppxManifestDocument.Parse(manifestContent); - - // Build a set of DLL names already registered in the manifest - // so we can do exact-name dedup instead of substring matching. - var registeredDlls = doc.GetRegisteredExtensionDllPaths(); - - var addedAny = false; - foreach (var component in components) - { - var classes = winmdService.GetActivatableClasses(component.WinmdPath); - if (classes.Count == 0) - { - continue; - } - - // Skip components whose DLL is already in the manifest or in entries we've already generated - if (!registeredDlls.Add(component.ImplementationDll)) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); - continue; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Adding {classes.Count} activatable class(es) for {component.ImplementationDll}"); - - doc.AddInProcessServerExtension(component.ImplementationDll, classes); - addedAny = true; - } - - return addedAny ? doc.ToXml() : manifestContent; - } - - /// - /// Inserts Package-level extension entries (e.g. InProcessServer) into a manifest string. - /// Correctly distinguishes Package-level <Extensions> from Application-level ones. - /// - internal static string InsertPackageLevelExtensions(string manifestContent, string extensionEntries) - { - var doc = AppxManifestDocument.Parse(manifestContent); - var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); - - // Parse the raw extension entries as XML fragments and add them - var wrapper = XElement.Parse($"<_wrap xmlns=\"{AppxManifestDocument.DefaultNs}\">{extensionEntries}"); - foreach (var entry in wrapper.Elements()) - { - extensions.Add(entry); - } - - return doc.ToXml(); - } - - /// - /// Generates Win32 SxS manifest entries from AppX manifests (Package or Fragment format). - /// Auto-detects the root element name (Package vs Fragment) per document. - /// - /// StringBuilder to append manifest entries to - /// Whether to redirect DLLs to %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% - /// List of DLL file names to track - /// List of paths to the input AppX manifest files or fragments - internal static void AppendAppManifestFromAppx( - StringBuilder sb, - bool redirectDlls, - IEnumerable inDllFiles, - IEnumerable inAppxManifests) - { - var dllFileFormat = redirectDlls ? - @" " : - @" "; - - var dllFiles = inDllFiles.ToList(); - var hasPackageManifest = false; - - foreach (var inAppxManifest in inAppxManifests) - { - XmlDocument doc = new(); - doc.Load(inAppxManifest.FullName); - - // Auto-detect root element name (Package or Fragment) - var prefix = doc.DocumentElement?.LocalName ?? "Package"; - var isPackage = prefix == "Package"; - if (isPackage) - { - hasPackageManifest = true; - } - - var nsmgr = new XmlNamespaceManager(doc.NameTable); - nsmgr.AddNamespace("m", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); - // Add InProcessServer elements to the generated appxmanifest - var xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:InProcessServer"; - XmlNodeList? inProcessServers = doc.SelectNodes(xQuery, nsmgr); - if (inProcessServers != null) - { - foreach (XmlNode winRTFactory in inProcessServers) - { - var dllFileNode = winRTFactory.SelectSingleNode("./m:Path", nsmgr); - if (dllFileNode == null) - { - continue; - } - - var dllFile = dllFileNode.InnerText; - var typesNames = winRTFactory.SelectNodes("./m:ActivatableClass", nsmgr)?.OfType(); - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(); - if (typesNames != null) - { - foreach (var typeNode in typesNames) - { - var attribs = typeNode.Attributes?.OfType().ToArray(); - var typeName = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "ActivatableClassId") - ?.InnerText; - var xmlEntryFormat = - @" "; - sb.AppendFormat(xmlEntryFormat, typeName); - sb.AppendLine(); - dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); - } - } - sb.AppendLine(@" "); - } - } - - // Only for Package manifests with redirect - if (isPackage && redirectDlls) - { - foreach (var dllFile in dllFiles) - { - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(@""); - } - } - // Add ProxyStub elements to the generated appxmanifest - dllFiles = [.. inDllFiles]; - - xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:ProxyStub"; - var inProcessProxystubs = doc.SelectNodes(xQuery, nsmgr); - if (inProcessProxystubs != null) - { - foreach (XmlNode proxystub in inProcessProxystubs) - { - var classIDAdded = false; - - var dllFileNode = proxystub.SelectSingleNode("./m:Path", nsmgr); - var dllFile = dllFileNode?.InnerText; - // exclude PushNotificationsLongRunningTask, which requires the Singleton (which is unavailable for self-contained apps) - // exclude Widgets entries unless/until they have been tested and verified by the Widgets team - if (dllFile == null || dllFile == "PushNotificationsLongRunningTask.ProxyStub.dll" || dllFile == "Microsoft.Windows.Widgets.dll") - { - continue; - } - var typesNamesForProxy = proxystub.SelectNodes("./m:Interface", nsmgr)?.OfType(); - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(); - if (typesNamesForProxy != null) - { - foreach (var typeNode in typesNamesForProxy) - { - if (!classIDAdded) - { - var classIdAttribute = proxystub.Attributes?.OfType().ToArray(); - var classID = classIdAttribute - ?.OfType() - ?.SingleOrDefault(x => x.Name == "ClassId") - ?.InnerText; - - if (classID != null) - { - var xmlEntryFormat = @" "; - sb.AppendFormat(xmlEntryFormat, classID); - classIDAdded = true; - } - } - var attribs = typeNode.Attributes?.OfType().ToArray(); - var typeID = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "InterfaceId") - ?.InnerText; - var typeNames = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "Name") - ?.InnerText; - var xmlEntryFormatForStubs = @" "; - if (typeNames != null && typeID != null) - { - sb.AppendFormat(xmlEntryFormatForStubs, typeNames, typeID); - sb.AppendLine(); - dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); - } - } - } - sb.AppendLine(@" "); - } - } - } - - if (hasPackageManifest && redirectDlls) - { - foreach (var dllFile in dllFiles) - { - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(@""); - } - } - } - - /// - /// Updates or inserts the Windows App SDK dependency in the manifest - /// - /// The manifest content to modify - /// The modified manifest content - private async Task UpdateWindowsAppSdkDependencyAsync(string manifestContent, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var winAppSdkInfo = await GetWindowsAppSdkDependencyInfoAsync(dotNetPackageList, taskContext, cancellationToken); - - if (winAppSdkInfo == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not determine Windows App SDK version, skipping dependency update"); - return manifestContent; - } - - var doc = AppxManifestDocument.Parse(manifestContent); - const string publisher = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"; - - var dependencies = doc.GetDependenciesElement(); - if (dependencies == null) - { - // Create Dependencies element and insert before Applications (per AppxManifest schema order) - dependencies = new XElement(AppxManifestDocument.DefaultNs + "Dependencies"); - var applications = doc.Document.Root?.Element(AppxManifestDocument.DefaultNs + "Applications"); - if (applications != null) - { - applications.AddBeforeSelf(dependencies); - } - else - { - doc.Document.Root?.Add(dependencies); - } - - dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", - new XAttribute("Name", winAppSdkInfo.RuntimeName), - new XAttribute("MinVersion", winAppSdkInfo.MinVersion), - new XAttribute("Publisher", publisher))); - - taskContext.AddDebugMessage($"{UiSymbols.Package} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} (v{winAppSdkInfo.MinVersion})"); - } - else - { - // Check for existing WindowsAppRuntime dependency (prefix match for version-specific names) - var existing = dependencies.Elements(AppxManifestDocument.DefaultNs + "PackageDependency") - .FirstOrDefault(e => e.Attribute("Name")?.Value?.StartsWith("Microsoft.WindowsAppRuntime.", StringComparison.OrdinalIgnoreCase) == true); - - if (existing != null) - { - existing.SetAttributeValue("Name", winAppSdkInfo.RuntimeName); - existing.SetAttributeValue("MinVersion", winAppSdkInfo.MinVersion); - existing.SetAttributeValue("Publisher", publisher); - - taskContext.AddDebugMessage($"{UiSymbols.Sync} Updated Windows App SDK dependency to {winAppSdkInfo.RuntimeName} v{winAppSdkInfo.MinVersion}"); - } - else - { - dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", - new XAttribute("Name", winAppSdkInfo.RuntimeName), - new XAttribute("MinVersion", winAppSdkInfo.MinVersion), - new XAttribute("Publisher", publisher))); - - taskContext.AddDebugMessage($"{UiSymbols.Add} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} to existing Dependencies section (v{winAppSdkInfo.MinVersion})"); - } - } - - return doc.ToXml(); - } - - /// - /// Gets the Windows App SDK dependency information from the locked winapp.yaml config and package source - /// - /// The dependency information, or null if not found - private async Task GetWindowsAppSdkDependencyInfoAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - try - { - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); - if (msixDir == null) - { - return null; - } - - // Get the runtime package information from the MSIX inventory - var runtimeInfo = GetWindowsAppRuntimePackageInfo(taskContext, msixDir, cancellationToken); - if (runtimeInfo == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not parse Windows App Runtime package information from MSIX inventory"); - return null; - } - - return runtimeInfo; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Error getting Windows App SDK dependency info: {ex.Message}"); - return null; - } - } - - private async Task GetRuntimeMsixDirAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - (var packageDependencies, var mainVersion) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); - if (packageDependencies == null || mainVersion == null) - { - return null; - } - - // Look for the runtime package in the package dependencies - var runtimePackage = packageDependencies.FirstOrDefault(kvp => - kvp.Key.StartsWith(BuildToolsService.WINAPP_SDK_RUNTIME_PACKAGE, StringComparison.OrdinalIgnoreCase)); - - // Create a dictionary with versions for FindWindowsAppSdkMsixDirectory - var usedVersions = new Dictionary - { - [BuildToolsService.WINAPP_SDK_PACKAGE] = mainVersion - }; - - if (runtimePackage.Key != null) - { - // For Windows App SDK 1.8+, there's a separate runtime package - var runtimeVersion = runtimePackage.Value; - usedVersions[runtimePackage.Key] = runtimeVersion; - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found runtime package: {runtimePackage.Key} v{runtimeVersion}"); - } - else - { - // For Windows App SDK 1.7 and earlier, runtime is included in the main package - taskContext.AddDebugMessage($"{UiSymbols.Note} No separate runtime package found - using main package (Windows App SDK 1.7 or earlier)"); - taskContext.AddDebugMessage($"{UiSymbols.Note} Available package dependencies: {string.Join(", ", packageDependencies.Keys)}"); - } - - // Find the MSIX directory with the runtime package - var msixDir = workspaceSetupService.FindWindowsAppSdkMsixDirectory(usedVersions); - if (msixDir == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Windows App SDK MSIX directory not found for dependent runtime package"); - return null; - } - - return msixDir; - } - - private async Task<(Dictionary? CachedPackages, string? MainVersion)> GetWinAppSDKPackageDependenciesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - string? mainVersion = null; - // Path 1: Try winapp.yaml (C++ / native projects) - if (configService.Exists()) - { - var config = configService.Load(); - mainVersion = config.GetVersion(BuildToolsService.WINAPP_SDK_PACKAGE); - } - else - { - // Path 2: Try .csproj via `dotnet list package --format json` - taskContext.AddDebugMessage($"{UiSymbols.Package} Querying NuGet package list..."); - - var allPackages = dotNetPackageList?.Projects? - .SelectMany(p => p.Frameworks ?? []) - .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - - var winAppSdkPkg = allPackages? - .FirstOrDefault(p => string.Equals(p.Id, BuildToolsService.WINAPP_SDK_PACKAGE, StringComparison.OrdinalIgnoreCase)); - - if (winAppSdkPkg != null && !string.IsNullOrEmpty(winAppSdkPkg.ResolvedVersion)) - { - mainVersion = winAppSdkPkg.ResolvedVersion; - } - } - - if (string.IsNullOrEmpty(mainVersion)) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} No {BuildToolsService.WINAPP_SDK_PACKAGE} package found in winapp.yaml"); - return (null, null); - } - taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App SDK main package: v{mainVersion}"); - try - { - // Query NuGet API for the dependency tree of this package - var deps = await nugetService.GetPackageDependenciesAsync(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion, cancellationToken); - - // Include the main package itself in the result - deps.TryAdd(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion); - - return (deps, mainVersion); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} {BuildToolsService.WINAPP_SDK_PACKAGE} v{mainVersion} not found in package source: {ex.Message}"); - } - - return (null, null); - } - - /// - /// Parses the MSIX inventory file to extract Windows App Runtime package information - /// - /// The MSIX directory containing the inventory file - /// Package information, or null if not found - private static WindowsAppRuntimePackageInfo? GetWindowsAppRuntimePackageInfo(TaskContext taskContext, DirectoryInfo msixDir, CancellationToken cancellationToken) - { - try - { - // Use the shared inventory parsing logic (synchronous version) - var packageEntries = WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken).GetAwaiter().GetResult(); - - if (packageEntries == null || packageEntries.Count == 0) - { - return null; - } - - // Look for the Windows App Runtime main package (not Framework packages) - var mainRuntimeEntry = packageEntries - .FirstOrDefault(entry => entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && - !entry.PackageIdentity.Contains("Framework")); - - if (mainRuntimeEntry != null) - { - // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) - var identityParts = mainRuntimeEntry.PackageIdentity.Split('_'); - if (identityParts.Length >= 2) - { - var runtimeName = identityParts[0]; - var version = identityParts[1]; - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App Runtime: {runtimeName} v{version}"); - - return new WindowsAppRuntimePackageInfo - { - RuntimeName = runtimeName, - MinVersion = version - }; - } - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} No Windows App Runtime main package found in inventory"); - taskContext.AddDebugMessage($"{UiSymbols.Note} Available packages: {string.Join(", ", packageEntries.Select(e => e.PackageIdentity))}"); - - return null; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Error parsing MSIX inventory: {ex.Message}"); - return null; - } - } - - private static readonly string[] patterns = new[] { "*.dll", "workloads*.json", "restartAgent.exe", "map.html", "*.mui", "*.png", "*.winmd", "*.xaml", "*.xbf", "*.pri" }; - - private static async Task CopyRuntimeFilesAsync(DirectoryInfo extractedDir, DirectoryInfo deploymentDir, TaskContext taskContext, CancellationToken cancellationToken) - { - await taskContext.AddSubTaskAsync("Copying Runtime Files", (taskContext, cancellationToken) => - { - foreach (var pattern in patterns) - { - var files = extractedDir.GetFiles(pattern, SearchOption.AllDirectories); - foreach (var file in files) - { - var relativePath = Path.GetRelativePath(extractedDir.FullName, file.FullName); - var destPath = Path.Combine(deploymentDir.FullName, relativePath); - - // Create destination directory if needed - var destDir = Path.GetDirectoryName(destPath); - if (!string.IsNullOrEmpty(destDir)) - { - Directory.CreateDirectory(destDir); - } - - file.CopyTo(destPath, overwrite: true); - - taskContext.AddDebugMessage($"{UiSymbols.Files} {relativePath}"); - } - } - - return Task.FromResult(0); - }, cancellationToken); - } - - /// - /// Prepares Windows App SDK runtime files for packaging into an MSIX by extracting them to the input folder - /// - /// The folder where runtime files should be copied - /// Cancellation token - /// The path to the self-contained deployment directory - private async Task PrepareRuntimeForPackagingAsync(DirectoryInfo inputFolder, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var arch = WorkspaceSetupService.GetSystemArchitecture(); - - var winappDir = winappDirectoryService.GetLocalWinappDirectory(); - - // Extract runtime files using the existing method - await SetupSelfContainedAsync(winappDir, arch, taskContext, dotNetPackageList, cancellationToken); - - // Copy runtime files from .winapp/self-contained to input folder - var runtimeSourceDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "self-contained", arch, "deployment")); - - if (runtimeSourceDir.Exists) - { - // Copy files recursively to maintain directory structure - foreach (var file in runtimeSourceDir.GetFiles("*", SearchOption.AllDirectories)) - { - var relativePath = Path.GetRelativePath(runtimeSourceDir.FullName, file.FullName); - var destFile = Path.Combine(inputFolder.FullName, relativePath); - - // Create destination directory if needed - var destDir = Path.GetDirectoryName(destFile); - if (!string.IsNullOrEmpty(destDir)) - { - Directory.CreateDirectory(destDir); - } - - file.CopyTo(destFile, overwrite: true); - - taskContext.AddDebugMessage($"{UiSymbols.Folder} Bundled runtime: {relativePath}"); - } - - taskContext.AddDebugMessage($"{UiSymbols.Check} Windows App SDK runtime bundled into package"); - } - else - { - throw new DirectoryNotFoundException($"Runtime files not found at {runtimeSourceDir}"); - } - - return runtimeSourceDir; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs index 4938757e..2efc0552 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs @@ -4,13 +4,14 @@ using Microsoft.Extensions.Logging; using System.IO.Compression; using System.Security; +using System.Reflection.PortableExecutable; using System.Text; using System.Text.RegularExpressions; using System.Xml; -using System.Xml.Linq; using WinApp.Cli.ConsoleTasks; using WinApp.Cli.Helpers; using WinApp.Cli.Models; +using WinApp.Cli.Services; using WinApp.Cli.Tools; namespace WinApp.Cli.Services; @@ -19,17 +20,232 @@ internal partial class MsixService( IWinappDirectoryService winappDirectoryService, IConfigService configService, IBuildToolsService buildToolsService, + IPowerShellService powerShellService, ICertificateService certificateService, IWorkspaceSetupService workspaceSetupService, IDevModeService devModeService, - IDotNetService dotNetService, INugetService nugetService, IWinmdService winmdService, - IPriService priService, - IPackageRegistrationService packageRegistrationService, ILogger logger, - ICurrentDirectoryProvider currentDirectoryProvider) : IMsixService + ICurrentDirectoryProvider currentDirectoryProvider, + IDotNetService dotNetService) : IMsixService { + [GeneratedRegex(@"^Microsoft\.WindowsAppRuntime\.\d+\.\d+.*\.msix$", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex WindowsAppRuntimeMsixRegex(); + [GeneratedRegex(@"]*>", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex IdentityElementRegex(); + [GeneratedRegex(@"Name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageNameRegex(); + [GeneratedRegex(@"Publisher\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackagePublisherRegex(); + [GeneratedRegex(@"]*\sId\s*=\s*[""']([^""']*)[""'][^>]*>", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxApplicationIdRegex(); + [GeneratedRegex(@"]*Name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageIdentityNameRegex(); + [GeneratedRegex(@"]*Publisher\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageIdentityPublisherRegex(); + [GeneratedRegex(@"]*Version\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageIdentityVersionRegex(); + [GeneratedRegex(@"]*Executable\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageApplicationExecutableRegex(); + [GeneratedRegex(@"(]*Name\s*=\s*)[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageIdentityNameAssignmentRegex(); + [GeneratedRegex(@"(]*\sId\s*=\s*)[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxApplicationIdAssignmentRegex(); + [GeneratedRegex(@"(]*Executable\s*=\s*)[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageApplicationExecutableAssignmentRegex(); + [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageElementOpenTagRegex(); + [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageOpenTagRegex(); + [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackagePropertiesCloseTagRegex(); + [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxApplicationOpenTagRegex(); + [GeneratedRegex(@"\s*EntryPoint\s*=\s*[""'][^""']*[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageEntryPointRegex(); + [GeneratedRegex(@"(]*?)(\s*/?>)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageVisualElementsOpenTagRegex(); + [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageRunFullTrustCapabilityRegex(); + [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageApplicationsTagRegex(); + [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageDependenciesCloseTagRegex(); + [GeneratedRegex(@"]*name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AssemblyIdentityNameRegex(); + + // DLL dedup regexes — extract registered file/path names for HashSet-based dedup + [GeneratedRegex(@"([^<]+)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxManifestPathElementRegex(); + + [GeneratedRegex(@"]*ProcessorArchitecture\s*=", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageIdentityArchitectureCheckRegex(); + [GeneratedRegex(@"]*ProcessorArchitecture\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AppxPackageIdentityArchitectureValueRegex(); + + // Matches the entire ... block containing x-generate + [GeneratedRegex(@"\s*\s*", RegexOptions.IgnoreCase | RegexOptions.Singleline, "en-US")] + private static partial Regex ResourceLanguageXGenerateBlockRegex(); + + // Extracts language tag from PRI dump qualifier strings like 'Language-en-US' + [GeneratedRegex(@"qualifiers=""[^""]*Language-([a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex PriDumpLanguageQualifierRegex(); + + // build:Metadata regexes + [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataPackageOpenTagRegex(); + [GeneratedRegex(@"IgnorableNamespaces\s*=\s*""[^""]*\bbuild\b", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataIgnorableNamespacesCheckRegex(); + [GeneratedRegex(@"(IgnorableNamespaces\s*=\s*""[^""]*)("")", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataIgnorableNamespacesAssignRegex(); + [GeneratedRegex(@"]*Name\s*=\s*""Microsoft\.WinAppCli""", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataWinAppCliItemCheckRegex(); + [GeneratedRegex(@"]*Name\s*=\s*""Microsoft\.WinAppCli""[^/]*/\s*>", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataWinAppCliItemReplaceRegex(); + [GeneratedRegex(@"([ \t]*)()", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataCloseTagRegex(); + [GeneratedRegex(@"([ \t]*)()", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex BuildMetadataPackageCloseTagRegex(); + + // Language (en, en-US, pt-BR, zh-Hans, etc.) – bare token + [GeneratedRegex(@"^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex LanguageQualifierRegex(); + + [GeneratedRegex(@"^scale-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + // scale-100, scale-200, etc. + private static partial Regex ScaleQualifierRegex(); + + // theme-dark, theme-light + [GeneratedRegex(@"^theme-(light|dark)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex ThemeQualifierRegex(); + + // contrast-standard, contrast-high + [GeneratedRegex(@"^contrast-(standard|high)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex ContrastQualifierRegex(); + + // dxfeaturelevel-9 / 10 / 11 + [GeneratedRegex(@"^dxfeaturelevel-(9|10|11)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex DxFeatureLevelQualifierRegex(); + + // device-family-desktop / xbox / team / iot / mobile + [GeneratedRegex(@"^device-family-(desktop|mobile|team|xbox|iot)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex DeviceFamilyQualifierRegex(); + + // homeregion-US, homeregion-JP, ... + [GeneratedRegex(@"^homeregion-[A-Za-z]{2}$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex HomeRegionQualifierRegex(); + + // configuration-debug, configuration-retail, etc. + [GeneratedRegex(@"^configuration-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex ConfigurationQualifierRegex(); + + // targetsize-16, targetsize-24, targetsize-256, ... + [GeneratedRegex(@"^targetsize-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex TargetSizeQualifierRegex(); + + // altform-unplated, altform-lightunplated, etc. + [GeneratedRegex(@"^altform-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex AltFormQualifierRegex(); + + /// + /// Sets up Windows App SDK for self-contained deployment by extracting MSIX content + /// and preparing the necessary files for embedding in applications. + /// + public async Task SetupSelfContainedAsync(DirectoryInfo winappDir, string architecture, TaskContext taskContext, DotNetPackageListJson? dotNetPackageList = null, CancellationToken cancellationToken = default) + { + await taskContext.AddSubTaskAsync("Setting up Self Contained", async (taskContext, cancellationToken) => + { + // Look for the Runtime package which contains the MSIX files + var selfContainedDir = winappDir.CreateSubdirectory("self-contained"); + var archSelfContainedDir = selfContainedDir.CreateSubdirectory(architecture); + + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken) ?? throw new DirectoryNotFoundException("Windows App SDK Runtime MSIX directory not found. Ensure Windows App SDK is installed."); + + // Look for the MSIX file in the tools/MSIX folder + var msixToolsDir = new DirectoryInfo(Path.Combine(msixDir.FullName, $"win10-{architecture}")); + if (!msixToolsDir.Exists) + { + throw new DirectoryNotFoundException($"MSIX tools directory not found: {msixToolsDir}"); + } + + // Try to use inventory first for accurate file selection + FileInfo? msixPath = null; + try + { + var packageEntries = await WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken); + if (packageEntries != null) + { + // Look for the base Windows App Runtime package (not Framework, DDLM, or Singleton packages) + var mainRuntimeEntry = packageEntries.FirstOrDefault(entry => + entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && + !entry.PackageIdentity.Contains("Framework") && + !entry.FileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && + !entry.FileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase)); + + if (mainRuntimeEntry != null) + { + msixPath = new FileInfo(Path.Combine(msixToolsDir.FullName, mainRuntimeEntry.FileName)); + taskContext.AddDebugMessage($"{UiSymbols.Package} Found main runtime package from inventory: {mainRuntimeEntry.FileName}"); + } + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Could not parse inventory, falling back to file search: {ex.Message}"); + } + + // Fallback: search for files directly with pattern matching + if (msixPath == null || !msixPath.Exists) + { + var msixFiles = msixToolsDir.GetFiles("Microsoft.WindowsAppRuntime.*.msix"); + if (msixFiles.Length == 0) + { + throw new FileNotFoundException($"No MSIX files found in {msixToolsDir}"); + } + + // Look for the base runtime package (format: Microsoft.WindowsAppRuntime.{version}.msix) + // Exclude files with additional suffixes like DDLM, Singleton, Framework, etc. + msixPath = msixFiles.FirstOrDefault(f => + { + var fileName = f.Name; + return !fileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && + !fileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase) && + !fileName.Contains("Framework", StringComparison.OrdinalIgnoreCase) && + WindowsAppRuntimeMsixRegex().IsMatch(fileName); + }) ?? msixFiles[0]; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Extracting MSIX: {msixPath.FullName}"); + + // Extract MSIX content + var extractedDir = new DirectoryInfo(Path.Combine(archSelfContainedDir.FullName, "extracted")); + if (extractedDir.Exists) + { + extractedDir.Delete(recursive: true); + } + extractedDir.Refresh(); + extractedDir.Create(); + + using (var archive = await ZipFile.OpenReadAsync(msixPath.FullName, cancellationToken)) + { + await archive.ExtractToDirectoryAsync(extractedDir.FullName, cancellationToken); + } + + // Copy relevant files to deployment directory + var deploymentDir = archSelfContainedDir.CreateSubdirectory("deployment"); + + // Copy DLLs, WinMD files, and other runtime assets + await CopyRuntimeFilesAsync(extractedDir, deploymentDir, taskContext, cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Check} Self-contained files prepared in: {archSelfContainedDir.FullName}"); + + return 0; + }, cancellationToken); + } + /// /// Parses an AppX manifest file and extracts the package identity information /// @@ -59,19 +275,35 @@ public static async Task ParseAppxManifestFromPathAsync(File /// Thrown when the manifest is invalid or missing required elements public static MsixIdentityResult ParseAppxManifestAsync(string appxManifestContent) { - var doc = AppxManifestDocument.Parse(appxManifestContent); + // Extract Package Identity information + var identityMatch = IdentityElementRegex().Match(appxManifestContent); + if (!identityMatch.Success) + { + throw new InvalidOperationException("No Identity element found in AppX manifest"); + } - var identity = doc.GetIdentityElement() - ?? throw new InvalidOperationException("No Identity element found in AppX manifest"); + var identityElement = identityMatch.Value; - var packageName = identity.Attribute("Name")?.Value - ?? throw new InvalidOperationException("AppX manifest Identity element missing required Name or Publisher attributes"); + // Extract attributes from Identity element + var nameMatch = AppxPackageNameRegex().Match(identityElement); + var publisherMatch = AppxPackagePublisherRegex().Match(identityElement); - var publisher = identity.Attribute("Publisher")?.Value - ?? throw new InvalidOperationException("AppX manifest Identity element missing required Name or Publisher attributes"); + if (!nameMatch.Success || !publisherMatch.Success) + { + throw new InvalidOperationException("AppX manifest Identity element missing required Name or Publisher attributes"); + } + + var packageName = nameMatch.Groups[1].Value; + var publisher = publisherMatch.Groups[1].Value; + + // Extract Application ID from Applications/Application element + var applicationMatch = AppxApplicationIdRegex().Match(appxManifestContent); + if (!applicationMatch.Success) + { + throw new InvalidOperationException("No Application element with Id attribute found in AppX manifest"); + } - var applicationId = doc.ApplicationId - ?? throw new InvalidOperationException("No Application element with Id attribute found in AppX manifest"); + var applicationId = applicationMatch.Groups[1].Value; return new MsixIdentityResult(packageName, publisher, applicationId); } @@ -84,770 +316,2721 @@ public static MsixIdentityResult ParseAppxManifestAsync(string appxManifestConte /// List of alias names (e.g. "myapp.exe") public static List ExtractExecutionAliases(string manifestContent) { - var doc = AppxManifestDocument.Parse(manifestContent); var aliases = new List(); - var root = doc.Document.Root; - if (root == null) - { - return aliases; - } - - foreach (var element in root.Descendants() - .Where(e => e.Name.LocalName == "ExecutionAlias" - && (e.Name.Namespace == AppxManifestDocument.Uap5Ns || e.Name.Namespace == AppxManifestDocument.DesktopNs))) + var matches = ExecutionAliasRegex().Matches(manifestContent); + foreach (Match match in matches) { - var alias = element.Attribute("Alias")?.Value; - if (alias != null) - { - aliases.Add(alias); - } + aliases.Add(match.Groups[1].Value); } - return aliases; } - /// - /// Resolves $placeholder$ tokens in manifest content. Handles $targetnametoken$ and $targetentrypoint$. - /// If the Executable attribute contains a placeholder and no --executable is provided, - /// attempts to infer by searching for .exe files in the input folder. - /// - private static string ResolveManifestPlaceholders(string manifestContent, string? executable, DirectoryInfo inputFolder, TaskContext taskContext) + [GeneratedRegex(@"<(?:uap5|desktop):ExecutionAlias\s+Alias\s*=\s*[""']([^""']*)[""']\s*/>", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex ExecutionAliasRegex(); + + public async Task AddSparseIdentityAsync(string? entryPointPath, FileInfo appxManifestPath, bool noInstall, bool keepIdentity, TaskContext taskContext, CancellationToken cancellationToken = default) { - // Check if manifest contains any placeholders at all - if (!PlaceholderHelper.ContainsPlaceholders(manifestContent)) + // Validate inputs + if (!appxManifestPath.Exists) { - return manifestContent; + throw new FileNotFoundException($"AppX manifest not found at: {appxManifestPath}. You can generate one using 'winapp manifest generate'."); } - var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Determine the executable name for $targetnametoken$ - if (!string.IsNullOrWhiteSpace(executable)) + if (!devModeService.IsEnabled() && noInstall == false) { - // --executable was provided explicitly - var nameWithoutExtension = Path.GetFileNameWithoutExtension(executable); - replacements[PlaceholderHelper.TargetNameToken] = nameWithoutExtension; - - // Also replace the Executable attribute value if it contains a placeholder - var doc = AppxManifestDocument.Parse(manifestContent); - if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) - { - doc.ApplicationExecutable = executable; - manifestContent = doc.ToXml(); - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Using specified executable: {executable}"); + throw new InvalidOperationException("Developer Mode is not enabled on this machine. Please enable Developer Mode and try again."); } - else - { - // Check if the Executable attribute in the manifest has a placeholder - var doc = AppxManifestDocument.Parse(manifestContent); - if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) - { - // Try to auto-infer by finding .exe files in the input folder root - var exeFiles = inputFolder.Exists - ? inputFolder.GetFiles("*.exe", SearchOption.TopDirectoryOnly) - .Where(f => !string.Equals(f.Name, "createdump.exe", StringComparison.OrdinalIgnoreCase)) - .ToArray() - : []; - - if (exeFiles.Length == 1) - { - var inferredExe = exeFiles[0].Name; - var nameWithoutExtension = Path.GetFileNameWithoutExtension(inferredExe); - replacements[PlaceholderHelper.TargetNameToken] = nameWithoutExtension; - doc.ApplicationExecutable = inferredExe; - manifestContent = doc.ToXml(); + if (entryPointPath == null) + { + var manifestContent = await File.ReadAllTextAsync(appxManifestPath.FullName, Encoding.UTF8, cancellationToken); - taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-inferred executable: {inferredExe}"); - } - else + // Resolve placeholders in memory only to extract the executable path + if (PlaceholderHelper.ContainsPlaceholders(manifestContent)) + { + // Without an explicit entrypoint, we can't resolve $targetnametoken$ + var executableMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); + if (executableMatch.Success && PlaceholderHelper.ContainsPlaceholders(executableMatch.Groups[1].Value)) { - var count = exeFiles.Length == 0 ? "no" : "multiple"; throw new InvalidOperationException( - $"The manifest contains a placeholder for the executable but {count} .exe files were found in the input folder. " + - "Edit the manifest to specify the executable or use --executable to specify the relative path to the exe."); + "The manifest contains a placeholder for the executable. " + + "Provide the entrypoint argument to specify the executable path."); } - } - } - - // Apply all placeholder replacements - manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent, replacements); - // Sanity check: ensure no unresolved placeholders remain - PlaceholderHelper.ThrowIfUnresolvedPlaceholders(manifestContent); + // Resolve built-in tokens (e.g. $targetentrypoint$) in memory to extract executable + manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent); + } - return manifestContent; - } + var execMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); + if (execMatch.Success) + { + entryPointPath = execMatch.Groups[1].Value; + } + } - /// - /// Resolves <Resource Language="x-generate"/> in the manifest by replacing it - /// with concrete language tags. Languages are extracted from the existing resources.pri - /// in the input folder; falls back to en-US when no PRI or no language qualifiers are found. - /// - private async Task ResolveResourceLanguageXGenerateAsync( - string manifestContent, - DirectoryInfo inputFolder, - TaskContext taskContext, - CancellationToken cancellationToken) - { - if (!ContainsXGenerateLanguage(manifestContent)) + // Validate inputs + if (!File.Exists(entryPointPath)) { - return manifestContent; + throw new FileNotFoundException($"EntryPoint/Executable not found at: {entryPointPath}"); } - taskContext.AddDebugMessage($"{UiSymbols.Note} Detected — resolving to concrete language(s)"); + taskContext.AddDebugMessage($"Processing entryPoint/executable: {entryPointPath}"); + taskContext.AddDebugMessage($"Using AppX manifest: {appxManifestPath}"); - var languages = new List(); + // Generate sparse package structure + // Fetch dotnet package list once for all downstream operations + var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - // Try to extract languages from existing resources.pri - var priFile = new FileInfo(Path.Combine(inputFolder.FullName, "resources.pri")); - if (priFile.Exists) + var (debugManifestPath, debugIdentity) = await GenerateSparsePackageStructureAsync( + appxManifestPath, + entryPointPath, + keepIdentity, + dotNetPackageList, + taskContext, + cancellationToken); + + // Update executable with debug identity + if (Path.HasExtension(entryPointPath) && string.Equals(Path.GetExtension(entryPointPath), ".exe", StringComparison.OrdinalIgnoreCase)) { - languages = await priService.ExtractLanguagesFromPriAsync(priFile, taskContext, cancellationToken); + var exePath = new FileInfo(entryPointPath); + await EmbedMsixIdentityToExeAsync(exePath, debugIdentity, taskContext, cancellationToken); } - if (languages.Count == 0) + if (noInstall) { - languages.Add("en-US"); - taskContext.AddDebugMessage($"{UiSymbols.Note} No language qualifiers found in PRI — defaulting to en-US"); + taskContext.AddDebugMessage("Skipping package installation as per --no-install option."); } else { - taskContext.AddDebugMessage($"{UiSymbols.Note} Resolved resource languages from PRI: {string.Join(", ", languages)}"); - } + // Register the debug appxmanifest + var entryPointDir = Path.GetDirectoryName(entryPointPath); + var externalLocation = new DirectoryInfo(string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir); - return ReplaceXGenerateLanguage(manifestContent, languages); - } + // Unregister any existing package first + await UnregisterExistingPackageAsync(debugIdentity.PackageName, taskContext, cancellationToken); - /// - /// Returns true if the manifest contains a <Resource Language="x-generate"/> element. - /// - internal static bool ContainsXGenerateLanguage(string manifestContent) - { - var doc = AppxManifestDocument.Parse(manifestContent); - return doc.GetResourceLanguages() - .Any(lang => string.Equals(lang, "x-generate", StringComparison.OrdinalIgnoreCase)); - } + // Register the new debug manifest with external location + await RegisterSparsePackageAsync(debugManifestPath, externalLocation, taskContext, cancellationToken); + } - /// - /// Replaces <Resource Language="x-generate"/> with concrete - /// <Resource Language="..."/> entries for each specified language. - /// - internal static string ReplaceXGenerateLanguage(string manifestContent, IList languages) - { - var doc = AppxManifestDocument.Parse(manifestContent); - doc.SetResourceLanguages(languages); - return doc.ToXml(); + return new MsixIdentityResult(debugIdentity.PackageName, debugIdentity.Publisher, debugIdentity.ApplicationId); } - /// - /// Creates an MSIX package from a prepared package directory - /// - /// Install certificate to machine - /// Publisher name for certificate generation (default: extracted from manifest) - /// Path to the manifest file (optional) - /// Enable self-contained deployment - /// Cancellation token - /// Result containing the MSIX path and signing status - public async Task CreateMsixPackageAsync( - DirectoryInfo inputFolder, - FileSystemInfo? outputPath, - TaskContext taskContext, - string? packageName = null, - bool skipPri = false, - bool autoSign = false, - FileInfo? certificatePath = null, - string certificatePassword = "password", - bool generateDevCert = false, - bool installDevCert = false, - string? publisher = null, - FileInfo? manifestPath = null, - bool selfContained = false, - string? executable = null, - CancellationToken cancellationToken = default) + public async Task AddLooseLayoutIdentityAsync(FileInfo appxManifestPath, DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, CancellationToken cancellationToken = default) { - // Validate input folder and manifest - if (!inputFolder.Exists) + // Validate inputs + if (!appxManifestPath.Exists) { - throw new DirectoryNotFoundException($"Input folder not found: {inputFolder}"); + throw new FileNotFoundException($"AppX manifest not found at: {appxManifestPath}. You can generate one using 'winapp manifest generate'."); } - // Warn if the input folder contains .pfx certificate files, which are likely - // development certificates that should not be included in the package payload. - var pfxFiles = inputFolder.EnumerateFiles("*.pfx", SearchOption.AllDirectories).ToList(); - if (pfxFiles.Count > 0) + if (!devModeService.IsEnabled()) { - foreach (var pfxFile in pfxFiles) - { - var relativePath = Path.GetRelativePath(inputFolder.FullName, pfxFile.FullName); - taskContext.AddStatusMessage($"{UiSymbols.Warning} PFX certificate file found in input folder: {relativePath}. Consider removing it before packaging."); - } + throw new InvalidOperationException("Developer Mode is not enabled on this machine. Please enable Developer Mode and try again."); } - // Check for an AppX subdirectory, which is a build artifact that should not be - // included in the package. Exclude it from staging and warn the user. - var excludedDirectories = new HashSet(StringComparer.OrdinalIgnoreCase); - var appxDir = new DirectoryInfo(Path.Combine(inputFolder.FullName, "AppX")); - if (appxDir.Exists) + taskContext.AddDebugMessage($"Using AppX manifest: {appxManifestPath}"); + + // If there is a csproj, warn the user that they should use `dotnet run` instead of `winapp run` + var csprojFiles = dotNetService.FindCsproj(inputDirectory); + var csproj = csprojFiles.Count > 0 ? csprojFiles[0] : null; + if (csproj != null) { - excludedDirectories.Add("AppX"); - taskContext.AddStatusMessage($"{UiSymbols.Warning} Found 'AppX' directory in input folder. It will be excluded from the package."); + throw new InvalidOperationException( + $"A .csproj file was found in the input directory: {csproj.FullName}. " + + $"Please use 'dotnet run' to run your application instead of 'winapp run'."); } - // Determine manifest path based on priority: - // 1. Use provided manifestPath parameter - // 2. Check for appxmanifest.xml or package.appxmanifest in input folder - // 3. Check for appxmanifest.xml or package.appxmanifest in current directory - FileInfo resolvedManifestPath; - if (manifestPath != null) + if (!outputAppXDirectory.Exists) { - resolvedManifestPath = manifestPath; - taskContext.AddDebugMessage($"{UiSymbols.Note} Using specified manifest: {resolvedManifestPath}"); + outputAppXDirectory.Create(); } - else + + // Recursive copy all files to output directory, but exclude the outputAppXFolder itself if it's inside the input directory + if (inputDirectory != null && !string.Equals(inputDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar), + outputAppXDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)) { - var resolvedFromSearch = FindManifestInDirectory(new DirectoryInfo(inputFolder.FullName)) - ?? FindManifestInDirectory(new DirectoryInfo(currentDirectoryProvider.GetCurrentDirectory())); + var outputFullPath = outputAppXDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; - if (resolvedFromSearch != null) - { - resolvedManifestPath = resolvedFromSearch; - taskContext.AddDebugMessage($"{UiSymbols.Note} Using manifest: {resolvedManifestPath}"); - } - else + foreach (var file in inputDirectory.EnumerateFiles("*", SearchOption.AllDirectories)) { - throw new FileNotFoundException($"Manifest file not found. Searched for appxmanifest.xml and package.appxmanifest in: input folder ({inputFolder.FullName}), current directory ({currentDirectoryProvider.GetCurrentDirectory()})"); + // Skip files that are inside the output folder (if output is nested inside input) + if (file.FullName.StartsWith(outputFullPath, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var relativePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName); + var destFile = new FileInfo(Path.Combine(outputAppXDirectory.FullName, relativePath)); + + destFile.Directory?.Create(); + file.CopyTo(destFile.FullName, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Files} Copied: {relativePath}"); } - } - if (!resolvedManifestPath.Exists) - { - throw new FileNotFoundException($"Manifest file not found: {resolvedManifestPath}"); + taskContext.AddDebugMessage($"{UiSymbols.Check} Copied files to output directory: {outputAppXDirectory.FullName}"); } - // Determine package name and publisher - var finalPackageName = packageName; - var extractedPublisher = publisher; - string? extractedVersion = null; + // Copy the appxmanifest to the output directory, if not already present + appxManifestPath.CopyTo(Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name), overwrite: true); - var manifestContent = await File.ReadAllTextAsync(resolvedManifestPath.FullName, Encoding.UTF8, cancellationToken); + // If its Package.appxmanifest, rename to appxmanifest.xml + if (string.Equals(appxManifestPath.Name, "Package.appxmanifest", StringComparison.OrdinalIgnoreCase)) + { + var renamedPath = Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml"); + var originalPath = Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name); + File.Move(originalPath, renamedPath, true); + taskContext.AddDebugMessage($"{UiSymbols.Files} Renamed Package.appxmanifest to appxmanifest.xml"); + appxManifestPath = new FileInfo(renamedPath); + } - // Resolve $placeholder$ tokens in the manifest - manifestContent = ResolveManifestPlaceholders(manifestContent, executable, inputFolder, taskContext); + var copiedAppxManifestPath = new FileInfo(Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name)); + var manifestContent = await File.ReadAllTextAsync(copiedAppxManifestPath.FullName, Encoding.UTF8, cancellationToken); + var executableMatch = outputAppXDirectory.EnumerateFiles("*", SearchOption.AllDirectories) + .FirstOrDefault(f => string.Equals(f.Extension, ".exe", StringComparison.OrdinalIgnoreCase)); - // Resolve with concrete language(s) from PRI - manifestContent = await ResolveResourceLanguageXGenerateAsync(manifestContent, inputFolder, taskContext, cancellationToken); + if (executableMatch == null) + { + throw new FileNotFoundException("No executable (.exe) file found in the output directory for token replacement."); + } - // Update manifest content to ensure it's either referencing Windows App SDK or is self-contained - // Fetch dotnet package list once for all downstream operations - var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); + manifestContent = manifestContent.Replace("$targetnametoken$", Path.GetFileNameWithoutExtension(executableMatch.Name), StringComparison.OrdinalIgnoreCase); + manifestContent = manifestContent.Replace("$targetentrypoint$", "Windows.FullTrustApplication", StringComparison.OrdinalIgnoreCase); + manifestContent = manifestContent.Replace("x-generate", "EN-US"); - // Determine executable path for ProcessorArchitecture auto-detection - string? resolvedExePath = null; + // If there is a pri file named after the executable, rename it to resources.pri + var priFilePath = Path.Combine(outputAppXDirectory.FullName, Path.GetFileNameWithoutExtension(executableMatch.Name) + ".pri"); + if (File.Exists(priFilePath)) { - var tempDoc = AppxManifestDocument.Parse(manifestContent); - var appExe = tempDoc.ApplicationExecutable; - if (appExe != null) - { - resolvedExePath = Path.Combine(inputFolder.FullName, appExe); - } + var resourcesPriPath = Path.Combine(outputAppXDirectory.FullName, "resources.pri"); + File.Move(priFilePath, resourcesPriPath, overwrite: true); + taskContext.AddDebugMessage($"{UiSymbols.Files} Renamed {Path.GetFileName(priFilePath)} to resources.pri"); } - (manifestContent, var packageArch) = await UpdateAppxManifestContentAsync(manifestContent, null, null, resolvedExePath, sparse: false, selfContained: selfContained, dotNetPackageList, taskContext, cancellationToken); + await File.WriteAllTextAsync(copiedAppxManifestPath.FullName, manifestContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), cancellationToken); - // Parse the manifest to extract identity, executable, and architecture info - var manifestDoc = AppxManifestDocument.Parse(manifestContent); + // Copy all assets + var originalManifestDir = appxManifestPath.DirectoryName; - try + if (!string.Equals(originalManifestDir, outputAppXDirectory.FullName, StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrWhiteSpace(finalPackageName)) - { - finalPackageName = manifestDoc.IdentityName ?? "Package"; - } - - if (string.IsNullOrWhiteSpace(extractedPublisher)) - { - extractedPublisher = manifestDoc.IdentityPublisher; - } - - if (string.IsNullOrWhiteSpace(extractedVersion)) - { - extractedVersion = manifestDoc.IdentityVersion; - } + var expandedFiles = GetExpandedManifestReferencedFiles(appxManifestPath, taskContext); + CopyAllAssets(expandedFiles, outputAppXDirectory, taskContext); } - catch + else { - finalPackageName ??= "Package"; + taskContext.AddDebugMessage($"{UiSymbols.Warning} Manifest directory and target directory are the same, skipping assets copy"); } - // Clean the resolved package name to ensure it meets MSIX schema requirements - finalPackageName = ManifestService.CleanPackageName(finalPackageName); + var identity = ParseAppxManifestAsync(manifestContent); - var defaultMsixFileName = (packageArch, extractedVersion) switch - { - (not null, not null) when !string.IsNullOrWhiteSpace(extractedVersion) => $"{finalPackageName}_{extractedVersion}_{packageArch}.msix", - (null, not null) when !string.IsNullOrWhiteSpace(extractedVersion) => $"{finalPackageName}_{extractedVersion}.msix", - (not null, _) => $"{finalPackageName}_{packageArch}.msix", - _ => $"{finalPackageName}.msix" - }; + // Update manifest content to ensure it's either referencing Windows App SDK or is self-contained + // Fetch dotnet package list once for all downstream operations + var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - FileInfo outputMsixPath; - DirectoryInfo outputFolder; - if (outputPath == null) - { - outputFolder = currentDirectoryProvider.GetCurrentDirectoryInfo(); - outputMsixPath = new FileInfo(Path.Combine(outputFolder.FullName, defaultMsixFileName)); - } - else + // Install the Windows App Runtime framework packages if not already present + await EnsureWindowsAppRuntimeInstalledAsync(dotNetPackageList, taskContext, cancellationToken); + + // Unregister any existing package first + await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); + + // Register the new debug manifest with external location + await RegisterLooseLayoutPackageAsync(copiedAppxManifestPath, taskContext, cancellationToken); + + return new MsixIdentityResult(identity.PackageName, identity.Publisher, identity.ApplicationId); + } + + /// + /// Ensures that the Windows App Runtime framework MSIX packages are installed on the machine. + /// Locates the runtime MSIX directory from the NuGet package cache and installs any + /// missing or outdated packages (Framework, DDLM, Singleton, Main) via Add-AppxPackage. + /// + private async Task EnsureWindowsAppRuntimeInstalledAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) +{ + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); + if (msixDir == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not locate Windows App Runtime MSIX packages. The runtime may need to be installed manually."); + return; + } + + var (installedCount, errorCount) = await workspaceSetupService.InstallWindowsAppRuntimeAsync(msixDir, taskContext, cancellationToken); + + if (errorCount > 0) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} {errorCount} runtime package(s) failed to install. The app may not launch correctly."); + } + else if (installedCount > 0) + { + taskContext.AddDebugMessage($"{UiSymbols.Check} Installed {installedCount} Windows App Runtime package(s)"); + } +} + +private async Task EmbedMsixIdentityToExeAsync(FileInfo exePath, MsixIdentityResult identityInfo, TaskContext taskContext, CancellationToken cancellationToken) + { + // Create the MSIX element for the win32 manifest + string assemblyIdentity = $@";"; + var existingManifestPath = new FileInfo(Path.Combine(exePath.DirectoryName!, "temp_extracted.manifest")); + + try { - if (Path.HasExtension(outputPath.Name) && string.Equals(Path.GetExtension(outputPath.Name), ".msix", StringComparison.OrdinalIgnoreCase)) + bool hasExistingManifest = await TryExtractManifestFromExeAsync(exePath, existingManifestPath, taskContext, cancellationToken); + if (!hasExistingManifest) { - outputMsixPath = new FileInfo(outputPath.FullName); - outputFolder = outputMsixPath.Directory!; + assemblyIdentity = string.Empty; } else { - outputFolder = new DirectoryInfo(outputPath.FullName); - outputMsixPath = new FileInfo(Path.Combine(outputPath.FullName, defaultMsixFileName)); + taskContext.AddDebugMessage("Existing manifest found in executable, checking for AssemblyIdentity..."); + var existingManifestContent = await File.ReadAllTextAsync(existingManifestPath.FullName, Encoding.UTF8, cancellationToken); + var assemblyIdentityMatch = AssemblyIdentityNameRegex().Match(existingManifestContent); + if (assemblyIdentityMatch.Success) + { + taskContext.AddDebugMessage("Existing AssemblyIdentity found in manifest, will not add a new one."); + assemblyIdentity = string.Empty; + } } } + finally + { + TryDeleteFile(existingManifestPath); + } - // Ensure output folder exists - if (!outputFolder.Exists) + var manifestContent = $@" + + + {assemblyIdentity} +"; + + // Create a temporary manifest file + var tempManifestPath = new FileInfo(Path.Combine(exePath.DirectoryName!, "msix_identity_temp.manifest")); + + try { - outputFolder.Create(); + await File.WriteAllTextAsync(tempManifestPath.FullName, manifestContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), cancellationToken); + + // Use mt.exe to merge manifests + await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); } + finally + { + TryDeleteFile(tempManifestPath); + } + } - // Create a temporary staging directory so we never modify the original input folder. - // All packaging operations (manifest updates, asset copies, PRI generation, self-contained - // runtime bundling) happen in this staging copy. The original target folder stays untouched. - var stagingDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"winapp-package-{Guid.NewGuid():N}")); - stagingDir.Create(); + /// + /// Embeds a manifest file into the Win32 manifest of an executable using mt.exe for proper merging. + /// + /// Path to the executable to modify + /// Path to the manifest file to embed + /// Cancellation token + private async Task EmbedManifestFileToExeAsync( + FileInfo exePath, + FileInfo manifestPath, + TaskContext taskContext, + CancellationToken cancellationToken = default) + { + // Validate inputs + if (!exePath.Exists) + { + throw new FileNotFoundException($"Executable not found at: {exePath}"); + } - taskContext.AddDebugMessage($"{UiSymbols.Note} Created staging directory: {stagingDir.FullName}"); + if (!manifestPath.Exists) + { + throw new FileNotFoundException($"Manifest file not found at: {manifestPath}"); + } + + taskContext.AddDebugMessage($"Processing executable: {exePath}"); + taskContext.AddDebugMessage($"Embedding manifest: {manifestPath}"); + + var exeDir = exePath.DirectoryName!; + var tempManifestPath = new FileInfo(Path.Combine(exeDir, "temp_extracted.manifest")); + var mergedManifestPath = new FileInfo(Path.Combine(exeDir, "merged.manifest")); try { - // Check if the manifest was generated by MSBuild and a .build.appxrecipe is available. - // When present, the recipe lists exactly which files belong in the package and their - // correct PackagePaths, producing a cleaner MSIX without build artifacts. - var isMSBuildGenerated = manifestDoc.Document.Root? - .Element(AppxManifestDocument.BuildNs + "Metadata")? - .Elements(AppxManifestDocument.BuildNs + "Item") - .Any(e => string.Equals(e.Attribute("Name")?.Value, "makepri.exe", StringComparison.OrdinalIgnoreCase)) == true; + bool hasExistingManifest = await TryExtractManifestFromExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - FileInfo? recipeFile = null; - if (isMSBuildGenerated) + if (hasExistingManifest) { - recipeFile = inputFolder.EnumerateFiles("*.build.appxrecipe", SearchOption.TopDirectoryOnly).FirstOrDefault(); - } + taskContext.AddDebugMessage("Merging with existing manifest using mt.exe..."); - if (recipeFile != null) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} MSBuild-generated manifest detected"); - taskContext.AddDebugMessage($"{UiSymbols.Files} Using appxrecipe for staging: {recipeFile.Name}"); - await CopyFilesFromRecipeAsync(recipeFile, stagingDir, taskContext, cancellationToken); + // Use mt.exe to merge existing manifest with new manifest + await RunMtToolAsync($@"-manifest ""{tempManifestPath}"" ""{manifestPath}"" -out:""{mergedManifestPath}""", true, taskContext, cancellationToken); } else { - // No recipe available — copy the entire input folder to staging - CopyDirectoryRecursive(inputFolder, stagingDir); - taskContext.AddDebugMessage($"{UiSymbols.Files} Copied input folder to staging directory"); + taskContext.AddDebugMessage("No existing manifest, using new manifest as-is"); + + // No existing manifest, use the new manifest directly + manifestPath.CopyTo(mergedManifestPath.FullName); } - // Write the updated manifest into the staging directory - var updatedManifestPath = Path.Combine(stagingDir.FullName, "appxmanifest.xml"); - await File.WriteAllTextAsync(updatedManifestPath, manifestContent, Encoding.UTF8, cancellationToken); + taskContext.AddDebugMessage("Embedding merged manifest into executable..."); - // Resolve executable path relative to the staging directory - var applicationExecutable = manifestDoc.ApplicationExecutable; - FileInfo? executablePath = applicationExecutable != null ? new FileInfo(Path.Combine(stagingDir.FullName, applicationExecutable)) : null; + // Update the executable with merged manifest + await RunMtToolAsync($@"-manifest ""{mergedManifestPath}"" -outputresource:""{exePath}"";#1", true, taskContext, cancellationToken); - // Pre-compute expanded manifest resources from the original manifest - var manifestIsOutsideInputFolder = !inputFolder.FullName.TrimEnd(Path.DirectorySeparatorChar) - .Equals(resolvedManifestPath.Directory!.FullName.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase); + taskContext.AddDebugMessage($"{UiSymbols.Check} Successfully embedded manifest into: {exePath}"); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to embed manifest into executable: {ex.Message}", ex); + } + finally + { + // Clean up temporary files + TryDeleteFile(tempManifestPath); + TryDeleteFile(mergedManifestPath); + } + } - // If manifest is outside input folder, copy its referenced assets into the staging directory - if (manifestIsOutsideInputFolder) + private async Task TryExtractManifestFromExeAsync(FileInfo exePath, FileInfo tempManifestPath, TaskContext taskContext, CancellationToken cancellationToken) + { + taskContext.AddDebugMessage("Extracting current manifest from executable..."); + + // Extract current manifest from the executable + bool hasExistingManifest = false; + try + { + await RunMtToolAsync($@"-inputresource:""{exePath}"";#1 -out:""{tempManifestPath}""", false, taskContext, cancellationToken); + tempManifestPath.Refresh(); + hasExistingManifest = tempManifestPath.Exists; + } + catch + { + taskContext.AddDebugMessage("No existing manifest found in executable"); + } + + return hasExistingManifest; + } + + /// + /// Creates a PRI configuration file for the given package directory + /// + /// Path to the package directory + /// Task context for logging and progress reporting + /// Default language qualifier (default: 'en-US') + /// Platform version (default: '10.0.0') + /// Pre-computed list of manifest-referenced resource file paths (relative to the package directory) to include in the PRI. Must be provided by the caller via . + /// Cancellation token + /// Path to the created configuration file + public async Task CreatePriConfigAsync( + DirectoryInfo packageDir, + TaskContext taskContext, + string language = "en-US", + string platformVersion = "10.0.0", + IEnumerable precomputedPriResourceCandidates = null!, + CancellationToken cancellationToken = default) + { + if (!packageDir.Exists) + { + throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); + } + + ArgumentNullException.ThrowIfNull(precomputedPriResourceCandidates); + + var resfilesPath = Path.Combine(packageDir.FullName, "pri.resfiles"); + var priResourceCandidates = precomputedPriResourceCandidates.ToList(); + + priResourceCandidates = [.. priResourceCandidates + .Where(path => PriIncludedExtensions.Contains(Path.GetExtension(path))) + .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; + + taskContext.AddDebugMessage($"PRI resource candidates discovered: {priResourceCandidates.Count}"); + + using (var writer = new StreamWriter(resfilesPath)) + { + foreach (var priFile in priResourceCandidates) { - var externalAssets = MrtAssetHelper.GetExpandedManifestReferencedFiles(resolvedManifestPath, taskContext); - MrtAssetHelper.CopyAllAssets(externalAssets, stagingDir, taskContext); + await writer.WriteLineAsync(priFile); } + } - taskContext.AddDebugMessage($"Creating MSIX package from staging: {stagingDir.FullName}"); - taskContext.AddDebugMessage($"Output: {outputMsixPath.FullName}"); + var configPath = new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); + var arguments = $@"createconfig /cf ""{configPath}"" /dq lang-{language}_scale-200 /pv {platformVersion} /o"; - // Generate PRI files if not skipped and no existing PRI from the build output - var existingPri = new FileInfo(Path.Combine(stagingDir.FullName, "resources.pri")); - if (!skipPri && !existingPri.Exists) - { - taskContext.AddDebugMessage("Generating PRI configuration and files..."); + taskContext.AddDebugMessage("Creating PRI configuration file..."); - // Expand manifest-referenced files from the staging manifest so that - // assets from both the input folder and external manifest are discovered. - var stagingManifest = new FileInfo(Path.Combine(stagingDir.FullName, "appxmanifest.xml")); - var priExpandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(stagingManifest, taskContext); - var priResourceCandidates = priExpandedFiles.Select(file => file.RelativePath); - await priService.CreatePriConfigAsync( - stagingDir, - taskContext, - precomputedPriResourceCandidates: priResourceCandidates, - cancellationToken: cancellationToken); - var resourceFiles = await priService.GeneratePriFileAsync(stagingDir, taskContext, cancellationToken: cancellationToken); - if (resourceFiles.Count > 0 && logger.IsEnabled(LogLevel.Debug)) + try + { + await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + taskContext.AddDebugMessage($"PRI configuration created: {configPath}"); + + var xmlDoc = new XmlDocument(); + xmlDoc.Load(configPath.FullName); + var resourcesNode = xmlDoc.SelectSingleNode("/resources"); + if (resourcesNode != null) + { + var indexNode = resourcesNode.SelectSingleNode("index"); + if (indexNode != null) { - taskContext.AddDebugMessage($"Resource files included in PRI:"); - await taskContext.AddSubTaskAsync("Pri Resources", async (taskContext, cancellationToken) => + if (indexNode.Attributes?["startIndexAt"]?.Value != null) { - foreach (var resourceFile in resourceFiles) + // set to relative path + indexNode.Attributes["startIndexAt"]!.Value = ".\\pri.resfiles"; + } + + var resfilesIndexerNode = xmlDoc.CreateElement("indexer-config"); + var typeAttr = xmlDoc.CreateAttribute("type"); + typeAttr.Value = "resfiles"; + resfilesIndexerNode.Attributes.Append(typeAttr); + + var delimiterAttr = xmlDoc.CreateAttribute("qualifierDelimiter"); + delimiterAttr.Value = "."; + resfilesIndexerNode.Attributes.Append(delimiterAttr); + + indexNode.AppendChild(resfilesIndexerNode); + + // Ensure folder-based indexer is configured to parse qualifiers from + // both folder names and file names (e.g. targetsize-48_altform-unplated). + var folderIndexerNode = indexNode + .SelectNodes("indexer-config") + ?.OfType() + .FirstOrDefault(node => + node.Attributes?["type"]?.Value?.Equals("folder", StringComparison.OrdinalIgnoreCase) == true); + + if (folderIndexerNode?.Attributes != null) + { + var folderAttributes = folderIndexerNode.Attributes; + + var folderNameAsQualifierAttr = folderAttributes["foldernameAsQualifier"]; + if (folderNameAsQualifierAttr == null) { - taskContext.AddDebugMessage(resourceFile.ToString()); + folderNameAsQualifierAttr = xmlDoc.CreateAttribute("foldernameAsQualifier"); + folderAttributes.Append(folderNameAsQualifierAttr); } - return Task.FromResult(0); - }, cancellationToken); + folderNameAsQualifierAttr.Value = "true"; + + var fileNameAsQualifierAttr = folderAttributes["filenameAsQualifier"]; + if (fileNameAsQualifierAttr == null) + { + fileNameAsQualifierAttr = xmlDoc.CreateAttribute("filenameAsQualifier"); + folderAttributes.Append(fileNameAsQualifierAttr); + } + fileNameAsQualifierAttr.Value = "true"; + } + + xmlDoc.Save(configPath.FullName); } } - else if (!skipPri && existingPri.Exists) + + return configPath; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to create PRI configuration: {ex.Message}", ex); + } + } + + private static readonly HashSet PriIncludedExtensions = new(StringComparer.OrdinalIgnoreCase) + { + ".png", + ".jpg", + ".jpeg", + ".gif", + ".bmp", + ".ico", + ".svg" + }; + + private static List<(FileInfo SourceFile, string RelativePath)> ExpandManifestReferencedFiles( + DirectoryInfo manifestDir, + IEnumerable referencedFiles, + TaskContext? taskContext, + Func? includeFile = null) + { + var expandedFilesByRelativePath = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var relativeFilePath in referencedFiles) + { + var logicalSourceFile = new FileInfo(Path.Combine(manifestDir.FullName, relativeFilePath)); + var sourceDir = logicalSourceFile.Directory; + + if (sourceDir is null || !sourceDir.Exists) { - taskContext.AddDebugMessage("Skipping PRI generation — existing resources.pri found in input folder"); + taskContext?.AddDebugMessage($"{UiSymbols.Warning} Source directory not found for referenced file: {relativeFilePath}"); + continue; } - // Handle self-contained deployment if requested - if (selfContained && executablePath != null) + var logicalBaseName = Path.GetFileNameWithoutExtension(logicalSourceFile.Name); + var variantBaseName = GetMrtVariantBaseName(logicalBaseName); + var extension = logicalSourceFile.Extension; + + var searchPattern = variantBaseName + "*" + extension; + var candidates = sourceDir.EnumerateFiles(searchPattern); + var anyIncludedForLogical = false; + + foreach (var candidateFile in candidates) { - taskContext.AddDebugMessage($"{UiSymbols.Package} Preparing self-contained Windows App SDK runtime..."); + if (includeFile != null && !includeFile(candidateFile)) + { + continue; + } - var winAppSDKDeploymentDir = await PrepareRuntimeForPackagingAsync(stagingDir, dotNetPackageList, taskContext, cancellationToken); + var candidateNameWithoutExtension = Path.GetFileNameWithoutExtension(candidateFile.Name); + if (!IsMrtVariantName(variantBaseName, candidateNameWithoutExtension)) + { + continue; + } - // Add WindowsAppSDK.manifest to existing manifest - var resolvedDeploymentDir = Path.Combine(winAppSDKDeploymentDir.FullName, "..", "extracted"); - var windowsAppSDKManifestPath = new FileInfo(Path.Combine(resolvedDeploymentDir, "AppxManifest.xml")); - await EmbedActivationManifestToExeAsync(executablePath, winAppSDKDeploymentDir, windowsAppSDKManifestPath, dotNetPackageList, taskContext, cancellationToken); - } + var relativeDir = Path.GetDirectoryName(relativeFilePath); + var candidateRelativePath = string.IsNullOrEmpty(relativeDir) + ? candidateFile.Name + : Path.Combine(relativeDir, candidateFile.Name); - await CreateMsixPackageFromFolderAsync(stagingDir, outputMsixPath, taskContext, cancellationToken); + expandedFilesByRelativePath[candidateRelativePath] = candidateFile; + anyIncludedForLogical = true; + } - // Handle certificate generation and signing - if (autoSign) + if (!anyIncludedForLogical && logicalSourceFile.Exists && (includeFile == null || includeFile(logicalSourceFile))) { - await SignMsixPackageAsync(outputFolder, certificatePassword, generateDevCert, installDevCert, finalPackageName, extractedPublisher, outputMsixPath, certificatePath, resolvedManifestPath, taskContext, cancellationToken); + expandedFilesByRelativePath[relativeFilePath] = logicalSourceFile; + } + else if (!anyIncludedForLogical && !logicalSourceFile.Exists) + { + taskContext?.AddDebugMessage($"{UiSymbols.Warning} Referenced file not found (no MRT variants): {logicalSourceFile}"); } } - catch (Exception ex) + + return [.. expandedFilesByRelativePath + .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase) + .Select(pair => (pair.Value, pair.Key))]; + } + + private static List<(FileInfo SourceFile, string RelativePath)> GetExpandedManifestReferencedFiles( + FileInfo manifestPath, + TaskContext taskContext) + { + var manifestDir = manifestPath.Directory; + if (manifestDir == null) { - throw new InvalidOperationException($"Failed to create MSIX package: {ex.Message}", ex); + taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest directory not found for: {manifestPath}"); + return []; } - finally + + taskContext.AddDebugMessage($"{UiSymbols.Note} Reading manifest: {manifestPath}"); + + var assetReferences = ManifestService.ExtractAssetReferencesFromManifest(manifestPath, taskContext); + var referencedFiles = assetReferences.Select(a => a.RelativePath); + return ExpandManifestReferencedFiles(manifestDir, referencedFiles, taskContext); + } + + /// + /// Generates a PRI file from the configuration + /// + /// Path to the package directory + /// Path to PRI config file (default: packageDir/priconfig.xml) + /// Output path for PRI file (default: packageDir/resources.pri) + /// Cancellation token + /// List of resource files that were processed + public async Task> GeneratePriFileAsync(DirectoryInfo packageDir, TaskContext taskContext, FileInfo? configPath = null, FileInfo? outputPath = null, CancellationToken cancellationToken = default) + { + if (!packageDir.Exists) { - // Clean up the staging directory - try + throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); + } + + var priConfigPath = configPath ?? new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); + var priOutputPath = outputPath ?? new FileInfo(Path.Combine(packageDir.FullName, "resources.pri")); + + if (!priConfigPath.Exists) + { + throw new FileNotFoundException($"PRI configuration file not found: {priConfigPath}"); + } + + var arguments = $@"new /pr ""{Path.TrimEndingDirectorySeparator(packageDir.FullName)}"" /cf ""{priConfigPath.FullName}"" /of ""{priOutputPath.FullName}"" /o"; + + taskContext.AddDebugMessage("Generating PRI file..."); + + try + { + var (stdout, stderr) = await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + // Parse the output to extract resource files + var resourceFiles = new List(); + var lines = stdout.Replace("\0", "").Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) { - if (stagingDir.Exists) + // Look for lines that match the pattern "Resource File: *" + const string resourceFileStr = "Resource File: "; + if (line.StartsWith(resourceFileStr, StringComparison.OrdinalIgnoreCase)) { - stagingDir.Delete(recursive: true); - taskContext.AddDebugMessage($"{UiSymbols.Note} Cleaned up staging directory"); + var fileName = line[resourceFileStr.Length..].Trim(); + if (!string.IsNullOrEmpty(fileName)) + { + resourceFiles.Add(new FileInfo(Path.Combine(packageDir.FullName, fileName))); + } } } - catch + + taskContext.AddDebugMessage($"PRI file generated: {priOutputPath}"); + if (resourceFiles.Count > 0) { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not clean up staging directory: {stagingDir.FullName}"); + taskContext.AddDebugMessage($"Processed {resourceFiles.Count} resource files"); } + + return resourceFiles; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to generate PRI file: {ex.Message}", ex); + } + } + + /// + /// Resolves $placeholder$ tokens in manifest content. Handles $targetnametoken$ and $targetentrypoint$. + /// If the Executable attribute contains a placeholder and no --executable is provided, + /// attempts to infer by searching for .exe files in the input folder. + /// + private static string ResolveManifestPlaceholders(string manifestContent, string? executable, DirectoryInfo inputFolder, TaskContext taskContext) + { + // Check if manifest contains any placeholders at all + if (!PlaceholderHelper.ContainsPlaceholders(manifestContent)) + { + return manifestContent; + } + + var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Determine the executable name for $targetnametoken$ + if (!string.IsNullOrWhiteSpace(executable)) + { + // --executable was provided explicitly + var nameWithoutExtension = Path.GetFileNameWithoutExtension(executable); + replacements[PlaceholderHelper.TargetNameToken] = nameWithoutExtension; + + // Also replace the Executable attribute value if it contains a placeholder + var execMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); + if (execMatch.Success && PlaceholderHelper.ContainsPlaceholders(execMatch.Groups[1].Value)) + { + manifestContent = AppxPackageApplicationExecutableAssignmentRegex().Replace( + manifestContent, $"${{1}}\"{executable}\""); + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Using specified executable: {executable}"); + } + else + { + // Check if the Executable attribute in the manifest has a placeholder + var execMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); + if (execMatch.Success && PlaceholderHelper.ContainsPlaceholders(execMatch.Groups[1].Value)) + { + // Try to auto-infer by finding .exe files in the input folder root + var exeFiles = inputFolder.Exists + ? inputFolder.GetFiles("*.exe", SearchOption.TopDirectoryOnly) + .Where(f => !string.Equals(f.Name, "createdump.exe", StringComparison.OrdinalIgnoreCase)) + .ToArray() + : []; + + if (exeFiles.Length == 1) + { + var inferredExe = exeFiles[0].Name; + var nameWithoutExtension = Path.GetFileNameWithoutExtension(inferredExe); + replacements[PlaceholderHelper.TargetNameToken] = nameWithoutExtension; + + manifestContent = AppxPackageApplicationExecutableAssignmentRegex().Replace( + manifestContent, $"${{1}}\"{inferredExe}\""); + + taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-inferred executable: {inferredExe}"); + } + else + { + var count = exeFiles.Length == 0 ? "no" : "multiple"; + throw new InvalidOperationException( + $"The manifest contains a placeholder for the executable but {count} .exe files were found in the input folder. " + + "Edit the manifest to specify the executable or use --executable to specify the relative path to the exe."); + } + } + } + + // Apply all placeholder replacements + manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent, replacements); + + // Sanity check: ensure no unresolved placeholders remain + PlaceholderHelper.ThrowIfUnresolvedPlaceholders(manifestContent); + + return manifestContent; + } + + /// + /// Resolves <Resource Language="x-generate"/> in the manifest by replacing it + /// with concrete language tags. Languages are extracted from the existing resources.pri + /// in the input folder; falls back to en-US when no PRI or no language qualifiers are found. + /// + private async Task ResolveResourceLanguageXGenerateAsync( + string manifestContent, + DirectoryInfo inputFolder, + TaskContext taskContext, + CancellationToken cancellationToken) + { + if (!ContainsXGenerateLanguage(manifestContent)) + { + return manifestContent; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Detected — resolving to concrete language(s)"); + + var languages = new List(); + + // Try to extract languages from existing resources.pri + var priFile = new FileInfo(Path.Combine(inputFolder.FullName, "resources.pri")); + if (priFile.Exists) + { + languages = await ExtractLanguagesFromPriAsync(priFile, taskContext, cancellationToken); + } + + if (languages.Count == 0) + { + languages.Add("en-US"); + taskContext.AddDebugMessage($"{UiSymbols.Note} No language qualifiers found in PRI — defaulting to en-US"); + } + else + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Resolved resource languages from PRI: {string.Join(", ", languages)}"); + } + + return ReplaceXGenerateLanguage(manifestContent, languages); + } + + /// + /// Returns true if the manifest contains a <Resource Language="x-generate"/> element. + /// + internal static bool ContainsXGenerateLanguage(string manifestContent) + { + return ResourceLanguageXGenerateBlockRegex().IsMatch(manifestContent); + } + + /// + /// Replaces the <Resources><Resource Language="x-generate"/></Resources> + /// block with concrete <Resource Language="..."/> entries for each specified language. + /// + internal static string ReplaceXGenerateLanguage(string manifestContent, IList languages) + { + var indent = " "; + var resourceEntries = string.Join(Environment.NewLine, languages.Select(lang => $"{indent}{indent}")); + var replacement = $"{indent}{Environment.NewLine}{resourceEntries}{Environment.NewLine}{indent}"; + + return ResourceLanguageXGenerateBlockRegex().Replace(manifestContent, replacement); + } + + /// + /// Extracts language qualifiers from a PRI file using makepri dump. + /// Returns a distinct, sorted list of BCP-47 language tags found in the PRI resource map. + /// + private async Task> ExtractLanguagesFromPriAsync( + FileInfo priFile, + TaskContext taskContext, + CancellationToken cancellationToken) + { + try + { + var dumpOutputFile = Path.Combine(Path.GetTempPath(), $"winapp-pri-dump-{Guid.NewGuid():N}.xml"); + var arguments = $@"dump /if ""{priFile.FullName}"" /of ""{dumpOutputFile}"" /o"; + + await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + if (!File.Exists(dumpOutputFile)) + { + return []; + } + + try + { + var dumpContent = await File.ReadAllTextAsync(dumpOutputFile, cancellationToken); + + // Extract language qualifiers from Candidate elements: + // or multi-qualifier like "Language-en-US, Scale-200" + var languages = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in PriDumpLanguageQualifierRegex().Matches(dumpContent)) + { + languages.Add(match.Groups[1].Value); + } + + return languages.OrderBy(l => l, StringComparer.OrdinalIgnoreCase).ToList(); + } + finally + { + File.Delete(dumpOutputFile); + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to extract languages from PRI: {ex.Message}"); + return []; + } + } + + /// + /// Creates an MSIX package from a prepared package directory + /// + /// Install certificate to machine + /// Publisher name for certificate generation (default: extracted from manifest) + /// Path to the manifest file (optional) + /// Enable self-contained deployment + /// Cancellation token + /// Result containing the MSIX path and signing status + public async Task CreateMsixPackageAsync( + DirectoryInfo inputFolder, + FileSystemInfo? outputPath, + TaskContext taskContext, + string? packageName = null, + bool skipPri = false, + bool autoSign = false, + FileInfo? certificatePath = null, + string certificatePassword = "password", + bool generateDevCert = false, + bool installDevCert = false, + string? publisher = null, + FileInfo? manifestPath = null, + bool selfContained = false, + string? executable = null, + CancellationToken cancellationToken = default) + { + // Validate input folder and manifest + if (!inputFolder.Exists) + { + throw new DirectoryNotFoundException($"Input folder not found: {inputFolder}"); + } + + // Warn if the input folder contains .pfx certificate files, which are likely + // development certificates that should not be included in the package payload. + var pfxFiles = inputFolder.EnumerateFiles("*.pfx", SearchOption.AllDirectories).ToList(); + if (pfxFiles.Count > 0) + { + foreach (var pfxFile in pfxFiles) + { + var relativePath = Path.GetRelativePath(inputFolder.FullName, pfxFile.FullName); + taskContext.AddStatusMessage($"{UiSymbols.Warning} PFX certificate file found in input folder: {relativePath}. Consider removing it before packaging."); + } + } + + // Determine manifest path based on priority: + // 1. Use provided manifestPath parameter + // 2. Check for appxmanifest.xml or package.appxmanifest in input folder + // 3. Check for appxmanifest.xml or package.appxmanifest in current directory + FileInfo resolvedManifestPath; + if (manifestPath != null) + { + resolvedManifestPath = manifestPath; + taskContext.AddDebugMessage($"{UiSymbols.Note} Using specified manifest: {resolvedManifestPath}"); + } + else + { + var resolvedFromSearch = FindManifestInDirectory(new DirectoryInfo(inputFolder.FullName)) + ?? FindManifestInDirectory(new DirectoryInfo(currentDirectoryProvider.GetCurrentDirectory())); + + if (resolvedFromSearch != null) + { + resolvedManifestPath = resolvedFromSearch; + taskContext.AddDebugMessage($"{UiSymbols.Note} Using manifest: {resolvedManifestPath}"); + } + else + { + throw new FileNotFoundException($"Manifest file not found. Searched for appxmanifest.xml and package.appxmanifest in: input folder ({inputFolder.FullName}), current directory ({currentDirectoryProvider.GetCurrentDirectory()})"); + } + } + + if (!resolvedManifestPath.Exists) + { + throw new FileNotFoundException($"Manifest file not found: {resolvedManifestPath}"); + } + + // Determine package name and publisher + var finalPackageName = packageName; + var extractedPublisher = publisher; + string? extractedVersion = null; + + var manifestContent = await File.ReadAllTextAsync(resolvedManifestPath.FullName, Encoding.UTF8, cancellationToken); + + // Resolve $placeholder$ tokens in the manifest + manifestContent = ResolveManifestPlaceholders(manifestContent, executable, inputFolder, taskContext); + + // Resolve with concrete language(s) from PRI + manifestContent = await ResolveResourceLanguageXGenerateAsync(manifestContent, inputFolder, taskContext, cancellationToken); + + // Update manifest content to ensure it's either referencing Windows App SDK or is self-contained + // Fetch dotnet package list once for all downstream operations + var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); + + manifestContent = await UpdateAppxManifestContentAsync(manifestContent, null, null, sparse: false, selfContained: selfContained, dotNetPackageList, taskContext, cancellationToken); + + try + { + if (string.IsNullOrWhiteSpace(finalPackageName)) + { + var nameMatch = AppxPackageIdentityNameRegex().Match(manifestContent); + finalPackageName = nameMatch.Success ? nameMatch.Groups[1].Value : "Package"; + } + + if (string.IsNullOrWhiteSpace(extractedPublisher)) + { + var publisherMatch = AppxPackageIdentityPublisherRegex().Match(manifestContent); + extractedPublisher = publisherMatch.Success ? publisherMatch.Groups[1].Value : null; + } + + if (string.IsNullOrWhiteSpace(extractedVersion)) + { + var versionMatch = AppxPackageIdentityVersionRegex().Match(manifestContent); + extractedVersion = versionMatch.Success ? versionMatch.Groups[1].Value : null; + } + } + catch + { + finalPackageName ??= "Package"; + } + + var executableMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); + + // If manifest Identity lacks ProcessorArchitecture, detect it from the executable PE header. + // If it already has one, warn when it doesn't match the actual executable. + string? packageArch = null; + if (executableMatch.Success) + { + var exePath = Path.Combine(inputFolder.FullName, executableMatch.Groups[1].Value); + var detectedArch = DetectPeArchitecture(exePath); + + var existingArchMatch = AppxPackageIdentityArchitectureValueRegex().Match(manifestContent); + if (!existingArchMatch.Success) + { + if (detectedArch != null) + { + manifestContent = IdentityElementRegex().Replace(manifestContent, m => + { + var tag = m.Value; + // Handle both self-closing () and open () tags + var insertPos = tag.EndsWith("/>") ? tag.Length - 2 : tag.Length - 1; + return tag.Insert(insertPos, $@" ProcessorArchitecture=""{detectedArch}"""); + }); + taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-detected ProcessorArchitecture: {detectedArch}"); + packageArch = detectedArch; + } + } + else + { + packageArch = existingArchMatch.Groups[1].Value; + if (detectedArch != null) + { + var manifestArch = existingArchMatch.Groups[1].Value; + if (!string.Equals(manifestArch, detectedArch, StringComparison.OrdinalIgnoreCase) + && !string.Equals(manifestArch, "neutral", StringComparison.OrdinalIgnoreCase)) + { + taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest ProcessorArchitecture is '{manifestArch}' but the executable is {detectedArch}. This will likely cause runtime failures."); + } + } + } + } + + // Clean the resolved package name to ensure it meets MSIX schema requirements + finalPackageName = ManifestService.CleanPackageName(finalPackageName); + + var defaultMsixFileName = (packageArch, extractedVersion) switch + { + (not null, not null) when !string.IsNullOrWhiteSpace(extractedVersion) => $"{finalPackageName}_{extractedVersion}_{packageArch}.msix", + (null, not null) when !string.IsNullOrWhiteSpace(extractedVersion) => $"{finalPackageName}_{extractedVersion}.msix", + (not null, _) => $"{finalPackageName}_{packageArch}.msix", + _ => $"{finalPackageName}.msix" + }; + + FileInfo outputMsixPath; + DirectoryInfo outputFolder; + if (outputPath == null) + { + outputFolder = currentDirectoryProvider.GetCurrentDirectoryInfo(); + outputMsixPath = new FileInfo(Path.Combine(outputFolder.FullName, defaultMsixFileName)); + } + else + { + if (Path.HasExtension(outputPath.Name) && string.Equals(Path.GetExtension(outputPath.Name), ".msix", StringComparison.OrdinalIgnoreCase)) + { + outputMsixPath = new FileInfo(outputPath.FullName); + outputFolder = outputMsixPath.Directory!; + } + else + { + outputFolder = new DirectoryInfo(outputPath.FullName); + outputMsixPath = new FileInfo(Path.Combine(outputPath.FullName, defaultMsixFileName)); + } + } + + // Ensure output folder exists + if (!outputFolder.Exists) + { + outputFolder.Create(); + } + + // Create a temporary staging directory so we never modify the original input folder. + // All packaging operations (manifest updates, asset copies, PRI generation, self-contained + // runtime bundling) happen in this staging copy. The original target folder stays untouched. + var stagingDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"winapp-package-{Guid.NewGuid():N}")); + stagingDir.Create(); + + taskContext.AddDebugMessage($"{UiSymbols.Note} Created staging directory: {stagingDir.FullName}"); + + try + { + // Copy input folder contents to staging directory + CopyDirectoryRecursive(inputFolder, stagingDir); + taskContext.AddDebugMessage($"{UiSymbols.Files} Copied input folder to staging directory"); + + // Write the updated manifest into the staging directory + var updatedManifestPath = Path.Combine(stagingDir.FullName, "appxmanifest.xml"); + await File.WriteAllTextAsync(updatedManifestPath, manifestContent, Encoding.UTF8, cancellationToken); + + // Resolve executable path relative to the staging directory + FileInfo? executablePath = executableMatch.Success ? new FileInfo(Path.Combine(stagingDir.FullName, executableMatch.Groups[1].Value)) : null; + + // Pre-compute expanded manifest resources from the original manifest + var manifestIsOutsideInputFolder = !inputFolder.FullName.TrimEnd(Path.DirectorySeparatorChar) + .Equals(resolvedManifestPath.Directory!.FullName.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase); + + // If manifest is outside input folder, copy its referenced assets into the staging directory + if (manifestIsOutsideInputFolder) + { + var externalAssets = GetExpandedManifestReferencedFiles(resolvedManifestPath, taskContext); + CopyAllAssets(externalAssets, stagingDir, taskContext); + } + + taskContext.AddDebugMessage($"Creating MSIX package from staging: {stagingDir.FullName}"); + taskContext.AddDebugMessage($"Output: {outputMsixPath.FullName}"); + + // Generate PRI files if not skipped and no existing PRI from the build output + var existingPri = new FileInfo(Path.Combine(stagingDir.FullName, "resources.pri")); + if (!skipPri && !existingPri.Exists) + { + taskContext.AddDebugMessage("Generating PRI configuration and files..."); + + // Expand manifest-referenced files from the staging manifest so that + // assets from both the input folder and external manifest are discovered. + var stagingManifest = new FileInfo(Path.Combine(stagingDir.FullName, "appxmanifest.xml")); + var priExpandedFiles = GetExpandedManifestReferencedFiles(stagingManifest, taskContext); + var priResourceCandidates = priExpandedFiles.Select(file => file.RelativePath); + await CreatePriConfigAsync( + stagingDir, + taskContext, + precomputedPriResourceCandidates: priResourceCandidates, + cancellationToken: cancellationToken); + var resourceFiles = await GeneratePriFileAsync(stagingDir, taskContext, cancellationToken: cancellationToken); + if (resourceFiles.Count > 0 && logger.IsEnabled(LogLevel.Debug)) + { + taskContext.AddDebugMessage($"Resource files included in PRI:"); + await taskContext.AddSubTaskAsync("Pri Resources", async (taskContext, cancellationToken) => + { + foreach (var resourceFile in resourceFiles) + { + taskContext.AddDebugMessage(resourceFile.ToString()); + } + return Task.FromResult(0); + }, cancellationToken); + } + } + else if (!skipPri && existingPri.Exists) + { + taskContext.AddDebugMessage("Skipping PRI generation — existing resources.pri found in input folder"); + } + + // Handle self-contained deployment if requested + if (selfContained && executablePath != null) + { + taskContext.AddDebugMessage($"{UiSymbols.Package} Preparing self-contained Windows App SDK runtime..."); + + var winAppSDKDeploymentDir = await PrepareRuntimeForPackagingAsync(stagingDir, dotNetPackageList, taskContext, cancellationToken); + + // Add WindowsAppSDK.manifest to existing manifest + var resolvedDeploymentDir = Path.Combine(winAppSDKDeploymentDir.FullName, "..", "extracted"); + var windowsAppSDKManifestPath = new FileInfo(Path.Combine(resolvedDeploymentDir, "AppxManifest.xml")); + await EmbedActivationManifestToExeAsync(executablePath, winAppSDKDeploymentDir, windowsAppSDKManifestPath, dotNetPackageList, taskContext, cancellationToken); + } + + await CreateMsixPackageFromFolderAsync(stagingDir, outputMsixPath, taskContext, cancellationToken); + + // Handle certificate generation and signing + if (autoSign) + { + await SignMsixPackageAsync(outputFolder, certificatePassword, generateDevCert, installDevCert, finalPackageName, extractedPublisher, outputMsixPath, certificatePath, resolvedManifestPath, taskContext, cancellationToken); + } + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to create MSIX package: {ex.Message}", ex); + } + finally + { + // Clean up the staging directory + try + { + if (stagingDir.Exists) + { + stagingDir.Delete(recursive: true); + taskContext.AddDebugMessage($"{UiSymbols.Note} Cleaned up staging directory"); + } + } + catch + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not clean up staging directory: {stagingDir.FullName}"); + } + } + + taskContext.AddDebugMessage($"MSIX package created successfully: {outputMsixPath}"); + if (autoSign) + { + taskContext.AddDebugMessage("Package has been signed"); + } + + return new CreateMsixPackageResult(outputMsixPath, autoSign); + } + + private async Task EmbedActivationManifestToExeAsync(FileInfo exePath, DirectoryInfo winAppSDKDeploymentDir, FileInfo windowsAppSDKAppXManifestPath, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + // Use applicationLocation for DLL content (where runtime files were copied by PrepareRuntimeForPackagingAsync) + var exeDir = exePath.Directory!; + + taskContext.AddDebugMessage($"{UiSymbols.Note} Generating activation manifest from: {windowsAppSDKAppXManifestPath}"); + taskContext.AddDebugMessage($"{UiSymbols.Package} Using DLL content from: {winAppSDKDeploymentDir}"); + + // Create a temporary manifest file + var tempManifestPath = new FileInfo(Path.Combine(exeDir.FullName, "WindowsAppSDK_temp.manifest")); + + try + { + // Build the entire manifest in memory, then write to disk once + var sb = new StringBuilder(); + sb.AppendLine(""); + sb.AppendLine(""); + + // Collect all AppX manifests (main package + component fragments) and their DLLs + (var packageDependencies, _) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); + if (packageDependencies == null || packageDependencies.Count == 0) + { + throw new InvalidOperationException("No Windows SDK packages found. Please install the Windows SDK or Windows App SDK."); + } + + var architecture = WorkspaceSetupService.GetSystemArchitecture(); + IEnumerable appxFragments = GetComponents(packageDependencies); + + // Combine all manifests: main AppxManifest.xml (Package root) + fragments (Fragment root) + var allManifests = new List { windowsAppSDKAppXManifestPath }; + allManifests.AddRange(appxFragments); + + // Combine all DLL file names from deployment dir and fragment native dirs + var allDllFiles = new List(winAppSDKDeploymentDir.EnumerateFiles("*.dll").Select(di => di.Name)); + allDllFiles.AddRange(appxFragments + .Select(fragment => Path.Combine(fragment.DirectoryName!, $"win-{architecture}\\native")) + .Where(Directory.Exists) + .SelectMany(dir => Directory.EnumerateFiles(dir, "*.dll")) + .Select(Path.GetFileName)!); + + // Single pass: process all AppX manifests (auto-detects Package vs Fragment root) + AppendAppManifestFromAppx( + sb, + redirectDlls: false, + inDllFiles: allDllFiles, + inAppxManifests: allManifests); + + // Phase 3: Discover and register third-party WinRT components (e.g., Win2D, WebView2) + // These packages ship .winmd files + native DLLs but no package.appxfragment + await AppendThirdPartyWinRTManifestEntriesAsync( + sb, architecture, dotNetPackageList, taskContext, cancellationToken); + + sb.AppendLine(""); + + // Single write to disk + await File.WriteAllTextAsync( + tempManifestPath.FullName, + sb.ToString(), + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + cancellationToken); + + // Use mt.exe to merge manifests + await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); + } + finally + { + TryDeleteFile(tempManifestPath); + } + } + + private IEnumerable GetComponents(Dictionary packageDependencies) + { + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + + // Find appx fragments in the NuGet global cache (lowercase-id/version/ layout) + var appxFragments = packageDependencies + .Select(package => new FileInfo(Path.Combine(nugetCacheDir.FullName, package.Key.ToLowerInvariant(), package.Value, "runtimes-framework", "package.appxfragment"))) + .Where(f => f.Exists); + return appxFragments; + } + + /// + /// Collects all user NuGet packages from winapp.yaml or .csproj. + /// Returns the full package dictionary (name → version) for WinRT component scanning. + /// + private async Task> GetAllUserPackagesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var packages = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Path 1: Try winapp.yaml + if (configService.Exists()) + { + var config = configService.Load(); + foreach (var pkg in config.Packages) + { + packages.TryAdd(pkg.Name, pkg.Version); + } + } + else + { + // Path 2: Try .csproj via `dotnet list package --format json` (cached) + try + { + var allPackages = dotNetPackageList?.Projects? + .SelectMany(p => p.Frameworks ?? []) + .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); + + if (allPackages != null) + { + foreach (var pkg in allPackages) + { + if (!string.IsNullOrEmpty(pkg.Id) && !string.IsNullOrEmpty(pkg.ResolvedVersion)) + { + packages.TryAdd(pkg.Id, pkg.ResolvedVersion); + } + } + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not retrieve package list from .csproj: {ex.Message}"); + } + } + + return packages; + } + + private async Task FetchDotNetPackageListAsync(CancellationToken cancellationToken) + { + var cwd = new DirectoryInfo(currentDirectoryProvider.GetCurrentDirectory()); + var csprojFiles = dotNetService.FindCsproj(cwd); + var csproj = csprojFiles.Count > 0 ? csprojFiles[0] : null; + if (csproj == null) + { + return null; + } + + return await dotNetService.GetPackageListAsync(csproj, cancellationToken: cancellationToken); + } + + /// + /// Discovers third-party WinRT components and appends their activatable class + /// entries to the in-memory SxS manifest (for self-contained deployment). + /// + private async Task AppendThirdPartyWinRTManifestEntriesAsync( + StringBuilder sb, + string architecture, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); + if (allPackages.Count == 0) + { + return; + } + + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + + // DiscoverWinRTComponents filters out packages that have a package.appxfragment + // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. + // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 + // are transitive WinAppSDK deps but need their own InProcessServer entries. + var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); + if (components.Count == 0) + { + return; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found {components.Count} third-party WinRT component(s) to register"); + + // Build a set of DLL names already registered in the manifest (from WinAppSDK fragments) + // so we can do exact-name dedup instead of substring matching. + var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in SxsFileNameRegex().Matches(sb.ToString())) + { + registeredDlls.Add(match.Groups[1].Value); + } + + foreach (var component in components) + { + var classes = winmdService.GetActivatableClasses(component.WinmdPath); + if (classes.Count == 0) + { + continue; + } + + // Skip components whose DLL is already in the manifest (from WinAppSDK fragments + // or a previous iteration) to avoid duplicate activatableClass entries. + if (!registeredDlls.Add(component.ImplementationDll)) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); + continue; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Registering {classes.Count} activatable class(es) from {component.ImplementationDll}"); + + sb.AppendLine($" "); + foreach (var className in classes) + { + sb.AppendLine($" "); + } + sb.AppendLine(" "); + } + } + + /// + /// Discovers third-party WinRT components and generates InProcessServer + /// extension entries for AppxManifest.xml (for packaged apps). + /// + private async Task AddThirdPartyWinRTExtensionsToAppxManifestAsync( + string manifestContent, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); + if (allPackages.Count == 0) + { + return manifestContent; + } + + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + var architecture = WorkspaceSetupService.GetSystemArchitecture(); + + // DiscoverWinRTComponents filters out packages that have a package.appxfragment + // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. + // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 + // are transitive WinAppSDK deps but need their own InProcessServer entries. + var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); + if (components.Count == 0) + { + return manifestContent; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Adding InProcessServer entries for {components.Count} third-party WinRT component(s)"); + + // Build a set of DLL names already registered in the manifest + // so we can do exact-name dedup instead of substring matching. + var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in AppxManifestPathElementRegex().Matches(manifestContent)) + { + registeredDlls.Add(match.Groups[1].Value); + } + + var extensionsSb = new StringBuilder(); + foreach (var component in components) + { + var classes = winmdService.GetActivatableClasses(component.WinmdPath); + if (classes.Count == 0) + { + continue; + } + + // Skip components whose DLL is already in the manifest or in entries we've already generated + if (!registeredDlls.Add(component.ImplementationDll)) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); + continue; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Adding {classes.Count} activatable class(es) for {component.ImplementationDll}"); + + extensionsSb.AppendLine(@" "); + extensionsSb.AppendLine(@" "); + extensionsSb.AppendLine($@" {component.ImplementationDll}"); + foreach (var className in classes) + { + extensionsSb.AppendLine($@" "); + } + extensionsSb.AppendLine(@" "); + extensionsSb.AppendLine(@" "); + } + + if (extensionsSb.Length == 0) + { + return manifestContent; + } + + return InsertPackageLevelExtensions(manifestContent, extensionsSb.ToString()); + } + + /// + /// Inserts Package-level extension entries (e.g. InProcessServer) into a manifest string. + /// Correctly distinguishes Package-level <Extensions> from Application-level ones. + /// + internal static string InsertPackageLevelExtensions(string manifestContent, string extensionEntries) + { + // IMPORTANT: These are Package-level extensions (e.g. windows.activatableClass.inProcessServer), + // NOT Application-level extensions. We must find a Package-level block + // (after ), not an Application-level one (inside ). + var extensionsCloseTag = ""; + var applicationsCloseTag = ""; + var applicationsCloseIndex = manifestContent.IndexOf(applicationsCloseTag, StringComparison.OrdinalIgnoreCase); + + // Look for AFTER — that's the Package-level one + var extensionsCloseIndex = applicationsCloseIndex >= 0 + ? manifestContent.IndexOf(extensionsCloseTag, applicationsCloseIndex, StringComparison.OrdinalIgnoreCase) + : -1; + + if (extensionsCloseIndex >= 0) + { + // Insert before the Package-level + return manifestContent.Insert(extensionsCloseIndex, extensionEntries); + } + + // No Package-level block exists — create one before + var packageCloseTag = ""; + var packageCloseIndex = manifestContent.LastIndexOf(packageCloseTag, StringComparison.OrdinalIgnoreCase); + if (packageCloseIndex >= 0) + { + var extensionsBlock = $" \n{extensionEntries} \n"; + return manifestContent.Insert(packageCloseIndex, extensionsBlock); + } + + return manifestContent; + } + + /// + /// Generates Win32 SxS manifest entries from AppX manifests (Package or Fragment format). + /// Auto-detects the root element name (Package vs Fragment) per document. + /// + /// StringBuilder to append manifest entries to + /// Whether to redirect DLLs to %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% + /// List of DLL file names to track + /// List of paths to the input AppX manifest files or fragments + internal static void AppendAppManifestFromAppx( + StringBuilder sb, + bool redirectDlls, + IEnumerable inDllFiles, + IEnumerable inAppxManifests) + { + var dllFileFormat = redirectDlls ? + @" " : + @" "; + + var dllFiles = inDllFiles.ToList(); + var hasPackageManifest = false; + + foreach (var inAppxManifest in inAppxManifests) + { + XmlDocument doc = new(); + doc.Load(inAppxManifest.FullName); + + // Auto-detect root element name (Package or Fragment) + var prefix = doc.DocumentElement?.LocalName ?? "Package"; + var isPackage = prefix == "Package"; + if (isPackage) + { + hasPackageManifest = true; + } + + var nsmgr = new XmlNamespaceManager(doc.NameTable); + nsmgr.AddNamespace("m", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); + // Add InProcessServer elements to the generated appxmanifest + var xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:InProcessServer"; + XmlNodeList? inProcessServers = doc.SelectNodes(xQuery, nsmgr); + if (inProcessServers != null) + { + foreach (XmlNode winRTFactory in inProcessServers) + { + var dllFileNode = winRTFactory.SelectSingleNode("./m:Path", nsmgr); + if (dllFileNode == null) + { + continue; + } + + var dllFile = dllFileNode.InnerText; + var typesNames = winRTFactory.SelectNodes("./m:ActivatableClass", nsmgr)?.OfType(); + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(); + if (typesNames != null) + { + foreach (var typeNode in typesNames) + { + var attribs = typeNode.Attributes?.OfType().ToArray(); + var typeName = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "ActivatableClassId") + ?.InnerText; + var xmlEntryFormat = + @" "; + sb.AppendFormat(xmlEntryFormat, typeName); + sb.AppendLine(); + dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); + } + } + sb.AppendLine(@" "); + } + } + + // Only for Package manifests with redirect + if (isPackage && redirectDlls) + { + foreach (var dllFile in dllFiles) + { + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(@""); + } + } + // Add ProxyStub elements to the generated appxmanifest + dllFiles = [.. inDllFiles]; + + xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:ProxyStub"; + var inProcessProxystubs = doc.SelectNodes(xQuery, nsmgr); + if (inProcessProxystubs != null) + { + foreach (XmlNode proxystub in inProcessProxystubs) + { + var classIDAdded = false; + + var dllFileNode = proxystub.SelectSingleNode("./m:Path", nsmgr); + var dllFile = dllFileNode?.InnerText; + // exclude PushNotificationsLongRunningTask, which requires the Singleton (which is unavailable for self-contained apps) + // exclude Widgets entries unless/until they have been tested and verified by the Widgets team + if (dllFile == null || dllFile == "PushNotificationsLongRunningTask.ProxyStub.dll" || dllFile == "Microsoft.Windows.Widgets.dll") + { + continue; + } + var typesNamesForProxy = proxystub.SelectNodes("./m:Interface", nsmgr)?.OfType(); + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(); + if (typesNamesForProxy != null) + { + foreach (var typeNode in typesNamesForProxy) + { + if (!classIDAdded) + { + var classIdAttribute = proxystub.Attributes?.OfType().ToArray(); + var classID = classIdAttribute + ?.OfType() + ?.SingleOrDefault(x => x.Name == "ClassId") + ?.InnerText; + + if (classID != null) + { + var xmlEntryFormat = @" "; + sb.AppendFormat(xmlEntryFormat, classID); + classIDAdded = true; + } + } + var attribs = typeNode.Attributes?.OfType().ToArray(); + var typeID = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "InterfaceId") + ?.InnerText; + var typeNames = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "Name") + ?.InnerText; + var xmlEntryFormatForStubs = @" "; + if (typeNames != null && typeID != null) + { + sb.AppendFormat(xmlEntryFormatForStubs, typeNames, typeID); + sb.AppendLine(); + dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); + } + } + } + sb.AppendLine(@" "); + } + } + } + + if (hasPackageManifest && redirectDlls) + { + foreach (var dllFile in dllFiles) + { + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(@""); + } + } + } + + private async Task SignMsixPackageAsync(DirectoryInfo outputFolder, string certificatePassword, bool generateDevCert, bool installDevCert, string finalPackageName, string? extractedPublisher, FileInfo outputMsixPath, FileInfo? certPath, FileInfo resolvedManifestPath, TaskContext taskContext, CancellationToken cancellationToken) + { + if (certPath == null && generateDevCert) + { + if (string.IsNullOrWhiteSpace(extractedPublisher)) + { + throw new InvalidOperationException("Publisher name required for certificate generation. Provide publisher option or ensure it exists in manifest."); + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Generating certificate for publisher: {extractedPublisher}"); + + certPath = new FileInfo(Path.Combine(outputFolder.FullName, $"{finalPackageName}_cert.pfx")); + await certificateService.GenerateDevCertificateAsync(extractedPublisher, certPath, taskContext, certificatePassword, cancellationToken: cancellationToken); + } + + if (certPath == null) + { + throw new InvalidOperationException("Certificate path required for signing. Provide certificatePath or set generateDevCert to true."); + } + + // Validate that the certificate publisher matches the manifest publisher + taskContext.AddDebugMessage($"{UiSymbols.Note} Validating certificate and manifest publishers match..."); + + try + { + await CertificateService.ValidatePublisherMatchAsync(certPath, certificatePassword, resolvedManifestPath, cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Check} Certificate and manifest publishers match"); + } + catch (InvalidOperationException ex) + { + // Re-throw with the specific error message format requested + throw new InvalidOperationException(ex.Message, ex); + } + + // Install certificate if requested + if (installDevCert) + { + certificateService.InstallCertificate(certPath, certificatePassword, false, taskContext); + } + + // Sign the package + await certificateService.SignFileAsync(outputMsixPath, certPath, taskContext, certificatePassword, cancellationToken: cancellationToken); + } + + private async Task CreateMsixPackageFromFolderAsync(DirectoryInfo inputFolder, FileInfo outputMsixPath, TaskContext taskContext, CancellationToken cancellationToken) + { + // Create MSIX package + var makeappxArguments = $@"pack /o /d ""{Path.TrimEndingDirectorySeparator(inputFolder.FullName)}"" /nv /p ""{outputMsixPath.FullName}"""; + + taskContext.AddDebugMessage("Creating MSIX package..."); + + await buildToolsService.RunBuildToolAsync(new MakeAppxTool(), makeappxArguments, taskContext, cancellationToken: cancellationToken); + } + + private async Task RunMtToolAsync(string arguments, bool printErrors, TaskContext taskContext, CancellationToken cancellationToken = default) + { + // Use BuildToolsService to run mt.exe + await buildToolsService.RunBuildToolAsync(new GenericTool("mt.exe"), arguments, taskContext, printErrors, cancellationToken: cancellationToken); + } + + private static void TryDeleteFile(FileInfo path) + { + try + { + path.Refresh(); + if (path.Exists) + { + path.Delete(); + } + } + catch + { + // Ignore cleanup failures + } + } + + /// + /// Recursively copies all files and subdirectories from source to destination. + /// + private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo destination) + { + destination.Create(); + + foreach (var file in source.EnumerateFiles()) + { + file.CopyTo(Path.Combine(destination.FullName, file.Name), overwrite: true); + } + + foreach (var subDir in source.EnumerateDirectories()) + { + var destSubDir = new DirectoryInfo(Path.Combine(destination.FullName, subDir.Name)); + CopyDirectoryRecursive(subDir, destSubDir); + } + } + + /// + /// Searches for appxmanifest.xml in the project by looking for .winapp directory in parent directories + /// + /// The directory to start searching from. If null, uses current directory. + /// Path to the project's appxmanifest.xml file, or null if not found + public static FileInfo? FindProjectManifest(ICurrentDirectoryProvider currentDirectoryProvider, DirectoryInfo? startDirectory = null) + { + var directory = startDirectory ?? currentDirectoryProvider.GetCurrentDirectoryInfo(); + + while (directory != null) + { + var found = FindManifestInDirectory(directory); + if (found != null) + { + return found; + } + + directory = directory.Parent; + } + + return null; + } + + /// + /// Checks a single directory for a manifest file (appxmanifest.xml or package.appxmanifest). + /// + internal static FileInfo? FindManifestInDirectory(DirectoryInfo directory) + { + var appxManifest = new FileInfo(Path.Combine(directory.FullName, "appxmanifest.xml")); + if (appxManifest.Exists) + { + return appxManifest; + } + + var packageManifest = new FileInfo(Path.Combine(directory.FullName, "package.appxmanifest")); + if (packageManifest.Exists) + { + return packageManifest; + } + + return null; + } + + /// + /// Detects the architecture of a PE file and returns an MSIX-style architecture string: + /// "x86", "x64", "arm", "arm64", or "neutral". + /// + /// Rules: + /// - Native PE images are classified from the COFF Machine field. + /// - Managed .NET IL-only images are classified using COR flags: + /// * I386 + ILOnly + Requires32Bit => x86 + /// * I386 + ILOnly + !Requires32Bit => neutral + /// - Mixed-mode / native-hosted managed images fall back to the native Machine field. + /// + /// Returns null if the file is not a valid PE image or uses an unsupported architecture. + /// + internal static string? DetectPeArchitecture(string filePath) + { + try + { + using var stream = File.OpenRead(filePath); + using var peReader = new PEReader(stream); + + var headers = peReader.PEHeaders; + var coff = headers.CoffHeader; + var cor = headers.CorHeader; + + ushort machine = (ushort)coff.Machine; + + // Native or mixed-mode case: use the PE machine directly. + if (cor is null) + { + return MapNativeMachine(machine); + } + + CorFlags flags = cor.Flags; + bool isIlOnly = (flags & CorFlags.ILOnly) != 0; + bool requires32Bit = (flags & CorFlags.Requires32Bit) != 0; + + // Managed IL-only assemblies need special handling. + // In particular, IL-only I386 without Requires32Bit is effectively AnyCPU/neutral. + if (isIlOnly) + { + return machine switch + { + 0x014C => requires32Bit ? "x86" : "neutral", // I386 + 0x8664 => "x64", // unusual for pure IL-only, but valid to preserve + 0x01C0 => "arm", // ARM + 0x01C4 => "arm", // ARMNT + 0xAA64 => "arm64", // ARM64 + _ => null + }; + } + + // Mixed-mode / native-entry managed image: machine matters. + return MapNativeMachine(machine); + } + catch + { + return null; + } + } + + private static string? MapNativeMachine(ushort machine) => machine switch + { + 0x014C => "x86", // IMAGE_FILE_MACHINE_I386 + 0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64 + 0xAA64 => "arm64", // IMAGE_FILE_MACHINE_ARM64 + 0x01C4 => "arm", // IMAGE_FILE_MACHINE_ARMNT + 0x01C0 => "arm", // IMAGE_FILE_MACHINE_ARM + _ => null + }; + + /// Path to the original appxmanifest.xml + /// Path to the entryPoint/executable that the manifest should reference + /// Cancellation token + /// Tuple containing the debug manifest path and modified identity info + public async Task<(FileInfo debugManifestPath, MsixIdentityResult debugIdentity)> GenerateSparsePackageStructureAsync( + FileInfo originalManifestPath, + string entryPointPath, + bool keepIdentity, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken = default) + { + var winappDir = winappDirectoryService.GetLocalWinappDirectory(); + var debugDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "debug")); + + taskContext.AddDebugMessage($"{UiSymbols.Note} Creating sparse package structure in: {debugDir.FullName}"); + + // Step 1: Create debug directory, removing existing one if present + if (debugDir.Exists) + { + taskContext.AddDebugMessage($"{UiSymbols.Trash} Removing existing debug directory..."); + debugDir.Delete(recursive: true); + } + + debugDir.Create(); + taskContext.AddDebugMessage($"{UiSymbols.Folder} Created debug directory"); + + // Step 2: Parse original manifest to get identity and assets + var originalManifestContent = await File.ReadAllTextAsync(originalManifestPath.FullName, Encoding.UTF8, cancellationToken); + + // Resolve placeholders in memory (never write back to the original manifest) + if (PlaceholderHelper.ContainsPlaceholders(originalManifestContent)) + { + var nameWithoutExtension = Path.GetFileNameWithoutExtension(entryPointPath); + var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [PlaceholderHelper.TargetNameToken] = nameWithoutExtension + }; + + // Also replace the Executable attribute if it has a placeholder + var executableAttrMatch = AppxPackageApplicationExecutableRegex().Match(originalManifestContent); + if (executableAttrMatch.Success && PlaceholderHelper.ContainsPlaceholders(executableAttrMatch.Groups[1].Value)) + { + var exeName = Path.GetFileName(entryPointPath); + originalManifestContent = AppxPackageApplicationExecutableAssignmentRegex().Replace( + originalManifestContent, $"${{1}}\"{exeName}\""); + } + + originalManifestContent = PlaceholderHelper.ReplacePlaceholders(originalManifestContent, replacements); + PlaceholderHelper.ThrowIfUnresolvedPlaceholders(originalManifestContent); + + taskContext.AddDebugMessage($"{UiSymbols.Note} Resolved manifest placeholders for debug identity"); + } + + var originalIdentity = ParseAppxManifestAsync(originalManifestContent); + + // Step 3: Create debug identity (optionally with ".debug" suffix) + var debugIdentity = keepIdentity ? originalIdentity : CreateDebugIdentity(originalIdentity); + + // Step 4: Modify manifest for sparse packaging and debug identity + var debugManifestContent = await UpdateAppxManifestContentAsync( + originalManifestContent, + debugIdentity, + entryPointPath, + sparse: true, + selfContained: false, + dotNetPackageList, + taskContext, + cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Note} Modified manifest for sparse packaging and debug identity"); + + // Step 5: Write debug manifest + var debugManifestPath = new FileInfo(Path.Combine(debugDir.FullName, "appxmanifest.xml")); + await File.WriteAllTextAsync(debugManifestPath.FullName, debugManifestContent, Encoding.UTF8, cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Files} Created debug manifest: {debugManifestPath.FullName}"); + + // Step 6: Copy all assets + var entryPointDir = Path.GetDirectoryName(entryPointPath); + if (!string.IsNullOrEmpty(entryPointDir)) + { + var entryPointDirInfo = new DirectoryInfo(entryPointDir); + var originalManifestDir = originalManifestPath.DirectoryName; + + if (!string.Equals(originalManifestDir, entryPointDirInfo.FullName, StringComparison.OrdinalIgnoreCase)) + { + var expandedFiles = GetExpandedManifestReferencedFiles(originalManifestPath, taskContext); + CopyAllAssets(expandedFiles, entryPointDirInfo, taskContext); + } + else + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Manifest directory and target directory are the same, skipping assets copy"); + } + } + + return (debugManifestPath, debugIdentity); + } + + /// + /// Creates a debug version of the identity by appending ".debug" to package name and application ID + /// + private static MsixIdentityResult CreateDebugIdentity(MsixIdentityResult originalIdentity) + { + var debugPackageName = originalIdentity.PackageName.EndsWith(".debug") + ? originalIdentity.PackageName + : $"{originalIdentity.PackageName}.debug"; + + var debugApplicationId = originalIdentity.ApplicationId.EndsWith(".debug") + ? originalIdentity.ApplicationId + : $"{originalIdentity.ApplicationId}.debug"; + + return new MsixIdentityResult(debugPackageName, originalIdentity.Publisher, debugApplicationId); + } + + /// + /// Updates the manifest identity, application ID, and executable path for sparse packaging + /// + private async Task UpdateAppxManifestContentAsync( + string originalAppxManifestContent, + MsixIdentityResult? identity, + string? entryPointPath, + bool sparse, + bool selfContained, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var modifiedContent = originalAppxManifestContent; + + if (identity != null) + { + // Replace package identity attributes + modifiedContent = AppxPackageIdentityNameAssignmentRegex().Replace(modifiedContent, $@"$1""{identity.PackageName}"""); + + // Replace application ID + modifiedContent = AppxApplicationIdAssignmentRegex().Replace(modifiedContent, $@"$1""{identity.ApplicationId}"""); + } + + if (entryPointPath != null) + { + // Replace executable path with relative path from package root + var entryPointDir = Path.GetDirectoryName(entryPointPath); + var workingDir = string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir; + string relativeExecutablePath; + + try + { + // Calculate relative path from the working directory (package root) to the executable + relativeExecutablePath = Path.GetRelativePath(workingDir, entryPointPath); + + // Ensure we use forward slashes for consistency in manifest + relativeExecutablePath = relativeExecutablePath.Replace('\\', '/'); + } + catch + { + // Fallback to just the filename if relative path calculation fails + relativeExecutablePath = Path.GetFileName(entryPointPath); + } + + modifiedContent = AppxPackageApplicationExecutableAssignmentRegex().Replace(modifiedContent, $@"$1""{relativeExecutablePath}"""); + } + + bool isExe = Path.HasExtension(entryPointPath) && string.Equals(Path.GetExtension(entryPointPath), ".exe", StringComparison.OrdinalIgnoreCase); + + // Only apply sparse packaging modifications if sparse is true + if (sparse) + { + // Add required namespaces for sparse packaging + if (!modifiedContent.Contains("xmlns:uap10")) + { + modifiedContent = AppxPackageElementOpenTagRegex().Replace(modifiedContent, @"$1 xmlns:uap10=""http://schemas.microsoft.com/appx/manifest/uap/windows10/10""$2"); + } + + if (!modifiedContent.Contains("xmlns:desktop6")) + { + modifiedContent = AppxPackageOpenTagRegex().Replace(modifiedContent, @"$1 xmlns:desktop6=""http://schemas.microsoft.com/appx/manifest/desktop/windows10/6""$2"); + } + + // Add sparse package properties + if (!modifiedContent.Contains("")) + { + modifiedContent = AppxPackagePropertiesCloseTagRegex().Replace(modifiedContent, @" true + disabled +$1"); + } + + // Ensure Application has sparse packaging attributes + if (!modifiedContent.Contains("uap10:TrustLevel") && isExe) + { + modifiedContent = AppxApplicationOpenTagRegex().Replace(modifiedContent, @"$1 uap10:TrustLevel=""mediumIL"" uap10:RuntimeBehavior=""packagedClassicApp""$2"); + } + + // Remove EntryPoint if present (not needed for sparse packages) + modifiedContent = AppxPackageEntryPointRegex().Replace(modifiedContent, ""); + + // Add AppListEntry="none" to VisualElements if not present + if (!modifiedContent.Contains("AppListEntry")) + { + modifiedContent = AppxPackageVisualElementsOpenTagRegex().Replace(modifiedContent, @"$1 AppListEntry=""none""$2"); + } + + // Add sparse-specific capabilities if not present + if (!modifiedContent.Contains("unvirtualizedResources")) + { + modifiedContent = AppxPackageRunFullTrustCapabilityRegex().Replace(modifiedContent, @"$1 + + "); + } + } + + // Update or insert Windows App SDK dependency (skip for self-contained packages) + if (!selfContained && (entryPointPath == null || isExe)) + { + modifiedContent = await UpdateWindowsAppSdkDependencyAsync(modifiedContent, dotNetPackageList, taskContext, cancellationToken); + } + + // Add InProcessServer entries for third-party WinRT components (e.g., Win2D, WebView2) + // In self-contained mode, activation entries go in the SxS manifest embedded in the exe, + // so we skip them here to avoid duplication. + if (!selfContained) + { + modifiedContent = await AddThirdPartyWinRTExtensionsToAppxManifestAsync(modifiedContent, dotNetPackageList, taskContext, cancellationToken); + } + + // Stamp build metadata with CLI version + modifiedContent = AddBuildMetadata(modifiedContent); + + return modifiedContent; + } + + /// + /// Adds or updates build:Metadata in the manifest with the CLI tool name and version. + /// Inserts the build namespace and IgnorableNamespaces entry if not already present. + /// + internal static string AddBuildMetadata(string manifestContent) + { + var version = VersionHelper.GetVersionString(); + + // Add xmlns:build namespace to if not present + if (!manifestContent.Contains("xmlns:build")) + { + manifestContent = BuildMetadataPackageOpenTagRegex().Replace(manifestContent, + @"$1 xmlns:build=""http://schemas.microsoft.com/developer/appx/2015/build""$2"); + } + + // Add 'build' to IgnorableNamespaces if not already listed + if (!BuildMetadataIgnorableNamespacesCheckRegex().IsMatch(manifestContent)) + { + if (manifestContent.Contains("IgnorableNamespaces")) + { + // Append 'build' to the existing IgnorableNamespaces value + manifestContent = BuildMetadataIgnorableNamespacesAssignRegex().Replace(manifestContent, + @"$1 build"""); + } + else + { + // No IgnorableNamespaces attribute exists — add one to + manifestContent = BuildMetadataPackageOpenTagRegex().Replace(manifestContent, + @"$1 IgnorableNamespaces=""build""$2"); + } + } + + var buildItemEntry = $@""; + + if (manifestContent.Contains(" + manifestContent = BuildMetadataPackageCloseTagRegex().Replace(manifestContent, + $"\n$1\n$1 {buildItemEntry}\n$1\n$2"); + } + + return manifestContent; + } + + /// + /// Updates or inserts the Windows App SDK dependency in the manifest + /// + /// The manifest content to modify + /// The modified manifest content + private async Task UpdateWindowsAppSdkDependencyAsync(string manifestContent, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + // Get the Windows App SDK version from the locked winapp.yaml config + var winAppSdkInfo = await GetWindowsAppSdkDependencyInfoAsync(dotNetPackageList, taskContext, cancellationToken); + + if (winAppSdkInfo == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not determine Windows App SDK version, skipping dependency update"); + return manifestContent; + } + + // Check if Dependencies section exists + if (!manifestContent.Contains("")) + { + // Add Dependencies section before Applications + manifestContent = AppxPackageApplicationsTagRegex().Replace(manifestContent, $@" + + +$1"); + + taskContext.AddDebugMessage($"{UiSymbols.Package} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} (v{winAppSdkInfo.MinVersion})"); + } + else + { + // Check if Windows App SDK dependency already exists + var existingDependencyPattern = @"]*Name\s*=\s*[""']Microsoft\.WindowsAppRuntime\.[^""']*[""'][^>]*>"; + var existingMatch = Regex.Match(manifestContent, existingDependencyPattern, RegexOptions.IgnoreCase); + + if (existingMatch.Success) + { + // Update existing dependency + var newDependency = $@""; + manifestContent = Regex.Replace( + manifestContent, + existingDependencyPattern, + newDependency, + RegexOptions.IgnoreCase); + + taskContext.AddDebugMessage($"{UiSymbols.Sync} Updated Windows App SDK dependency to {winAppSdkInfo.RuntimeName} v{winAppSdkInfo.MinVersion}"); + } + else + { + // Add new dependency to existing Dependencies section + manifestContent = AppxPackageDependenciesCloseTagRegex().Replace(manifestContent, $@" + $1"); + + taskContext.AddDebugMessage($"{UiSymbols.Add} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} to existing Dependencies section (v{winAppSdkInfo.MinVersion})"); + } + } + + return manifestContent; + } + + /// + /// Gets the Windows App SDK dependency information from the locked winapp.yaml config and package source + /// + /// The dependency information, or null if not found + private async Task GetWindowsAppSdkDependencyInfoAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + try + { + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); + if (msixDir == null) + { + return null; + } + + // Get the runtime package information from the MSIX inventory + var runtimeInfo = GetWindowsAppRuntimePackageInfo(taskContext, msixDir, cancellationToken); + if (runtimeInfo == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not parse Windows App Runtime package information from MSIX inventory"); + return null; + } + + return runtimeInfo; + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Error getting Windows App SDK dependency info: {ex.Message}"); + return null; + } + } + + private async Task GetRuntimeMsixDirAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + (var packageDependencies, var mainVersion) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); + if (packageDependencies == null || mainVersion == null) + { + return null; + } + + // Look for the runtime package in the package dependencies + var runtimePackage = packageDependencies.FirstOrDefault(kvp => + kvp.Key.StartsWith(BuildToolsService.WINAPP_SDK_RUNTIME_PACKAGE, StringComparison.OrdinalIgnoreCase)); + + // Create a dictionary with versions for FindWindowsAppSdkMsixDirectory + var usedVersions = new Dictionary + { + [BuildToolsService.WINAPP_SDK_PACKAGE] = mainVersion + }; + + if (runtimePackage.Key != null) + { + // For Windows App SDK 1.8+, there's a separate runtime package + var runtimeVersion = runtimePackage.Value; + usedVersions[runtimePackage.Key] = runtimeVersion; + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found runtime package: {runtimePackage.Key} v{runtimeVersion}"); + } + else + { + // For Windows App SDK 1.7 and earlier, runtime is included in the main package + taskContext.AddDebugMessage($"{UiSymbols.Note} No separate runtime package found - using main package (Windows App SDK 1.7 or earlier)"); + taskContext.AddDebugMessage($"{UiSymbols.Note} Available package dependencies: {string.Join(", ", packageDependencies.Keys)}"); } - taskContext.AddDebugMessage($"MSIX package created successfully: {outputMsixPath}"); - if (autoSign) + // Find the MSIX directory with the runtime package + var msixDir = workspaceSetupService.FindWindowsAppSdkMsixDirectory(usedVersions); + if (msixDir == null) { - taskContext.AddDebugMessage("Package has been signed"); + taskContext.AddDebugMessage($"{UiSymbols.Warning} Windows App SDK MSIX directory not found for dependent runtime package"); + return null; } - return new CreateMsixPackageResult(outputMsixPath, autoSign); + return msixDir; } - private async Task FetchDotNetPackageListAsync(CancellationToken cancellationToken) + private async Task<(Dictionary? CachedPackages, string? MainVersion)> GetWinAppSDKPackageDependenciesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) { - var cwd = new DirectoryInfo(currentDirectoryProvider.GetCurrentDirectory()); - var csprojFiles = dotNetService.FindCsproj(cwd); - var csproj = csprojFiles.Count > 0 ? csprojFiles[0] : null; - if (csproj == null) + string? mainVersion = null; + // Path 1: Try winapp.yaml (C++ / native projects) + if (configService.Exists()) { - return null; + var config = configService.Load(); + mainVersion = config.GetVersion(BuildToolsService.WINAPP_SDK_PACKAGE); } + else + { + // Path 2: Try .csproj via `dotnet list package --format json` + taskContext.AddDebugMessage($"{UiSymbols.Package} Querying NuGet package list..."); - return await dotNetService.GetPackageListAsync(csproj, cancellationToken: cancellationToken); - } + var allPackages = dotNetPackageList?.Projects? + .SelectMany(p => p.Frameworks ?? []) + .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - private async Task SignMsixPackageAsync(DirectoryInfo outputFolder, string certificatePassword, bool generateDevCert, bool installDevCert, string finalPackageName, string? extractedPublisher, FileInfo outputMsixPath, FileInfo? certPath, FileInfo resolvedManifestPath, TaskContext taskContext, CancellationToken cancellationToken) - { - if (certPath == null && generateDevCert) - { - if (string.IsNullOrWhiteSpace(extractedPublisher)) + var winAppSdkPkg = allPackages? + .FirstOrDefault(p => string.Equals(p.Id, BuildToolsService.WINAPP_SDK_PACKAGE, StringComparison.OrdinalIgnoreCase)); + + if (winAppSdkPkg != null && !string.IsNullOrEmpty(winAppSdkPkg.ResolvedVersion)) { - throw new InvalidOperationException("Publisher name required for certificate generation. Provide publisher option or ensure it exists in manifest."); + mainVersion = winAppSdkPkg.ResolvedVersion; } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Generating certificate for publisher: {extractedPublisher}"); - - certPath = new FileInfo(Path.Combine(outputFolder.FullName, $"{finalPackageName}_cert.pfx")); - await certificateService.GenerateDevCertificateAsync(extractedPublisher, certPath, taskContext, certificatePassword, cancellationToken: cancellationToken); } - if (certPath == null) + if (string.IsNullOrEmpty(mainVersion)) { - throw new InvalidOperationException("Certificate path required for signing. Provide certificatePath or set generateDevCert to true."); + taskContext.AddDebugMessage($"{UiSymbols.Warning} No {BuildToolsService.WINAPP_SDK_PACKAGE} package found in winapp.yaml"); + return (null, null); } - - // Validate that the certificate publisher matches the manifest publisher - taskContext.AddDebugMessage($"{UiSymbols.Note} Validating certificate and manifest publishers match..."); - + taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App SDK main package: v{mainVersion}"); try { - await CertificateService.ValidatePublisherMatchAsync(certPath, certificatePassword, resolvedManifestPath, cancellationToken); + // Query NuGet API for the dependency tree of this package + var deps = await nugetService.GetPackageDependenciesAsync(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion, cancellationToken); - taskContext.AddDebugMessage($"{UiSymbols.Check} Certificate and manifest publishers match"); - } - catch (InvalidOperationException ex) - { - // Re-throw with the specific error message format requested - throw new InvalidOperationException(ex.Message, ex); - } + // Include the main package itself in the result + deps.TryAdd(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion); - // Install certificate if requested - if (installDevCert) + return (deps, mainVersion); + } + catch (Exception ex) { - certificateService.InstallCertificate(certPath, certificatePassword, false, taskContext); + taskContext.AddDebugMessage($"{UiSymbols.Warning} {BuildToolsService.WINAPP_SDK_PACKAGE} v{mainVersion} not found in package source: {ex.Message}"); } - // Sign the package - await certificateService.SignFileAsync(outputMsixPath, certPath, taskContext, certificatePassword, cancellationToken: cancellationToken); + return (null, null); } - private async Task CreateMsixPackageFromFolderAsync(DirectoryInfo inputFolder, FileInfo outputMsixPath, TaskContext taskContext, CancellationToken cancellationToken) + /// + /// Parses the MSIX inventory file to extract Windows App Runtime package information + /// + /// The MSIX directory containing the inventory file + /// Package information, or null if not found + private static WindowsAppRuntimePackageInfo? GetWindowsAppRuntimePackageInfo(TaskContext taskContext, DirectoryInfo msixDir, CancellationToken cancellationToken) { - // Create MSIX package - var makeappxArguments = $@"pack /o /d ""{Path.TrimEndingDirectorySeparator(inputFolder.FullName)}"" /nv /p ""{outputMsixPath.FullName}"""; + try + { + // Use the shared inventory parsing logic (synchronous version) + var packageEntries = WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken).GetAwaiter().GetResult(); - taskContext.AddDebugMessage("Creating MSIX package..."); + if (packageEntries == null || packageEntries.Count == 0) + { + return null; + } - await buildToolsService.RunBuildToolAsync(new MakeAppxTool(), makeappxArguments, taskContext, cancellationToken: cancellationToken); - } + // Look for the Windows App Runtime main package (not Framework packages) + var mainRuntimeEntry = packageEntries + .FirstOrDefault(entry => entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && + !entry.PackageIdentity.Contains("Framework")); - private static void TryDeleteFile(FileInfo path) - { - try - { - path.Refresh(); - if (path.Exists) + if (mainRuntimeEntry != null) { - path.Delete(); + // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) + var identityParts = mainRuntimeEntry.PackageIdentity.Split('_'); + if (identityParts.Length >= 2) + { + var runtimeName = identityParts[0]; + var version = identityParts[1]; + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App Runtime: {runtimeName} v{version}"); + + return new WindowsAppRuntimePackageInfo + { + RuntimeName = runtimeName, + MinVersion = version + }; + } } + + taskContext.AddDebugMessage($"{UiSymbols.Note} No Windows App Runtime main package found in inventory"); + taskContext.AddDebugMessage($"{UiSymbols.Note} Available packages: {string.Join(", ", packageEntries.Select(e => e.PackageIdentity))}"); + + return null; } - catch + catch (Exception ex) { - // Ignore cleanup failures + taskContext.AddDebugMessage($"{UiSymbols.Note} Error parsing MSIX inventory: {ex.Message}"); + return null; } } /// - /// Recursively copies all files and subdirectories from source to destination, - /// skipping any top-level directories whose names appear in . + /// Copies files referenced in the manifest to the target directory. /// - private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo destination, HashSet? excludedDirectories = null) + private static void CopyAllAssets(List<(FileInfo SourceFile, string RelativePath)> expandedFiles, DirectoryInfo targetDir, TaskContext taskContext) { - destination.Create(); + var filesCopied = 0; - foreach (var file in source.EnumerateFiles()) + foreach (var (sourceFile, relativePath) in expandedFiles) { - file.CopyTo(Path.Combine(destination.FullName, file.Name), overwrite: true); + var targetFile = new FileInfo(Path.Combine(targetDir.FullName, relativePath)); + + targetFile.Directory?.Create(); + sourceFile.CopyTo(targetFile.FullName, overwrite: true); + filesCopied++; + + taskContext.AddDebugMessage($"{UiSymbols.Files} Copied manifest resource: {relativePath}"); } - foreach (var subDir in source.EnumerateDirectories()) - { - if (excludedDirectories != null && excludedDirectories.Contains(subDir.Name)) - { - continue; - } + taskContext.AddDebugMessage($"{UiSymbols.Note} Copied {filesCopied} files to target directory"); + } - var destSubDir = new DirectoryInfo(Path.Combine(destination.FullName, subDir.Name)); - CopyDirectoryRecursive(subDir, destSubDir); + // ltr / rtl + private static bool IsLayoutDirectionQualifier(string token) + { + return token.Equals("ltr", StringComparison.OrdinalIgnoreCase) || + token.Equals("rtl", StringComparison.OrdinalIgnoreCase); + } + + private static bool IsSingleQualifierToken(string token) + { + if (string.IsNullOrEmpty(token)) + { + return false; } + + return LanguageQualifierRegex().IsMatch(token) + || ScaleQualifierRegex().IsMatch(token) + || ThemeQualifierRegex().IsMatch(token) + || ContrastQualifierRegex().IsMatch(token) + || DxFeatureLevelQualifierRegex().IsMatch(token) + || DeviceFamilyQualifierRegex().IsMatch(token) + || HomeRegionQualifierRegex().IsMatch(token) + || ConfigurationQualifierRegex().IsMatch(token) + || TargetSizeQualifierRegex().IsMatch(token) + || AltFormQualifierRegex().IsMatch(token) + || IsLayoutDirectionQualifier(token); } - /// - /// Searches for appxmanifest.xml in the project by looking for .winapp directory in parent directories - /// - /// The directory to start searching from. If null, uses current directory. - /// Path to the project's appxmanifest.xml file, or null if not found - public static FileInfo? FindProjectManifest(ICurrentDirectoryProvider currentDirectoryProvider, DirectoryInfo? startDirectory = null) + private static bool IsQualifierToken(string token) { - var directory = startDirectory ?? currentDirectoryProvider.GetCurrentDirectoryInfo(); + if (string.IsNullOrEmpty(token)) + { + return false; + } - while (directory != null) + var parts = token.Split('_'); + + foreach (var part in parts) { - var found = FindManifestInDirectory(directory); - if (found != null) + if (!IsSingleQualifierToken(part)) { - return found; + return false; } - - directory = directory.Parent; } - return null; + return true; } /// - /// Checks a single directory for a manifest file (appxmanifest.xml or package.appxmanifest). + /// Returns true if is a valid MRT + /// variant of the logical base name (dots allowed in base name). /// - internal static FileInfo? FindManifestInDirectory(DirectoryInfo directory) + private static bool IsMrtVariantName(string logicalBaseName, string candidateNameWithoutExtension) { - var appxManifest = new FileInfo(Path.Combine(directory.FullName, "appxmanifest.xml")); - if (appxManifest.Exists) + if (string.IsNullOrWhiteSpace(logicalBaseName) || string.IsNullOrWhiteSpace(candidateNameWithoutExtension)) { - return appxManifest; + return false; } - var packageManifest = new FileInfo(Path.Combine(directory.FullName, "package.appxmanifest")); - if (packageManifest.Exists) + // Split by '.'; "Logo.scale-200.theme-dark" -> ["Logo", "scale-200", "theme-dark"] + var parts = candidateNameWithoutExtension.Split('.'); + + if (parts.Length == 0) { - return packageManifest; + return false; } - return null; + // First token must match logical base name (case-insensitive) + if (!parts[0].Equals(logicalBaseName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // No qualifiers -> exact logical name, valid + if (parts.Length == 1) + { + return true; + } + + // All remaining tokens must be valid MRT qualifiers + for (int i = 1; i < parts.Length; i++) + { + if (!IsQualifierToken(parts[i])) + { + return false; + } + } + + return true; } /// - /// Updates the manifest identity, application ID, and executable path for sparse packaging + /// For a qualified logical name like "Logo.scale-100" or "Logo.targetsize-24_altform-unplated", + /// returns the unqualified asset family base (e.g. "Logo"). + /// If the name has no trailing qualifier tokens, returns the original name unchanged. /// - private async Task<(string Content, string? DetectedArchitecture)> UpdateAppxManifestContentAsync( - string originalAppxManifestContent, - MsixIdentityResult? identity, - string? entryPointPath, - string? exePath, - bool sparse, - bool selfContained, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) + private static string GetMrtVariantBaseName(string logicalBaseName) { - var doc = AppxManifestDocument.Parse(originalAppxManifestContent); + ArgumentException.ThrowIfNullOrWhiteSpace(logicalBaseName); - if (identity != null) + var parts = logicalBaseName.Split('.'); + if (parts.Length <= 1) { - doc.IdentityName = identity.PackageName; - doc.ApplicationId = identity.ApplicationId; + return logicalBaseName; } - if (entryPointPath != null) + // Find the earliest segment where every remaining segment is a valid qualifier token. + for (int i = 1; i < parts.Length; i++) { - var entryPointDir = Path.GetDirectoryName(entryPointPath); - var workingDir = string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir; - string relativeExecutablePath; - - try + var allRemainingAreQualifiers = true; + for (int j = i; j < parts.Length; j++) { - relativeExecutablePath = Path.GetRelativePath(workingDir, entryPointPath); - relativeExecutablePath = relativeExecutablePath.Replace('\\', '/'); + if (!IsQualifierToken(parts[j])) + { + allRemainingAreQualifiers = false; + break; + } } - catch + + if (allRemainingAreQualifiers) { - relativeExecutablePath = Path.GetFileName(entryPointPath); + return string.Join('.', parts[..i]); } - - doc.ApplicationExecutable = relativeExecutablePath; } - bool isExe = Path.HasExtension(entryPointPath) && string.Equals(Path.GetExtension(entryPointPath), ".exe", StringComparison.OrdinalIgnoreCase); + return logicalBaseName; + } - if (sparse) + /// + /// Checks if a package with the given name exists and unregisters it if found + /// + /// The name of the package to check and unregister + /// Cancellation token + /// True if package was found and unregistered, false if no package was found + public async Task UnregisterExistingPackageAsync(string packageName, TaskContext taskContext, CancellationToken cancellationToken = default) + { + taskContext.AddDebugMessage($"{UiSymbols.Trash} Checking for existing package..."); + + try { - // Add required namespaces for sparse packaging - doc.EnsureNamespace("uap10", AppxManifestDocument.Uap10Ns); - doc.EnsureNamespace("desktop6", AppxManifestDocument.Desktop6Ns); + // First check if package exists + var checkCommand = $"Get-AppxPackage -Name '{packageName}'"; + var (_, checkResult, _) = await powerShellService.RunCommandAsync(checkCommand, taskContext, cancellationToken: cancellationToken); - // Add sparse package properties - var properties = doc.Document.Root?.Element(AppxManifestDocument.DefaultNs + "Properties"); - if (properties != null && properties.Element(AppxManifestDocument.Uap10Ns + "AllowExternalContent") == null) + if (!string.IsNullOrWhiteSpace(checkResult)) { - properties.Add(new XElement(AppxManifestDocument.Uap10Ns + "AllowExternalContent", "true")); - properties.Add(new XElement(AppxManifestDocument.Desktop6Ns + "RegistryWriteVirtualization", "disabled")); - } + // Package exists, remove it + taskContext.AddDebugMessage($"{UiSymbols.Package} Found existing package '{packageName}', removing it..."); - // Ensure Application has sparse packaging attributes - var app = doc.GetFirstApplicationElement(); - if (app != null && isExe && app.Attribute(AppxManifestDocument.Uap10Ns + "TrustLevel") == null) + var unregisterCommand = $"Get-AppxPackage -Name '{packageName}' | Remove-AppxPackage"; + await powerShellService.RunCommandAsync(unregisterCommand, taskContext, cancellationToken: cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Check} Existing package unregistered successfully"); + return true; + } + else { - app.SetAttributeValue(AppxManifestDocument.Uap10Ns + "TrustLevel", "mediumIL"); - app.SetAttributeValue(AppxManifestDocument.Uap10Ns + "RuntimeBehavior", "packagedClassicApp"); + // No package found + taskContext.AddDebugMessage($"{UiSymbols.Note} No existing package found"); + return false; } + } + catch (Exception ex) + { + // If check fails, package likely doesn't exist or we don't have permission + taskContext.AddDebugMessage($"{UiSymbols.Note} Could not check for existing package: {ex.Message}"); + return false; + } + } - // Remove EntryPoint if present (not needed for sparse packages) - doc.ApplicationEntryPoint = null; + /// + /// Registers a sparse package with external location using Add-AppxPackage + /// + /// Path to the appxmanifest.xml file + /// External location path (typically the working directory) + /// Cancellation token + public async Task RegisterSparsePackageAsync(FileInfo manifestPath, DirectoryInfo externalLocation, TaskContext taskContext, CancellationToken cancellationToken = default) + { + taskContext.AddDebugMessage($"{UiSymbols.Clipboard} Registering sparse package with external location..."); - // Add AppListEntry="none" to VisualElements if not present - var ve = doc.GetVisualElements(); - if (ve != null && ve.Attribute("AppListEntry") == null) - { - ve.SetAttributeValue("AppListEntry", "none"); - } + var registerCommand = $"Add-AppxPackage -Path '{manifestPath.FullName}' -ExternalLocation '{externalLocation.FullName}' -Register -ForceUpdateFromAnyVersion"; - // Add sparse-specific capabilities if not present - var capsElement = doc.GetCapabilitiesElement(); - bool hasUnvirtualizedResources = capsElement?.Elements() - .Any(e => string.Equals(e.Attribute("Name")?.Value, "unvirtualizedResources", StringComparison.OrdinalIgnoreCase)) == true; - if (!hasUnvirtualizedResources) + try + { + var (exitCode, output, error) = await powerShellService.RunCommandAsync(registerCommand, taskContext, cancellationToken: cancellationToken); + + if (exitCode != 0) { - doc.EnsureCapability("unvirtualizedResources", AppxManifestDocument.RescapNs); - doc.EnsureCapability("allowElevation", AppxManifestDocument.RescapNs); + if (string.IsNullOrWhiteSpace(error)) + { + throw new InvalidOperationException($"PowerShell command failed with exit code {exitCode}"); + } + + throw new InvalidOperationException(error.Trim()); } + + taskContext.AddDebugMessage($"{UiSymbols.Check} Sparse package registered successfully"); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to register sparse package: {ex.Message}", ex); } + } + + public async Task RegisterLooseLayoutPackageAsync(FileInfo manifestPath, TaskContext taskContext, CancellationToken cancellationToken = default) + { + taskContext.AddDebugMessage($"{UiSymbols.Clipboard} Registering loose layout package..."); - // Convert to string for remaining string-based operations - var modifiedContent = doc.ToXml(); + var registerCommand = $"Add-AppxPackage -Register '{manifestPath.FullName}'"; - // Update or insert Windows App SDK dependency (skip for self-contained packages) - if (!selfContained && (entryPointPath == null || isExe)) + try { - modifiedContent = await UpdateWindowsAppSdkDependencyAsync(modifiedContent, dotNetPackageList, taskContext, cancellationToken); - } + var (exitCode, output, _) = await powerShellService.RunCommandAsync(registerCommand, taskContext, cancellationToken: cancellationToken); - // Add InProcessServer entries for third-party WinRT components (e.g., Win2D, WebView2) - // In self-contained mode, activation entries go in the SxS manifest embedded in the exe, - // so we skip them here to avoid duplication. - if (!selfContained) + if (exitCode != 0) + { + if (string.IsNullOrWhiteSpace(output)) + { + throw new InvalidOperationException($"PowerShell command failed with exit code {exitCode}"); + } + + throw new InvalidOperationException(output.Trim()); + } + + taskContext.AddDebugMessage($"{UiSymbols.Check} Package registered successfully"); + } + catch (Exception ex) { - modifiedContent = await AddThirdPartyWinRTExtensionsToAppxManifestAsync(modifiedContent, dotNetPackageList, taskContext, cancellationToken); + throw new InvalidOperationException($"Failed to register package: {ex.Message}", ex); } + } - // Stamp build metadata with CLI version - modifiedContent = AddBuildMetadata(modifiedContent); + private static readonly string[] patterns = new[] { "*.dll", "workloads*.json", "restartAgent.exe", "map.html", "*.mui", "*.png", "*.winmd", "*.xaml", "*.xbf", "*.pri" }; - // Auto-detect ProcessorArchitecture from the executable PE header if not already set. - // Without this, ARM64 Windows resolves framework dependencies to ARM64 DLLs even for x64 apps. - string? detectedArch = null; - if (exePath != null) + private static async Task CopyRuntimeFilesAsync(DirectoryInfo extractedDir, DirectoryInfo deploymentDir, TaskContext taskContext, CancellationToken cancellationToken) + { + await taskContext.AddSubTaskAsync("Copying Runtime Files", (taskContext, cancellationToken) => { - (modifiedContent, detectedArch) = AutoDetectProcessorArchitecture(modifiedContent, exePath, taskContext); - } + foreach (var pattern in patterns) + { + var files = extractedDir.GetFiles(pattern, SearchOption.AllDirectories); + foreach (var file in files) + { + var relativePath = Path.GetRelativePath(extractedDir.FullName, file.FullName); + var destPath = Path.Combine(deploymentDir.FullName, relativePath); + + // Create destination directory if needed + var destDir = Path.GetDirectoryName(destPath); + if (!string.IsNullOrEmpty(destDir)) + { + Directory.CreateDirectory(destDir); + } - return (modifiedContent, detectedArch); + file.CopyTo(destPath, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Files} {relativePath}"); + } + } + + return Task.FromResult(0); + }, cancellationToken); } /// - /// Adds or updates build:Metadata in the manifest with the CLI tool name and version. - /// Inserts the build namespace and IgnorableNamespaces entry if not already present. + /// Prepares Windows App SDK runtime files for packaging into an MSIX by extracting them to the input folder /// - internal static string AddBuildMetadata(string manifestContent) + /// The folder where runtime files should be copied + /// Cancellation token + /// The path to the self-contained deployment directory + private async Task PrepareRuntimeForPackagingAsync(DirectoryInfo inputFolder, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) { - var version = VersionHelper.GetVersionString(); + var arch = WorkspaceSetupService.GetSystemArchitecture(); + + var winappDir = winappDirectoryService.GetLocalWinappDirectory(); + + // Extract runtime files using the existing method + await SetupSelfContainedAsync(winappDir, arch, taskContext, dotNetPackageList, cancellationToken); + + // Copy runtime files from .winapp/self-contained to input folder + var runtimeSourceDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "self-contained", arch, "deployment")); + + if (runtimeSourceDir.Exists) + { + // Copy files recursively to maintain directory structure + foreach (var file in runtimeSourceDir.GetFiles("*", SearchOption.AllDirectories)) + { + var relativePath = Path.GetRelativePath(runtimeSourceDir.FullName, file.FullName); + var destFile = Path.Combine(inputFolder.FullName, relativePath); + + // Create destination directory if needed + var destDir = Path.GetDirectoryName(destFile); + if (!string.IsNullOrEmpty(destDir)) + { + Directory.CreateDirectory(destDir); + } - var doc = AppxManifestDocument.Parse(manifestContent); + file.CopyTo(destFile, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Folder} Bundled runtime: {relativePath}"); + } - doc.EnsureNamespace("build", AppxManifestDocument.BuildNs); - doc.AddIgnorableNamespace("build"); - doc.SetBuildMetadata("Microsoft.WinAppCli", version); + taskContext.AddDebugMessage($"{UiSymbols.Check} Windows App SDK runtime bundled into package"); + } + else + { + throw new DirectoryNotFoundException($"Runtime files not found at {runtimeSourceDir}"); + } - return doc.ToXml(); + return runtimeSourceDir; } } diff --git a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs deleted file mode 100644 index f61b4601..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.Logging; -using Windows.Management.Deployment; - -namespace WinApp.Cli.Services; - -/// -/// Manages MSIX package registration, unregistration, and installation using -/// the Windows WinRT API directly, without -/// shelling out to PowerShell. -/// -internal sealed class PackageRegistrationService(ILogger logger) : IPackageRegistrationService -{ - // HRESULT 0x80073CFB = ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD (developer mode not enabled) - // HRESULT 0x800704EC = ERROR_ACCESS_DISABLED_BY_POLICY (group policy blocks sideloading) - private const int ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD = unchecked((int)0x80073CFB); - private const int ERROR_ACCESS_DISABLED_BY_POLICY = unchecked((int)0x800704EC); - - /// - public async Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default) - { - var manifestUri = new Uri(Path.GetFullPath(manifestPath)); - var pm = new PackageManager(); - - try - { - var result = await pm.RegisterPackageAsync( - manifestUri, - null, - DeploymentOptions.DevelopmentMode | DeploymentOptions.ForceApplicationShutdown - ).AsTask(cancellationToken); - - if (!result.IsRegistered) - { - var errorText = result.ErrorText ?? "Unknown error"; - throw new InvalidOperationException( - $"Failed to register package: {errorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); - } - - logger.LogDebug("Package registered from loose layout: {ManifestPath}", manifestPath); - } - catch (Exception ex) when (IsDeveloperModeError(ex)) - { - throw new InvalidOperationException( - "Windows Developer Mode is required to register MSIX packages from loose files. " + - "Open Settings > System > For Developers and enable Developer Mode.", ex); - } - } - - /// - public async Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default) - { - var manifestUri = new Uri(Path.GetFullPath(manifestPath)); - var externalUri = new Uri(Path.GetFullPath(externalLocation) + Path.DirectorySeparatorChar); - var pm = new PackageManager(); - - try - { - var options = new RegisterPackageOptions - { - ExternalLocationUri = externalUri, - DeveloperMode = true, - ForceUpdateFromAnyVersion = true, - }; - - var result = await pm.RegisterPackageByUriAsync( - manifestUri, - options - ).AsTask(cancellationToken); - - if (!result.IsRegistered) - { - var errorText = result.ErrorText ?? "Unknown error"; - throw new InvalidOperationException( - $"Failed to register sparse package: {errorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); - } - - logger.LogDebug("Sparse package registered: {ManifestPath} (external: {ExternalLocation})", manifestPath, externalLocation); - } - catch (Exception ex) when (IsDeveloperModeError(ex)) - { - throw new InvalidOperationException( - "Windows Developer Mode is required to register MSIX packages from loose files. " + - "Open Settings > System > For Developers and enable Developer Mode.", ex); - } - } - - /// - public async Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) - { - var pm = new PackageManager(); - - // FindPackagesForUser with name+publisher requires both to match. - // Use the single-string overload to find by family name prefix, then filter by name. - var allUserPackages = pm.FindPackagesForUser(string.Empty); - var matchingPackages = allUserPackages - .Where(p => string.Equals(p.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - if (matchingPackages.Count == 0) - { - return false; - } - - foreach (var pkg in matchingPackages) - { - var fullName = pkg.Id.FullName; - logger.LogDebug("Removing package: {PackageFullName}", fullName); - - var result = await pm.RemovePackageAsync(fullName).AsTask(cancellationToken); - - if (!string.IsNullOrEmpty(result.ErrorText)) - { - logger.LogWarning("Warning removing package {PackageFullName}: {Error}", fullName, result.ErrorText); - } - } - - return true; - } - - /// - public async Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default) - { - var packageUri = new Uri(Path.GetFullPath(packagePath)); - var pm = new PackageManager(); - - var result = await pm.AddPackageAsync( - packageUri, - null, - DeploymentOptions.ForceApplicationShutdown - ).AsTask(cancellationToken); - - if (!string.IsNullOrEmpty(result.ErrorText)) - { - throw new InvalidOperationException( - $"Failed to install package '{Path.GetFileName(packagePath)}': {result.ErrorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); - } - - logger.LogDebug("Installed package: {PackagePath}", packagePath); - } - - /// - public string? GetInstalledVersion(string packageName) - { - var pm = new PackageManager(); - // Use the single-parameter overload and filter manually. - // The (userId, name, publisher) overload rejects empty/null publisher - // because string.Empty marshals as null HSTRING in WinRT interop. - var allUserPackages = pm.FindPackagesForUser(string.Empty); - - foreach (var pkg in allUserPackages) - { - if (string.Equals(pkg.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) - { - var v = pkg.Id.Version; - return $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}"; - } - } - - return null; - } - - /// - public List FindDevPackages(string packageName) - { - var pm = new PackageManager(); - var allUserPackages = pm.FindPackagesForUser(string.Empty); - var results = new List(); - - foreach (var pkg in allUserPackages) - { - if (!string.Equals(pkg.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - string? installLocation = null; - try - { - installLocation = pkg.InstalledLocation?.Path; - } - catch - { - // InstalledLocation can throw if the path no longer exists - } - - var v = pkg.Id.Version; - results.Add(new DevPackageInfo( - FullName: pkg.Id.FullName, - Name: pkg.Id.Name, - Version: $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}", - InstallLocation: installLocation, - IsDevelopmentMode: pkg.IsDevelopmentMode)); - } - - return results; - } - - private static bool IsDeveloperModeError(Exception ex) - { - return ex.HResult == ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD - || ex.HResult == ERROR_ACCESS_DISABLED_BY_POLICY; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs deleted file mode 100644 index d7340d6d..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Reflection.PortableExecutable; - -namespace WinApp.Cli.Services; - -/// -/// Static helper for detecting executable architecture from PE headers. -/// -internal static class PeHelper -{ - /// - /// Detects the architecture of a PE file and returns an MSIX-style architecture string: - /// "x86", "x64", "arm", "arm64", or "neutral". - /// - /// Rules: - /// - Native PE images are classified from the COFF Machine field. - /// - Managed .NET IL-only images are classified using COR flags: - /// * I386 + ILOnly + Requires32Bit => x86 - /// * I386 + ILOnly + !Requires32Bit => neutral - /// - Mixed-mode / native-hosted managed images fall back to the native Machine field. - /// - /// Returns null if the file is not a valid PE image or uses an unsupported architecture. - /// - internal static string? DetectPeArchitecture(string filePath) - { - try - { - using var stream = File.OpenRead(filePath); - using var peReader = new PEReader(stream); - - var headers = peReader.PEHeaders; - var coff = headers.CoffHeader; - var cor = headers.CorHeader; - - ushort machine = (ushort)coff.Machine; - - // Native or mixed-mode case: use the PE machine directly. - if (cor is null) - { - return MapNativeMachine(machine); - } - - CorFlags flags = cor.Flags; - bool isIlOnly = (flags & CorFlags.ILOnly) != 0; - bool requires32Bit = (flags & CorFlags.Requires32Bit) != 0; - - // Managed IL-only assemblies need special handling. - // In particular, IL-only I386 without Requires32Bit is effectively AnyCPU/neutral. - if (isIlOnly) - { - return machine switch - { - 0x014C => requires32Bit ? "x86" : "neutral", // I386 - 0x8664 => "x64", // unusual for pure IL-only, but valid to preserve - 0x01C0 => "arm", // ARM - 0x01C4 => "arm", // ARMNT - 0xAA64 => "arm64", // ARM64 - _ => null - }; - } - - // Mixed-mode / native-entry managed image: machine matters. - return MapNativeMachine(machine); - } - catch - { - return null; - } - } - - private static string? MapNativeMachine(ushort machine) => machine switch - { - 0x014C => "x86", // IMAGE_FILE_MACHINE_I386 - 0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64 - 0xAA64 => "arm64", // IMAGE_FILE_MACHINE_ARM64 - 0x01C4 => "arm", // IMAGE_FILE_MACHINE_ARMNT - 0x01C0 => "arm", // IMAGE_FILE_MACHINE_ARM - _ => null - }; -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PowerShellService.cs b/src/winapp-CLI/WinApp.Cli/Services/PowerShellService.cs new file mode 100644 index 00000000..ff448f3a --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/PowerShellService.cs @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics; +using WinApp.Cli.ConsoleTasks; + +namespace WinApp.Cli.Services; + +/// +/// Service for executing PowerShell commands +/// +internal class PowerShellService : IPowerShellService +{ + /// + /// Runs a PowerShell command and returns the exit code and output + /// + /// The PowerShell command to run + /// Whether to run with elevated privileges (UAC prompt) + /// Optional dictionary of environment variables to set/override + /// Cancellation token + /// Tuple containing (exitCode, stdout, stderr) + public async Task<(int exitCode, string output, string error)> RunCommandAsync( + string command, + TaskContext taskContext, + bool elevated = false, + Dictionary? environmentVariables = null, + CancellationToken cancellationToken = default) + { + var elevatedText = elevated ? "elevated " : ""; + taskContext.AddDebugMessage($"Running {elevatedText}PowerShell: {command}"); + if (elevated) + { + taskContext.AddDebugMessage("UAC prompt may appear..."); + } + + // Build a safe, profile-less, non-interactive PowerShell invocation + // Prefix command to suppress progress records that can otherwise pollute stderr output. + var preparedCommand = $"$ProgressPreference='SilentlyContinue'; {command}"; + + static string ToEncodedCommand(string s) + { + var bytes = System.Text.Encoding.Unicode.GetBytes(s); // UTF-16LE + return Convert.ToBase64String(bytes); + } + var encoded = ToEncodedCommand(preparedCommand); + + var psi = new ProcessStartInfo + { + FileName = "powershell.exe", + Arguments = $"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand {encoded}", + UseShellExecute = elevated, // Required for elevation, must be true for Verb=runas + RedirectStandardOutput = !elevated, // Always redirect when not elevated so we can capture output + RedirectStandardError = !elevated, + RedirectStandardInput = !elevated, // close stdin so PS never waits for input + CreateNoWindow = !elevated, + WindowStyle = elevated ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden + }; + + // Apply custom environment variables if provided (only when not elevated) + // When elevated, UseShellExecute=true which doesn't support environment variables + if (!elevated) + { + if (environmentVariables is not null) + { + foreach (var kvp in environmentVariables) + { + psi.Environment[kvp.Key] = kvp.Value; + } + } + + // Always clear PSModulePath to prevent PowerShell Core module conflicts when calling Windows PowerShell + // This fixes the issue where calling powershell.exe from PowerShell Core causes module loading errors + if (!psi.Environment.ContainsKey("PSModulePath")) + { + psi.Environment["PSModulePath"] = ""; + } + } + + if (elevated) + { + psi.Verb = "runas"; // This triggers UAC elevation + } + + using var process = Process.Start(psi); + if (process == null) + { + return (-1, string.Empty, "Failed to start PowerShell process"); + } + + string stdOut = string.Empty, stdErr = string.Empty; + + if (!elevated) + { + // Read both streams concurrently to avoid deadlocks + var outTask = process.StandardOutput.ReadToEndAsync(cancellationToken); + var errTask = process.StandardError.ReadToEndAsync(cancellationToken); + + // Close stdin immediately; we won’t provide input + await process.StandardInput.FlushAsync(cancellationToken); + process.StandardInput.Close(); + + await Task.WhenAll(outTask, errTask); + stdOut = outTask.Result.Trim(); + stdErr = NormalizePowerShellErrorStream(errTask.Result); + } + + await process.WaitForExitAsync(cancellationToken); + + if (process.ExitCode != 0 && !string.IsNullOrWhiteSpace(stdErr)) + { + taskContext.AddDebugMessage($"PowerShell error: {Environment.NewLine}{stdErr.Trim()}"); + } + else if (!string.IsNullOrWhiteSpace(stdOut)) + { + taskContext.AddDebugMessage($"PowerShell output: {Environment.NewLine}{stdOut.Trim().TrimStart(Environment.NewLine).TrimEnd(Environment.NewLine)}"); + } + + // For elevated commands, exit codes may not be reliable, so we return 0 if no exception occurred + var exitCode = elevated ? (process.ExitCode == 0 ? 0 : process.ExitCode) : process.ExitCode; + + return (exitCode, stdOut, stdErr); + } + + private static string NormalizePowerShellErrorStream(string stream) + { + if (string.IsNullOrWhiteSpace(stream)) + { + return string.Empty; + } + + var trimmed = stream.Trim(); + + if (!trimmed.Contains("= 0) + { + section = section[..noteIndex]; + } + + section = StripXmlTags(section); + section = DecodeClixmlEscapes(section); + section = section.Replace("#< CLIXML", string.Empty, StringComparison.OrdinalIgnoreCase); + + var lines = section + .Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Trim()) + .Where(line => !string.IsNullOrWhiteSpace(line)); + + var normalized = string.Join(" ", lines).Trim(); + return string.IsNullOrWhiteSpace(normalized) ? trimmed : normalized; + } + + private static string StripXmlTags(string value) + { + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + var sb = new System.Text.StringBuilder(value.Length); + var inTag = false; + + foreach (var character in value) + { + if (character == '<') + { + inTag = true; + continue; + } + + if (character == '>') + { + inTag = false; + sb.Append(' '); + continue; + } + + if (!inTag) + { + sb.Append(character); + } + } + + return sb.ToString(); + } + + private static string DecodeClixmlEscapes(string value) + { + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + var sb = new System.Text.StringBuilder(value.Length); + + for (var i = 0; i < value.Length; i++) + { + if (i + 6 < value.Length + && value[i] == '_' + && (value[i + 1] == 'x' || value[i + 1] == 'X') + && IsHex(value[i + 2]) + && IsHex(value[i + 3]) + && IsHex(value[i + 4]) + && IsHex(value[i + 5]) + && value[i + 6] == '_') + { + var hex = value.AsSpan(i + 2, 4); + var codePoint = Convert.ToInt32(hex.ToString(), 16); + + sb.Append(codePoint switch + { + 0x000D => '\r', + 0x000A => '\n', + 0x0009 => ' ', + _ => (char)codePoint + }); + + i += 6; + continue; + } + + sb.Append(value[i]); + } + + return sb.ToString(); + + static bool IsHex(char c) + => (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PriService.cs b/src/winapp-CLI/WinApp.Cli/Services/PriService.cs deleted file mode 100644 index 8f3286aa..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/PriService.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Text.RegularExpressions; -using System.Xml; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; -using WinApp.Cli.Tools; - -namespace WinApp.Cli.Services; - -/// -/// Handles PRI (Package Resource Index) configuration, generation, and language extraction -/// via MakePri.exe. -/// -internal partial class PriService( - IBuildToolsService buildToolsService) : IPriService -{ - // Extracts language tag from PRI dump qualifier strings like 'Language-en-US' - [GeneratedRegex(@"qualifiers=""[^""]*Language-([a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex PriDumpLanguageQualifierRegex(); - - /// - /// Creates a PRI configuration file for the given package directory - /// - public async Task CreatePriConfigAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - IEnumerable precomputedPriResourceCandidates, - string language = "en-US", - string platformVersion = "10.0.0", - CancellationToken cancellationToken = default) - { - if (!packageDir.Exists) - { - throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); - } - - ArgumentNullException.ThrowIfNull(precomputedPriResourceCandidates); - - var resfilesPath = Path.Combine(packageDir.FullName, "pri.resfiles"); - var priResourceCandidates = precomputedPriResourceCandidates.ToList(); - - priResourceCandidates = [.. priResourceCandidates - .Where(path => MrtAssetHelper.PriIncludedExtensions.Contains(Path.GetExtension(path))) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; - - taskContext.AddDebugMessage($"PRI resource candidates discovered: {priResourceCandidates.Count}"); - - using (var writer = new StreamWriter(resfilesPath)) - { - foreach (var priFile in priResourceCandidates) - { - await writer.WriteLineAsync(priFile); - } - } - - var configPath = new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); - var arguments = $@"createconfig /cf ""{configPath}"" /dq lang-{language}_scale-200 /pv {platformVersion} /o"; - - taskContext.AddDebugMessage("Creating PRI configuration file..."); - - try - { - await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - taskContext.AddDebugMessage($"PRI configuration created: {configPath}"); - - var xmlDoc = new XmlDocument(); - xmlDoc.Load(configPath.FullName); - var resourcesNode = xmlDoc.SelectSingleNode("/resources"); - if (resourcesNode != null) - { - var indexNode = resourcesNode.SelectSingleNode("index"); - if (indexNode != null) - { - if (indexNode.Attributes?["startIndexAt"]?.Value != null) - { - // set to relative path - indexNode.Attributes["startIndexAt"]!.Value = ".\\pri.resfiles"; - } - - var resfilesIndexerNode = xmlDoc.CreateElement("indexer-config"); - var typeAttr = xmlDoc.CreateAttribute("type"); - typeAttr.Value = "resfiles"; - resfilesIndexerNode.Attributes.Append(typeAttr); - - var delimiterAttr = xmlDoc.CreateAttribute("qualifierDelimiter"); - delimiterAttr.Value = "."; - resfilesIndexerNode.Attributes.Append(delimiterAttr); - - indexNode.AppendChild(resfilesIndexerNode); - - // Ensure folder-based indexer is configured to parse qualifiers from - // both folder names and file names (e.g. targetsize-48_altform-unplated). - var folderIndexerNode = indexNode - .SelectNodes("indexer-config") - ?.OfType() - .FirstOrDefault(node => - node.Attributes?["type"]?.Value?.Equals("folder", StringComparison.OrdinalIgnoreCase) == true); - - if (folderIndexerNode?.Attributes != null) - { - var folderAttributes = folderIndexerNode.Attributes; - - var folderNameAsQualifierAttr = folderAttributes["foldernameAsQualifier"]; - if (folderNameAsQualifierAttr == null) - { - folderNameAsQualifierAttr = xmlDoc.CreateAttribute("foldernameAsQualifier"); - folderAttributes.Append(folderNameAsQualifierAttr); - } - folderNameAsQualifierAttr.Value = "true"; - - var fileNameAsQualifierAttr = folderAttributes["filenameAsQualifier"]; - if (fileNameAsQualifierAttr == null) - { - fileNameAsQualifierAttr = xmlDoc.CreateAttribute("filenameAsQualifier"); - folderAttributes.Append(fileNameAsQualifierAttr); - } - fileNameAsQualifierAttr.Value = "true"; - } - - xmlDoc.Save(configPath.FullName); - } - } - - return configPath; - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to create PRI configuration: {ex.Message}", ex); - } - } - - /// - /// Generates a PRI file from the configuration - /// - public async Task> GeneratePriFileAsync(DirectoryInfo packageDir, TaskContext taskContext, FileInfo? configPath = null, FileInfo? outputPath = null, CancellationToken cancellationToken = default) - { - if (!packageDir.Exists) - { - throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); - } - - var priConfigPath = configPath ?? new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); - var priOutputPath = outputPath ?? new FileInfo(Path.Combine(packageDir.FullName, "resources.pri")); - - if (!priConfigPath.Exists) - { - throw new FileNotFoundException($"PRI configuration file not found: {priConfigPath}"); - } - - var arguments = $@"new /pr ""{Path.TrimEndingDirectorySeparator(packageDir.FullName)}"" /cf ""{priConfigPath.FullName}"" /of ""{priOutputPath.FullName}"" /o"; - - taskContext.AddDebugMessage("Generating PRI file..."); - - try - { - var (stdout, stderr) = await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - // Parse the output to extract resource files - var resourceFiles = new List(); - var lines = stdout.Replace("\0", "").Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); - - foreach (var line in lines) - { - // Look for lines that match the pattern "Resource File: *" - const string resourceFileStr = "Resource File: "; - if (line.StartsWith(resourceFileStr, StringComparison.OrdinalIgnoreCase)) - { - var fileName = line[resourceFileStr.Length..].Trim(); - if (!string.IsNullOrEmpty(fileName)) - { - resourceFiles.Add(new FileInfo(Path.Combine(packageDir.FullName, fileName))); - } - } - } - - taskContext.AddDebugMessage($"PRI file generated: {priOutputPath}"); - if (resourceFiles.Count > 0) - { - taskContext.AddDebugMessage($"Processed {resourceFiles.Count} resource files"); - } - - return resourceFiles; - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to generate PRI file: {ex.Message}", ex); - } - } - - /// - /// Extracts language qualifiers from a PRI file using makepri dump. - /// Returns a distinct, sorted list of BCP-47 language tags found in the PRI resource map. - /// - public async Task> ExtractLanguagesFromPriAsync( - FileInfo priFile, - TaskContext taskContext, - CancellationToken cancellationToken) - { - try - { - var dumpOutputFile = Path.Combine(Path.GetTempPath(), $"winapp-pri-dump-{Guid.NewGuid():N}.xml"); - var arguments = $@"dump /if ""{priFile.FullName}"" /of ""{dumpOutputFile}"" /o"; - - await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - if (!File.Exists(dumpOutputFile)) - { - return []; - } - - try - { - var dumpContent = await File.ReadAllTextAsync(dumpOutputFile, cancellationToken); - - // Extract language qualifiers from Candidate elements: - // or multi-qualifier like "Language-en-US, Scale-200" - var languages = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in PriDumpLanguageQualifierRegex().Matches(dumpContent)) - { - languages.Add(match.Groups[1].Value); - } - - return languages.OrderBy(l => l, StringComparer.OrdinalIgnoreCase).ToList(); - } - finally - { - File.Delete(dumpOutputFile); - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to extract languages from PRI: {ex.Message}"); - return []; - } - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs b/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs index fc76c595..e4dbf0df 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/WorkspaceSetupService.cs @@ -36,7 +36,7 @@ internal class WorkspaceSetupService( IBuildToolsService buildToolsService, ICppWinrtService cppWinrtService, IPackageLayoutService packageLayoutService, - IPackageRegistrationService packageRegistrationService, + IPowerShellService powerShellService, INugetService nugetService, IManifestService manifestService, IDevModeService devModeService, @@ -1069,38 +1069,6 @@ public class MsixPackageEntry return packageEntries; } - /// - /// Reads the actual package Name and Version from the AppxManifest.xml inside an MSIX file. - /// The MSIX inventory file can have incorrect package names (e.g., the DDLM), so we read - /// the real identity directly from the package to ensure correct installation checks. - /// - private static (string? Name, string? Version) ReadMsixIdentity(string msixFilePath, TaskContext taskContext) - { - try - { - using var zip = System.IO.Compression.ZipFile.OpenRead(msixFilePath); - var manifestEntry = zip.GetEntry("AppxManifest.xml"); - if (manifestEntry == null) - { - return (null, null); - } - - using var stream = manifestEntry.Open(); - var doc = System.Xml.Linq.XDocument.Load(stream); - var identityElement = doc.Root?.Elements() - .FirstOrDefault(e => e.Name.LocalName == "Identity"); - - var name = identityElement?.Attribute("Name")?.Value; - var version = identityElement?.Attribute("Version")?.Value; - return (name, version); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Could not read identity from {Path.GetFileName(msixFilePath)}: {ex.Message}"); - return (null, null); - } - } - /// /// Installs Windows App SDK runtime MSIX packages for the current system architecture /// @@ -1119,8 +1087,8 @@ private static (string? Name, string? Version) ReadMsixIdentity(string msixFileP var msixArchDir = Path.Combine(msixDir.FullName, $"win10-{architecture}"); - // Build list of packages to evaluate - var packagesToCheck = new List<(string FilePath, string PackageName, string NewVersion, string FileName)>(); + // Build package data for PowerShell script + var packageData = new List(); foreach (var entry in packageEntries) { var msixFilePath = Path.Combine(msixArchDir, entry.FileName); @@ -1130,57 +1098,117 @@ private static (string? Name, string? Version) ReadMsixIdentity(string msixFileP continue; } - // Read the actual package identity from the MSIX's AppxManifest.xml. - // The inventory file's PackageIdentity can differ from the real installed name. - var (packageName, newVersionString) = ReadMsixIdentity(msixFilePath, taskContext); - if (packageName == null) - { - // Fallback: parse from inventory identity string - var identityParts = entry.PackageIdentity.Split('_'); - packageName = identityParts[0]; - newVersionString = identityParts.Length >= 2 ? identityParts[1] : ""; - } + // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) + var identityParts = entry.PackageIdentity.Split('_'); + var packageName = identityParts[0]; + var newVersionString = identityParts.Length >= 2 ? identityParts[1] : ""; - packagesToCheck.Add((msixFilePath, packageName, newVersionString ?? "", entry.FileName)); + packageData.Add($"@{{Path='{msixFilePath}';Identity='{entry.PackageIdentity}';Name='{packageName}';Version='{newVersionString}';FileName='{entry.FileName}'}}"); } - if (packagesToCheck.Count == 0) + if (packageData.Count == 0) { return (0, 0); } - taskContext.AddDebugMessage($"{UiSymbols.Info} Checking and installing {packagesToCheck.Count} MSIX packages"); + // Create compact PowerShell script with reusable function + var script = $@" +function Test-PackageNeedsInstall($pkg) {{ + $exactMatch = Get-AppxPackage | Where-Object {{ $_.PackageFullName -eq $pkg.Identity }} + if ($exactMatch) {{ return $false }} + + $existing = Get-AppxPackage -Name $pkg.Name -ErrorAction SilentlyContinue + if (-not $existing) {{ return $true }} + + $shouldUpgrade = $false + foreach ($p in $existing) {{ if ([version]$pkg.Version -gt [version]$p.Version) {{ $shouldUpgrade = $true; break }} }} + return $shouldUpgrade +}} + +$packages = @({string.Join(",", packageData)}) +$toInstall = @() + +foreach ($pkg in $packages) {{ + if (Test-PackageNeedsInstall $pkg) {{ + $toInstall += $pkg.Path + Write-Output ""INSTALL|$($pkg.FileName)|Will install"" + }} else {{ + Write-Output ""SKIP|$($pkg.FileName)|Already installed or newer version exists"" + }} +}} + +if ($toInstall.Count -gt 0) {{ + Write-Output ""INSTALLING|$($toInstall.Count) packages will be installed"" + foreach ($path in $toInstall) {{ + try {{ + Add-AppxPackage -Path $path -ForceApplicationShutdown -ErrorAction Stop + Write-Output ""SUCCESS|$(Split-Path $path -Leaf)|Installation successful"" + }} catch {{ + Write-Output ""ERROR|$(Split-Path $path -Leaf)|$($_.Exception.Message)"" + }} + }} +}} else {{ + Write-Output ""COMPLETE|No packages need to be installed"" +}}"; + + taskContext.AddDebugMessage($"{UiSymbols.Info} Checking and installing {packageEntries.Count} MSIX packages"); + + // Execute the batch script + var (exitCode, output, _) = await powerShellService.RunCommandAsync(script, taskContext, cancellationToken: cancellationToken); + + // Parse the output to provide user feedback + var outputLines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Trim()) + .Where(line => !string.IsNullOrEmpty(line)); var installedCount = 0; var errorCount = 0; - foreach (var (filePath, packageName, newVersion, fileName) in packagesToCheck) + foreach (var line in outputLines) { - // Check if already installed with same or newer version - var installedVersion = packageRegistrationService.GetInstalledVersion(packageName); - if (installedVersion != null) + var parts = line.Split('|', 3); + if (parts.Length < 2) { - if (Version.TryParse(installedVersion, out var existing) && - Version.TryParse(newVersion, out var incoming) && - existing >= incoming) - { - taskContext.AddDebugMessage($"{UiSymbols.Check} {fileName}: Already installed or newer version exists"); - continue; - } + continue; } - taskContext.AddDebugMessage($"{UiSymbols.Info} {fileName}: Will install"); + var action = parts[0]; + var fileName = parts[1]; + var message = parts.Length > 2 ? parts[2] : ""; - try + switch (action) { - await packageRegistrationService.InstallPackageAsync(filePath, cancellationToken); - installedCount++; - taskContext.AddDebugMessage($"{UiSymbols.Check} {fileName}: Installation successful"); - } - catch (Exception ex) - { - errorCount++; - taskContext.AddDebugMessage($"{UiSymbols.Note} {fileName}: {ex.Message}"); + case "SKIP": + taskContext.AddDebugMessage($"{UiSymbols.Check} {fileName}: {message}"); + break; + + case "INSTALL": + taskContext.AddDebugMessage($"{UiSymbols.Info} {fileName}: {message}"); + break; + + case "INSTALLING": + if (!string.IsNullOrWhiteSpace(message)) + { + taskContext.AddDebugMessage($"{UiSymbols.Info} {message}"); + } + break; + + case "SUCCESS": + installedCount++; + taskContext.AddDebugMessage($"{UiSymbols.Check} {fileName}: {message}"); + break; + + case "ERROR": + errorCount++; + taskContext.AddDebugMessage($"{UiSymbols.Note} {fileName}: {message}"); + break; + + case "COMPLETE": + if (!string.IsNullOrWhiteSpace(message)) + { + taskContext.AddDebugMessage($"{UiSymbols.Check} {message}"); + } + break; } } diff --git a/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.packaged.xml b/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.packaged.xml index 62a7c33f..5bd3e076 100644 --- a/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.packaged.xml +++ b/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.packaged.xml @@ -40,9 +40,9 @@ DisplayName="{PackageName}" Description="{Description}" BackgroundColor="transparent" - Square150x150Logo="Assets\MedTile.png" - Square44x44Logo="Assets\AppList.png"> - + Square150x150Logo="Assets\Square150x150Logo.png" + Square44x44Logo="Assets\Square44x44Logo.png"> + diff --git a/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.sparse.xml b/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.sparse.xml index 2bbcf9c0..6695eb06 100644 --- a/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.sparse.xml +++ b/src/winapp-CLI/WinApp.Cli/Templates/appxmanifest.sparse.xml @@ -42,8 +42,8 @@ DisplayName="{PackageName}" Description="{Description}" BackgroundColor="transparent" - Square150x150Logo="Assets\MedTile.png" - Square44x44Logo="Assets\AppList.png"> + Square150x150Logo="Assets\Square150x150Logo.png" + Square44x44Logo="Assets\Square44x44Logo.png"> diff --git a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj index 0a4a9753..92a069e2 100644 --- a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj +++ b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj @@ -2,7 +2,7 @@ Exe - net10.0-windows10.0.19041.0 + net10.0-windows enable enable winapp @@ -26,7 +26,7 @@ full true true - + false false @@ -54,7 +54,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/winapp-GUI/winapp-GUI.sln b/src/winapp-GUI/winapp-GUI.sln new file mode 100644 index 00000000..09648573 --- /dev/null +++ b/src/winapp-GUI/winapp-GUI.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36408.4 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winapp-GUI", "winapp-GUI\winapp-GUI.csproj", "{8C1F6AEE-36CD-4871-A2AF-97566468E684}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|ARM64.Build.0 = Debug|ARM64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|x64.ActiveCfg = Debug|x64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|x64.Build.0 = Debug|x64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|x64.Deploy.0 = Debug|x64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|x86.ActiveCfg = Debug|x86 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|x86.Build.0 = Debug|x86 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Debug|x86.Deploy.0 = Debug|x86 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|ARM64.ActiveCfg = Release|ARM64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|ARM64.Build.0 = Release|ARM64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|ARM64.Deploy.0 = Release|ARM64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|x64.ActiveCfg = Release|x64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|x64.Build.0 = Release|x64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|x64.Deploy.0 = Release|x64 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|x86.ActiveCfg = Release|x86 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|x86.Build.0 = Release|x86 + {8C1F6AEE-36CD-4871-A2AF-97566468E684}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DAC9315A-1024-4379-BB63-9DD2BB81E2C5} + EndGlobalSection +EndGlobal diff --git a/src/winapp-GUI/winapp-GUI/App.xaml b/src/winapp-GUI/winapp-GUI/App.xaml new file mode 100644 index 00000000..6ab21cd0 --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/App.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/winapp-GUI/winapp-GUI/App.xaml.cs b/src/winapp-GUI/winapp-GUI/App.xaml.cs new file mode 100644 index 00000000..e88986dd --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/App.xaml.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Microsoft.UI.Xaml.Shapes; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace winapp_GUI +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : Application + { + private Window? _window; + + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + InitializeComponent(); + } + + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + _window = new MainWindow(); + _window.Activate(); + } + } +} diff --git a/src/winapp-GUI/winapp-GUI/Assets/AppIcon.ico b/src/winapp-GUI/winapp-GUI/Assets/AppIcon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d5b858defe0c656a66aa7ca969c150e9ab7ac373 GIT binary patch literal 204629 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-100.png b/src/winapp-GUI/winapp-GUI/Assets/BadgeLogo.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f0101b8a0dfd55f7f1c766d3823f07f203337b GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9E$svykh8Km+7D465v z;uumf=k8Q*E(Qe-R_6=?7zotm(&J6T6XzcK|Zs0P~(6IQtx#0Z7 zHx5!ewh0uw>}KeFa#!*FtMd{o^I`-G)3-Z#CpKIpuG&9u6{1-oD!Mw6wYN91Dt#8d-1W@5;e33eU-t>cI5j;{{)?fBM>qf2x#PU} z`N!H%8IRpv{K$MuwB4TY}0A})yJhIHQz;6--&&6Hno-|V9xjAR*7O}j|7iO|k1|%Oc%$NbBI14-?iy0WiR6&^0Gf3qFQ1F1K zi(^Q|oVU~Oay2OMxN%qg57*l(!Bf9~Vx3l1UK5Ayx!jkHC6rONgjc|TZYM{1X z_Tg@i4prsi)}ZX1n;zE$y;Nhoi+1kvYjM)K`dxk3gb7(UQeg7mmNmDpyLzyw ze1Fo}Q7U*!lu^k{rQIvkTlCY!m0Ja72`D{&skVeQKr-Tl(Sm?Un!W7qCoXw^Nx9Z~ zVfC7A242Z7sk=Bc8#U`>rCuAV2u%t#T<3gYug-VH?!V`p!}|0&e! z03P(RynhCw^G*S4_gPt2(*hA%C+D40C=w}Pg|DkL_qz*Fpmj6Qins~T%C*PbzpT_K2hzBU^F~m9{Gu%RcwSGo zEq*+LFV!Br@v^I``MTP!9jrn?;h0Xs*S4&;>1vFt+3hDLB}{bSG*UXtu%Vg3kV61W zJk~q#S|($f;7#9#?~Hf8Rr-qy?qk_n7yi1no1t(1dCvKaiyvwv6$vD?Y+i8Eu|a~R z14QiEvisRn)+fO|N!RzRY1l1&a*s=kSb@lqOFQ?5uvkB2{^q!uMWcSb>kG+;EIoT( z|MGL=UdU2$@6)~2iS0rL%0Rmtq}`^Qvcx>tDDH58v+{$xSt*Om1JOT6f|$I@*3LWV zucN9^sCv-)M`r+&m$|#UkjO#*7NG)nJ&oxLxayRaG^{q-Jbgp`x@X2YtN$E#ZTNa( zo4NZ^udR05c|J@ExW)eI`qg!-pR=9a%2HW(H(cotXQbpN_MDy*yY7hxW-uBH#5tVT zkqC4;%cnOMU!SVOzqn|?W>NuLg94Yd5=F$VlsQj_tYjL%E9R` z_o=6>D;ygHUy6MBBJWtOFu!SD!2(m`t4#CCw*6O$TICqbrtom~wV*pOO*Zdc>Wr?* lo%r9@mB=HC2yPs#dc7Lc33rvdHvkhCgQu&X%Q~loCIA%d`jG$t literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-100.png b/src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff0f08fabc085705085375f38c3655c5d9d8105 GIT binary patch literal 8082 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ób{I6oBLhuj#C>eQ;>m$_mQ*1c3uFXQicn4$`~TwiQ5jci`dx(v-4 z>QmF5(#FKQdTr%g)gHv_oOT0GKA}qV?c#n1NWpjlB-o#M4HNReYA_j*4oFL5G&p;L$F%DhCJJ2_nJSu~u9X`!fIiAr~zjOKE zMn)H*mT`8(1|u|-sc5i9{mZ}(El=aL_f{z z+%nVTh!>5I(L^c?$~Cq={0+Op^{+aYk@u|$U5fyaKy8lHY$++OL?k(LS*}% zfw;^U{_A%Odb#ZnO`52kP~pkN5+S_@Scxr9lrR|NSl6aV;>z3p^P=l0VIFXwh4_T5 zmH=6nBhoVmgj7*hJI1pKc366zu0^(KZw(ZeM&d3KMdFg0%|bZ73V?8MAJ)s99L5f| zdRm~nTRt^W@Sn`Ysk}72iqIy^v8Jp$OQ7l48O|hDdDF}*tte?G>m+mnL3v4JbC$>)C@4T^;PAn3;l(opd<3g36a3nNZHU&5SuMmPiX51j4JI~F2BO99rZdhc zpIFZX8}?`pc%Sv@xu7<(0pDW{abopOm$;5FkTPs1hC;7sot8-EHgux;<^YrK zYIWKParLGxPF7!eT(mpYVAG)t3-M`Y+cN7HFJXU7rE9h5hDQnu>h-kFUcCUIKd)q5 zzs@oia0%L#I#qYnmjRFD^jrAoezY9O`pj4AxDWQ z#H_rLNvtf_95s!hUW@C_GUrcF&H3eHO(qGty)JI6Uj8m<5OkGdbd`+?*pW?0Pa`moVE-+u(AN;lRjx9(r%c z8-IO>T-xK`Gkblb^EIxgd$NGlTBCvyb5L|JAuFis%`eUu&vqe~S3SYrW7}KvfTgB8 zsbt#zQ52g=vKy1EGa9TE=j2pWW!=5 zd)BFHZ5(Z-Qv(0YS@f_Bq?fhTX{Hd6y~-t-cmdD?JRgKwQN)$nMi-aVQC;MlVg&0l zRhL%0pMbbQ0}V<}?s#?1e2I`2c1=s5BlVbPJ|i0b&P!vkLqlgnlB7v(6|=3k6hx|K zF2z#`Lvk8FU~on zxqO9^VHm3_s(ZSJXD0vRK)b1j%XbexYddV^RRnUXrE zVQx?OX&5@0kx(nZB6G{%%e&tz3f+X=_65wTtm*gx&s_XyVvUjch)r8awB@R)k} z0PR_tN)GW39t?pl7Mg!sDcTNX>m|_M;{o)vo}%mg8`hmJt?#+F7HM`4LefI%`8!ih z1=E~laqyjCXOvCtZStfC0UD&TFFLDwsUgv-z??U++u@A5(Blynzn_!VRwnF86+~gD ziJNf|O)Uyt$Pq8cX*r}?dh+IGd(i>RsLv-s9pf?;IM)43A5Zbq`IB?=f;MxkX&z^H z;yQ2mZHYpG>#q6&la?j~sF;&jlw3?B-x|Slmmw3&qgCUS3YlBRfYcU|^Ry|DPs3Ap z2Izv6Uj4R0eGV%>Tf^MdMl8So$?MZg6P!heV7B#%UIM~i#9tQqcV!g-dHnmv+ z+f^s^vBL1H?_;??I=V=INTj26&q8PjFW{1#*>!tx(y!Uyge0uJWt`1RkA+N4(i3Am zay`6$3iy2-P61ytKR5yC-#mxSFPuS4zXKCnGqg+PN9RJ{G8)D;tNg;$6e)(ermw|tr4wNCQX3@wSTy=af(P3}{MwX2N0Gs|OOiu+CoyG)IxZ9) zWbKxgsH|i4X57v>E|&_<FB4cW4uZMxs<7*5C1Vo)*XwLLByg8ftvbSi=rxO~3dWg#*0(8e{BkHM+~YM> zx`KJ`o8~8%**bYOvfFh$CgwJQr!sV!lC z(+QhM^%=x$CBXrWpwKhsmr#WBYwSlPf>6N&jeNj}UFQ$M5U;MLM~QU9H1{#=iVRB= zOSz^XZ%$I}76TyKf3miXNZ?klQxT6)4{+YSEo`K1d8UeTjrU_gIE`tgm_jD9AEhjOm23VXbWxX}PfCW5mb#T;RJk zuH{;L@LB*#>Lo&eYo^PdC^D?#>#pKJ2pPW+K)>WU`F*DNXaE(xZ#imfB)`R6aMqBA zgXNd`E5^yaB{&ILQEG0S=ZaK=&Cy{F21V=I>hLOB4~PgLbM1SHwd#^rC^GHZtnAQYP!4!_5%;?Gm2 zTqoMyXQ9rg9(U#^<{LH*)A+%I>k|y;z<|`YKPCbl?Ry`Z*&VD?q>5C+KWNqN=407Y z$*^m3h0Y9%i(F>DR{mI2t(_=(K?95?joh?J!-UOTFQ81;OnfX_sAI zGu;4>+s%zNI#6+t(6u8FO&ED7w^LXRhDe~qa70=;oeklFE5*W{`VFY4iLH}4mD)cR?Tyb z=`1Nh7_;7+L#v>_V7|Id6>>TFQ|ZKv|S{xD*D|elGVCF*c=w&mr}+S7e^l`I$U1q8~nTJ z)Z3%b%hs=LrdKZ~Sg9a$7s3O5cSFl@J*Rg6WM2i_r;bFHV`pXxiOq z2C+cw(R-eo))p^xbik1$(sYnyqBb)cQ%!yl5d*=^ z$`!7OhKmka8T?>>=N95@AE@{ASedk69E@{V{?S@P*Ub@87KgE}c`H$xO8Kg*>fEE7 z->#V`w{$pW^uva?&{n_N?C$u_c3~VncUl2hi;wdRp`^u}W3#%AI&XCfx!Ze_bp7fV zm7kv7=-Zjce8JZ5t?4Y68KH;AZ$v)5wy~m$zKbTCCi5QjsfmhLAYNF!t(5Y6#ir|M za;GXy#Kv(MhD5s1AP_@GpI4^J(nYgO&=yk_ysecwkPK2gs6f_ z$Pnl(v{hY~ZPu|S2lc-u{IR&^YSXl}j<`$KK$Neua=FYnZ@SFNF#J?5{HuGYp>II^ zRZ_{^Dl&c1k$QBywIJ7yZYr@Q@oGE@@xTNv$2w1j9u)t-UQX8a%Ej9{uPZr%Lj$>+H%lgo zxSUXk?dE&-Mw*wKa{&{fxd{;CaeCk!+NhJLoOA~G=JA}-_zW=A=3#ze#_nT|-K8QS zq(PX<3-`RYW)>}iTFn?vkzINLniX)L-exj^d7XVZF3NL@TrTFtjm<0tw|gPtBlniM zrT#^7EgNJ8h8L}xGo(KfA2M`5w1D&ak}C>&gHoJ3L>Mp;)IbFc#8%)eYWxA26j&!T zATaG<-1xad>pf3~JImD8|HI?B5q z50FgYXXESiRD=90r@D5`4w{XxrY(6uAj_%5 zxn%|7mP^~khI+ZRi`tN>R7AH(u_X!TdlhPXHOU6ihEJ z)7f(m>oez0Cs14Mr?!-+=h>ZIPWyGN*?1351}C~r>vV}ssSo_tYnQ_A_S4GEZ>jM* z(E07v$`_%#`@TuV_QQ=>>ccSCWbo_FY+O%oAS?VNL>Yl-4Uik5SJR_JkSNgvK*$E+HJ6==uk`PpJ?TZ^2% zNH=R-KV&OE(NZq3Bd5K+nn&2hmZueYumU4{JWA16I_k`!?XvGt!s1Bk>B zXZycV(y-PEA)&kDgI#zZVP1j2u^i>1BTde|Fxgi;9Kv7pvQ@Qq5!jC{sjz;iG(G!R z`j)(|73~5Iw7VwPlxa(7Q=;;i5vU)%9e<9YvDS?rxSm(Yvezy~UF*KgjpL z5g9;3b3TzK=TL)bn^s_J+83EReE;fpI2mnfaB+~Gokx&At(A|D@4U#bB`XdMW8Zqh z%$7%P^Vd$BI>oR=!PU|3c;pp9!Cf@?r|MOOLYpwpD2wZ%ZS&QOCg1b5)fre3^QUB? zK|XT~9gnkCvX09qtY_uM(4W}!iwH-Y22eBwcUe^te0Ymn#6U?j2H0B`)Rv>I4BkB1?3kPzskm^rG3go3611P!^$!OK8~2^N!qn<)v*_1gfI*dOz|cIHu>7=?tUOjLaa19OMOBVAD$3Rn-_XI{|N4i-m` zO~Gttz2J{(ve;%qEY9CS8N#J6Dh2)-W7wmrQ)~j^V?5qf!{nKjrEhf5579Juo@|n0 zB8%MWCWHfgmvFWl-CiNtet&$h*JgC!+(Bb?-h?9r20Kse&D5A!yjTnaEH#VRd0m*C zr>*^Ee|R{OH1Bf^(Y@QKC?NJb-K4bh@noTjhQGZ%pJHltufCd9T#=yS3O<*Q=9RL$ zZ*Tv8c?3-Lz6|y2qDGEoKN>noQc1g7_z7Vwh%pZ%|4D!GvxPf(C*5{uO}?%>#3S?5 zK5P7H9Rl!ikAVgq<{o?+**R|SS;sxas~0Z3icT|7YNKm-OQK>ax>7y(-$a9-vPXKY z{hB*_%Mxr{qWfCPB~p)^TJgKl#O34=ku=k7uhZ`M#Kl0wi=)v=BVz2#VX-D@VBClrh(g3Df1?w5OE89+e0|73kg^AU zt6-gW#3;ATz3Cov8;nP%MjrJu7wuH^qgH`{mCjhbqUC`{#1sI%+SapD}%7uO8QL48IuBqDTwj9}Z2Ah41k+S5e{AASp^iYpVzfcu)PvxjdBzDcDMF}y8ZK-)_0kI-OPxG7 zZy*=Ti3ELzpsGy~0#uUzvp+Mx<1|MOl3T~xq%EiI>bP)xi&%Q7Udt)~N6^q0)VXZ{ z);Q0wgH2s3vRO>s+$v3dkpEyQPSqe*=qC{hnh5z7RE@D3??XKC4Hl)uFQ*nz?$K?YeHff>HCf>ySg;3$Qf0CDm4kLff*cA$sk)v zGaoJF$5gz>FX%uUt28nCnPwNeq7}3W_;0Ccy+d!1S<`R?5lrF88H(ht+CJ?Z=L;6i@sAZY)y#rD2%dtPlN-IY5Kd zGd?YWBR8tiu*5LS+&ns>M1|kU0{VIH_nw~BSN@mA zTF}KUGmBR}WAiU`hYj+-k@{Ewz&z%&Wxv>efEN`iEXVNYip6Pjm z1^xPBW9_XJ$0D2;3t)$Yw--JXP=o}0osZ)8bg_@)F>F6HhAaA>89ibKQ&N8 zav*3(+^RS?Kj83#9+l>-nQOjw1C1Q~5@^UVSWT_jDnm`a#I87951%^AdD!j>=edVdR(k`$!R8@>Er6N{U_@Gbsh>+*M=<;pV&#W4E?Y2LI1R0pHCHlr+ct_Jfx zxF*W}$B)e>`oxC(Bu9ans@x;&qs56x#BM9P^>>)IU85cRBG$8tNPu7W&$$X9j%Lxj zRe|#$6@4)FIgp1czBJxH-&DeEqGz#sNSwP_aT%gQ(ta~o7AIB{?w7WTC1T})VK`D1 zNum*HKGj+Oo4YqB(l8^P4%{aVD-$IzB@U6^hR$u(lSrXyF*7AQ|UJ8RG>yG-mf zl`fV4Teq6wvY|Ia$tF`wj%{b9U8>ORQH-X{FC9ICxu@&HuY<}Y*n8+{!;qjUu%>~P zC~leSkwn)!@8mlX;m?n}pZ1G7gB{Qo{O`yVeZq>P-ZQr#<=mlD61Tb&|Ff-u+APk! z(9oJESGclV;VID@s9s+yPWV3br)=^@G|QFss~6=rdXIwo5eK>`34WZr-v<)B-i9U9 zFX#f(qi)h>kxoD5q_u>$tZyU?^_m1v(W>-MP^%%hz58bKFP%&{*$XZJUzRDL_p43O zeC1UNB zzh3{NijQAp=^qf{98o~nNxJj9gVR7GFy_X%Kjg2!MMWiUkCrA1;?VF0+&_Atztq-D zyBNvFrtgzQddc$kjQXWs-GoUOQ+Mj7kH$d>vYSlS(81ASLZKYYJjfZ1k zI|bWxr0E$x`@r$Ui;bA8Hs9*360KA`dH>{%r1%_0Bp?|*UKZ=y2PJ>@^BGWR&vAvX zDMM?ul5@_6!w^K?kO7Ikd?#W|qA5WLGpAkL>Yh<)-em070-YDR>dmi>XU{q^R}|wF zzs*QsNeZ2KpU%HIV{C0K-B+Bq7|YVqDx$RoJZg8JS!>)}NYFc)ODbF8%ViMGD>gd?bX&<| z6zHKl3xSMl2}Aq1hX*5!p}Iqqn0za znY5eIopDpeK5|1HT;W^2X<|+JTMe`{oMOD$p$!7*W(O#EyWRxVT za+3%-)pE;=S^&~99Q-j1cT}nlEWJ9t)A^G#$Re@Pk(%r&iF?k1n|6mlhp>cwU1XLS z_xVdy7b~Ze8SHVG*K0r}(sPPO<#kV@Jfa!kSdT8&ici~1nigx$DKeIuyxB^P$TP$u z0rk(~IAKQZq0{d~P?uI5%X|7HiPqG1>eT2zqg(t1&{t-`v%6IA}fJ(kD}<-?7m}B=_hY_h%~Vy787k(wu2w z^EB(Ep7~2!evzsmv!<9L%-o^U9C}69UlHE-bSUaehpq{?pDuq8GXuT_{o~cWC*qKD zgyppXLv=cN3B3v4y0Twz{sy`nR;T4$$3dt* zX7@(465gZRciOefEpsf@B;@_T&n%uhWI$`R`)+ms=*o(kM>ND`5FO35^(K zdLK84a_&t&khKz|vR3norc_%Y1LpCbQA%J*>TWns!e^wMJaX!mW#>c9c}~jRxGbD8 z6SkI8MS(%Zg8-6?+gC=53?6#7+{wxwG++o#be46&VJ1uqWC4e9_5SrMIx_btl9y%B z2;=k65n~YC+B$hTG+y+8U5cp(VuwxgwW{Yx$|u|LTJhJ=l1NBt@K&&T~4@Tpwt)CX`mV!!+TT0i&w~64DDR=_ZWgI(4eP#G* z<54PiB0I#>dB_KCQm0RGvb@d&WKowFV7WVdkD;Yf4 zd)7!^E`kRh?=5nHEB~q8koF@|&%vOA$>Z1<+=xc)4~6%_-JUbtimWv@^&@`EmR-vp zCZ8SW52kFc1jYOw-5Mq?MyCmG&OI1m^UXBj^2=Km0*&#)1T`;&*5|9kL!J zYP<4Z`y}?>-mHt=ii8+qDLpyq%V%K#k{=nnJk&d`rWKi}ke%vF#Z>;SrAA}LbTagZ zXc6M(pWjt3o-i%}f8DEaD#buh`y5+ZH$m;s>XZP^cU0Bm`j|u(*>P2x_^d2_qA{L8 z)NHExdi%=YB38l6s@AWB2FR#w{KuLh?>%$<9=poqEpbcJ4^uc zKfPNHdp0cIO%M{n!^TvDxo9-}XD;WP3`Vs!>JP;cl7WG?f{LYP8KJ+`5t0LU(tuK3Mrmp5$S~K0ZFxUhEFh zPvXDzk`q>|k7&+=$5;lZ(%;DlE0w?(i6L@q2)Qg5pYx|Teq7i0C%cS&Dbm($`mof5 zR&6|1U&>_qCv&+V;7L(L`VR{lh#0ngNX$!rPYe?b9>jO+nS|a1X7s$BvCx2W>E1ek zUI!lb5ikBs_TEIOt+(ovYAlW5b>{QW&R^w=88O5XU*Cw_bkOtTU{6rrZ@kP7&8amO zE$(B$D3v>uPn`bNc4o0Nfxqgv*=?fbDE*n2%4+LM`1ZK)@H+P1MjL>7FW>V#Orl_cO8g9JxX>3WxU+2`3cUXXuB2X_u*#Det;_6@wLO zvoR?HyAj((hkEx2>m)GFFo@gM%vG@FCs`^6Gh{YYP(K|T2jy&eg_|x}(4tFKu_%1xa&5<&^yzH#0zvO4b%Xx^K0BZfc}_#*w2iE-mM6q_s|l_p0sW@GTg z49y`QHgV-obbW5u`5#%MujK0v%DlLer{E!d^}hEg-~MhsBYWlW8Wl$2VUE_q-x1gR zIx+{{NnrgTkUpZ3kH~pU%)3nWh|9y>-eGAF%Xf)AuQ#jLB~)kAk6S8z;n848Wb$#=^>R}n7>1NGLaL>Fc&YqAY9@|l zJ9R?l5f?@8FG~pkztsP6>ZLUOVK$r|#odO6XC1KYPn|zN!x=m957=QI%ASNSHke*4 z6%=;YzJChh4qz^Led(m_dbm^%KaHpm>y$QGHD;5=q!`_-rIYQ>$Vl?&Mp3iZzed$2 zN3cP&?n+rR@FoYjA~+y)`I2AtWLNQ4MINARCEmUW@cnc65M-&g=^jW70Hi-Rul^Et zFUZ{q3_?yp-<0lu@$-ooa&FfL7xy_@weW79TOULYG&l}T`roMRPOERjMLi$ix1}gE zoK|LKe!hy?;L&|?AW0*QsTA)_^CpE)`Zf`86E1vQx;Lg-<(aylXt;K)Kr6~fr~+b@ zNZRcu7;5TBuT|uATiqf15N|4O#`UCvKz+W4pJ88gR`?-QWmws7-O_jDo@t46 z*ErT+a5#c8+PesdmaCOQhB)$w3bq*GE&H%gk>(tFM-JElCv{ct(uaLCqs`3wpI`S< z`Oq!$6V0!F=xH3i-B)TncF4rsB+(47E0(~vE9x9Eq`1uXYrE|&&MbE}U2Vn#D4{*O z2s*Yt>`^*Jt}Z2}gHMIVGHH}y*gYyMB3S9sI(Fe$lTJvTK{6ZDY}VE$txzUMwP$j{ zzx$)#yq=J(sWTHw%V&ZMw!kJqT}5fBA%`8sWck1QV*b+=zodT7ykOy{=lglHa}xs#%!zh zon-k)LtRtf6i9Cd#*jkg%Ni-1s;*hXLKh6SBB!$$R2Hwj5N46}ix(t|*D=&V>DZMs zLq5!QB{D-n*%aJz-sX~Ex)~d z&j6$Lsbr9)$SOwY6OH(+sInme3aIE8?s1GJfYiHHe(Wt4Mj-QypjWhHg)qx1Ctbmgy>^S(pf9U5dp*h4D>-n!P~IH z>9Y!Zc(c7{)0Qe(kB@Re+$R-p2Y3-{KKe_N!cRX&pc{K}EC2_tS?9nQp zI%bgNLqY68$L{7F&WV1w!mdAN^!L zd}o3%G%Qhxze&rfX_~{|!x0Sdv`An%uu>kQic3^Nz&H7Siqu`R!d^{`l=smJV|hj@ z+}tcjuy`Hx*A@Ztza3OAa|DxU4@A|_@1DC7_;g*bZg326=_9-+< z!y{Cz#_;qW+UWJ-6+`)wwcGC@A@&(_zwUCU>r}>ESY@?c6&=G;fSU;aWdlPQ(WXRoV)Z8`8*_S{wp;0k$RzdxlE`pewH?wc6 zuqKghwu`Y-U`coA72=pg!!+q6g0D%*0g%gs;m%*1WpQtp1XQvHTRNFH+zqJ@DidUeh4V>6=mnvL|we+IZA5POm zqBC|@CZOPwkRK4tNM?wqOi?5Dj^u3ZY=cO~y`FN}W0$V%w=FT^0Fft>p=Mq7oQaC# zsJe+Z$GO~*_%NQz?(ASVFFeGg2Gtz*r@g@`MxodvCbMWBEqMhPewIDjkjrbf-D)sK z(LQC!3qoMLi<0e4GU1GzcIYZJfuuM;X3e{L#EMH=nvG*;vFQt^gy8Rr*F+dj>8E;) z`S&B4+NPoYLAj!+EG@5*-Dm{2rF7=|fg>Isj`E>?<|iq51-mr&XLl?ht%Z*UwfTAz74wZ{pA8>Bm5} z91}6Yz3rmLdA5kG&y&$R)9NzW?a`^GlDLK=8arb{QIhkAc-qufN325+19!Ud}c%U~14ibD#%JqsapTc-wJ~0Ho zGvBWTonMyNMQ*~X0y12v~!X?_~EpfV(SoC%;mC9s)9ZrsJ8gz=la z0BrwL(SA~tV5sH?^HwF%(XS7ho8_sU%GG)mXn!8MR61*yiVLHA90|~_J#{8A;Z#`G zG4vDFWC`OHCHc_fvNC*J7^6=e%)}7qCVNSvPBFs#iZW{D8f@?E$el+fsW^VczsG-j zapHk;wA#uleG@R&8tVI~C?wl{y?S6_QYOmBE34`Nq;6*gZfdnC_u*lid}_FF3Py;H zEXl*nrf3`*Gr+S@O%w+r0p|Ii@v0P%55a@-rDLzhUJxul-g7cy1blBJV-tj1!JP6A z$2OHM9@A83+ast++*`R`FM@S-Vz_5>DOEP9UN&tik2XGnQ%B?aPqW4iD^9#BG8JPJ znn0tlRc`xVSTk5k=+Av$+@BE!Blo?m?&i_8JPa-M;Q&TzH+TQde*=spDi7lD3T z4vee8Un~c)tfhWi;M~tp+qa(v$@G6kuBK+4g`)1MCqSR2izJnKVVHc?o1Ub<=(fO; zFBs=9^);5D`m;0KY@eV`i&0Q3KNBh){uv};bgA$6r7ya4s)opcw)GS%d3;U?&c1)e zW!_Iv`=~F)zWftM$5T+p`_b3);^c{6f4t$d*cmlhgf3GGomYF?^2HH&t>pjM9k!^W z>*Jl8r(d8i11+vi;#UB^kv@OffH@9}ju{^9$e03|WC2^OK?i;l=vx#I>KS6Rs@IcH z122TWU>v%|q4(sf@``gFu_QK+u7w|ihv7RODro)z1K=3C!j(y>;LS2+vgD-2+_F=@ zSG-V`n7P!H$CB>VUMb@hze92X$1&Q6?x8HPwL1o!AJq*OxeZ{vb=tn7aH}Z72B)!k zalg|=vngV~Ez?_wo3`v6q35^DV_k@?6`uK6PD*hr+=FwWjlb1Exi_jHow99QsST#t z=GdCw`_!`hC!^NCKb&y3d{;ht?v5$SMp>GJ*avs#fbzn`_V}gGgFby0e;{pV*cZE< zS5w?brDYry!P#`E5{hmq?o-Hqqr4zIbz4T^yi-_8YeYY}m`Og_X1}EDdBWi~uIIbW zd3&!BJ+%l|u|#erXpL3kbMy{S*sI?Y;=dEQjin$>khmQyA5?q?roqq% zKZgRyzjsY8VIRI)%o!h-+9g)9du;{$Chw+i~`jU1LE<#>~OyvhFy37f*fPInE4;CYlpI5%> zvebM>WhiY;NTkRJ**sF9%tkDE_n+7)oUm>L9@BU)Yu`Y}S2U<_=9oesxf9sEm3e2G zLm>zia#;UbLQgTy6;@l;G_9Ph~a*=##(YM>Kn!F_PDRQOO2<@Vf?e?y_=5ky%>q*pcU9>Z~fOs`I zpBS;O_$Whx(_*c`Uck%ky3`BHaJp6STzT?UA-I3vB%hEA{zJ#wvWTDC0SVr~m&LkV z*&$haZIG6|v^xz|t$RCUd7aVmRH9I0+qL!20zmWizYhA`D-g$@ubs}^lNxU{<5Xt3 zh!}{YLK?_bB_j(^ZMJMS&ze<_GBJnfT)ah5Vxa;UFzeH8nH@$PD+dQ3-8Wwil!)-Q zVBMOT$&u%ql0%?rR~yf+zb(P5e>(8@z!(CH(8cVTL<(LL2*dtvB<&|A&t`{eMYCh= zd>kEb&er?MkSR7|Ki;}#oiuLCdmawxc5oq=Z|aw;XsgdzflJ4A6rMdbOnDmGk1ozL zQc8~#Yma2DCueRGWmj3BW>)g+l)akE#W&KFXor-W1hy(Q+>8k%amKtRtbM7r4cBh> zY5wl=9W*&$0xE&e@=t)MuF@0pTCfJyggb^)FWa#^RTiooj<8lv9AvKr$5Vsk2{w<)lHVkFYS3CA*{VnsJci54Z}e)GH|jyhjONJ#T7=IBriwZuhe{okti#cGI*@ z3Jy-VMf93ZxL0`4?cIbb6*6bum;DG#N3+8*cT-&Z&=Wz<J&D z%RF<7p4ym7q>wYB)*R*B*tp_4eQ>cGc>?{e?&{kAPSVgO0lIQ^uBUhT?!1KxEbisz zTGTw5{pTS(Ry)Vnju~CvM<50saH?iM<4jy~Ubj~))|py58En7jepUObYut& zUpb>y%S;N!8!T@v;VxIPOq;-dv;5qeC-I8?70V(tm(SvA!sy_f?FsX=-SI@{ zRC(jSy%(DfbEvw7Nd#+dJ!@u(tk5ClU@J=9m6^ z94#n%=T@PD9RVS4*&z$!+0^0kRZwYmO;`)RmqkPhefb}`D<40zB@pN42Sc73rKLL& zxckpg!qS$2U5|AES*i=f-ALEq?Y<+OM6+&^fNIz;-i84bE=vmf!1%p-h#(yYt{`Mio*yI(q|w8*-we&q}5D#%NmmgXiXWx%a-|=w1Y?M`%S; zxRbe~3tS#x8`3#h+kDvdz#2kMHqFv>>=}>>R!|6Ud-`*i=Q97um{k?wBj1WQY%h7#cSvOD8!`BQ*dfIRttxB zfiRkXSBKFn!OxMaCZsK9;MX2qrb*o}5?ygD1{Rw}eI_Zif6V zjrNe)lvY=6>mJCSQ>!M)ye%`D@s;z$-!($S&-reX8!nXXqAk83{-ZjSBcmb6dp~*R zvh<6K94sPRca>Q78V})suN=Jp$jWZzIC4G~_W4bTT^FxeTNS9t@zysjIDhj7QYwNdUkBMT&BdzYl?X3#Yz`1WVoRLX_^k-+q9?@cK>7(6Oaz+> z8lX=a99hVOB*uhcC$5fHilO+)Xrl8cuAN#Xs<#2}@ZNj3<>Q}z@V;izU*8&`JZ59U z7vW$ew911l)q#WC-~5MZ$mA9%NM4=XQq8F6c>BX1!!4p3D0p_806?{=qL1Ys!prq`SMjySqdh1f;vWN0&%R_kh7bngJs>Vq@$x@B4Ya|HSwCc|YvD z-+R5zxz0J)buFN~W;_|cHzZp%}f| zb6%Nw`8Q(jL+thP*di2VOY&)v^Ra#pmxtR;1*oaT{u)!O6IHpf`jos>-*V247=2yvskXO$?{bz2gX3D$kXm zvk~qPs;^(+HkfGWt(_Z%KQgs%x7&Qzetxfm;bM0RLePl#zA`~dP(i6vcF^g(aoLfR zs9fKbbDt}M_K}(M!C@MbS|++|BZmdyxR>cm@p&ZGahXI^_qTf3+5be@n)_-nrB2ty zZtjtDG^0H9+uGpI(4@8l4NZDou-`=uutIS-D`7R_mX8xVehdKJ@`Z~)^~0$u1z#RZ&lOLLe(4DkCEH)d+sqgv9_69qw`5sN!u!rhzeZkpYDx7D ztHJXhLU_eLqN`4rA|rnC{j5@v)iryZk^X++IqmcNq%0mL$yd7{yd^E(=szW{%7Yo> zzWLBMlePW|M~T)%ar#lB3waO9s%#Nw#GBFaP?K-wmFedx$EUhUim@v4-A#e=%bs21 zu+c^xBs_7pakkKKT%Mqj$Ey3$=&%j)kN8nBhu-cPSH}Uhlp)k&TRo);tqmaC_Z*(1 zFhB?6V|WMu5$^A-Ki=SQD4X!z-gw*cGw}_f0>8xP!XNqla~utf15A^zzXo*kjMr{G zT2&DptQ(4-B1pE0mF*BK!yTaz+*QkjLyi}@W$aTJd<>FfX=;Tz%9V*sTcnM(Nc1=q zg^BI+Nuygx?m;65p+K1$&-6dqW|J-C;NT)@63S)SL=kUMJe;Ln7Zti2&W)E)-vmIEoFA2uKUjmq=xra6cq-?}8otqxKae{dFU$0sWr9}Wsqc^&sr~ib zE^9HXGA@7bwkGGo?!}6VhFNX1LOvxX+8*1FKm%GJV=6agEDjI}2>!X<7rapl@V-tM z#o3gvv+}CvhclO|wQqTU)6}t5Bxb4vfBb{doBfB+BkR2EsvR3u{o-5^n@&oCYy48E z=?OB_vG!3JI%0p;qN*w``QUo^e}r^P7ex!FK@higc*^)@0IN9y=fun#z2NcQ^RYoFbB~{8rZXRbK)I*;ehx5Q$=^{+W z!k;~)9oZt>=O`?Upp1Uk;9$cSb9#S%tHXphAUjUK`;kF?eEFN>imn*m!9@&fwo{-L zY{yKVmw(*ig@z)bQXU6!3U^GcH}u%g?vjzj19OAi2fzf@(5UJ>-ReQT>Rq}i&|4%DNvur{1+hA~i z5Aa;`ch*IYobzI`WsB+7xLy6{4durR%#ktAUb-JnDh}NFq#83jZ>gb%N<}<`HTktx zN_jARD~hag)~rs$Z3SoF=@!_m{b`|eUPrdkEtF(7G*Hkcu$bPHI@rJXZ$tbLh?^HI z&UOb+oI@2)of=uOy`90tPYETB8w4e?H(C*nw9tqWi-f$s_Qc-eN0w!M2dcZw8!5A( z-#a-FxZ7U_V1-OR{6{nTyIfNl2nfUJS1ZXB@#pwMU>R0V=kWHiInB3p@-{nhW}htS zaS4v%I)|66M{^UkV+k083JH?wKi?%XvOM8T`wVe#cK<+*djC7?N^lt!GB-mwaSY6nNG-2WMl`^aILYpIk-q_P_D877#Wpeay=)|&o5}IbEzJ>Fx z37`0*sdjE#OpC!Mo)2=WbXAsw4s;{v8==hCK!TA zuJg8sew-sQ74x?9wceDa8|WQJ02;upN7|)qpHLV4_mS`^oT|thJ=hFcYqU9XjOsm0 zm;Y)!Vg_~p_u_9_66Xq|b?iAC_oU1m?gqZJTGMWCN&wVqs2g6N= zEmWXB&vZLU^D}cP6;{pE9bY}!e<@bXAzVr|-uvx%7CRGkX1f{_re!YLjNgblV)p`R z{#1Q1m+&HkyViZVpp(CmUJ-vMSCaMqM=Hy=Tw)E-6+=t>v+0i>*m|gW042K^?|J(Af4+0&zz{1?9;-1>BhA6ClrGA zDeMO}*SYm|gP>vA6`t9kruy+H8t2|kt_yo;w?Sf^2ZJUNJUw@()KxPZ3rqh$4PT*C zDYqKcx3=`AUEi&5((@4R?2=q{{^iyT&D~4ss!?70lp{03sd25XY8@vP{j}w|1C=7S z67PVNX#9~u!!n!dN#fw{pseei(OyFlrs55%nOp0VQu%4YFQ>?@9*@J5REXxg=sN5V zvsV#~Hq|+;l{WhH=j3FHLm+EsNj{&muNpmsd5qOrf@W4&&Upv{kJ+^|oD-IAXy zj%9b?6ca_KH~Sk0M^e=O0oAF$3&`L4r987WMp_+Seq7>xx4a7rL&IKo?yV=;tagy+ zwgzH4_IYu7h;QGW*lWt%m==FX3~zvk+Ubeq?c;FsZBHO#UedLrnfN4ef*bItrqi<5 zW$E*weYX6tW^}Or!chr%VT_~Zc;c|vr9rQi!vie$e~ZC~CDF3D-1mVqE<%sL-?Y)~ z-q}~s*tf=>WGh2wOgW4sY-9_Kq-`w`wgbYV?-G?kwbtedw2WWd=VTZMvk9y(02*{b z79mHfnw!h60uA1&Qz8v1C+(M?JoV)lpa95lQu{lYA3?c2yF$x>zzFi_!vik_MnY6a z6-+FQhEtVHCk(Dj0B#R6TNhcucQXS2aza@j;(Q#^9SP0mvuE?!9y#dk54mcPbRBUN z?)_X~SnSoay=YLQ+Ns3q9PUMQX{fxk5%f(!-qDMyBL9V4S0)8rrj-%FA_Q=b^^xqq z3m%?Vk5#$dqi0lil@=pY;tK3kxJtDpb>CfaP|7l#-_E^M=+IFL{&^xXtwi1z4qxTk&UGu0v@3evIz21gBLdZhus8Cc))qiMxh)vJld|t|Cd5%?d~`yJZb^F^TIv-` zN&0N~=u||C3EXPaHy>jZecw$tFc#-;%4N}2vzIvXh(`6lLVW1p8$~%^{vRyxh zhtbb|mF!CBDkD49A$UN(&a&;eQs0 z4Zp1e zAzm+{?k_Z_3(A!SL4GziB0;p9X)Dp260sry&|H{_^Ox#{X!-yM=yG7|iPsRU^BJ(! zhq9@Y`hQ0;%pqQQCH`eA>Cu;E~VFxIP>4!H^(L04sn5%UuenEjQ&N%2HAD(H>R+Yqf(t-Hf1V20_&>|-1mR++Rp88(9ZabQ?p ze!xrN>vT~G*GNXs3J^|Vmu@DY{?=RX`(d(fVi7eDU>5kDoB zcGFGdIzM#luq+*`Z61z;3kVcX_K#sqSlUFD>U*G1QNP*@C6HGqCh!S6ZwAF&$I`Yw zFM>O!JYSKXd+^fiX8(^)7HK9X3WPINZ)Wc$1V5ioQ~bmrjp!D3b`VyhT?7T!2NDZk zW3ZKeLOrHD@(BstuPvM#<#&>OXuJ}iF0dBbbhz!PJllQr9^z7o7}VS|o1_heWuJCM zf6vvr_Y8lV#+}36I~mdSyZUJR@WjY7(eW$JL#*fj9tMk9fM*Dh9+j(LHZ|#|^l8d% zLe$Mu?GyTo_nQc*nXx$;nxzVHCP!&ht|21x+CVw@=A3QTk|a-wB&RVJn5N&TVM?kM zJdjLBpn4ccm@&UUSw%nAP4JLfCJo}}e9NmLnIE3!CN(%|Ir-XeTzh*(SFb4CUklQE zPU`R>w6(QcLObx59kpALzi}IfTZAM+ntn4MXi)l4uU-!A=DeQiqGx*_5alFvM-5%; zP{7YSp1q&FZR@$ngiXXwTN#VV95U-O4z;t%SuOg+b>$LuUNpDO`#ZNsUL}CJjZ@Oe zZI#vXNw7($$J6oUc4ImFb*9`frhfn2w^qe+Ru9W)>)692zgi*-fh8AayuciOvWR6p z6*g}9?{UhBB;WZwuQ|Xip9?lnu=LSudE;*r;IC`~n+#7QK8CEpSf(rDgyajyHn)dC znfOV{(}2dhi?q0R*JjgoM0!v_@g<gR%~qb5DMeMcjY&?kr2F$L*Ctg2l>bvAAKri{wjq>PNmChL>nLapX`o6^ zO%E2G`h__`$6?R2K1Y7<2j2T$)%VFS28<%U09@R)=xP2MR3TGN@=Noh~du5+1MYpZx& zbKWf{NGy)LVxF(>MnCbp=M{>hgrBL?$$R^3i`SU8m!8Se^Y9=(P%PvN^#M=ebP0Lc z0op%PH+{V4qM0YgSF2$@-nP-0L>U!yI4vsAB-m+dC?LMf@6}M@9}%G~QyNU(@Kfw1 z@DD~s{$JlFE8FmAdAQ8nNUK%GY?1Vu4wk9VCLmWm4!uQXdCC2vl)x<uk7rP*S0*hLc~$3iaC*Y`b>muJdzPyEkV8+8RCIbzHo$oC ztv4mzZtiO_3nOqhLeh9u4jbdL0%HnByhHS0I41K~rx|sqy-&R~jPdcE-7xO+p^+2ZeWtJ!t*-&lZl^G!^EOa{W?*{_qlDCw%zEnIiNhD9vIUlHmdS8>?CYD~W9v3lf+ogR_i|Lf7ZhDmGj?i%RN~k|1 z7GLiA(EtOVp6Yl6&C+{Ti-p`zM>=#qWzKV60^>0M)2_UrJ;No;aNxM3g-fSs${`bf zmUx{87@Bp&_d29^xS!Mg|BLd6MTiSNLw@EZ!*o{Rk(Fc2lcyvmwxmA^jsvQ@qhD62 z)u?2Uq;wMdtYd8Ll9SYd6Vqj{D{oI2>E|E&z8v8NkZ~~#%rktTfmKu8KdfbLfq2&Y zaI_dC`fakT{AJhsEzcY7X_dexvK!G3;CyW_KMs?o>W%J2C)SmKA;;}>XSn3mC67kmq?ls%XSH=K>gBtG z*~O5K&xVZz5xU~$McD+WyBC4K<{=o-aqWN9t{N}3U+~`s%mFzy_aqi3Er$6iW1710 zX-;ac78Xk%RnUIF`OfXS|6a!5Ys_<7uACL2zs#Gczk5?PR4BqmtKf2GmU5>7$nEq_ zT@P4HKUl{BLRds^JoHvq-bJ_O`7Fc+KbCKoOGRoDW^D;nNi{2gPkbc1d3x@Z@AHBX zhD&pG!s97yTrYfeIIc!M`%jKYS)F31lUm|~VfC9*ZfzWSOGRBao0vh~zcd7;RyWyI zR!$|)RqGHb+so`0vf>_m;Xd_65ny1~F`=e*E<7N+v+9cl2zmrt@eS|RdphR!^-iU{ zO}KJ&Bk4K6;|3#YrNw&|DvA#BJ7UWJ*FS7{yf)bTao!m*Kcd85`xf7Q%i7eOCg>wD z%@!q*Xml&6J$o9kr6tf1oSGJQ{#sa#QMkip%PEwi?3*A zxIT)^HZ$aRyc8RLmOjAew5ST&OhGRZ5BSX!kres5r;fUvn|0i5jenc?r23OdvdAvg zjm9^(2wz+o=elO5{6tu3<1Us8Q`GXx=bBmhBfk$&!bhfCIy?%6nfRV_rl(i7ILVOM zw2-r~u*@sAQ5K=E+vme%S&{LuYsb*#I43*z*g;V+#p$Su(B$1WN=;z<`dq7UCt9O6M@HoB98_Iwk|mhNL?={Lt*qqqcY zJ}Xt}(@Bz_R@A$!%fCbFMf24qplM00&47<^SH14IjDUu>TZ7QUO|MJ-x2&5k zf~$WobX;xVxXoZ-i>#kPKGyZ89N=5JI`__g30eA}oB-V#b3WC);}1`blhXDw^`z$J z`J(kX4%ALpU8(Y#8x?B*^j1m$87I7O>O8|9bZnF=%x-@)2C0w3a z^Ey@ovPp|(f?GqT9=Ei$Nl|&s$;K-vAyvDQPPtL0!fAXtsfTuWwVSi;YC=2-lH7xB+*SA(Se^sMo z$c;z#>9yAj^mjksPMgUlK+Ovl*sjg;%L>|!p7m!=MdS6D`KkLqs7y_O^DXMxLtI}e z)(`!UeltH-i`gHFZhaMGu{&sPG?rc=zfh9fWxrc?0BikHeHc5HjoYatf!D&ROa3rD zhn-1ruB5Q<_L{aN;|H6_84*WQlXRHfh+3@HRo&dF@g))BK+(7qeJYt)etMsVOGj}G z=h?da_S>_1EL)@+SauzFsD-E$?fHIxpNC_EUQdPS7l_YFR*q_-;qW;hQ}?k1g<(b9 zVEPo<^jVv5yZk@qab#E~m;VV}hp(_)=lLlYyp3oxaFqYqct11XK*}^?fAPPN!Gk8agH(-(gkhx)U}-juO1T zK?I^61fJ@PN;)H9j-w~M%GYYMUD+lD(T!czIw2fp$=V5rqV?E5yn6G$v;6cp1qp)ir0)- z-4AhVB2%%?ryREzeW0#%9sQ=xgl&7Uztcwg+UksA_{STwFB;@O*uPqfyxF!lTL=}8 z{!mmzK!EcS(-JC2)khB}MbCylN_{$LGZ^~iW8q`Hr|(%+bY)$4Wh!iX6Kft^=)b5&W?7lGQ; z^3#9_aJsz2+n-%o!9ugRk9telqX?UoaDP~*|M_EQSc#6=SXo)=^z~o>``q{;{_f|? zqR+Caltiz4HuBalx1Hsk{f@N+aGj8N33J+gS6}^3jRWaW>l=YQf$&9tNK0Eiv^TxN z>2KanhvMcn)_$AuchuHKy}^!ZMoR94^Z-=jk^f~-g-C{E3qDk*qVa+LtK|?JlR~B$i560Zvx%t;T*m&kiTj$8p3op=sX2u3nYerOSb3+};`5HqD9!+Df#W zSfjLa>QPThA!$zq$1yrG7y$h=2cJhiY7S}vP&DAOt8WPjPBi-miNLb#A*m@T;0L`Y zz(w#;mwoGKc#OzL-Td?hHhUU-xi$VAb#A;m3r79rl+#2Z;%o-|XDe7~ua`sc(yF9P zcgJS1v%p>(>vn>4lLzO#T?KS(>8`z@Ty4P`L^7!lNsy!w9Pb|PT+aC_lBdSBOCGDX zEe0C{?L@m&Wk~x8M2er-TH$ZY@YSq844t-zv+8h zi|e7#gzet z2Wd1|oXBbb8D;O}+&khcl>Lg8Gy(V7>U>0C|0jO~o;v&rEG=L6ppe18;7D8U{#ea_ z*)pc4@&{C!Y~<1a|3Mmw2>s+BIYkqbK?oOhP=9gPJDd`(Y2N^I)zNA`zHK1^fpHWm zmINAgL|cit2+)TSdVm45s_LIBKZuSOTaFoX%3Lo4YON1N@>N~D`)g|p7+YHB_?(qg zuupIvk~Q?J9ovROHQsRAjvGJuQ0n`N5WAzNMtavXj&I6e1!Z1@%fdxn}2s@6^&np*{7R)4QU7=sedDP`4 ziB1VP)!Oo(#h<=pXd+U=B9S)1K50G^G_xaBT`w|l=C4=RJ7> zTCukEWYR9wdurvV*kPt&tk-#IvDU9;nFYkfmhzhRWU3oV-To64U3rgjzCOegL^X_S z+dG5Z`#g5Wk5;|LR}4wYrsZVavV@>P>`h(j(n|AeT1fBX24TG}u{-ht`Rbg9?Qb#9 zA4-1X@c2XCLKSw7lC0m~l$5Y?bdLvvFbb}HC4WJ<=f$wC<~MH6iy;W3Jmh%mj!zRy z|GfHG<;AK{M`3EH|A^_*jgPR)Ui#6#xl-JyMoH11zeRhYV#5w>N`Y)Ymu(>@{D-|( zhoP~l)4Q!;-HP(XG=BfdoHCRk8(4AUwyZh!+DHVo(Fi(D-S$U{J}sLbHnnckq;fo4 zO6{tQwcs}4#DtldD-io3BA4rD%L~KOSDS~hVC^~sxqQ^v()>#5g}3)h8KiSzZqAx~ z-yOk5ipX6hTRDB6FO6Z37niY7@%nL>gmxZ_0DT3yw!k|xrO!MpkB$xuh2K_d_S<;6 zUTe9c0G}MlL0YfLR*l_>XGgs@G*;wx*Przm0V&2015;;>lwU7RkLIQ)%&+#{dihN! z23|v~_k4noSL#Rfkm&M!dF=$&N~2|JFs}1qd)n_V0nG0#W`f#F6$z4!Jtld?_Wp6F-$wyhBv+Sy(;FihRMhud7AWyr}pZq@%CQOJL zB{O?}V_(2%^%F{68s>RM-ztqMOZnLZeinCY(D|`4lrVlT@U*Ro>RC$10xlG6N4D^* zw5ouA+I|;O-gUW6I-@_1y!2I@TkYH3gq={`*TI%QEX{+vMlK&vP#7wqQOEKS0`$E@ zrz>2Bs$S3qsav~WOEm*4fBH-75swLzZv2n&1tArono5^qsZlTPd!};q?c8O3$u(-8 zWWFnC$FaN`oT=UAf4w;W%3ByUJl*?YlC~pUFfcE40XQMBNWClM9@4r;(qcYuDcfu8 zp=dlN2@>vmX)g&YM;wJamaa}F9&Mj zI#9YZ5NX%xE^xEQ@M%2|zH{7{yAI#u8#>zO*WpKbUad{T!P@hK+kwvTZqw=wd@zuv zd|Sw0R9ji|Z8kzsnB?(lZe*Q-5?l9~90J;RKLN2mlG|S|(zOKm-Qw$uw=TB&TyP18 zT)(~<{%V5F!$%gzN-K(*f2f0s4$a{JXkX0LgT{SwI)Xg-1Fvq==ezFzet;+k?;__H zBNHTS)T>W$6uIe_+ClLYhD$L9qurFH1o{hZ^L1jcp0!pIgv|Qw*Ja+O%;%5e>OJqn ztsu?ITg=x78U6gb`TPC|uv3Tes=07dJd;4@z9RlJCsI=y@H1!=Uo=?+)j~2~T;I_t zDgw_NV_NbiDcCDAfOfp(?Rh3cbT`^c^8+<9kgZ(pUghA{I}{c%vW*?aRm=h1%0FMms`PTji}X zE??!OA5$KbvwCDJ?N;LJG7D9==b{?^GFM>e?YXenU|1p~Q4|AL4xsksH1is!yY>E- zKc-}rGxby-1IZafVm~xI$ga`E^DC{a0sa{hO3V_V(4p$_3?u=G+#XTM^uffP6-5yV zyyd*T_g0{#+mO8EygJPqegXvSk2hK@99Pd=yD*dd4*_-r-|y9R2d zEwEcL74qFub*A(iz zTxks%TeqzDh9|bc>VlE9W3pf>!z{yzBRb)$YO{&oKEld0`v(S!n(tK~G;Ku2SlR4b zUU2R3bSxd_ml{^IQ}}|=Va=M;3zoZOZpKrV=Qn$oWN8oG4}&&YmXrtX3+(a_Vg9uV zt+r#N-Gk+}jFG5mus>wH+!PwkCY&#V#{BT9_S$nt&Wfom;^cs=dfIZ04Q;Gz>`=UR zHVdoMIVbBOo`*Yp zb{xGwZCiXdQP#MgW13i@l*zD(s?7V{jTP@!>B zRf+n2>ivT{#L{mr(5R(6l>GfCo~qn-H((6SPbxRc>i^$P&G@L%7k~l-y*#IjGZ?fcvB%OwP1KF7|z>i}A&*;)cWKhptW5oMcX zrn*N;OIIfy2L`X}SFYJWT3YvB^*E-JHAUWkv(t_iL}{l82BGK1pG>e_Fc)@tm9z%=Z&yF>D)f;ifGUtC#^Te;o+o4VX9DqX+8 zDgmWp<7VKRth(tKoBe`b2q4W7v>rII5UoL%IY|G_YVR< z)Jy9lH=e7ZH*_Z=-&Ut}QRd>`1^ngwYaj4Y{ew~~1sZ+%#W)pL%*w?+c}!-SWcNky zwaEGPx>(Hrtdd37zRXZ-S+yWpIJU@;A$Et;R~N^#o$$cx7y1pq16t&i^-sh@)}`~Z zsy9$$Ju&k+j0&%$_M3() zyXqBIp&N^bQJD59UJeh3POjpY%JE-%DkM`g9GEsxT<87>B`5M!;s_C{*_$Exqw%Qa zK{rxg=iIQH^lN%B|8n->LW;ZO_hhxz+_2x|etJ6YM@zfbqSWlB^rh_gSCY~Ctf#bY zNwkK$4z{8g8H^~rNH$tucRn%bdJ+@RL9-81G2S)f-Dao`|9;YSGhHlne*9RjC#eWwto zmOV6bU`?^;Z=8&5u`@ieGAL$PK(t4B4LCjd+}!5ekvP{1%jed8mO4VLy^1BohdKQs z8T3EXoNkO3M2oyk%aw=Z=(xx^#%Y=S!3Cdj%9X}3nzj4SzYI*9U{H(m3d1gO*5?1>lhaePhS>~mO5n0G8`?^&B@qM<&%v@46 zL7zd$!HXof9v8b>ygS#5r)qM9d^=oK)o+PB?>s4d``5HE6V!(KM-aWNdlSis1>G|Z^E8~Y$V3iUjd~4mwBXs6W z_1a?ZYV2G0OK%f0%(0ZU-87*z3BUO1rzIXw`9hy?R`h*Fq$Tn(!Z!G@7d-j`WChmY zLAMrZlX<$kVYeLx>%QID?I8~{&&2O78KRQU0q+)6RM7D_qlp?vt~HPK`s-|V^;*& z>ADlv6NhtZRAEQNHwy+|2JTk1*j1_a=LwuMzpS^bE~t+s8rxk87;2sfwO$%o_s`h{ z6yfa_IU@H%stlfp2|?7U!vUf*>-)1B3kwgHROmsa6HgqZFi7}QPh-V5?D|fVZ<#Uk z;RSsQkrW@B-w9ne*UZ){Oshj@!kgFndZ0FfXMq!Q$SQr%dNw7T72i}`A<4C@oY)1ou5|Ob3D^c@b<_&9+1DA69Jja&*(0C&e_FC_l+SA!LP==O@WUUr ze|kuE_T>*(*#`}ZkljD+eKYo6`r{!QF$vVHgsCM@u27&^YRHEI$`B|_OYX$qnDy6= z3sQ@wdc@{yzPfpC)KmURnlD-xzl+j!+OEc=MYA1+t#U+NQQJzbvE43H-NUK$jEt`u8rtsqZE-@^D>!pN zf%bS68Y}T{YS?wc?|NJYh zWFN!6C0zj9Qhb72Pw(>mpBe7Hbsi3zL-+TI{OjXtqFCf%zn(a-!C zx%}i$hY$sYOuXfPG5jqDIB2->nq{RQTDl(VfXtnr8dB=P+X{*xvv89FoOMzKg>HRY z)sYO}uC*$|apU}Hv%V(!CP~Bd#{SXAFVqWT=|hV7vKR9-pQ_7iKm3s!Ml>=bNN~Q= zB<5wSPce(2SpZ%EAC|tHuDs#K%E8ARLla)+)T;^Y#T`Hj)${Z!z0RryeA6nnYU~!Y z(a+pBDVyV04C~E!#^;Ie^|}v-_^C8NAg`?D$Yh^GmY5({>ht+u&{+fOu~y1TW0$`q-e(*NTBB2sZS?&w&Z%x%b77e+^*}0gGE!FQ^pffbUzo zBG_C#U4~)wR~x(t4<*w6izu$a>-Q|27Bd{4T{XI^|5|*tx6FP$AXV3*qGb%pZw#7v z7N(Kr$wBpJ>q|x7JFNj2%?3md=`g|e6UB7SyLIQ@Bj zOYx=%@{8<#P|d9#F+e^w7zyfUD=h{PNgIFRaq3ejq+OP;M2|YXx*jQ>{W{oV6@liu)L}7@Vf#&Rz+>sXt=+1@CQJT1^+Vk0n}1q)9(8 zhTq=bH$Dg`{@33AG8DEqv+t zegbDAvhk{pIgoX)8TjmmHq;s#G`Y60YGS56GkeX(=$MRaAa2%HK!v+QpL6FtoMR@PrV;W|_5p^set zjpxKzS!CL2WFuN^%+%@w{#^gjFv+a?bm4DUN3yA*`@Fo37{`X>&4jCh;52_$D=z-W zwR`}f#()SVpeLY5Gi^}x(yPl$JHg!bxxeeGo?|S6s9AIAzBg?e!%Q1JaTR8qMt%+^ zKSt9w{b#R$XCWyk%$ z$o*ePrtpyyo zWOEYdzF}X}UbWM;%LPx{8CO=R&yUl{N^=E6Vn2bd5GHg_+}~V>F4k7V1%%}{^R_vi zm~>hf!F_x1TVMK`mL9l>Z)-k=Jdt=#Mf;zlbFC@#SDJ`8Fr{MMFQ@@{t$MH_TzIVz z5np64+w^7`v4DIRx;D1)pn&A%jxe*oX3YT3U7%X>eq$Rnz#YzS(86=af3@F#%zbd? z=G?V`=BNeTuTr^Hqz)3mQT&_~9fS)3qT5{?pdb)!>l?<6L~7%QeDwd|XwWC)1hg}u zmfs+Ak)iTSzaAz}*l%4fz?j$^dy7NYte9EG`?zF3uI$(%f4|^Z-8bpQt=u{pMxUWLIQ5^o& zSlGx_p(-qOwS&2(iBrY`u4)XZyc?Tm{ z&`W_>Sa)d5L>vq2EqN#&7M6+2BOWZQkp9=pSXfw3{_lhTdnNyO82;}X{QoL5pxBb2 X0@CgGh|ic5h$SnjBvJR(B<%kIRycH+ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-400.png b/src/winapp-GUI/winapp-GUI/Assets/LargeTile.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..447259e770107c8fd04949209d4e66226e997b12 GIT binary patch literal 167896 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

MDYz6Y(xrAaKDzyLib0E2s9tfJd7W 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 YSFMIL|M;B#KToHtX{b?s@8Q$`1-v1O*#H0l literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-100.png b/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..d762331c859eee440a66c7f68bd7c333916ec07d GIT binary patch literal 2746 zcmd6p`9Bkk1IJY)_BiHl(jrHOkUI;xa^whOXpXVmvclwOIm2jQawG~la;(iXMMym6 z$n~H($`NbNcrZqqBYZvo#q-1G^ZxPk%jflZeKH*FZwT;%__?^a1gx#hosL=Xug;u4 zcIXN8;xYIltlaN&aY-ors}t5v@*7-S{Grz7*Ii;t)?UPAiMz@4cHCc|={1@{yi~*U z=P5z~3c+hQ%A+c6g`n(P#ZoI^r{YVdl}J0vX*!r%iJhLZ9DP^wCXDiCN;tdGHl#}I zgeOqGfG0Rdt>H{7&o8t$-QYgh5EIRsZT;jhr8V!+=o`%{($Xt&c*xZ2h#N*or=0Qe zX6cy>rXYd9OA^4u|2jIQ$URsF)y(xv_;uY=Hx9yU!AfN>7zWW$W{Ip9hra#|Ym6;5 zn+4pGw$~k}?fzii;hqWa_RWH4^4Sn9uLT0jw zCNl_Dqg`O6Hx{E}xp?BBrLP{x3s>$F(Nl$s-;nbXc58^dug8erN*dQsXXw~3Jn zO(&OAMQ{oY-=9nTzSkR9*f6OgR6!jxt<-vO>!y~KeK}r1eK|0qw6-SaaZmX&nJYVzjczZ@5 zqXx9B>IIKd5z$O2>8ySnZYlrD@@u~=h;}3q`B%ld$HHj!AO!D(igy3lBeewBm7j|= zz357cy;7oAb=E*UCw9;2nO2LtK!cIJ-$b_}W^fygIz3n`(Z1PRz=Zc2*MF(DXTYj& zsARK|%QasTh`gBaKvvY49^3M9lV4R;-YA{dBaM1Wb}3?a_;7RE__uW{_HM~mvmBPdpa8lGqtVaX*r@#{?Q5{ z*-9#nZ2Jw&335JI<+r@Kd07P-p6W_q&1s)UXiDNA#`-P{Mx;SWpZz)#cvtk+&1@59 z?(5c|b~G#r<-8F-jS%9MgYGOcYrQrSn_9p~h^uf=mFTsb%)Va%`&g#2A(*+=15dr6h!~FKCy%Bn#RgWVWpXSscoDs$67){N;^xAl}cc4{>KCLI%(!W1u;O4uw zISs#ZSA-JbdH#HIDW!=Xw7qxuB>cU89qQ)So>dkoR~QHLJID54cqc>65oK;nD;+Lc zzWR2@{*$Qpj+BI#;Y3)T{p*=?1Mk!b9!9+0AN)>xaKJadC5lcxSP~O_sV_ZspfNX; z-f2P(KKNgiB@|&HcZH?hMSv>nchr@nQ^0DXu3(!G4rL&5*#@)35HTsX{6qt5W( z@cPydUGwc3Lb%hJFt#r?;w@kqVV6*m8*7brwWwJwvq-d9By)Ss&nIdzotxKtYQyUT zLQAE(D1wMw)DK{?d+s&VC>hPL`>R;H zL_<}2TNp`(evCnFVnx%1WTGwF) zO={cvz`H~x!BIw{-Bi8+)2T5dfAw$T56UL6COBAExT`uV=o5B?Q$aFL_CL4tt9{b1 zFm|zZg{>RE?hI)7yJZ%xeIM$Dc;B55$^U%eJidG@6gmnV*3q6V2UJRxws-}3TW)sU! z{W-7WT$+yD-olzWKyWn7NGdTzd@?Es=*QL{-u_81FJ0@t`|BKaX)mVS1Sn{#?y8d{ zu`fQ-D+h$50U_Qo(pYQ28GXNVy5WZDN;HS7D?Ybnu>k2FTs|q!Q1V)HJ_NW| zQt-N>RY;|DIzutPtn}*q`eAXFJBkwp7KZY;KGpP4#e}tjED)rBU@eXq@2?4U(yYRG z7uz50lS|V?B^onaV7yqTE$pXc;*!$nGZ|&VQ$6Y9=@$vZz89@$J;xanT2J-bbL4_~ zS3CK41o30L9s)HKa>FCygu{6RzGv_1YM?RWquy~8K~Y@x-Rc3?%FBHfBkVxrV9|_A zotwbR?9;s`vNSbw=bcJ*m z5b-@b0Suy}eO+SzQBVsPMBoFbM06w8(fIno-|-!8D;Jp|^q_ArMk6p)6IrxbXl-|V oM*8x3sB&_Ss&Xg)>*zY-LYK-eiK*b+kIyKVwS~Po(bOmD|HQuiTmS$7 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-125.png b/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..744000c301203bb47e7737ed49b698afcd9e59b5 GIT binary patch literal 3505 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 TzxwG^qXL*1nH!cFxJ3RB5#}Ij literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-200.png b/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..4f135c81e9c35ac1960d13340dced8d1ac77ebce GIT binary patch literal 7693 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-400.png b/src/winapp-GUI/winapp-GUI/Assets/Square150x150Logo.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..bbcefd87f1f7a94ce704534691c1ac283df690a3 GIT binary patch literal 31941 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 ABLDyZ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png new file mode 100644 index 0000000000000000000000000000000000000000..93f157ef03074cbce3600ea92833c35772b63a0c GIT binary patch literal 671 zcmV;Q0$}}#P)ZCB&l)FsuNx9T>7@ z;{J^V%*_O0Q-aYCDwC%ID2j~l%^sfYR*3;OnB#DSbp>~>pA$ufCt#VxE|Jc%;N8Cne=KpGozR99W)&{R#(KM~PH=e^HA@ut$mwF^nK zdIszxlbK&mY{AHbZR%KU^8nL4MZs`}c)~^t48#f=m!0P!qX#Nyj2JZ5t;z6vOorO-iCwxVxZ{qG|K{(H- z^Wv!(@?YB?fI_qnxsUUMIG+Dx_|B(i!+2S$qt+P+iHtgEDjBv@Y*KLpW2R0G zPRk`B64K!qw#K-FI&9Mni;a-PP$m4mMw8)XxNW&d`wtfD^1p{$CBpy!002ovPDHLk FV1nn1H8B7H literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png new file mode 100644 index 0000000000000000000000000000000000000000..921cd04416d86377b612a8fd7b0b3ee76b2b9c6e GIT binary patch literal 972 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png new file mode 100644 index 0000000000000000000000000000000000000000..fab08a59d90b6718e9d4bc1a02cc673d7deda701 GIT binary patch literal 1328 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 FV1nn1H8B7H literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-256.png new file mode 100644 index 0000000000000000000000000000000000000000..d040d1f809b026f94f10348cbc9802d34f4003b9 GIT binary patch literal 47900 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.altform-unplated_targetsize-32.png new file mode 100644 index 0000000000000000000000000000000000000000..fab08a59d90b6718e9d4bc1a02cc673d7deda701 GIT binary patch literal 1328 zcmV-01<(44P)k7RCt`dS6yfvRTTba=FaZsKc*U!v`Nv1*fyqYOCeaqQm{y( z5m9WRAZV~41t01|Q7RN)5?_2!Ay5$Y!HTvJ8nwkr5meIBYJ-hwP}C+(6BBLHq%qq~ z=GSxXW;ZjN+3h5X2WDsH-m~X^=iKj{dqz|q`1Dt(;x5-Q@qdb<>Tn%vMnG}zRfJxm z*nrCuAq|Al&DQa~YXrcsqQZ4yz$Wi)E29)v!suq}WW5{0`^M-@$AxJ*D}WI~l+?<) zP$5)*+g?&qpR-B1=MjpEX*7OTW!T+VwM6P{0A498 z)KOX-K>-0s(w_~}k>GNROZy6x&~|x}CIgPiTUHoJjb+g%5I|aPQ_5^1fvTw+xP~tc zeG;PBab|FBc;(n7Y@qYw%%NRqtXo4fse19r%R6!HuUqK;VFYJ>nt)+1=TYu02oVxy z{4|A}&ym-jii3%2S*G+hN-3O}PvZJi0tZ_k#4qEsSTLPsrvKFz@SbxN7>H#WGT)10 zIs{p@sJ(dl?eEY~Rf2~%R;IQf9|?mZ&SPAHa{-jBr;8&i^a#4n4q)g;0v&s|;n{b- zK@FWD?>^rwGss&)yldsn0`_clIw_xZJxEnSbwv?@R4{pK4$nPOkNxkRN5jExv~90N zSNlWYZJ8nxj!`d=b|x?aK^~SDL~#7#2!G{p&slKs>J;Ln_tNgo_+WoC zPFxtl3v`-N(nlm`Dd{gBH(ldL4BFB|s-f*$7S+}PXIi&tBxaj2~kHXXqopY`Fb zFMp;@a0A2d1CaKZFK$ZsVk2RN4U8Cjy&N4z)@BUdQKyIPmBLXlbfi zMdueR(sNZq=NE%82tZMEDe%56D2@r7?*9X)E{{oeFG)6(m6t6hv$D8`o2w(@2}bu{ zd7do_mVa9WreTlBSMpmPQc77BphfEAc+ zvHjf+qH!h|bHgK=@3cp>ZKz4;`p?7vp8+mcR{8!nz^)6afGQ$fB5bYx5ZVGE$mwG4 zCI~ZzOj6Yzx&kI3Ru$3LYJ@O$a4z1QG8{gd!J_vQIjP8f#oEkvcXsp2qK$Zq6j>)F ztYmSPj^l3&Fs$2m0Ts2)$kdcX21It(2!`>|?58kw^gxGmvBQHDKneYN>Ud$zL1YjE z_5&9vF@r{OqpOP9zI3A*A8bB}FJB#tHY12-TPm^sjfpsS{SMadzJPCD8;_i99l=mo zu(cTMP7;W}A?qz@nuaW&0$mY#6(IwXIV}{9Oy;*y)6jwd5$LD(mBto?X^iVg4Yh+| z_tUqJiafCOU4)I(Vv3?b+a1j)i)zfs0r@EXWL8K2u@xoe|EZ(1=o!$_uggxDx(;EH z_C`S{LL4$l>#MHc#*V*gkzug_+(L=k4+8vT1!ooxAPb~+ZfFC?8_3SkaICr>?`|lC zLNeYJNY~GV?iQ_eaZHRJg-%V>H2#H=B@h^M=v)bE2^08 z;+9+(g`tEK*RABpiiY+>@skB$;Q%d`W2QB>hv3_3_dS&@AXWT$6E*XUp1raY>G`tU1 z?>vj$f7hAjR-@=M0c-|sWgjgEVlOzP?gv640g}C|Y@FhGwD3?Sf*Bs=;mwfRD%1Q2zESrJH`{$Sp zdQrnf2HXgtM;0U%$#&va(I3s36>`TL4qig}rAD0iv;+ltS(rt{AHUd$152l4+_1hR zW`0xolVqz73?vZMNgXGi*NTV?+Q6&0NfdK_B#d< z17(++us=#Xw@NC>etUbs%(*qyJy-SM$U2GSPN@896V~lHiy31Ih@j-rC?}>jIjp7@ z1IMZwQ8cojS;g4(;WSMDd@qh%Y{0=K(=eVBZ}`pgl4Tu-&R@l~8~2crHUk2PiO+kR zPh$D6r_C5Yk>kW%fQQme7(K$vWM&rB7&7EQrz>gPTo<_~x|Cmaf5<@Pg? zf|xb(ONjx8MVyJO3>5)h>ToTpTYzqg$2vPI>+t>$Wmq+L0`DY`tRsg~RwEGTFr|Fs zh2I^;p{fQb85%mQiUU0<#X?rz6o;azTV9q!an9;J=W+9HJJ!ydfL_^|rowVms^-J3 z<`yjcz7$)?1Lu?2-EN8cj1G14RF$w#N^Z=&B=Qjp22&q49<4#ewPvh*xe(7kGteCU zrSvkE{Co;k_4m=6ZY6dSlIu4ehq_RzVUQP=t$lrnKu+jOUmwCN6c@FVW!qX_OO{P0 z2K9Ic#@ro~(2;S_6d6V^gdi!|JqMSb3`E&p=euN9|GwQ{ZFXj4YNRR*ciRFTGx*-T zoXKesxgRkDQY6u+yVHirBOXNwoe-CI$qB;)0?r&SA{3L=y4KKQmT@xlL~;cgnPfVT z<=akUB0m1PTZYhK}keGRCt{2SY2owRTTbaXJ&WvA5)D<+N5YgY#URyr4TG)DOe=Y zh$uEv5Hwhjf)DkfC>4q?i7!5=5GjcIU`1O9joMP%yAK$`?d$*$Ld5R6V{3@wXW^C-3CGRQ`HmpFr|HdWk zx-ejqb!}@ws4Qg0#*T@3H{$(I%7=OOrsKl29GT(rV8O&x*L=`?a%(6>*dnqyeify~ zx#FFy4`p5%D|M5YG&u zqdWJf3tiW7yz4S{9XyRJGDVC`Z`fH^fsU>r)Nd@M`ydWH z6{30ANX^sd;3KP6m*yitZsCw|)JzcOD?DrA-jYHzZ>vOkQ8o&)bd(fkp(vLF+6SL1 zn@3`yz%Xs;Oq7%`3t=nFkkHnSZu~hF5kBiDtDCk1o$lP14ous{8k#(>6bGH1EEr@6 zvj`tWm_vbg=(~Pg7@UNrt4sQ$6I=Wo?Mq}eizd2$O#yb)lw1$un|Kqg?YKjOJPvNSH zGry1FjbptcWhn_KCx^5Mhi5D_hE}6KR8DV+HWMGVR;|MO-(JKB`F=oC7p%NMJ;lV5 z7GXA&WzP{c%=pC-2j(nUI$pswo%m|rk(535jyc14^4Sd|$hv|rkU0d%#>&}o!h5L`3$5$7IG4=N>?UrszI;7HC z;)WZ+nS*ol%ZJXPlIo8W`?jN|VionwB+S7X zVS-$M>yTHU2!R9BvP@yk0Hwt6Oazz5!`Rz!KYkgW!mR1Yie4Ud;7=MdkIAZ=FJQNgf_tS3KtunG8BF58yW5k{MpQL{^}SS6b8kXgk@1zR@sRnl|I< zx4%Id)gbRYS0^&e;uJ2E;nKmE3&*) zc4sLc<~-@A!sU_LGTfCPL{Lw5knu*?m?QVtmU8jVo6DxPMItwIosp44C@;N9=e3s4 zd$H%?`%qt7lE%r+8^W?a6|G_?7KL!G$ur1S_{}>n*>{VxTTe1St>vX+XUJUuklnIZ%`%GmL4z~pVpD*zLRjd$R dZrpZ+{{dlj@+B8MZU6uP002ovPDHLkV1gunsiXh^ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-125.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..9554a6a03cd24d71a99d04084b0a618ab2ce64d4 GIT binary patch literal 1843 zcmV-32h8}1P)deY7XwP}dwUG)4$^XbRKEkX?{J)N6Z7MaO!r@6^QZj!c;e&J|n&n{iN zRbN5(E}D?bM4*u|ZV3n~l9ljw_d3b5b zEXX2j7oZSPS#&`O5@DCXNwarOp?6dohsbtU6(#rs0eEC`Txw|KU1E*bg+MTf{Wa~0Bd3R(mJY=t?$I{SIeO&$_@0)!iZfAl_Z(WQRVB$Z_EzKv5CXO(S7tfi9ECOJ7;P(Ki5uOnBzE?o+4rQ@kUf``yDB1 z9t(j-MFjMwB!KC`D1FmopwEx@S1-i;!YrqvIw!|mG22C#PeQ6UQKn~(A@af!t>+{J zXmEVyGzf^@>1#s@4+loc^QcxLt^Ao>d?s0e7B8d6=yTbz!tt3i!Vm?bC+{!7L&a0b z;n>GGx66fwjy`NTbdd*WeL`SmrcQ9vxuu`9m7n3m)-{XZ@p_|Lj89v%*iBvi*md#} zGJUdX<#00?vsr3jJD=tgxtWDSfsr_n781#vQ9TG#%K~+w>6OZWTf(WDCW25qLL#ixdFc2hd zsiYAJ2Ws2#>t9{?;MoOOUUdlbXJq3$Dt5K%ZIHr*M;Zss8SR)%cGl7}h}xE3%$}YL z$rT!IF-nC6%$=UabIH24UMg_2@$;KYux#Tmm{<8dR+P`gj`d}{e~8Rfq_}tN;uS2L zm!Euch7*w5uKPoHV$%`K&d-eKSJmjLJS~eZS46jf^KI9#?eO2&urdceie=xde-K}j z>t9n|MEzCxl^{H_7l&(FaICfit4gNd8gdLQv*@H?Tz#(9F4S=_GyCwTdTxTtOYTAz z6}=TpixDF486b@@Voe>rsQCN@yHy_ZHDW@@X$36%7`Ed^lV;V#b6bv~xqASwJW|Yi zu`3FH%H#AY3dpJvpz!}M;U#FXSI(8Ss$h@}m6b@eKz=jp~A&*L@jeF0c zYF{0~)H{Q6%r3GB!EiQvqK?Ti6JbG>W%MDF@+KNquA9BjOp&qYbPIPJ>0S|a?bne( z6}pGgG;JNl@P^xdZAa->c-S*c&hT;*WX{1@WWA>}L!>=7&C7#z2WcrU-5WJR6Cg%lU!CdaPM+4aDuhZ?YX^&*tbn#?aslMUu=bJKlzkjICk6CRV^=>;Q?8lr@V3qInM|pa7PaDs0L!iH4}Wa{uE;B39FxT==R$_#R2XXxx!Dfu*v&uc`RVx`R$#s78^h~Sv|2l5ppK45W h+#xqnafjSQ#lI}|erLMG5QP8$002ovPDHLkV1f(=gAD)x literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-150.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd4858d9bc6875c4117fbda9998c1230517b92a GIT binary patch literal 2315 zcmV+m3H0`fP)SBhGl2sHNn7g|5$-Bt zBvE5(O`6(2iZy8|q*!W`)`q5kNE836DW;`WvDIo?wbYO{Eq7=Ykpk6Tp`_jcR(dCJ zSGbeo?)GDL`aJKGcTgS8Z zS!^tXWoT+@ba>|%J8Xu_!iz@CAtD-ikdQH$-2}{ngkUdJrcSXkYdgEK?CO}yv*YVT zL|nQP@Ipj_IzmPxoS->8JJqq4S>L&h>ynvF0}Bf>yYA|yB)$WIcA65+NF zFHI$(rmIhpMj|L-35_wu)yTTRlSUK_OdnNEB=jR>_|(%wSP=TR34qjP=>-hF=4I$&@}ZR?7!|_BK|0ss+E@vKAYEaR>|B8u5p3cjFt+97bJK#%teSha}k& z&0=xPk*Zyk=w_tt04i-C@5ts=ql`_4 zZIZSOLY2Wr|ihhHiW>$5X;R(^(IVbatuA)+mnB^wQiqBC~~&#AG>G8=Xi9 zH~Ou0i*e_5^RfMvKJ5F~Sv1f|x1LV8rc#7U19~u+E6q6T|i)QPKq5?#CTql#&lj)3b`rJ6~d*V&8 z(zh(0gD1ZH8LYZ`wy2a$CWMWUWeJS@fPbheIbv(0}fdZTCK;@k4~y zpw}ZzU4=qn^c?$;wnG}P|6qers*0pf+B73h2tl1fT`YtM!HuY{hG&3F~(B;8rrEdmdhk`WTCz&K}IB!J*5XK!Y$^ zipUg6`U<<6mYj;rpVff>U5;alf~OdH!|&a7Ee?}Mxc%{7{P3QY*z(*FtX|xPSGRQu z5)z3Nxm*Rqm!>G-Qe97~UQiZ|Fl#y|XAMVIz*5x`10cuf&{| z82g2Blv=+Z;&HD{qDm#E1geIeMH7fq)lskPGDnqCy|jrTT=|*xoah~bdrpwK?2p| zhTnYlCH{Y=g99LxW8cduLiC}ypj)#7E z2){de1~cm;hQlolRXJAGv4IBzM1c8V3f3vHk89xW1zmyY}~ECw-C>YPTBp zr(6h3gdPcMB@5%^S-FfSJ2Wm*g^uGPNeXN$0ujNJKJeZ!Hr>7q zf7-SNgBKt9?U4wjc=n`-m91$Dh!7U}x`u>E6?3 zneFz6>n5=q&8buUJ?AyqfHv1x@HbRW-~n4`;OxmcQ>YoU<=;MN&ekDwXOX+yN6y-1 z`#{bLYL@S zA0rW^O{J0~Fg`{iB%7z{1205G#83SlUb^3l=IYpG>g?*kQmpx6Ka002ovPDHLkV1i>$PeuR$ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-200.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b57e342833e18d538ab879808a5edb7ef6c2b5 GIT binary patch literal 3342 zcmV+p4e|1cP)jtU}0yxao(zoSBM#HbJ) zF)9Q{j0(XKqe5`Rs1O`6Dg;N23c(ShLU6m*eXj+_9)1zCT$TqB1f$95SnM7`oQi{qS|O1Vw}hs=-r9Cw07#*0D@T zuj{gFJovIa(fqlr4Srx{ecJ~h--nt2bS>f0g$qFmPE+j#{Rkk^CjPptOwjv$IoWl; z^}Xb<*CC&Dk*W?(AQ!CimfjVw8f2}m9dLNgIxkkP;C zyjI+_bRId&xAqO8CKYGTuw?dRG!u=PKwihJQLwKTME2fq4#8{U6D5iKlp75~ zB-qFcLCK<%#F|y-GAXIsmNl|i^TWb9eGMP8?vwIVbJ(P2m~EMjS#DE-HpQI%3fCA5 zim6==m&4#lQA`&^aa_suVH6*^Q6f|~%|bFBBNt-kRq`*U62h%gqH~3u$6{fLG73=g zv({1G6olbNghsGs&nO<>)Q;!hI*7WeV$Bze3;C>U5H%B(=Pk5ZW?)#yw->kI=4JD9 zdM~rV!4ZiDe|V%H8{a*IRBg&sQrp{Xl{EyjkzWcu%L#5u%jaYpJ~4s?%?){BL~&?d z+;T;71$zmkh`Xa!Cs{-{9-xu=pemuVV@0CDd6X+3Y#(*Sny7r&_2?FlyG9u8 zm?(i@w?pK|g|b^!!!R88ob5U7uEQr#oE`&j0{CD;Bww+$yRc9K!7e8#4;j%`8Jbj4 z6*C~=>+tltk4?AAx~5V0j(8vw$b@PeuRsZ(C^6;<_Y)*E{{E6yd~eC=NX8V5IgKV<&=-&%6>Z#oP5BSiTK+S()zToeTfab4wKJ8Y~I_&Ou?lKn(*Vx=Ha5ROb15KRJtaE6>CA7tMjDk03#8ur={IMfP}r za)inhzPw1ocHp&y!9x;@)AXn1ztF$0ecXj@?;Rm-Sq2ZRSj-aod2$Ki>uxEK%eFz7 zF$$bcTDZZ+n67A0;ni4fLkdt_8$|OcRn=G=&5bqqIA%a`z2WZ;H&u6h7V9p+%M{BtX+97ZlL6nsLuWp zQQvucdk>5e-L@ZiJcP;;&}7RidyeAHr?=zA3uYpzDtQ;n@!&QE7Y#vzgtyHTtD%#L z?71DCxZ}!&U}14uL-Q$w1b-KWdx_wX{O09-?FY^uOsYxZpSzFX`qf+UpOwFp4*Nm-Z+42Vsj^?oI8QM76M8MI>R*)MbZ*sFV#4LKElGpngj+(@UiNt z9oRrL`tGY2vRv`A+djs9|M(E?-2+V9y|QDnOkml)2zR17xHx>$UX;xDlz>#AJ9|=u z#Et*`9NQ1{V0uF}KKiVmEiX|mHypF2u$91adtsCW7sk`&Lc!dS=kjj@z^-Ak`n(qM z=k#}O^p~d{B-*Pd6Cq0@bB*Oq5Rp~WO`j&Fi1mAn-88qyK) zu!Jjn#bq`-t$R3xG#4?F!89T>MaDQOG}sNgfVi!rhe)gAj^$_I%+^NsU~X@$)I}I^ z?#p`|<$Y2y-D)A5H2d z1e7Zu930-u$}%TyY;1CPy_{tKZ<@zSg0#fg$T3=+WCV``L>$Q_>3{L+#|*hWS#M{w zSn{UWouK&%zMkycQP%wLZIhCwga}ITQA5{8jI36)V9-fQWNS;Q@3$R3Yv-B8W6UeP zLF5WVaQ7d+J3C1E3Mw`@a#RS87!`seMup&rQ6V^DR0xh36@nv1h2V%$Avj`G2#y&4 Y1MpF07*qoM6N<$f`Pbg&;S4c literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-400.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..c509648334a5a557d9510baa9223b95535af71cb GIT binary patch literal 10828 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 af8&3P;;MlYHx^3(00000SJt*{r z5l&x3QxS{k@GLhH7y;+7ZOcYubrj8sO8WPq5QgJ&cyt&9r;yUrLLQ<TlTz5I{(_ z;Na5fHgvYuB3H0baD41Z#OXxcHj4h|lQ?(#8LFb}m;Dj|U1dm{HU`JOB44nj=Wk}x zL;)irN=59&3N+F>|&l_xEAmH4uh$NaL1rxPh0ZAsw%$Yl5kgei>@y_B~xvzVbZgHLqn zx;DC6iGoucN-;c9mqhR5e+&O!qSDKk_|%xBSY`4Kcx-)nSbs1eV!)#Jv6c0{QD zXBV@$GVm6Mo2$bmS>GW)Z86TqOm3(a+V7A z(nwN7;x2qI7(n4Lm}bCs9K4>*g*3k^I#ryi8}Mz@2m~OeQ}7&zQFBF#=GJB(m+mWT s3+~B>Djf)ry4BtITW*JtfB(_{0T-7lm~*~UKL7v#07*qoM6N<$f>3yWvH$=8 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000000000000000000000000000000000000..921cd04416d86377b612a8fd7b0b3ee76b2b9c6e GIT binary patch literal 972 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-32.png b/src/winapp-GUI/winapp-GUI/Assets/Square44x44Logo.targetsize-32.png new file mode 100644 index 0000000000000000000000000000000000000000..4288fffa085f4c6c55a403229a9cbca4bc2bec5d GIT binary patch literal 1044 zcmV+v1nc{WP)joIvv`_nlS zv#xvRZq@`*s0S{~otb;iXTEdh%&fo%Ht`AIqXSrY?6+RV#XNYf_Jcq^tb)VHel8HW zPI{zs-|~t8(ms;_bx{GPod=pW-Fp#80>E|5a(_AiJCL(y^2roj*sc#&;&IwxQ1(%I zn|hZGmM~Bk7pgB{u&V)>qVRlt-MRoPn;YT#e$gobd43R@woZ1&3?xT0xH<3wrlpUg z1)u<2PP?mP1LXYWOngvG3cg}d9Sm+EtbxuL5<@AppZrVtG^zz4;|5b9@{Xsp^yO1< zd^lc!7^fb;V&KwW^5sR0j0C8$IPMIj@cZp2a6QTcrgFh{^h;1~fdm$uV}E;#@|AJk z5kPQ;bk@eN*OMxl3bsdU5E+oyS}I313TM3hcI+$I|Bd(CFvS#Otl-ft!u{a z^(`_Xy`n2}Ye-qBzw>eeFD45xx$zbNFM)a|fKtoK0Eag%!*?5}Ye*@=l>OT~ia+l@ zLqnV?sA?@5pu`6`v|UdHwjDb(%rs;2SB}X!D9BkmGG}Pr1)mE*ndxMt;3RWTaukop zCUIcPG92C7igQ;I_(LXdChw}`ogF|G+gu2Nn^O9VHwJOGqaAH6^Huxi)y;S`l*Z}5 zA7COEk^VCRRADr`;qe^Wmp94aO|pfSiR03ZKE$N{-+r+a|2-X_I{=9hH)%l`ER(-! zl(nta638(Z-4BQGG?hg|jfG#X_TxvXNcW@R8S6z=0JN3vCR5n7Y7(yNV){rj#Zp}p)av>-4a~9-jCxK zZprZuEI!L!V9#wu5Sl8=X;fdQ!qt(3oZ7rJMleg`f+8W@~E!C>Tk5;?g~|E~+o<4VU3c zmKdHWo}{|c5`7vPOkF^F224k zPuB@u%ya<5^7>o|{BQ}?Nf=bo_lAO{W&{{Hvv-BY*L$;%@1N2C4&W6L$Z--y!J#Dp O0000N|JG0|| zW_P;N>CE=bE>cVQlgZ4SIp_TUcg}x3BV(GAJkj?TJyihqG#kqdtn?`N2C>Y;liSAnVz(f+dcQAsg<86ql zn(>}704oiQ#!$I%CU$LHLT~ZpB@s%p4?&^V7;$G)P0o;q+W#5Gmp@-bLD1E9$A}96 zTibvl8P6ZrwcymnK0L8vK3@C!H1<4t2R6NT3?FR16SHp!BBsX2##vpXlS{SsGy5ot z4?*G;=iE>(0EP`DXWX=WK1O320)C$%bY4yX`=42cg503d)2G-YgOeoby+7l{yO-Qv zD910MwIhtZwasJ?GBnb8MHUdD75B>`>1^E9=x{CB#6ilC}Y5#>|%iCm+A82sWT2_Iufh8cF`;ph5U%elOS}vXyl;8 zgnO)SxkRbRM0nXaU~T|*rhOw(Y+O=?eNQik3CDRAan|R3c{*XHv#S z%sWPwBy6oZi75WcBGzyjyb?!%@`}4-NpCD=1ri zRaMd2)sK&!T#BN+5aL?YFnUHRWt_6C8>+Qs^=&vu{e~h}9DwuMd)W~d#lyo<0^o-z zDG>Eog1PL&i&YJ1XzfMa&NW6NC*LkhG6oQ$jPY=DH%_(nppYVKTu%ikdw!oEkWqi88=LPfNA*i9vF^Cxh45z7b;bBaU5;w!N$AFt`2}4Dlwb>?!(GmwPVFjG8LtyQy2$=)ek4y zE@Q*edB_b0aPavR7@^d8Mgc`;HA=SN$J^gG;vBhER?KizlGy`b(bF$WM)5IB0;VJ8 z+Z#!^^>5YVlc$!Uv@nEFAZ9>h!oOehJ9bh$P-rK;Qr5>R)(C3S#C#Me4EgW_snI>J zAHo|OZpZzLZ${VUA?*14SL{1_-bm5Ss?UoRa|2*6 zRm{TK&VDr0RYc&}d~9x`oV?O1#1@1do7`9sKr!Y10iTPWIxW4eEtd?ME%Z+<-G&O5 z%?KsBF|S6GG9H?T?2Zcn^ElPy{vM-({`f1ERR5}$vSd87R56*Zs5kqXvm}a8pDd%F z0$z1vCwE=U)r%GFT>}xU+g*owCAsjHZh0XA7kUOUL>`LO#XK9qr$TxHM1*v->8~)C zC{D-jJj8;Y-$RYy$qTWgL|Jr;x+(H1bT;&#olajZ5a|H;1W9%k#_gag9a&A9`)^Jb zKRCK+VB#l-Wn#B8PHW*m*(rn5A;Bbzh($07*qoM6N<$g4=`zc>n+a literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/StoreLogo.backup.png b/src/winapp-GUI/winapp-GUI/Assets/StoreLogo.backup.png new file mode 100644 index 0000000000000000000000000000000000000000..1683891231bd612711fd1ce99f9311b73b1e3f98 GIT binary patch literal 4280 zcmb7FXFyZSw%#Fh1r3B=6p$90p-K})Y9M$Z6qTY>=}0KjY#=oPQUak%5lCo;A|Oo; zAXTL!ND~OsL5irL+`!T6x%a*M%IqI|X4cI5zHhBHGY9Vurh$_PEnO`D1OfmM`2!B# z1D615N-An9N@{8kH611RFEe>eRzm|}GD+-bW#fY@ zTr@PY_VkJ;_R%u4$a_D4@=F`XC1jUZ2&kiTY?}Whd*S?d1OOnrrvy=xZD%0>2n?d6 z15<*3F*rhiuoy8h!(|LT%)ZuewwZg_=8N#&d`_TpamQF-grH*b(@C~30bb$apn&&gLWR}E~_ z*9ubR4?&#;yBZknc;sJ}98KchvEX~W8V0viN7_@AqBUyGvU@`Ee@${xkePXrnO*0# zTasfUS%ph8rX8`|MfDtsdP5c!^Gf=Y}PV?jY( zO~pJ7j7h4lkI>^onsD#S&Q^2As;5CIM}efk)hy~rf2O`BE5xsXi`Z?w&4uMI%*tU_ zygc4S<2_lE)u5YHegH&y!LEw_=2pck^MQtbf$Pr~QC!I7g#ha4!lc$#b*T_p`GcwnDt1 zr{|1cH>A(1WKY7R#ZyT^jm3z+T-7u)`E!%C+bN-HZS8LpmETrMjdDyf^zF4whFI6T z^NhQZJ-1qt8OBf8?;mVELf zIfokzF#%k&Ug@o7f?WVL^^Jel?Ax*~1m)`xTr5ij(LM*hF%hPZz)H$l0}T>jk-B4Y?=oTI#T<<~%b0aM}YQHE|0 zMb|biU8G@k`g5D;ynKAvgRlY8TJHa3EU3Y1xp%mm|8yAr6G30$(Q)}KDIfMDYv8|I z&mI|7S_v|~m%QG;F^6mQql`cOE9`&rEc-Xd{{{B%#$Tuza%=6nX=Q0K(VaRa!Ta#` z;}%0gcB0B}r=x^zrDg_Va#GI;VoZ~*8X3C+@WN4+HdAPSp_d@Xh@%MSm9_`7`pWQ1 z3iZd=@zP#>*W*`{=Is9dHNCIt&UihuBao+08&wqSk)cs_zY6sS7bgRw2VZBD{T%Tg z4NP=r?R=V)QT44KiLHV5H_qEf8@I7^lsMZCVPiY;>3piz3hsuS#B1Js?(5W`YvzKn z#LX+O40BmrTWN<+IS@_2v24k~w>N!1Ks>D`>5dl6?v57A*Hn$h*fzelA%NMEVYfHW zM);L^P!?6SmG*e?e0E_dmGHI2tyf$j+;1awW$ZuV=M{V3%wxJ>g5Wk)FY0Jd^x8FF z=BinHI(a>A3-Nqgw10?;!PK=!zP9(Yl8P|Qow%xRh}vku>+0Y&jjP1XU>1dllBYjp z`XC;f0QlJ~`N7kjyhmxM%yEx3!TNXCrJCrqfqF85?Cz8Y=rvv*#*j+TR|O(N#C`IV z&(^g}rqutdG8NqBLzTq>)~x6%0V5&Ko4FsGP^Uo5U-=TRDg~Sm+<|XE)Z#fbvlN^? z4glIa63I0N&H&}iQCdz>PwpjHei3P^(q%7oTd$Ry3dZa~<1oY8y=!*IY?nPwpD4O1 z{vFZx_PG&Oa)GEIonH4SEWOm~%=w-2I>Jy=@Mua{OKL*PvUYNtA*N%_xJ_v{%0<=< z4L2LoEeX?xTSYE}vjg&+4|FHP|4%agfK4Ma+h%5Ej#xHkXDIgFhWeXVwr#ENQNELo zsPgJk(sbGdDt_zyziCS-Ad8myDSFE{{{Yws?;j0}?Ya*-mjeLcuzJiriVk^bB|Du0 zDrWq-y4j6k1#ZZjmfLsOC}*1u=3bUbKz4_8ToPD|on(!zE?)_^gd&slvWQGu@#SWk zJS$^_h`R(xndHdDpt`x-&dyivdbr2^V3VR&!g_Jx9c4JW*j?X6TQ#Zf0MN#cUrrnO zkudKDydK&V@V3=(4q_LibY2x@v1k&`)z0fMv`Z7!!-h&oPK+lBecL2_Dnn#0kBtzo zZEMS+s5B=uFLj*5nwah=8$y`oWL!|*-iY$nXrvcD!C>M?s=d0&0l+8~pPe-9P8dXG zuA7K-E;!YHy_qw+X#ve$+R=rnJk%Xa?@@GeyyvvOp)qB$fE@UwQLJNjhC>;VV%JlF zHRK&MZbn5K-Zl(iVf^Ak;CI_Yee7(Oqf#lYv)T7M!x?=Zog!Vn!*Qjb*ayGji7U2H zyk*57QU8NB>s|Mk${AJuKKV-s3uYGV^~nMc|9yzYiv9jI-`A3Lf9&j)tul)6>-c;s z7*#_cpHMWjAC@oZ@HYLcW}lKP`DDdWB@a$ir$SdsmTUcldOB zbPn|G7)lHVnH{&|=fC|S@#=q*S&I5hE|Kr`S(|)~IED=Y{0JVA6@ghQx1Y^903K?# zsg3L?udFT={F%-gv&cqb__94=6zBR)=Y_kaFpG3NMXP?jQuSLmLX3tf0VcTwV`unu z6@e9MV(lN8(F&?`jD+;D7bQ!6e{8{S^fab2wB&_=uDswHwn5iqU2?D+0F4aqufV1hsa;LN>nJj*u(VT6qG2~mtU@*AN0rxdl`Js| z$U^nl@L^Y3+c8WpVa{a#IKVy7uc?&NplX1FkqY#1jiJY?Ei?D9sLjz*`IU+(G~uqf zT;~Gk*_v}ws9b2)Y_|I9=*>ow@&0p{=sNM;aY9zoYjgnkSV-^=d)@)SL(k^kt-1>< zAXQjQmt7m{TYXp=g5TVFoKH$$FX4#RFjZuRwsF@xhPfY~c@id^`@V_+J;sb^vn{Pf zq%N(VQP#90S(IMRM^v5GSBQlo+kId+nj(jLE`-WZoS}Ei0f5DHX%NX z_YOVxbyQw5n3r)YjnAbhOLn-C*ZR=Djs0Ve68)y6@?yW!h>wk+c(fvmA(7yjn%SuK z!@OgPnMjRyu}_kX?p{0q{L^f5qC{D>3d;89PLSl_^hVdjTU+|M*^y=q-SrWXQTT7s ztlYGhF&wMm3=ySX<23>(tZ`PE!>9P#WZmpmG|KQ^375Yu!BcoYNZ$#c@yB=Q=q?JD znlwIP@Nq+v`LKxOp&7rrHpIgAKL_7o zreifofkNn2<4xp!ifoa?vibYPAndbH_Cm+a23BjW&(H5Mi-~-mzgXW}VDDks-Tcp& z@NgG!2gcx@eX&p)jg|faNoxvQQdHzlcMR(Fo6{ET3WzZ+S!jI9W4(SY#k>PsG>0k} z@nq@#E`sSLO)gz;Gl&^7cNR>;UP(xB#c-fl^_qVFRB>^x+KE0fr^;wJo{pyOvK2$^jg+2^I+}T4lpb`sGQ$luE zYwmF7GkfeZ6J-!w3$|W09{CD0Z1-eHg&`xAQQ@NUle=m3MmC6*j{=Q0vIzRNh+2c| zc~v;Vr|FnWUjv0!6t{Wca*;2&uLR_Ga{JCmSQ4|f_kK=?m%F!QT^wM(k`A;?tex9B z=}{SaY9;O2$6oK&VcyO<#yd3nNSj;W6Sr$VL@11@$9_h>^IA}{aXDc(5q>@-Ux;`P z6ka!etJQp!?>{-UVfg$w_j`gb_cumdYRjmC_1htDda zz}elv81|4mc_IhEPoQVNkeTnFdU?pEdQ3erDZWnk+)By=rr+w^arX;?;g*aB(a#eoq*f0ik{+4-nSJ9x|jW;lA;=-kpt8Rbikml7Z5RJeDpwWl_I!Uv&OC~w?f`F7VlPAtBQ@}3B1G&%yPqw}cWfWtoIccCQAU*~Xq6QKtq9Rk# zR`EFdoMUcl<+Dz_9ny1<_n)Bu4-md@Dg*y7k?wX6PxXZnjs5!&O;W+|YA6jDN++SK zTx2vF!~ChEFtRLwnECI5@n{;_I(zX|Q#%3jNWthb*Vxq?!qQ8|W8E#6A>Db#U^!n| z+s&c&mn_*gJ-Zd}?EVeqMMegP)Yv0Y6E&mDtac=Vy@xx*u}-S4p!RVT`MjtZQ-SY) zX{RIAFm>!un6X%LzFR;nlnld<8E01D^&P)R$E846fpEl9jE{Bcb2~BptO~rlat@Zg zunXg=O7ZdCb-3e|-6-*c@k<#{!sg-y*g6gCKHXfy{iQnFwfy^68| z=_SuKbxb*8wBtQ}Axy3=CxP4!W74QHu?DwYwj!yR+v!+s&DQ4g)sj3#WEZN_a0SA< z-qqcMs~&5hYePHk$>GL3?}93Eb0110cK<%|RA1a%3%~AF@+pC^-YlfKy&J)3jz9*1 zBBVn=_aI8Qpr8|B;&}l&8I3^b0|3N+3b~#L1M%zf*kLCn5L41h5C_P=dLy(;>G8w; zR+++B`DBOCefMgb)Lx`>bBM=zCyfk(6_bJd&(zPpCu!^P)Riqo(hY)XR!Um@0*k!xk{Z)Q&@9LelybL%u2y!I{?#J( z**={uOcTm-uw5{>U~>k`3`!3T6-)+Gu6lH)$vtX8KS z>BzBeT>sS9Vy7>kI08@Ics^#HJzOw_!y#cKI{DD$^Csiw*%MH||0o&`9K)_d9cVgs zN-Ey2jh;RkIJjTN5*EghKi zXuZh3o#O31dgf3TFa=^|10NDF1b{HPZ)XvfARkqNTlTl%g zJ*1+-j9=78W@0|yeXm$96pAL7W)PeAwW9Ws&vD(Q;|R!jabhaab}Gz9R8KF?0?!d? z6RDL(W#>UMX5Q%uqsl43hAj~LTdDrpge#|y!n$P_gC8}_9CNvXyjD^_yYkMLYX|YJ zCf>+&-N4hEn()%*W^7+O57Wj~;!8S-xsTQ33UUM+R?k6kKo>v~iRAz!jrIZ*!srZ9 zscF^@yP8(&h{g;n!Jnst=%e5lAa8pAqG{Mo9_i{Q8u0KnGqCcFJ(xAI3ZJa06(B;P z2)S$n?Wg)EU`AatkkRCNgD5ZY;lq`)@bbCUxTW?Sis*3=V8hqZ-gye^w;gePD$N5(Ch-GZ zV=G5q>+MVZDv}Pb*r(%tI?Xxt2e7QJhQNeSMa9`Yi)O%#g~{mSAoyz=nt11{d(qn2 zi!+EP8x@?QaD*lgpZ`3+7c1WU7Cq#4SInORjw^|}hJhPCAG&*jxb4MVcyDhDhEnlm zIc#?tlw3YHx!w za>|j>pe2oN$DeXML-S09$WybEPy&>iHS`ES6`($*CY#THOyBn2XBbDG8RqL=P+S6^6ooqQjq z-3;G8hi^|$s82i<@#BV_haaxlQyP@cdo)dHD^eh=^tOZTSbp^se70sbT2J)Ct4rQw zByY}At=&lVuoWlH2C9b^qqMjPJDNL$ZaD)fqaE1T_y^X!@eOXcbUY?kmnGvx=IN$- zWhqvFiLVpqmG`~18=o|`3EgC*{C*@r?)6E^hn}H|E%ojz*=E`Oa=m1-)l@>Qajxsx z0hAZJe21v$7ZWOIVlwki>J!(Nzxy!N){%I#N_{zil(i;hAZ5j-Lq3qUIsJCO$qjZY zlaww?f02B6(_|{x-H&Ed+BudfR!M^z5c9E>{ubH;2&8|i^w&WQAP`N``sav&_{2|z z6b$a~!YLkEr7PosgD=}$Rp3p|_ZrEcIUT2l^;0JasTQ&KnUZmt9GTph<9|09F$a&0 axASkHt{h@c?`{hK0000j{00009a7bBm000&x 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 O0000002t}1^@s6I8J)%00009a7bBm000&x z000&x0ZCFM@Bjb~K1oDDRCt`_TnUg=#TowI%$wPr*=rV9TowUA0Z{@(5UG?z1;HX7 zNi4CV(K1@45-Fo-QpUuJiK)`ynW(9Xv0^MrgPQVA#GoePLc%2?2qYp4B7zH9c4gUR z=Y09Q-yCmxy5H+JJK!eyt7hjN-GBf6_kVZKnoT!6yTb^YF{D#qV8I3h1+cRB7D~Zz zym#v_K65^Iz7xk%jVtTt1>zVw^zmmw6ILqOWzywPVpgq)KsJcmd(}I!pU7M{TwXXW zxZ}$+n*JDunklmJoH&hO2uLMEj9CebNfmu`R!0T8eJ$ZzMx3gR4jvUvI`pa9SoSAj zYT!5~;3h=_1qKYW^uSGlk1`kogjxD5IQERPX|u;25X>6*Drj4?0l}i&4b{}(X7K$Ce?pwe^6^T*1np_f##fbK{#jh?<#g-QA1p*9jiwHbh=V1nVe@Ki&_ zsNO^xqXtDW|MXF4h?oLmAug8XEwVhr%HBxd3Dl}`AH#u1GD)k1mO?1cz7F;z@$8yi z*n6lCkx)>Jofs1V-Tyc^d(+={xUI(&5B1sK{`hg+ZCfWN%?A@-5}-+RS$yw}!_D<4@& z;uXZ!#TQ`0h!{zso`djWsI#5;Ok|44&?07LZ`i`>cu~EeG>^$5#A%EjR*xBzhhuzm zJ?f%iVrB;CPies??cF$Q!g2h5#Na5-C8>*6nP`qhkWOcm>ZGa;s45ed&dKQq70-`X^c6p4y%4L2LUpODO#0ODv2BCO~8V4$B~^1kc~>BDOQc;Kb*zA z4r#5;EP)D@%E=0VDv&$S;w4ZOEw$=A4)A1_lh7bBC%_6}^fZv|X$A^Y3T54IDDQ^t z-pnEdgSq=k)jgekyyR6rP9~F7QXm6UE8?|mG@XazShjJejnu`<=0({kgf{$D?pkU* zEO#ByNi{Unk(>>lN2X(NL)5j86Qz%ICxAU2y}0V})i8oa)p8GB*g zyX!n+R;{)H^i_3WYS>sCX7K}p{b5!y4IIkBrb-UHAWfoWSoVCp1}{o?w6J>^=5j&w zZvDYqUf-O3P`4;jLoaleuAg?p0r_N{yDDBA53a7?V;mJuzY~)%%X>)K5UO4>@9gFC zRE$uy6Js+~K3)p`BO5)`OW(9JJA62v!P3>cux3{W z>LZ~dhUo^dveCW3iLYWccsBTvcnXhxdn&G(eX`@J7+94Xlp45_lJxWLeG41*bfG3} z7LA}{lG1@_a;wO|JCn@*K$6m#5sgt?I)hahQbFSPB8xAMvE#nqQNhiXctoL*Gy>*h#`%RK@m8jMh? zp_{u`;bjbCSY#s z6DdT=@S8y+-=t#9bIT3YalHDCPSou|kN-Z@=}=z^^%Sh0cSlc!|&b=9UTGQ;s# z7xaQs?U*!{`Lcsw4W@YM3wjtX^>gq{g%7_>qxndg5Jf#IIx9-EjCF`*1(=IgB zgi)34rydd7JS!_GRePO&C|i~Rmhdc0;RuWn4)+|vy0!y&e*GuNP`d+%;y6AQ!L4(~ zz9mM*b9r%D!rI$DE zMV}(WQ0j-9f!E38ib12L;c@L?8uMuTJA1Kg^FCZQV-%iPFf-dIjWkV^Gs&Zxlt`v| zdsY!WWBgG1H1p>jzxo?q-0-QAdX?Q2m{lx-ZV9R!G+Vo+2aX$-5wP#8k}B zaP~0+drO{e*D-wIPzY6g_oBx=4i?qfFfX z^m_c_)h)arcKz&=aPo-$odWWm?x^KQP+AXrqC02_3hpHh`jC2MQdwhOAS}nIAN_$C zwQL)Va1alWRE-ASdx;w*BeoKcG|oJ5RDXqTgZ>U?OcAq8{FpK7&s#aJ`{z%w8I?|OJCz@@%=uKX>R=O7 zM2K4~B}$fnlrf5Vt!h%E&R*sP?Nc}zQoUIdhhf60%~-KzA1;1q1zx)4Y{cp!xMS&h z+)a!!vW&VVD^d9xTXqa~?2{d2%TMcQraLlV-Y`WI3bpaeuk@lvkDWBkKrC>qX^%9j8PY58O0A+*f?3d z)z_Cm+y27{n{GLy4dVKiwIeNq5E` zh7PWv2ysMU0U$H5-(9jEcOL5FTVXt)kQzjXHCK5TgDpUipOR+YZ%OM;VpenhcY`b! zWFGF7MQ7uJhu+3V`?|6C(kb}<*C&(x5*US?c?jT%ciMQW6Ai0RC#$T7H?t({SFH$J zt%FArxbDfdeD}q0j_8J4UHc`29zbI(f~QvR!nNm|h)JWH3Q044k0InKS zV&&ouHZQgdz4qfyeCyFxEL}Jgbv5DqvGOD(%oftcwB{KV!Vz)~m;bsIt9Kp5klHZc zIrmMSTh+mf_)3FUz|SORFirIJE$x_h-(Ruh`mbQjuoy4+$?PIqt5B#4AAj~azVrCs z(Ym99_ZKzP4^&hQ{eo(zN2RLlVn|&W>vnhIoO|BHFE5{pZ_XGEyRMbaw+V;sA27VQ zx($n#Zopnjq=#e~r5aa7pa0N7fO1zxW}m!ldVH1A0vW=g-X^fUKN@ouKLkFQ(e=Sit{YxZ`W*i|Vun7OL)*X0* zVyxTFKLvBX)PiLjKgInNobEi>gF&@byzcHi_$wXXMb!L-4*Gbd12{odU~^_WqBCUCuuVI^PDsp8iae!tJE8oR!ZUcW`UlSCDILarN?dG zLK_DmVZRrwqR)|32KA(JM)tWIeQYkdBA{WY$5Ys>V$z+B(%D2bQe`5}ex%u(<~4GD zLeKFVK7It)fy&CtNFi3S@9o#wMAj(Gid{N?BbYxwpOdwm8s*2!wY3{N`BOlXWr$^D zX|nk4buCF>#Bs(;M`aw$lJl0W7|W&l`uZ?-cmsZW^>nOl>)=0`6Vn0_ief{JVxXA$ zh-EI(IO}>fv24cTvKc21;rV4e9>@02y2<`|=!;IARb>Y85ix7=t6OpB{L^sx%(49Q zzXJFFX)8XY`87n{e{0A<%fQz~P2BtXKk;FEH|9$WU!coa&2~pUf_Hy!pK?EH;3t>y39iiQ*9t44 z6r(EE8ff6U6?UwM_LHxbgS%3>tlm~w*+Y3L>N|EZ&K`4qdA8ORZ}5x&41id5xmoN+si#-|J zIUHh+6kx|DrLw6c1-2_y*r}Mq!AA(PgAI0s6O1Kr?7~ifk^uqH1wtT#K+;MGt&Y{P z+SR^m(mltUp6)M%~>8Q(XO`MwO=<1tRj*h6kIS)~tQ0;^Aob3Y;ep}Beq^A2{_9#Hrsd!GP}17j2KOJlD4jY5 zV!$_SU?ZAl(5J8;NkKPNAhuxFLAr*^%8!iT)w1CbjEwU`5lADQ`Y=_JDW+%^d?*5` z2XsDscn>spplsuqq;^RHRJ<$)$$s@m!CWQu{sn#ZrX`euBAC!xkSAdv+hEdn1?TLK@9wGG~F5{^+J@}8++tJ>dvld8* zE|+Rjv5E%{74dhM9*?`PIaSB1C3%~9SMQWhJFjhU*y43?^`hzMeq<$n@cbrpjmW2> zlFEjZD=x;hx8UYa%wTsF6ifP)y%WqsH~6L0qIULKf{0UGgr*H6UE8o8RW9R6s#D14 z@udrA;b$*zL51notfX7zy^z|yz1!;pN8ZtrLu)?kD{%n|yv#%AqFgSa8ayxLGK`u} zrcx=PQmuN|`D}*i+Pqi4S}8N#Tdm>J3Ci&j+qC$^eHU3Mw6N?ZitZXjd9AHmL3eur zqem35^Ux5o*|Dw$5mbi_jf^Or-CQwudK2A$F z;M6^hFaDG%6I8X&HIR0ND6Qc$mSwa3ZysNZr7yncl}s5u0@t2>ocH`2zjz%F|JU1I z`B7bMxZ%9nUim-%Y7HKEdV_b}(U!xVSDwrSGL)JNM;Jy*<;X&#;tP3oiO*>(L9LQd zMT-y8=Rd3jlB|;*v@=!aO$p{mx3%PItwt2Gd|7e-7_Iy|EDzOQhS$kS*mScpn$;06 zwA!Kg7TcN}Ke?_>rN!?>VYsNBxJZB)4{21E*bxU|^x~ISm<_n+x|2P%G-G@REZ+6c_LZV%KXkJ>i7(k`>-nbIN5);GnbbBCs8d_HQ5z|Ufu#VU5s1#akinaEflF7Z zRM~{^XE`s*1nbBXK&6NTo;wHi-&Gn zfD?`#%N`s~RJjybrB3KN4h{{K@$%L_6k4*@CkPXu)w{D~ELxwF29@ymz^$NruY>jQBH0s6A1yXh@EJzkDc}j_-aeIX}5yTX4{X=Eb z+Q!NFP{F09OnOdJ3&hv0aktL)V2H<1BmZ(QCNnRs`+lp^T4qM%n3&f{TU+N^B z4K4!1X?I%6R732v%|P4633A;e=fN+FyCq?yVGaV_n3rb7B*C#kAoM)6bpS}u$BHF; z^<`7Xm3X1*sn6pg65DC}L)q|zT$W6UgFzs4w`CG?bfF7=#BwhvBWM)f3)_^|=5pI&CU0n!5?h*6%=PYd(e}$(azJr}6G|+s#zn5YnAGAwW4B zMF6)QGsbmzl)}AsGIwM{56MO~8aUr!4R@SpOc;ss+V{!2CdDiBXRAAGyv10*&8d@! ztg2j`cr^>cz98tzs^>-vNnBSZV;r@QKXC&d;zHAL0diGA$TmbLT=_Z~>d-sp6=H|hHB_LSKB{v%lWx0mw7h}f zU!!B=cpak5fKzxDyR(o9;brN;Y5?5$rH%)kFK<9+Cg&oyW2nOs8)SkxnJ?K*hG5Sd(MS@1T3lvQLd zywP=E7bux)5N%RSGb=;t5Op;=Hk0-BIX@)s3QL+_?H?>5&mRBn#mD1oSD%DJA&<-E zPsRLcW3YJXYHZy%i0+O8a+Vzs{YjGts$;pXZ=HzXI;v72YNhg8((q%NNclWZkz`OU zm$3SsU3lfaJ>E%4z9J4BF7p$9xPYySyRMvvGv-W$TP>kj9P%d5XBSP!qFLi`&$8F? z*vnh6i>b3trp5|Va#Ey7sb87bL@&#%auQHDzOJ`YhFflq((|t(goi``9G>#UD+_Wv5T^crTWQYJK87^7~0++wkB`i*U<%v+&~; z@8ZeTJ=n5$0EK)8BU*CtRihYFwWo6OU-9T%FPBB-Tq0-CYI`>3ZHbPgQ~~J%JJJVD zfQ)B}Gg$oNSMlI~zl~D=5PwMpU87oX`lN1L!US>6!l^jn*wG$h@m&@filqwpi%BO= z>&Cs)7T~L&J|4?g_28GQw_$D1KI}O(%)wrC9b?4Bz-V zc47zn*XcEwdsGK5ePD&}ktHB=rC*s9Ng&a%iGrY9-_$^Kl#0HU3cBc24TNh^ixUJ7 z;R~0=QV|Lmqdb}>SBHEO@ss+YI^*vX=5x%R1h1``rmu}-lc=(&3F^>Q&0t!mIKwuq7_M+v z4*em$$wYx{S!Lc7VdU}t?ta|(FDtQg{~#W|X(3LXIgU95Wu?AOyHYGr!>N?Z>v(0Bu_1KOK-nWC}78)eq3??^H{xoAF78+_;(hUoIYzJxS=QcA@&n}-YcmwQYmlZ=o1cs?0X{Osg6Hx{%RiqV_X9K-r{rmXbLoa!4*KOU0CATi} zg;Mr~!rj>3y#s7zFkx)p?+K%uMf%I`V8WQ`3q#R>4m}0x8JYTCM^HectCNt(YD+># zwjtsVrAqMj(7=#jbRBq^-m#$EH>4{SeuqaYfx>5QZZ&N6mJVv9>be95UV)BRFhh zgUciI5Os;UsdJC%#?sr*!ZqLfJ)G7Y6Ur8EMg8y#=V0%F0bI#e#;R@mysU0lurf{) zghA2U&Q?^B378*`r8{R2sZX8AUY0-3PU7k)maC`^9uCz4dQ=1VPw(CY{s%L z2g0BXxNyIAYaiX|1Z|-;>NXV#KCFrJJ%|_H*^T1R5Q}ni%4eNi6G1a-;&}yU95^z7sKJqHQbHf6(wYCsgMK-CVLih}b zPZ`zanlX7aJ~?MRmcF+NEytf~(@@fWoE)w5dOsD@`60LwLK+L~E_!Ac4u3^jj zTsC9dy-x!Q6)2g=_e6N>6-(i6ju~hEslE1*E%+%j+<$f9OdK;|RJ1aMm2$x;$KdhX zfSdpI74KDrNB{CnESNnpZZm|gg{1s_mny#V%tpBUx*4ZVP{(0wOV85>X@Yl-$YbNK zeyn94j4Ql#?*yCAuGC0fD>|1$R(^p0=Cd+Mpc_0?BfUfwsbZk}rL z@OMGF+gsSm+>CSQj>n&Vd=?7=hm_E%SS({)XG>u8%W7Z>D1l>7?|v*^x(2N++2D~p zox(aa)hm`Yxq68x7slQLhjIOb zzef-I?PjZ9JlNlm=5iYSa6kaV^Cu+iF($1X9#b54E1vOQ^7#@zyJ$x3lTae1Fej(e z7==O$cI-ZgYad*JSGM(G3|CdP6-01gp?Vafp4`NSMp|W(9!x2&1ac%U<07=kqbPt39T|~o1{Ui5w*-?=?0MSXyf=bpo>=I@{le+OEreM!#`Wq zgVii({Py*y;F8m)BK93(Zd}<^&14FAZ^u4-<)^Fgi`TZJldXyl77UX4R7XuKQOwH` zmQ|!qA<#jX(zcxrRA?98>+0JGMTbWxuyIT%TMi83s_(AA4QFn{U02P+(c?$K-v!K} zR4n29zgv%Q{b~bt4is7a)*wL{&XS zX{?&?Cy&xc_F@a2p_VD=2A@izm_UBc-QuwwE!fYD`<+j$#kYU+CiV@M_~A#49p%R+ zA2CE^IPHm+~*TSgQx$UHF~w}e~L zvaUDdY{bFw+mgveNp6aZS^E-c)&E9vnm;v4L8F0YGaYXE@1dlLhAP5m*!dXM!&eTq z6rbL~6@|h7U`e^^E7VRlqxe2U-#``lOq7pG!-%QAtd4;CR#7u~B7qHliGN3h{qivh zM^kr*TaPC=bwb3eB%6G{ptpYrTXywh!srgnp3;Tye(rSq{MD`A7HG69NIQ37w#U&8 zGxy~~uMN?Q_fc)JB%&L=H7nv z9vouPeJ0LM0;Jeg@w;5CxLES+CM=je2^q%8jpxr||7IdC1f8=s&dcY)$qe`R zNdK^a>zjL*zvg9_yFB%!)-XX`$;FP=9KQb42K@T<4{+|B@t&U^`x5p4Q|KEkCCOcz=S!e0Y!m8@HDN{(b~M>c3`NA0xiI9uy5g+Fmvrr@1ayQJvx6ow7tJDlyGr zYrSC?%lY5^kpK2adJu39$5@NdjB%IEMk;>QaaB?$^w8@Kd-FlS=|(?kVjHq1HCW9M zwyU|;(NroJN}M|X!p?9*L`xNoH7|`0em(7*An?SGU~)K;!l0-+WU0CA$UyVeZ7%$@ zsftSzR1E{9jk-fP(mbhUj`}dAP~?Es@YkZn==#WlZZI0bqfZRcbm|gz9m0r#qw%uj zWMX|lj3iYiUszol{Tx%Tc zVjk0+R;Nqz=WbePAYPK7;SVV{$^Z?j+DK>9j&$T?$AwNaSfoyx17_8fu5}X7;%hEg zWdlt0HBTMkqJb|t4DSG^<={xI2RUwNDn9rstKekwmO`Owg_^x_9(6p#y(>3Xo(7j1 zlZ4*`OOta{Yl|f+hpJyl`EpzaD3`oc?M}B`8seX~YXsy7I_9Rzfr2J*&<#p6(@Ios ru&zlYjHW5yNR{(u%2|64pVRk$&n@|^Mpj#A00000NkvXXu0mjf#MiL} literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-400.png b/src/winapp-GUI/winapp-GUI/Assets/StoreLogo.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..39e3c9893f04bd28eb126e71cdfd266e152320ec GIT binary patch literal 26548 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Upload.png b/src/winapp-GUI/winapp-GUI/Assets/Upload.png new file mode 100644 index 0000000000000000000000000000000000000000..13342ffafe15a4ca0f46b51a747ca12d4d2c1fbf GIT binary patch literal 594 zcmeAS@N?(olHy`uVBq!ia0vp^DIm1^kmP;+PV+Ee3|cdK39C=w1Nd_7X*}_SE-%wCGOJYTeDy8 zem?1+hgWLdbkmRx`Y*4C^t7LB>_`$)7Cfr&rqmVR<07(OqEn;JP$)3$f5z-(&(e>Z zE?V<&^2f-aEo+`{?RSZ+=Q+zUNig-S;WO5g9h+F#jrSSbnZ{Y`cbvL!Bc>Y9U0m~C zVsZ9_$iTWa59`cs341>7GZf?e6fvc6r&`*Mw-U)06>U%3_D}6&uS>m4%8TQ6 zXH0mLTlqf!X;j3bAN>ju3l%!1Q;sfAQ>f&fx+jP8)1q(7e!c5n;yLr>e_2OY>r{pX z&BC6_p3f(-z3O+|`8?2)nfrL}@;TODkJmoFcmKoz*W}>I^J)&h+hSAneDjKTn^Kdj zZ`D=nexG}OkyTTN0v|_Gn?f1}k+!%=#_QJIPnLIWkS#yO~89SA$~B)78&qol`;+0Oqp$ As{jB1 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-100.png b/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..35fd0e71c3790e67347e7514395e596c768c09ec GIT binary patch literal 2919 zcmds(`!^E~AIB#pWFh6YmPqKETZ@rPj{7a9<-S~&l0`5T_+htK7l_xp3+=X`#8WxKiB10|Iu0RRBd(ZL1| z0Ej^M_F{3dz3x6%yS6tZZaMhG0s!)-|6L-EaP?&X;E!xkKFV^a}J)kQ2 zFf+7OL54CyzFXu}t8xAi!{`vFmeco-=f2&Rj@GRXwqi4z9__%cucOZ9)lu*~@V zy~ecSb{*pDU-gUK;4PTpb@RS0>UV@E`0Mtw8S*`j0-GDk!QHkSjUhClHw@RTx(;8F zLR$A|8=;0>4B+tdk(N8e+M_2*-yx&`&h z34)mb_N3=$l)lE;KGiF02r^l#cbAT0!}Dq|U6$Lyh$c|`(P(RRx)^NSJ!j|j z-A2J_RYRQJ-H4K_@ZKHPz%6o{e1hOcwm`FJGw|zKZWO9|#53sG%MP+XZh6 z58i%X$!AYpLfaHOxfRWH1&!RtuSAjbhh1WLg*z#ea8mFmiHjM89HMG5uOz#Fb9!4@ zS5s?qjObor@_soL{e}GE$g~C=$ePM zzhQ|4O+~7hec7G=oAjgeLp^$#lk+<-bm$L(adqFMmBoOgsJhL#zOF{NhTB-$>W^~) zMS*pB9kg_BSQKyN;?qZ;+fdo%)|2^C$e#9fY56!|5uUd>gz*c26wi&ogyZZKc zb!HF?f(F0KveMY*KO=kzoUU37SXoI zqK<8b_T+U=kV69IxqZtK`nEFA{py|X&Zu(g;DlCI*RMR9(C1?dhxW3obz!Ra);oXWe=CUjASc6w^SXVeIFaI-LWH z;teSuT+V=EK10oEB>N>5t@-bb~rakb<=*TLEh?utg z{Wn?@frsvUY;Ir#$t+JEG1y*uMa;@_?*CT1bTy6&T^xTgu;`0lO6Oi`NC_>FAF3kY zBmKVHHn?e>c{)&!=HwridK$E}f1iO2RGrIdF)*r3Nm)zpq=bA%**W&X6)H^zOy81u zxm<+zm2(VcKNDCSUSmll%jsXUV9Q_erbV$K{t_7@otC5L=<+R}oHQ`2eJ~Rl^sE z^x3o@xZU|GQ=!4yL#UqTX#U1sh8&xtqV*jUH_0Y~@$fKjzu}mqYhSNTrL&Ivb*~?F ziLi0u+F03jrV7_*)}oSz3~&(+cZo}zkwN&Ot>;-{}A`582dB%yHCY&;j*^p3sH7^d*a2aQ6fCgF! zg%uSw?$Q@@FQ2z$ zK;)V*JE{%s>Gw?5d<5qD3)_`LCORB`<+Ej%yX3+DuLNBfn6?aYE#Jn+;D`(bzA94g>WuZ0@+2oxK|EYw} zVZUY@6e7lw?>U87VKNjtGW)yD!ZNTYw0%PwJxgM*Kd3{u^4|!OJ2_%Ds;Gw}yu5tO z^lDyY>U`8M`cKWUFBOB_6w@z$R@hTVdY zB!9g=sV~?+8x1i^zCv1a`%XOtm|Jn|xfofG)ce;nRS9~QO_?Z>yN@F<^PNVyz-BGq zxqAQg8*c;-=)JoH?(jJ&kL;;c8fqp>be+3Ur$x__hz|`@3ojAR1j;ca5)Y)bi-VM7 zTuln*D}vc3NVJ$V!aifePKX+@nc;pL_VE}veZovqdXGtHk&2RwRUeFMu(JW4z(fm_ zDRQ!5{|>k<*C`Opp{AZw={agOKEv}%Wp?yzgYINo{U0#ge@OJ(E{UZ&|GGGDagUq< Nj<&8gjaGqo{{<>+kyii! literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-125.png b/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..98d26de9cd1a0512c9357aba544cb81e1661b3d2 GIT binary patch literal 3815 zcmd^C_d6SE8%|I)OKX-Y)heYm4yuQPP!w(LRhv>XQhP*#qJtV$t%|K$VkF0^V^3 zJl`g5`7BbC%BX2X-l9fF^zMEH-Ndo5VI<}UeRca`tmMiv1NF*vo;DCFaDmg|IUCFP z@*@$B6NaJGWA0=lJq*b%xih*|;G}IAswwfX3jF>XUuhd(l71fgoUmX*yxJ{#6>D=* zS^d!s0xGhO?%!BF*Q1#>cxgf@Qd7~rgr5TdxGx0&@FW1_nqh!@1bzG9U7AW-EE+&rRcqS4W%i7_Ta8-0o6hCh;BY7fZSSaOoI(%~V?pEp)z0V3_g z6OjfVcVXz^0vvSmh|i@qQa`6JGt?-mr5L!F>qXimWV<+Vz36>Q-d%Qss!WSi@1cK!mqb^P zch*-^)TH?{O*};9LUex~J&9Cf%#x8y$JGiGH0<_ff` zGL#rw>@YY+lX7_EWK_#Inc;JEO5}SX^f0DzO~D?SnL1ln$^E+65Ll>b4$)9?S(=V5 z*Dq|i+9UZyPfAL)cesIdr5Dd0llqSCA|9tZmRkNSP8iDd&F~)jvzR004Z_h3V(rD5 z5dW`(P5(x&TSrOfU*MeN@3HyOk@CBQ9eiK#<9Wu?L$#?q$$6zoPGeoBWA9Q~+EuJ3 z_=={``q#z}3QtTj%1so>Wbx!a=@MLbv?CcEoV!h47XLmkige)nOl#0)*)U|f6Kg^N zv%wG0+vCxt_uI32D~4p|KU%)IuVqJ1U)6(vOfPcur~46N%-ZrJXXO0?^_C(EdZll` zJoX#8Cb^BTvjC^9%!d=-GzHh7#!M+*o%+Xe7Ahi*ZSC0Dhe&Wjx%cJE%&;k!xWSlL z@cypd5ZjYvV-d9LSG)WCn!E<}^LoW@S2a;<7p_i%R5ZYzEtg61C38*Coe)w+ymdMV zL{t}vev46&pLQ^wVC1~L~3EDr?xPzi>#yiTx*RM|Ma+8x;pS! zRwu7nBnvpLFD0ekqL~0u^@_4I_v#F&k2(T{p2r~xb~Skc=4vose6`s7K8;73SHwQN zGh$<@yH!$Z7f`Qz9b*6KBpYu64OAcZ=!MVN#Qp&I8AwUxY1&|?-`+ztMG^c6!*cX~ zVdkS@3=0a-tyD$?w$>^T<9p;}+VwZMBq_W+=FSkN3lRF28Te2|IXBi$oXuLbgqHKb zg4Bm4SivLj{c8!87g_HE*Y=7 z%&EL2-H6Uoa7J6PTWeK3w>>YBNX`E|lv99~VKuZ}%$f^l>B6y||na%?WENL%CCcsV~^ z%m;BmCUm$q!3BVgE>BW`EB^8@BK|z4yaFqB*hB zw8^^cVnV!4z%+amtWMqw_Gwne?!L?Fh@BicSnF)Ib8Dq#4NyppQ|CZ_y*i8jk1IcB z51ym-YfVgLxc!veI&Ie3C%D`xuU<$2`Y|bhyOg>XXjapD#bgyCV!MdA11Igl>F&R= z&FozN)U6ysM^!pj-AdtdRBxyl>I(IaKVT|`zbb;qY*nxoGj* zj*DP!rV$9W(JDIr5OlgpZwL$Sx|*&!h-SA+IQ!C;oUto&#njdT5oud_oS(IYf#m{x zuO%2C1a5{kO%z44vtG!IWGcBgy+0+`8kvP#?4y~ z{jv{i@)aSHI2W;_pfWg5*G_HiMtwjdLeW{ZjCbuej?zT78YZ729+N0v;$V+7W|{s` zoi&8m1&sdLeS6ESIXCGHqamC%{dX!!wc`#?gGBFEW4&b#?b+LVuDbl`Z_!$)X4M-LykjlB%*>vli>zC4vw+s)Ik*3-?S2D-`AroyDu zmkD!i9i98-lsA#6G4*+T%}Muz2G0%Y53|#ZloA~R zIpy>#WOMXTV#K$*MW|(s_Eq_Lh-HNpvW$M%QK@gP_WQ0xrVx|cNaQE#vOYQPsg3;! zIngn0qBx{$a~n^Go__vNnf|eACgv0~v-c*AE3s06CRF1*QBIzOgPh`yvUNhIqa87V z?2N%5{mJ9z@RB}P&9a`J*@mU-;+f~pOm9TI?eyMWyC`oP5VJn*_LyXhMTwd#O1cf0 z`)Om{Mm4tMOzo@PO8Ba^289?|$uBx#Vf&IDm>MAs!p$u8o4QmYIvjJ@PE;EH<%6Sb zU9?O%IFo|GYcCW7ncc z!7oNPtsTP1aMjYROz@n0W(>x*Y+^>^|7(i!yCOnD_ES}ccG2;uZ*|7pUU5)!%xA_z z;+o&)KgF83JL+6QNzo_k&7-_iMUbvl4*c1s-h|l6AQ-tr_N2^6n0*2t)~4R<@^*}q^GrV} z&-Qm{%~Pk#Q2R#ctSzVnUF;OMR}KHQ4211IB{gm=;$qfC(8RA~=s|yn2XFHVoX3C( zD|~nu%Xrcm>EtS8qq|1`5RWLY5AO^-24%SF{84Itx@6YAa$Xmc+TCXxp2IpQLVYAL zvK*YIEBXp*)X%JekrPu+{YA-rjfikw-p~a9_W}R*9UuOCB0P)JACl*%$W{h$u;qW{0Jz^| z0s2{SlselJ)bMFiTMdN3ZzhhNd1HfwKmmhjT$RfGPw&K-5?TiTF4SK~-+*&BD$bZ> zjOBwxv5v7m8h!7>oH?z3N+5c!0ryitApf=w3Ux#q*9XWS{=e>e{@3)ao&v=zw(bV; TO^Gr)SAdD3nE^`Q{l$L(%N*h~ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-150.png b/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..41acca5e639bc4cc740f35b0cddd160982e493af GIT binary patch literal 5153 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-200.png b/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..28e4f2643f7966a777bf9720984cf0caec76d343 GIT binary patch literal 8307 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 Y31FCU_n@x$F9N{TE7q5*F1kPcA2YpCsQ>@~ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-400.png b/src/winapp-GUI/winapp-GUI/Assets/Wide310x150Logo.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..c3fa9c8ab5a4a350fde76dc97769188fce45d325 GIT binary patch literal 34736 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ⅇ3M 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 bH|)ggq1a@5wvT~Far~L4?&GpYFW&q=1C{l8 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Controls/CertificatePickerControl.xaml b/src/winapp-GUI/winapp-GUI/Controls/CertificatePickerControl.xaml new file mode 100644 index 00000000..f74faa9e --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/Controls/CertificatePickerControl.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/src/winapp-GUI/winapp-GUI/Controls/FileNamePanelControl.xaml.cs b/src/winapp-GUI/winapp-GUI/Controls/FileNamePanelControl.xaml.cs new file mode 100644 index 00000000..2a73d4eb --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/Controls/FileNamePanelControl.xaml.cs @@ -0,0 +1,26 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System; + +namespace winapp_GUI.Controls +{ + public sealed partial class FileNamePanelControl : UserControl + { + public FileNamePanelControl() + { + this.InitializeComponent(); + } + + public string FileName + { + get => FileNameText.Text; + set => FileNameText.Text = value; + } + + public event RoutedEventHandler CancelClicked + { + add { CancelButton.Click += value; } + remove { CancelButton.Click -= value; } + } + } +} diff --git a/src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml b/src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml new file mode 100644 index 00000000..2b1f88d1 --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml.cs b/src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml.cs new file mode 100644 index 00000000..143c85fa --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/Controls/ProgressPanelControl.xaml.cs @@ -0,0 +1,21 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System.Collections.ObjectModel; +using winapp_GUI; + +namespace winapp_GUI.Controls +{ + public sealed partial class ProgressPanelControl : UserControl + { + public ProgressPanelControl() + { + this.InitializeComponent(); + } + + public ObservableCollection ProgressCards + { + get => (ObservableCollection)ProgressItemsControl.ItemsSource; + set => ProgressItemsControl.ItemsSource = value; + } + } +} diff --git a/src/winapp-GUI/winapp-GUI/MainWindow.xaml b/src/winapp-GUI/winapp-GUI/MainWindow.xaml new file mode 100644 index 00000000..dbe9d6e4 --- /dev/null +++ b/src/winapp-GUI/winapp-GUI/MainWindow.xaml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

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 s7hHS4mt5} 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-150.png b/src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..3e25f2b0cc7aefe7e94596ffdee5cd63fd5391ac GIT binary patch literal 2658 zcmb_eXEYlM8+L0HMT-crXM=>4*sDdIc`~Ca=e&_q~yw8v4ocDdt^PcydC&koQmzj~9k&23nSzixfe#t)nf}ZZO zqWhwlE{OrHXBS9C1r+-if9RWw{-B~_1n46)EyGH-OCnK#VZPzM$gW7Qvzsp3ykLCp z6$PvWO;WP18TEdKmT5_*WV-o_R7;&IgJ<;(MFSkMt@;)hu|BtM z(nDt3ndf^~9xXx|Dcx|srY+B=jSzKqvv_RXd4BA#XY*>=k}9qwoV~EMveaXmAr)8h|#ctf=~#)-|%<>U9`51?eS`2s)PTk>moqfL~|3VT=ccc9@qv10z*gNa)E zA>Wz=LrS8_otn}0yP3B%1vl0~E@%XtBKGU(GYyy~2XKXK8W(Cy+gP z`=h&NkGU1C+1J5wj#npLDb6OsVwfyNGFCBRl81jPXrfMH*uvRc6x{ML^}_S6d-C() zl4Hl}z4_lMb7O3+aq)}YLj$U1@SqFckC9&L>Ye;Ea z3!M1HJ8BabVltJIWE&m?^)E`_;Z*p`n=$Udm)KlJVOsRFOZZNKjSG1=Vzf(NB|G3s zcGG9_cFKhInW~ANW&+VAbsHrz<$eh$@Ps$lP>%6aR=A}3*Wt2v;wcVL_kGpET>M&F zEcrNe(j~%9cGcmJXc9n;;3-`exi6h@V`ZF^U>m<(%vh5{G-Tp;FS`CVRF5x47f7%T zVXKk==-$rh_ZuY;J&uV<;hGc|kgN<|`UW*V9*|^wQuk8CzEPkh2V$&}99-{dTP)!g zkuUbbA?!3bfdO>AUziiJL^T&nd4LeiqM1W`sn;Xd^m#a2+soEBhp~at;z)hkh&yiY zrA6r6>^#1eWDv`FtmV{xkGQF|*Rv+6uUwI{(xJ5XgdNU+YxPFk;poqQLb6Wj&MSyc zALT$kz=`TeQ%he5l}U}!hIa;I5>Z$@(4TkfZul4@nG@WI*x@Om=hkTOI8QtdD%t4V zp4XY$Pg^Sq9yQ5~8pM)PyFsl5MU_8((yAOr+sGK9)ePN)Kh1C?y==*M@Iw$?#2c{L zaxJgKzd(nT*a^0Sd80net_H87(r0`>23qyUcS_>?iqiA12UUJ6(t;B2L%lNuvM^TP zYm`YQvFTgU!%YA0*F@=T|u^V?1g3ogyIiPU>mVHL&8W4P3M6 z&7l`OPwg+S;XZ6fGEorA0)&Mw(f9TR-dx_OGrJIXCCcKHvXg%$}N z<#y?$>!Gf?K36rA0zo!()9+g+3hK_3W;#a(xm71SS*B2*5|tl`8yrmvu&j_5h0ZQg zIBd}tw0^Zv>QZkrc`-#L>k+QBhy&7Mc+2s|CTL&-nGp7KP$AH9WH!op(!&ZR%m&%= zfvrkqASDw5)k%7A(&J}U{<5<5b~y_bWM#9~uML}?xmhM+UrFUccat@wQMrq6k_X04 z=NjJ4EW~7UalB2nT9$OI{~}va*(D2-+dcaMjiDU2%FCW#->U=ky>2y=8LALq^_Cx>Am}eBP71)wv(wI3Cr^E#?7Umpp zj_z_*$k7Dab|zx^BXl}Wgv|hZU5BbaIz$}9Vy8Lr-E}OHb1LZH?@~;S+VV_2Wb1+@ z>*7YaKE32h_E@>#oqj$eC0Qqn`Pa?=CgPxO=GYKM+A!pCo zrHG+-@&@qzrDnWFWNM86(6nlXV5zZoHq8XC=n1K>gK;a_<=sUm2P9v9`Q1`47z{{< z=(8C|FJ!QDp5Wb_h=2v3mCL5PkF3KDhVm3O-V_ga8r^n^`T3rR&pmuTt*^V;uq*oY z6xmQ3_Ps0|k;!VFesbmfYah5Oxl0>Mpt?6K+BqT;L6j{QFy*| zUEqwjX!GUX4jwbxx+SiG+U;jv)1R&_OeLkJYJPF!Fv!H_pL1~t>KAo^)+D|w2e6*1 zM!XMm=U{1d7BVV}n;Xj&GdE1CL`VarDiU47>7N4lxMe;zeMr&j>RI-*q3IWaa%{6b z1i~`$mih*AO(sj|R&9#ReQtsH@Wo%*Lf3c*wn5#e5;9aS>#swnjFhJ2T^e^W+$w8* zF|Iwzclf!i7l=oh#DNi{m_A)+5OklcKD>vbXyKd}9ZWhZw&y` zv;jXHnvU%s)E@1}Ws6tjrtKb8%xb=m+x-pi4&sV`gvS|=j57#86;y8t<%MFuk^%q+ zF%0)^30Y@qc{D6v6GGv$_XTeCpqXESm}8~=*5;RMxPjDn|*p28maVkj1i4mF7f{W0d)MM literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-200.png b/src/winapp-GUI/winapp-GUI/Assets/SmallTile.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..219846ec0128b094e753efde12e0feaf8f384956 GIT binary patch literal 3927 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.png b/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.png new file mode 100644 index 0000000000000000000000000000000000000000..1683891231bd612711fd1ce99f9311b73b1e3f98 GIT binary patch literal 4280 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 Y31FCU_n@x$F9N{TE7q5*F1kPcA2YpCsQ>@~ literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-125.png b/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..16ec99c0283439d224e3cddb3fb64790c11fcdbe GIT binary patch literal 12048 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 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-150.png b/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d4a8827a65ad8766c172de2eb06d6e638c0f18 GIT binary patch literal 17952 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 SrMIHUSXGw8`*OPg literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-200.png b/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..c3fa9c8ab5a4a350fde76dc97769188fce45d325 GIT binary patch literal 34736 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ⅇ3M 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 bH|)ggq1a@5wvT~Far~L4?&GpYFW&q=1C{l8 literal 0 HcmV?d00001 diff --git a/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-400.png b/src/winapp-GUI/winapp-GUI/Assets/SplashScreen.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..83c4d2522df96fb22be11229afc544b832d0c85d GIT binary patch literal 165742 zcmeFYRajeF+cjLlrAR5or9iO&1&R|W?oiy_3Bg@k++B*dP~2SuySTecC9;64*bi3e>w0k2ma;2za03N1OIa1Uk?1s zfqyyhF9-hRz`q>$e~1G&YmaB~fe*rg_c!Mkw*qX}@beSs*}Z4yuV#ws(QE5!(E*Q< zTKLi9uZ7O*wyB4S4#jkB#K3KvC{pJbu|_>H;+nPnKFFN@{G4Xyd^O9PfBAgso@}He z4^DD>x{GubzFz8Yr7~F>Z=dM%SVN-0ubWOQU>&*J@4F_vJGxx*f^T`L;g42G|E>XN zQN)D9`R(OM!j9>$4YvV)j%%~5_M<-MqTKqHu>&8(b;g5F?Dl)UgASz`@0jA+!(g5G z>y9gPaO^_8FQ9A%lm&lWd1GKi?r?rBlD6khaqbi~vDa!7?|aEV?-94L*b`#pFymOU zXs!fd-@nSPhZ%Je4_&&zM;_tcI#-Rpot@5warzF|R$1i;X)lTcA1EA#m>;&RfT04? zJ^Q_Zfmrbix$v&_@a-mu$C5Ab3h6m>19s`WIxxIF=04R?KR>E7%8TA@Lo~HefgT@k zVE4cV%G>wrJ)QB^6Nt8zCM;uSWI3rxV zlU;t0d2_?5)b)6l7x?QO5r`Z!{h#a+2gUyP^)Kpozb>y%XYxAdKfyAk$9%n9EQ}XO zws?gDCg|d{n_0|Ml&Maa$ux!IYdg%+=*9(DqUh9HLx^VxqQR;I{Nt`}^>JV(q1>k< zT*v280#vF?SN$Ewp16~_PZ!*>y%g5sD0Vo@n->~KM>xBD6I|D?VD5)tXtRw?w8qYX zz(T%_uOr!Y`)bxvXUEQsK+?=Tu6BnbKZ16{d7TP40T$Q+>ElhDfNZRDX-EB)9ekfQ zhVjpw;doM^2gJZG?;5DCT~rooK<%G^o!-Jm96kZ%t5C!QZ{7-Vvng^$uPtX`Q6xUz z)DM!osw^R_HO)J-U;#q*j!q=(a?Lj~2nE$0tsXcpVA$sl&^cHypBHW*oK%|45g zhmD9`n|bV5&$@GGnzKKyD;FiKvvR?uS7Tvf|0KLVDna1Rhn8Q+5taXmkpDu^oBYok ze2n-N;QuHH4ETKXO2xn>s-Qii-x!SyH?nW#Kw`wLL&S}FgeqvX_(2*GIq%VLxd_MT z88;80J|JcUUjJH-6Z!sO#j81C`$EAuJ(@JAeA_SWate+#7#j$-44~dUyYebeh~vAy zvPI67ZQql-!m2aaV722)lWtq=BYO`83qFSH0i6X&y767xdIPQN0UHkfcUV#E=9EyR z1Fj)rZ^fUI&p6KbjXt|0$3bJq!}KgarNni8b?n1D!hJE|)Yq>OXTc95v#tX@{^1o5 zjsCt39=Y`&ofsHttAaPLbl{k-iG)?d{1zJ9?c=X_JdCc855}SjFq5>}iZ+9Fk-mke zapz4`UfIVFop_&@auJXRyX7kZpb?d*mCUj`)ynB+OP?Pp(#bF#(QoQ8k@xw5b(1Zc zyt91Xv&mUyUs=mL1eTYGL9ZU%!L)Owof^YZ`bev2tBjv*Fe z(cFSM_34E?9aa$9v&a`b({yE;WEWJAT)?#?$@*@EhNSOwhP_Ooo>SrpB zK^ubq|6e%Xd{+8r0ekIATSzHO-&9K49?2CFmud*K@U2n#GBcs5S=+a-d z^O}`s-oY0)(slwJUCAPfhdX%2-qq5AE*8~_+=|ro31ZCewHr&_=cLqka+BS|2CwLQ z_$8T181YiDE|f4_%wgpk)Dw%=(fIW=o%~~zP{lO`({q9oEuC%Ewt&J-ac23@NG6WrWzvkLxCRMbQ&LD-koJdy`T?^lD zpF+d3^9pVWzC)D6`rEm|;4{D8GH~gSl6hV-24fjt|N^n

+ public async Task SetupSelfContainedAsync(DirectoryInfo winappDir, string architecture, TaskContext taskContext, DotNetPackageListJson? dotNetPackageList = null, CancellationToken cancellationToken = default) + { + await taskContext.AddSubTaskAsync("Setting up Self Contained", async (taskContext, cancellationToken) => + { + // Look for the Runtime package which contains the MSIX files + var selfContainedDir = winappDir.CreateSubdirectory("self-contained"); + var archSelfContainedDir = selfContainedDir.CreateSubdirectory(architecture); + + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken) ?? throw new DirectoryNotFoundException("Windows App SDK Runtime MSIX directory not found. Ensure Windows App SDK is installed."); + + // Look for the MSIX file in the tools/MSIX folder + var msixToolsDir = new DirectoryInfo(Path.Combine(msixDir.FullName, $"win10-{architecture}")); + if (!msixToolsDir.Exists) + { + throw new DirectoryNotFoundException($"MSIX tools directory not found: {msixToolsDir}"); + } + + // Try to use inventory first for accurate file selection + FileInfo? msixPath = null; + try + { + var packageEntries = await WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken); + if (packageEntries != null) + { + // Look for the base Windows App Runtime package (not Framework, DDLM, or Singleton packages) + var mainRuntimeEntry = packageEntries.FirstOrDefault(entry => + entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && + !entry.PackageIdentity.Contains("Framework") && + !entry.FileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && + !entry.FileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase)); + + if (mainRuntimeEntry != null) + { + msixPath = new FileInfo(Path.Combine(msixToolsDir.FullName, mainRuntimeEntry.FileName)); + taskContext.AddDebugMessage($"{UiSymbols.Package} Found main runtime package from inventory: {mainRuntimeEntry.FileName}"); + } + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Could not parse inventory, falling back to file search: {ex.Message}"); + } + + // Fallback: search for files directly with pattern matching + if (msixPath == null || !msixPath.Exists) + { + var msixFiles = msixToolsDir.GetFiles("Microsoft.WindowsAppRuntime.*.msix"); + if (msixFiles.Length == 0) + { + throw new FileNotFoundException($"No MSIX files found in {msixToolsDir}"); + } + + // Look for the base runtime package (format: Microsoft.WindowsAppRuntime.{version}.msix) + // Exclude files with additional suffixes like DDLM, Singleton, Framework, etc. + msixPath = msixFiles.FirstOrDefault(f => + { + var fileName = f.Name; + return !fileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && + !fileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase) && + !fileName.Contains("Framework", StringComparison.OrdinalIgnoreCase) && + WindowsAppRuntimeMsixRegex().IsMatch(fileName); + }) ?? msixFiles[0]; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Extracting MSIX: {msixPath.FullName}"); + + // Extract MSIX content + var extractedDir = new DirectoryInfo(Path.Combine(archSelfContainedDir.FullName, "extracted")); + if (extractedDir.Exists) + { + extractedDir.Delete(recursive: true); + } + extractedDir.Refresh(); + extractedDir.Create(); + + using (var archive = await ZipFile.OpenReadAsync(msixPath.FullName, cancellationToken)) + { + await archive.ExtractToDirectoryAsync(extractedDir.FullName, cancellationToken); + } + + // Copy relevant files to deployment directory + var deploymentDir = archSelfContainedDir.CreateSubdirectory("deployment"); + + // Copy DLLs, WinMD files, and other runtime assets + await CopyRuntimeFilesAsync(extractedDir, deploymentDir, taskContext, cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Check} Self-contained files prepared in: {archSelfContainedDir.FullName}"); + + return 0; + }, cancellationToken); + } + + private async Task EmbedActivationManifestToExeAsync(FileInfo exePath, DirectoryInfo winAppSDKDeploymentDir, FileInfo windowsAppSDKAppXManifestPath, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + // Use applicationLocation for DLL content (where runtime files were copied by PrepareRuntimeForPackagingAsync) + var exeDir = exePath.Directory!; + + taskContext.AddDebugMessage($"{UiSymbols.Note} Generating activation manifest from: {windowsAppSDKAppXManifestPath}"); + taskContext.AddDebugMessage($"{UiSymbols.Package} Using DLL content from: {winAppSDKDeploymentDir}"); + + // Create a temporary manifest file + var tempManifestPath = new FileInfo(Path.Combine(exeDir.FullName, "WindowsAppSDK_temp.manifest")); + + try + { + // Build the entire manifest in memory, then write to disk once + var sb = new StringBuilder(); + sb.AppendLine(""); + sb.AppendLine(""); + + // Collect all AppX manifests (main package + component fragments) and their DLLs + (var packageDependencies, _) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); + if (packageDependencies == null || packageDependencies.Count == 0) + { + throw new InvalidOperationException("No Windows SDK packages found. Please install the Windows SDK or Windows App SDK."); + } + + var architecture = WorkspaceSetupService.GetSystemArchitecture(); + IEnumerable appxFragments = GetComponents(packageDependencies); + + // Combine all manifests: main AppxManifest.xml (Package root) + fragments (Fragment root) + var allManifests = new List { windowsAppSDKAppXManifestPath }; + allManifests.AddRange(appxFragments); + + // Combine all DLL file names from deployment dir and fragment native dirs + var allDllFiles = new List(winAppSDKDeploymentDir.EnumerateFiles("*.dll").Select(di => di.Name)); + allDllFiles.AddRange(appxFragments + .Select(fragment => Path.Combine(fragment.DirectoryName!, $"win-{architecture}\\native")) + .Where(Directory.Exists) + .SelectMany(dir => Directory.EnumerateFiles(dir, "*.dll")) + .Select(Path.GetFileName)!); + + // Single pass: process all AppX manifests (auto-detects Package vs Fragment root) + AppendAppManifestFromAppx( + sb, + redirectDlls: false, + inDllFiles: allDllFiles, + inAppxManifests: allManifests); + + // Phase 3: Discover and register third-party WinRT components (e.g., Win2D, WebView2) + // These packages ship .winmd files + native DLLs but no package.appxfragment + await AppendThirdPartyWinRTManifestEntriesAsync( + sb, architecture, dotNetPackageList, taskContext, cancellationToken); + + sb.AppendLine(""); + + // Single write to disk + await File.WriteAllTextAsync( + tempManifestPath.FullName, + sb.ToString(), + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + cancellationToken); + + // Use mt.exe to merge manifests + await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); + } + finally + { + TryDeleteFile(tempManifestPath); + } + } + + private IEnumerable GetComponents(Dictionary packageDependencies) + { + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + + // Find appx fragments in the NuGet global cache (lowercase-id/version/ layout) + var appxFragments = packageDependencies + .Select(package => new FileInfo(Path.Combine(nugetCacheDir.FullName, package.Key.ToLowerInvariant(), package.Value, "runtimes-framework", "package.appxfragment"))) + .Where(f => f.Exists); + return appxFragments; + } + + /// + /// Collects all user NuGet packages from winapp.yaml or .csproj. + /// Returns the full package dictionary (name → version) for WinRT component scanning. + /// + private async Task> GetAllUserPackagesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var packages = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Path 1: Try winapp.yaml + if (configService.Exists()) + { + var config = configService.Load(); + foreach (var pkg in config.Packages) + { + packages.TryAdd(pkg.Name, pkg.Version); + } + } + else + { + // Path 2: Try .csproj via `dotnet list package --format json` (cached) + try + { + var allPackages = dotNetPackageList?.Projects? + .SelectMany(p => p.Frameworks ?? []) + .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); + + if (allPackages != null) + { + foreach (var pkg in allPackages) + { + if (!string.IsNullOrEmpty(pkg.Id) && !string.IsNullOrEmpty(pkg.ResolvedVersion)) + { + packages.TryAdd(pkg.Id, pkg.ResolvedVersion); + } + } + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not retrieve package list from .csproj: {ex.Message}"); + } + } + + return packages; + } + + /// + /// Discovers third-party WinRT components and appends their activatable class + /// entries to the in-memory SxS manifest (for self-contained deployment). + /// + private async Task AppendThirdPartyWinRTManifestEntriesAsync( + StringBuilder sb, + string architecture, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); + if (allPackages.Count == 0) + { + return; + } + + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + + // DiscoverWinRTComponents filters out packages that have a package.appxfragment + // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. + // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 + // are transitive WinAppSDK deps but need their own InProcessServer entries. + var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); + if (components.Count == 0) + { + return; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found {components.Count} third-party WinRT component(s) to register"); + + // Build a set of DLL names already registered in the manifest (from WinAppSDK fragments) + // so we can do exact-name dedup instead of substring matching. + var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in SxsFileNameRegex().Matches(sb.ToString())) + { + registeredDlls.Add(match.Groups[1].Value); + } + + foreach (var component in components) + { + var classes = winmdService.GetActivatableClasses(component.WinmdPath); + if (classes.Count == 0) + { + continue; + } + + // Skip components whose DLL is already in the manifest (from WinAppSDK fragments + // or a previous iteration) to avoid duplicate activatableClass entries. + if (!registeredDlls.Add(component.ImplementationDll)) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); + continue; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Registering {classes.Count} activatable class(es) from {component.ImplementationDll}"); + + sb.AppendLine($" "); + foreach (var className in classes) + { + sb.AppendLine($" "); + } + sb.AppendLine(" "); + } + } + + /// + /// Discovers third-party WinRT components and generates InProcessServer + /// extension entries for AppxManifest.xml (for packaged apps). + /// + private async Task AddThirdPartyWinRTExtensionsToAppxManifestAsync( + string manifestContent, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); + if (allPackages.Count == 0) + { + return manifestContent; + } + + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + var architecture = WorkspaceSetupService.GetSystemArchitecture(); + + // DiscoverWinRTComponents filters out packages that have a package.appxfragment + // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. + // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 + // are transitive WinAppSDK deps but need their own InProcessServer entries. + var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); + if (components.Count == 0) + { + return manifestContent; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Adding InProcessServer entries for {components.Count} third-party WinRT component(s)"); + + var doc = AppxManifestDocument.Parse(manifestContent); + + // Build a set of DLL names already registered in the manifest + // so we can do exact-name dedup instead of substring matching. + var registeredDlls = doc.GetRegisteredExtensionDllPaths(); + + var addedAny = false; + foreach (var component in components) + { + var classes = winmdService.GetActivatableClasses(component.WinmdPath); + if (classes.Count == 0) + { + continue; + } + + // Skip components whose DLL is already in the manifest or in entries we've already generated + if (!registeredDlls.Add(component.ImplementationDll)) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); + continue; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Adding {classes.Count} activatable class(es) for {component.ImplementationDll}"); + + doc.AddInProcessServerExtension(component.ImplementationDll, classes); + addedAny = true; + } + + return addedAny ? doc.ToXml() : manifestContent; + } + + /// + /// Inserts Package-level extension entries (e.g. InProcessServer) into a manifest string. + /// Correctly distinguishes Package-level <Extensions> from Application-level ones. + /// + internal static string InsertPackageLevelExtensions(string manifestContent, string extensionEntries) + { + var doc = AppxManifestDocument.Parse(manifestContent); + var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); + + // Parse the raw extension entries as XML fragments and add them + var wrapper = XElement.Parse($"<_wrap xmlns=\"{AppxManifestDocument.DefaultNs}\">{extensionEntries}"); + foreach (var entry in wrapper.Elements()) + { + extensions.Add(entry); + } + + return doc.ToXml(); + } + + /// + /// Generates Win32 SxS manifest entries from AppX manifests (Package or Fragment format). + /// Auto-detects the root element name (Package vs Fragment) per document. + /// + /// StringBuilder to append manifest entries to + /// Whether to redirect DLLs to %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% + /// List of DLL file names to track + /// List of paths to the input AppX manifest files or fragments + internal static void AppendAppManifestFromAppx( + StringBuilder sb, + bool redirectDlls, + IEnumerable inDllFiles, + IEnumerable inAppxManifests) + { + var dllFileFormat = redirectDlls ? + @" " : + @" "; + + var dllFiles = inDllFiles.ToList(); + var hasPackageManifest = false; + + foreach (var inAppxManifest in inAppxManifests) + { + XmlDocument doc = new(); + doc.Load(inAppxManifest.FullName); + + // Auto-detect root element name (Package or Fragment) + var prefix = doc.DocumentElement?.LocalName ?? "Package"; + var isPackage = prefix == "Package"; + if (isPackage) + { + hasPackageManifest = true; + } + + var nsmgr = new XmlNamespaceManager(doc.NameTable); + nsmgr.AddNamespace("m", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); + // Add InProcessServer elements to the generated appxmanifest + var xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:InProcessServer"; + XmlNodeList? inProcessServers = doc.SelectNodes(xQuery, nsmgr); + if (inProcessServers != null) + { + foreach (XmlNode winRTFactory in inProcessServers) + { + var dllFileNode = winRTFactory.SelectSingleNode("./m:Path", nsmgr); + if (dllFileNode == null) + { + continue; + } + + var dllFile = dllFileNode.InnerText; + var typesNames = winRTFactory.SelectNodes("./m:ActivatableClass", nsmgr)?.OfType(); + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(); + if (typesNames != null) + { + foreach (var typeNode in typesNames) + { + var attribs = typeNode.Attributes?.OfType().ToArray(); + var typeName = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "ActivatableClassId") + ?.InnerText; + var xmlEntryFormat = + @" "; + sb.AppendFormat(xmlEntryFormat, typeName); + sb.AppendLine(); + dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); + } + } + sb.AppendLine(@" "); + } + } + + // Only for Package manifests with redirect + if (isPackage && redirectDlls) + { + foreach (var dllFile in dllFiles) + { + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(@""); + } + } + // Add ProxyStub elements to the generated appxmanifest + dllFiles = [.. inDllFiles]; + + xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:ProxyStub"; + var inProcessProxystubs = doc.SelectNodes(xQuery, nsmgr); + if (inProcessProxystubs != null) + { + foreach (XmlNode proxystub in inProcessProxystubs) + { + var classIDAdded = false; + + var dllFileNode = proxystub.SelectSingleNode("./m:Path", nsmgr); + var dllFile = dllFileNode?.InnerText; + // exclude PushNotificationsLongRunningTask, which requires the Singleton (which is unavailable for self-contained apps) + // exclude Widgets entries unless/until they have been tested and verified by the Widgets team + if (dllFile == null || dllFile == "PushNotificationsLongRunningTask.ProxyStub.dll" || dllFile == "Microsoft.Windows.Widgets.dll") + { + continue; + } + var typesNamesForProxy = proxystub.SelectNodes("./m:Interface", nsmgr)?.OfType(); + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(); + if (typesNamesForProxy != null) + { + foreach (var typeNode in typesNamesForProxy) + { + if (!classIDAdded) + { + var classIdAttribute = proxystub.Attributes?.OfType().ToArray(); + var classID = classIdAttribute + ?.OfType() + ?.SingleOrDefault(x => x.Name == "ClassId") + ?.InnerText; + + if (classID != null) + { + var xmlEntryFormat = @" "; + sb.AppendFormat(xmlEntryFormat, classID); + classIDAdded = true; + } + } + var attribs = typeNode.Attributes?.OfType().ToArray(); + var typeID = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "InterfaceId") + ?.InnerText; + var typeNames = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "Name") + ?.InnerText; + var xmlEntryFormatForStubs = @" "; + if (typeNames != null && typeID != null) + { + sb.AppendFormat(xmlEntryFormatForStubs, typeNames, typeID); + sb.AppendLine(); + dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); + } + } + } + sb.AppendLine(@" "); + } + } + } + + if (hasPackageManifest && redirectDlls) + { + foreach (var dllFile in dllFiles) + { + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(@""); + } + } + } + + /// + /// Updates or inserts the Windows App SDK dependency in the manifest + /// + /// The manifest content to modify + /// The modified manifest content + private async Task UpdateWindowsAppSdkDependencyAsync(string manifestContent, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var winAppSdkInfo = await GetWindowsAppSdkDependencyInfoAsync(dotNetPackageList, taskContext, cancellationToken); + + if (winAppSdkInfo == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not determine Windows App SDK version, skipping dependency update"); + return manifestContent; + } + + var doc = AppxManifestDocument.Parse(manifestContent); + const string publisher = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"; + + var dependencies = doc.GetDependenciesElement(); + if (dependencies == null) + { + // Create Dependencies element and insert before Applications (per AppxManifest schema order) + dependencies = new XElement(AppxManifestDocument.DefaultNs + "Dependencies"); + var applications = doc.Document.Root?.Element(AppxManifestDocument.DefaultNs + "Applications"); + if (applications != null) + { + applications.AddBeforeSelf(dependencies); + } + else + { + doc.Document.Root?.Add(dependencies); + } + + dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", + new XAttribute("Name", winAppSdkInfo.RuntimeName), + new XAttribute("MinVersion", winAppSdkInfo.MinVersion), + new XAttribute("Publisher", publisher))); + + taskContext.AddDebugMessage($"{UiSymbols.Package} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} (v{winAppSdkInfo.MinVersion})"); + } + else + { + // Check for existing WindowsAppRuntime dependency (prefix match for version-specific names) + var existing = dependencies.Elements(AppxManifestDocument.DefaultNs + "PackageDependency") + .FirstOrDefault(e => e.Attribute("Name")?.Value?.StartsWith("Microsoft.WindowsAppRuntime.", StringComparison.OrdinalIgnoreCase) == true); + + if (existing != null) + { + existing.SetAttributeValue("Name", winAppSdkInfo.RuntimeName); + existing.SetAttributeValue("MinVersion", winAppSdkInfo.MinVersion); + existing.SetAttributeValue("Publisher", publisher); + + taskContext.AddDebugMessage($"{UiSymbols.Sync} Updated Windows App SDK dependency to {winAppSdkInfo.RuntimeName} v{winAppSdkInfo.MinVersion}"); + } + else + { + dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", + new XAttribute("Name", winAppSdkInfo.RuntimeName), + new XAttribute("MinVersion", winAppSdkInfo.MinVersion), + new XAttribute("Publisher", publisher))); + + taskContext.AddDebugMessage($"{UiSymbols.Add} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} to existing Dependencies section (v{winAppSdkInfo.MinVersion})"); + } + } + + return doc.ToXml(); + } + + /// + /// Gets the Windows App SDK dependency information from the locked winapp.yaml config and package source + /// + /// The dependency information, or null if not found + private async Task GetWindowsAppSdkDependencyInfoAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + try + { + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); + if (msixDir == null) + { + return null; + } + + // Get the runtime package information from the MSIX inventory + var runtimeInfo = GetWindowsAppRuntimePackageInfo(taskContext, msixDir, cancellationToken); + if (runtimeInfo == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not parse Windows App Runtime package information from MSIX inventory"); + return null; + } + + return runtimeInfo; + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Error getting Windows App SDK dependency info: {ex.Message}"); + return null; + } + } + + private async Task GetRuntimeMsixDirAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + (var packageDependencies, var mainVersion) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); + if (packageDependencies == null || mainVersion == null) + { + return null; + } + + // Look for the runtime package in the package dependencies + var runtimePackage = packageDependencies.FirstOrDefault(kvp => + kvp.Key.StartsWith(BuildToolsService.WINAPP_SDK_RUNTIME_PACKAGE, StringComparison.OrdinalIgnoreCase)); + + // Create a dictionary with versions for FindWindowsAppSdkMsixDirectory + var usedVersions = new Dictionary + { + [BuildToolsService.WINAPP_SDK_PACKAGE] = mainVersion + }; + + if (runtimePackage.Key != null) + { + // For Windows App SDK 1.8+, there's a separate runtime package + var runtimeVersion = runtimePackage.Value; + usedVersions[runtimePackage.Key] = runtimeVersion; + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found runtime package: {runtimePackage.Key} v{runtimeVersion}"); + } + else + { + // For Windows App SDK 1.7 and earlier, runtime is included in the main package + taskContext.AddDebugMessage($"{UiSymbols.Note} No separate runtime package found - using main package (Windows App SDK 1.7 or earlier)"); + taskContext.AddDebugMessage($"{UiSymbols.Note} Available package dependencies: {string.Join(", ", packageDependencies.Keys)}"); + } + + // Find the MSIX directory with the runtime package + var msixDir = workspaceSetupService.FindWindowsAppSdkMsixDirectory(usedVersions); + if (msixDir == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Windows App SDK MSIX directory not found for dependent runtime package"); + return null; + } + + return msixDir; + } + + private async Task<(Dictionary? CachedPackages, string? MainVersion)> GetWinAppSDKPackageDependenciesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + string? mainVersion = null; + // Path 1: Try winapp.yaml (C++ / native projects) + if (configService.Exists()) + { + var config = configService.Load(); + mainVersion = config.GetVersion(BuildToolsService.WINAPP_SDK_PACKAGE); + } + else + { + // Path 2: Try .csproj via `dotnet list package --format json` + taskContext.AddDebugMessage($"{UiSymbols.Package} Querying NuGet package list..."); + + var allPackages = dotNetPackageList?.Projects? + .SelectMany(p => p.Frameworks ?? []) + .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); + + var winAppSdkPkg = allPackages? + .FirstOrDefault(p => string.Equals(p.Id, BuildToolsService.WINAPP_SDK_PACKAGE, StringComparison.OrdinalIgnoreCase)); + + if (winAppSdkPkg != null && !string.IsNullOrEmpty(winAppSdkPkg.ResolvedVersion)) + { + mainVersion = winAppSdkPkg.ResolvedVersion; + } + } + + if (string.IsNullOrEmpty(mainVersion)) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} No {BuildToolsService.WINAPP_SDK_PACKAGE} package found in winapp.yaml"); + return (null, null); + } + taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App SDK main package: v{mainVersion}"); + try + { + // Query NuGet API for the dependency tree of this package + var deps = await nugetService.GetPackageDependenciesAsync(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion, cancellationToken); + + // Include the main package itself in the result + deps.TryAdd(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion); + + return (deps, mainVersion); + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} {BuildToolsService.WINAPP_SDK_PACKAGE} v{mainVersion} not found in package source: {ex.Message}"); + } + + return (null, null); + } + + /// + /// Parses the MSIX inventory file to extract Windows App Runtime package information + /// + /// The MSIX directory containing the inventory file + /// Package information, or null if not found + private static WindowsAppRuntimePackageInfo? GetWindowsAppRuntimePackageInfo(TaskContext taskContext, DirectoryInfo msixDir, CancellationToken cancellationToken) + { + try + { + // Use the shared inventory parsing logic (synchronous version) + var packageEntries = WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken).GetAwaiter().GetResult(); + + if (packageEntries == null || packageEntries.Count == 0) + { + return null; + } + + // Look for the Windows App Runtime main package (not Framework packages) + var mainRuntimeEntry = packageEntries + .FirstOrDefault(entry => entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && + !entry.PackageIdentity.Contains("Framework")); + + if (mainRuntimeEntry != null) + { + // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) + var identityParts = mainRuntimeEntry.PackageIdentity.Split('_'); + if (identityParts.Length >= 2) + { + var runtimeName = identityParts[0]; + var version = identityParts[1]; + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App Runtime: {runtimeName} v{version}"); + + return new WindowsAppRuntimePackageInfo + { + RuntimeName = runtimeName, + MinVersion = version + }; + } + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} No Windows App Runtime main package found in inventory"); + taskContext.AddDebugMessage($"{UiSymbols.Note} Available packages: {string.Join(", ", packageEntries.Select(e => e.PackageIdentity))}"); + + return null; + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Error parsing MSIX inventory: {ex.Message}"); + return null; + } + } + + private static readonly string[] patterns = new[] { "*.dll", "workloads*.json", "restartAgent.exe", "map.html", "*.mui", "*.png", "*.winmd", "*.xaml", "*.xbf", "*.pri" }; + + private static async Task CopyRuntimeFilesAsync(DirectoryInfo extractedDir, DirectoryInfo deploymentDir, TaskContext taskContext, CancellationToken cancellationToken) + { + await taskContext.AddSubTaskAsync("Copying Runtime Files", (taskContext, cancellationToken) => + { + foreach (var pattern in patterns) + { + var files = extractedDir.GetFiles(pattern, SearchOption.AllDirectories); + foreach (var file in files) + { + var relativePath = Path.GetRelativePath(extractedDir.FullName, file.FullName); + var destPath = Path.Combine(deploymentDir.FullName, relativePath); + + // Create destination directory if needed + var destDir = Path.GetDirectoryName(destPath); + if (!string.IsNullOrEmpty(destDir)) + { + Directory.CreateDirectory(destDir); + } + + file.CopyTo(destPath, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Files} {relativePath}"); + } + } + + return Task.FromResult(0); + }, cancellationToken); + } + + /// + /// Prepares Windows App SDK runtime files for packaging into an MSIX by extracting them to the input folder + /// + /// The folder where runtime files should be copied + /// Cancellation token + /// The path to the self-contained deployment directory + private async Task PrepareRuntimeForPackagingAsync(DirectoryInfo inputFolder, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var arch = WorkspaceSetupService.GetSystemArchitecture(); + + var winappDir = winappDirectoryService.GetLocalWinappDirectory(); + + // Extract runtime files using the existing method + await SetupSelfContainedAsync(winappDir, arch, taskContext, dotNetPackageList, cancellationToken); + + // Copy runtime files from .winapp/self-contained to input folder + var runtimeSourceDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "self-contained", arch, "deployment")); + + if (runtimeSourceDir.Exists) + { + // Copy files recursively to maintain directory structure + foreach (var file in runtimeSourceDir.GetFiles("*", SearchOption.AllDirectories)) + { + var relativePath = Path.GetRelativePath(runtimeSourceDir.FullName, file.FullName); + var destFile = Path.Combine(inputFolder.FullName, relativePath); + + // Create destination directory if needed + var destDir = Path.GetDirectoryName(destFile); + if (!string.IsNullOrEmpty(destDir)) + { + Directory.CreateDirectory(destDir); + } + + file.CopyTo(destFile, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Folder} Bundled runtime: {relativePath}"); + } + + taskContext.AddDebugMessage($"{UiSymbols.Check} Windows App SDK runtime bundled into package"); + } + else + { + throw new DirectoryNotFoundException($"Runtime files not found at {runtimeSourceDir}"); + } + + return runtimeSourceDir; + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs new file mode 100644 index 00000000..f61b4601 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging; +using Windows.Management.Deployment; + +namespace WinApp.Cli.Services; + +/// +/// Manages MSIX package registration, unregistration, and installation using +/// the Windows WinRT API directly, without +/// shelling out to PowerShell. +/// +internal sealed class PackageRegistrationService(ILogger logger) : IPackageRegistrationService +{ + // HRESULT 0x80073CFB = ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD (developer mode not enabled) + // HRESULT 0x800704EC = ERROR_ACCESS_DISABLED_BY_POLICY (group policy blocks sideloading) + private const int ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD = unchecked((int)0x80073CFB); + private const int ERROR_ACCESS_DISABLED_BY_POLICY = unchecked((int)0x800704EC); + + /// + public async Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default) + { + var manifestUri = new Uri(Path.GetFullPath(manifestPath)); + var pm = new PackageManager(); + + try + { + var result = await pm.RegisterPackageAsync( + manifestUri, + null, + DeploymentOptions.DevelopmentMode | DeploymentOptions.ForceApplicationShutdown + ).AsTask(cancellationToken); + + if (!result.IsRegistered) + { + var errorText = result.ErrorText ?? "Unknown error"; + throw new InvalidOperationException( + $"Failed to register package: {errorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); + } + + logger.LogDebug("Package registered from loose layout: {ManifestPath}", manifestPath); + } + catch (Exception ex) when (IsDeveloperModeError(ex)) + { + throw new InvalidOperationException( + "Windows Developer Mode is required to register MSIX packages from loose files. " + + "Open Settings > System > For Developers and enable Developer Mode.", ex); + } + } + + /// + public async Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default) + { + var manifestUri = new Uri(Path.GetFullPath(manifestPath)); + var externalUri = new Uri(Path.GetFullPath(externalLocation) + Path.DirectorySeparatorChar); + var pm = new PackageManager(); + + try + { + var options = new RegisterPackageOptions + { + ExternalLocationUri = externalUri, + DeveloperMode = true, + ForceUpdateFromAnyVersion = true, + }; + + var result = await pm.RegisterPackageByUriAsync( + manifestUri, + options + ).AsTask(cancellationToken); + + if (!result.IsRegistered) + { + var errorText = result.ErrorText ?? "Unknown error"; + throw new InvalidOperationException( + $"Failed to register sparse package: {errorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); + } + + logger.LogDebug("Sparse package registered: {ManifestPath} (external: {ExternalLocation})", manifestPath, externalLocation); + } + catch (Exception ex) when (IsDeveloperModeError(ex)) + { + throw new InvalidOperationException( + "Windows Developer Mode is required to register MSIX packages from loose files. " + + "Open Settings > System > For Developers and enable Developer Mode.", ex); + } + } + + /// + public async Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) + { + var pm = new PackageManager(); + + // FindPackagesForUser with name+publisher requires both to match. + // Use the single-string overload to find by family name prefix, then filter by name. + var allUserPackages = pm.FindPackagesForUser(string.Empty); + var matchingPackages = allUserPackages + .Where(p => string.Equals(p.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (matchingPackages.Count == 0) + { + return false; + } + + foreach (var pkg in matchingPackages) + { + var fullName = pkg.Id.FullName; + logger.LogDebug("Removing package: {PackageFullName}", fullName); + + var result = await pm.RemovePackageAsync(fullName).AsTask(cancellationToken); + + if (!string.IsNullOrEmpty(result.ErrorText)) + { + logger.LogWarning("Warning removing package {PackageFullName}: {Error}", fullName, result.ErrorText); + } + } + + return true; + } + + /// + public async Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default) + { + var packageUri = new Uri(Path.GetFullPath(packagePath)); + var pm = new PackageManager(); + + var result = await pm.AddPackageAsync( + packageUri, + null, + DeploymentOptions.ForceApplicationShutdown + ).AsTask(cancellationToken); + + if (!string.IsNullOrEmpty(result.ErrorText)) + { + throw new InvalidOperationException( + $"Failed to install package '{Path.GetFileName(packagePath)}': {result.ErrorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); + } + + logger.LogDebug("Installed package: {PackagePath}", packagePath); + } + + /// + public string? GetInstalledVersion(string packageName) + { + var pm = new PackageManager(); + // Use the single-parameter overload and filter manually. + // The (userId, name, publisher) overload rejects empty/null publisher + // because string.Empty marshals as null HSTRING in WinRT interop. + var allUserPackages = pm.FindPackagesForUser(string.Empty); + + foreach (var pkg in allUserPackages) + { + if (string.Equals(pkg.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) + { + var v = pkg.Id.Version; + return $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}"; + } + } + + return null; + } + + /// + public List FindDevPackages(string packageName) + { + var pm = new PackageManager(); + var allUserPackages = pm.FindPackagesForUser(string.Empty); + var results = new List(); + + foreach (var pkg in allUserPackages) + { + if (!string.Equals(pkg.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + string? installLocation = null; + try + { + installLocation = pkg.InstalledLocation?.Path; + } + catch + { + // InstalledLocation can throw if the path no longer exists + } + + var v = pkg.Id.Version; + results.Add(new DevPackageInfo( + FullName: pkg.Id.FullName, + Name: pkg.Id.Name, + Version: $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}", + InstallLocation: installLocation, + IsDevelopmentMode: pkg.IsDevelopmentMode)); + } + + return results; + } + + private static bool IsDeveloperModeError(Exception ex) + { + return ex.HResult == ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD + || ex.HResult == ERROR_ACCESS_DISABLED_BY_POLICY; + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs new file mode 100644 index 00000000..d7340d6d --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Reflection.PortableExecutable; + +namespace WinApp.Cli.Services; + +/// +/// Static helper for detecting executable architecture from PE headers. +/// +internal static class PeHelper +{ + /// + /// Detects the architecture of a PE file and returns an MSIX-style architecture string: + /// "x86", "x64", "arm", "arm64", or "neutral". + /// + /// Rules: + /// - Native PE images are classified from the COFF Machine field. + /// - Managed .NET IL-only images are classified using COR flags: + /// * I386 + ILOnly + Requires32Bit => x86 + /// * I386 + ILOnly + !Requires32Bit => neutral + /// - Mixed-mode / native-hosted managed images fall back to the native Machine field. + /// + /// Returns null if the file is not a valid PE image or uses an unsupported architecture. + /// + internal static string? DetectPeArchitecture(string filePath) + { + try + { + using var stream = File.OpenRead(filePath); + using var peReader = new PEReader(stream); + + var headers = peReader.PEHeaders; + var coff = headers.CoffHeader; + var cor = headers.CorHeader; + + ushort machine = (ushort)coff.Machine; + + // Native or mixed-mode case: use the PE machine directly. + if (cor is null) + { + return MapNativeMachine(machine); + } + + CorFlags flags = cor.Flags; + bool isIlOnly = (flags & CorFlags.ILOnly) != 0; + bool requires32Bit = (flags & CorFlags.Requires32Bit) != 0; + + // Managed IL-only assemblies need special handling. + // In particular, IL-only I386 without Requires32Bit is effectively AnyCPU/neutral. + if (isIlOnly) + { + return machine switch + { + 0x014C => requires32Bit ? "x86" : "neutral", // I386 + 0x8664 => "x64", // unusual for pure IL-only, but valid to preserve + 0x01C0 => "arm", // ARM + 0x01C4 => "arm", // ARMNT + 0xAA64 => "arm64", // ARM64 + _ => null + }; + } + + // Mixed-mode / native-entry managed image: machine matters. + return MapNativeMachine(machine); + } + catch + { + return null; + } + } + + private static string? MapNativeMachine(ushort machine) => machine switch + { + 0x014C => "x86", // IMAGE_FILE_MACHINE_I386 + 0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64 + 0xAA64 => "arm64", // IMAGE_FILE_MACHINE_ARM64 + 0x01C4 => "arm", // IMAGE_FILE_MACHINE_ARMNT + 0x01C0 => "arm", // IMAGE_FILE_MACHINE_ARM + _ => null + }; +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PriService.cs b/src/winapp-CLI/WinApp.Cli/Services/PriService.cs new file mode 100644 index 00000000..8f3286aa --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/PriService.cs @@ -0,0 +1,240 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Text.RegularExpressions; +using System.Xml; +using WinApp.Cli.ConsoleTasks; +using WinApp.Cli.Helpers; +using WinApp.Cli.Tools; + +namespace WinApp.Cli.Services; + +/// +/// Handles PRI (Package Resource Index) configuration, generation, and language extraction +/// via MakePri.exe. +/// +internal partial class PriService( + IBuildToolsService buildToolsService) : IPriService +{ + // Extracts language tag from PRI dump qualifier strings like 'Language-en-US' + [GeneratedRegex(@"qualifiers=""[^""]*Language-([a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex PriDumpLanguageQualifierRegex(); + + /// + /// Creates a PRI configuration file for the given package directory + /// + public async Task CreatePriConfigAsync( + DirectoryInfo packageDir, + TaskContext taskContext, + IEnumerable precomputedPriResourceCandidates, + string language = "en-US", + string platformVersion = "10.0.0", + CancellationToken cancellationToken = default) + { + if (!packageDir.Exists) + { + throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); + } + + ArgumentNullException.ThrowIfNull(precomputedPriResourceCandidates); + + var resfilesPath = Path.Combine(packageDir.FullName, "pri.resfiles"); + var priResourceCandidates = precomputedPriResourceCandidates.ToList(); + + priResourceCandidates = [.. priResourceCandidates + .Where(path => MrtAssetHelper.PriIncludedExtensions.Contains(Path.GetExtension(path))) + .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; + + taskContext.AddDebugMessage($"PRI resource candidates discovered: {priResourceCandidates.Count}"); + + using (var writer = new StreamWriter(resfilesPath)) + { + foreach (var priFile in priResourceCandidates) + { + await writer.WriteLineAsync(priFile); + } + } + + var configPath = new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); + var arguments = $@"createconfig /cf ""{configPath}"" /dq lang-{language}_scale-200 /pv {platformVersion} /o"; + + taskContext.AddDebugMessage("Creating PRI configuration file..."); + + try + { + await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + taskContext.AddDebugMessage($"PRI configuration created: {configPath}"); + + var xmlDoc = new XmlDocument(); + xmlDoc.Load(configPath.FullName); + var resourcesNode = xmlDoc.SelectSingleNode("/resources"); + if (resourcesNode != null) + { + var indexNode = resourcesNode.SelectSingleNode("index"); + if (indexNode != null) + { + if (indexNode.Attributes?["startIndexAt"]?.Value != null) + { + // set to relative path + indexNode.Attributes["startIndexAt"]!.Value = ".\\pri.resfiles"; + } + + var resfilesIndexerNode = xmlDoc.CreateElement("indexer-config"); + var typeAttr = xmlDoc.CreateAttribute("type"); + typeAttr.Value = "resfiles"; + resfilesIndexerNode.Attributes.Append(typeAttr); + + var delimiterAttr = xmlDoc.CreateAttribute("qualifierDelimiter"); + delimiterAttr.Value = "."; + resfilesIndexerNode.Attributes.Append(delimiterAttr); + + indexNode.AppendChild(resfilesIndexerNode); + + // Ensure folder-based indexer is configured to parse qualifiers from + // both folder names and file names (e.g. targetsize-48_altform-unplated). + var folderIndexerNode = indexNode + .SelectNodes("indexer-config") + ?.OfType() + .FirstOrDefault(node => + node.Attributes?["type"]?.Value?.Equals("folder", StringComparison.OrdinalIgnoreCase) == true); + + if (folderIndexerNode?.Attributes != null) + { + var folderAttributes = folderIndexerNode.Attributes; + + var folderNameAsQualifierAttr = folderAttributes["foldernameAsQualifier"]; + if (folderNameAsQualifierAttr == null) + { + folderNameAsQualifierAttr = xmlDoc.CreateAttribute("foldernameAsQualifier"); + folderAttributes.Append(folderNameAsQualifierAttr); + } + folderNameAsQualifierAttr.Value = "true"; + + var fileNameAsQualifierAttr = folderAttributes["filenameAsQualifier"]; + if (fileNameAsQualifierAttr == null) + { + fileNameAsQualifierAttr = xmlDoc.CreateAttribute("filenameAsQualifier"); + folderAttributes.Append(fileNameAsQualifierAttr); + } + fileNameAsQualifierAttr.Value = "true"; + } + + xmlDoc.Save(configPath.FullName); + } + } + + return configPath; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to create PRI configuration: {ex.Message}", ex); + } + } + + /// + /// Generates a PRI file from the configuration + /// + public async Task> GeneratePriFileAsync(DirectoryInfo packageDir, TaskContext taskContext, FileInfo? configPath = null, FileInfo? outputPath = null, CancellationToken cancellationToken = default) + { + if (!packageDir.Exists) + { + throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); + } + + var priConfigPath = configPath ?? new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); + var priOutputPath = outputPath ?? new FileInfo(Path.Combine(packageDir.FullName, "resources.pri")); + + if (!priConfigPath.Exists) + { + throw new FileNotFoundException($"PRI configuration file not found: {priConfigPath}"); + } + + var arguments = $@"new /pr ""{Path.TrimEndingDirectorySeparator(packageDir.FullName)}"" /cf ""{priConfigPath.FullName}"" /of ""{priOutputPath.FullName}"" /o"; + + taskContext.AddDebugMessage("Generating PRI file..."); + + try + { + var (stdout, stderr) = await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + // Parse the output to extract resource files + var resourceFiles = new List(); + var lines = stdout.Replace("\0", "").Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) + { + // Look for lines that match the pattern "Resource File: *" + const string resourceFileStr = "Resource File: "; + if (line.StartsWith(resourceFileStr, StringComparison.OrdinalIgnoreCase)) + { + var fileName = line[resourceFileStr.Length..].Trim(); + if (!string.IsNullOrEmpty(fileName)) + { + resourceFiles.Add(new FileInfo(Path.Combine(packageDir.FullName, fileName))); + } + } + } + + taskContext.AddDebugMessage($"PRI file generated: {priOutputPath}"); + if (resourceFiles.Count > 0) + { + taskContext.AddDebugMessage($"Processed {resourceFiles.Count} resource files"); + } + + return resourceFiles; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to generate PRI file: {ex.Message}", ex); + } + } + + /// + /// Extracts language qualifiers from a PRI file using makepri dump. + /// Returns a distinct, sorted list of BCP-47 language tags found in the PRI resource map. + /// + public async Task> ExtractLanguagesFromPriAsync( + FileInfo priFile, + TaskContext taskContext, + CancellationToken cancellationToken) + { + try + { + var dumpOutputFile = Path.Combine(Path.GetTempPath(), $"winapp-pri-dump-{Guid.NewGuid():N}.xml"); + var arguments = $@"dump /if ""{priFile.FullName}"" /of ""{dumpOutputFile}"" /o"; + + await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + if (!File.Exists(dumpOutputFile)) + { + return []; + } + + try + { + var dumpContent = await File.ReadAllTextAsync(dumpOutputFile, cancellationToken); + + // Extract language qualifiers from Candidate elements: + // or multi-qualifier like "Language-en-US, Scale-200" + var languages = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in PriDumpLanguageQualifierRegex().Matches(dumpContent)) + { + languages.Add(match.Groups[1].Value); + } + + return languages.OrderBy(l => l, StringComparer.OrdinalIgnoreCase).ToList(); + } + finally + { + File.Delete(dumpOutputFile); + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to extract languages from PRI: {ex.Message}"); + return []; + } + } +} From 4e82a8953a62e50a1978bc44bf964a61559cbd8e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:05:47 +0000 Subject: [PATCH 5/7] Clean up branch: remove files not in alzollin/vsc base Co-authored-by: chiaramooney <34109996+chiaramooney@users.noreply.github.com> --- docs/debugging.md | 141 --- plugin.json | 67 -- samples/cpp-app-winui/.gitignore | 8 - .../cpp-app-winui/Assets/LockScreenLogo.png | Bin 229 -> 0 bytes .../Assets/LockScreenLogo.scale-200.png | Bin 432 -> 0 bytes samples/cpp-app-winui/Assets/SplashScreen.png | Bin 1579 -> 0 bytes .../Assets/SplashScreen.scale-200.png | Bin 5372 -> 0 bytes .../Assets/Square150x150Logo.png | Bin 733 -> 0 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 1755 -> 0 bytes .../cpp-app-winui/Assets/Square44x44Logo.png | Bin 326 -> 0 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 637 -> 0 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 283 -> 0 bytes samples/cpp-app-winui/Assets/StoreLogo.png | Bin 456 -> 0 bytes .../cpp-app-winui/Assets/Wide310x150Logo.png | Bin 806 -> 0 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 2097 -> 0 bytes samples/cpp-app-winui/CMakeLists.txt | 100 -- samples/cpp-app-winui/README.md | 53 -- samples/cpp-app-winui/app.manifest | 19 - samples/cpp-app-winui/appxmanifest.xml | 54 -- samples/cpp-app-winui/main.cpp | 134 --- samples/cpp-app-winui/winapp.yaml | 15 - samples/electron/Assets/AppList.png | Bin 1774 -> 0 bytes samples/electron/Assets/AppList.scale-125.png | Bin 2238 -> 0 bytes samples/electron/Assets/AppList.scale-150.png | Bin 2462 -> 0 bytes samples/electron/Assets/AppList.scale-200.png | Bin 3466 -> 0 bytes samples/electron/Assets/AppList.scale-400.png | Bin 8370 -> 0 bytes .../electron/Assets/AppList.targetsize-16.png | Bin 721 -> 0 bytes ...AppList.targetsize-16_altform-unplated.png | Bin 721 -> 0 bytes .../electron/Assets/AppList.targetsize-20.png | Bin 910 -> 0 bytes ...AppList.targetsize-20_altform-unplated.png | Bin 910 -> 0 bytes .../electron/Assets/AppList.targetsize-24.png | Bin 1083 -> 0 bytes ...AppList.targetsize-24_altform-unplated.png | Bin 1083 -> 0 bytes .../Assets/AppList.targetsize-256.png | Bin 13555 -> 0 bytes ...ppList.targetsize-256_altform-unplated.png | Bin 13555 -> 0 bytes .../electron/Assets/AppList.targetsize-30.png | Bin 1306 -> 0 bytes ...AppList.targetsize-30_altform-unplated.png | Bin 1306 -> 0 bytes .../electron/Assets/AppList.targetsize-32.png | Bin 1378 -> 0 bytes ...AppList.targetsize-32_altform-unplated.png | Bin 1378 -> 0 bytes .../electron/Assets/AppList.targetsize-36.png | Bin 1613 -> 0 bytes ...AppList.targetsize-36_altform-unplated.png | Bin 1613 -> 0 bytes .../electron/Assets/AppList.targetsize-40.png | Bin 1683 -> 0 bytes ...AppList.targetsize-40_altform-unplated.png | Bin 1683 -> 0 bytes .../electron/Assets/AppList.targetsize-48.png | Bin 1922 -> 0 bytes ...AppList.targetsize-48_altform-unplated.png | Bin 1922 -> 0 bytes .../electron/Assets/AppList.targetsize-60.png | Bin 2067 -> 0 bytes ...AppList.targetsize-60_altform-unplated.png | Bin 2067 -> 0 bytes .../electron/Assets/AppList.targetsize-64.png | Bin 2210 -> 0 bytes ...AppList.targetsize-64_altform-unplated.png | Bin 2210 -> 0 bytes .../electron/Assets/AppList.targetsize-72.png | Bin 2767 -> 0 bytes ...AppList.targetsize-72_altform-unplated.png | Bin 2767 -> 0 bytes .../electron/Assets/AppList.targetsize-80.png | Bin 2851 -> 0 bytes ...AppList.targetsize-80_altform-unplated.png | Bin 2851 -> 0 bytes .../electron/Assets/AppList.targetsize-96.png | Bin 3269 -> 0 bytes ...AppList.targetsize-96_altform-unplated.png | Bin 3269 -> 0 bytes samples/electron/Assets/MedTile.png | Bin 3912 -> 0 bytes samples/electron/Assets/MedTile.scale-125.png | Bin 8537 -> 0 bytes samples/electron/Assets/MedTile.scale-150.png | Bin 12278 -> 0 bytes samples/electron/Assets/MedTile.scale-200.png | Bin 5401 -> 0 bytes samples/electron/Assets/MedTile.scale-400.png | Bin 46433 -> 0 bytes .../electron/Assets/StoreLogo.scale-125.png | Bin 2337 -> 0 bytes .../electron/Assets/StoreLogo.scale-150.png | Bin 2416 -> 0 bytes .../electron/Assets/StoreLogo.scale-200.png | Bin 2875 -> 0 bytes .../electron/Assets/StoreLogo.scale-400.png | Bin 7659 -> 0 bytes samples/electron/Assets/WideTile.png | Bin 4541 -> 0 bytes .../electron/Assets/WideTile.scale-125.png | Bin 9760 -> 0 bytes .../electron/Assets/WideTile.scale-150.png | Bin 14010 -> 0 bytes .../electron/Assets/WideTile.scale-200.png | Bin 6344 -> 0 bytes .../electron/Assets/WideTile.scale-400.png | Bin 55873 -> 0 bytes samples/electron/Assets/app.ico | Bin 18745 -> 0 bytes .../AppLauncherServiceTests.cs | 76 -- .../AppxManifestDocumentTests.cs | 780 ---------------- .../FakeDebugOutputService.cs | 21 - .../FakePackageRegistrationService.cs | 74 -- .../IncrementalCopyHelperTests.cs | 280 ------ .../WinApp.Cli.Tests/MrtAssetHelperTests.cs | 395 -------- .../UnregisterCommandTests.cs | 201 ---- .../Assets/msix_default_assets/AppList.png | Bin 326 -> 0 bytes .../msix_default_assets/AppList.scale-200.png | Bin 637 -> 0 bytes ...AppList.targetsize-24_altform-unplated.png | Bin 283 -> 0 bytes .../Assets/msix_default_assets/MedTile.png | Bin 733 -> 0 bytes .../msix_default_assets/MedTile.scale-200.png | Bin 1755 -> 0 bytes .../Assets/msix_default_assets/WideTile.png | Bin 806 -> 0 bytes .../WideTile.scale-200.png | Bin 2097 -> 0 bytes .../WinApp.Cli/Commands/UnregisterCommand.cs | 174 ---- .../WinApp.Cli/Helpers/ManifestHelper.cs | 32 - .../Services/AppxManifestDocument.cs | 626 ------------- .../WinApp.Cli/Services/DebugOutputService.cs | 214 ----- .../Services/IDebugOutputService.cs | 24 - .../Services/IPackageRegistrationService.cs | 71 -- .../WinApp.Cli/Services/IPriService.cs | 29 - .../Services/IncrementalCopyHelper.cs | 107 --- .../WinApp.Cli/Services/MrtAssetHelper.cs | 281 ------ .../Services/MsixService.Identity.cs | 771 --------------- .../Services/MsixService.Runtime.cs | 881 ------------------ .../Services/PackageRegistrationService.cs | 206 ---- .../WinApp.Cli/Services/PeHelper.cs | 82 -- .../WinApp.Cli/Services/PriService.cs | 240 ----- 97 files changed, 6156 deletions(-) delete mode 100644 docs/debugging.md delete mode 100644 plugin.json delete mode 100644 samples/cpp-app-winui/.gitignore delete mode 100644 samples/cpp-app-winui/Assets/LockScreenLogo.png delete mode 100644 samples/cpp-app-winui/Assets/LockScreenLogo.scale-200.png delete mode 100644 samples/cpp-app-winui/Assets/SplashScreen.png delete mode 100644 samples/cpp-app-winui/Assets/SplashScreen.scale-200.png delete mode 100644 samples/cpp-app-winui/Assets/Square150x150Logo.png delete mode 100644 samples/cpp-app-winui/Assets/Square150x150Logo.scale-200.png delete mode 100644 samples/cpp-app-winui/Assets/Square44x44Logo.png delete mode 100644 samples/cpp-app-winui/Assets/Square44x44Logo.scale-200.png delete mode 100644 samples/cpp-app-winui/Assets/Square44x44Logo.targetsize-24_altform-unplated.png delete mode 100644 samples/cpp-app-winui/Assets/StoreLogo.png delete mode 100644 samples/cpp-app-winui/Assets/Wide310x150Logo.png delete mode 100644 samples/cpp-app-winui/Assets/Wide310x150Logo.scale-200.png delete mode 100644 samples/cpp-app-winui/CMakeLists.txt delete mode 100644 samples/cpp-app-winui/README.md delete mode 100644 samples/cpp-app-winui/app.manifest delete mode 100644 samples/cpp-app-winui/appxmanifest.xml delete mode 100644 samples/cpp-app-winui/main.cpp delete mode 100644 samples/cpp-app-winui/winapp.yaml delete mode 100644 samples/electron/Assets/AppList.png delete mode 100644 samples/electron/Assets/AppList.scale-125.png delete mode 100644 samples/electron/Assets/AppList.scale-150.png delete mode 100644 samples/electron/Assets/AppList.scale-200.png delete mode 100644 samples/electron/Assets/AppList.scale-400.png delete mode 100644 samples/electron/Assets/AppList.targetsize-16.png delete mode 100644 samples/electron/Assets/AppList.targetsize-16_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-20.png delete mode 100644 samples/electron/Assets/AppList.targetsize-20_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-24.png delete mode 100644 samples/electron/Assets/AppList.targetsize-24_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-256.png delete mode 100644 samples/electron/Assets/AppList.targetsize-256_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-30.png delete mode 100644 samples/electron/Assets/AppList.targetsize-30_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-32.png delete mode 100644 samples/electron/Assets/AppList.targetsize-32_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-36.png delete mode 100644 samples/electron/Assets/AppList.targetsize-36_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-40.png delete mode 100644 samples/electron/Assets/AppList.targetsize-40_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-48.png delete mode 100644 samples/electron/Assets/AppList.targetsize-48_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-60.png delete mode 100644 samples/electron/Assets/AppList.targetsize-60_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-64.png delete mode 100644 samples/electron/Assets/AppList.targetsize-64_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-72.png delete mode 100644 samples/electron/Assets/AppList.targetsize-72_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-80.png delete mode 100644 samples/electron/Assets/AppList.targetsize-80_altform-unplated.png delete mode 100644 samples/electron/Assets/AppList.targetsize-96.png delete mode 100644 samples/electron/Assets/AppList.targetsize-96_altform-unplated.png delete mode 100644 samples/electron/Assets/MedTile.png delete mode 100644 samples/electron/Assets/MedTile.scale-125.png delete mode 100644 samples/electron/Assets/MedTile.scale-150.png delete mode 100644 samples/electron/Assets/MedTile.scale-200.png delete mode 100644 samples/electron/Assets/MedTile.scale-400.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-125.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-150.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-200.png delete mode 100644 samples/electron/Assets/StoreLogo.scale-400.png delete mode 100644 samples/electron/Assets/WideTile.png delete mode 100644 samples/electron/Assets/WideTile.scale-125.png delete mode 100644 samples/electron/Assets/WideTile.scale-150.png delete mode 100644 samples/electron/Assets/WideTile.scale-200.png delete mode 100644 samples/electron/Assets/WideTile.scale-400.png delete mode 100644 samples/electron/Assets/app.ico delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/AppLauncherServiceTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.scale-200.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.targetsize-24_altform-unplated.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.scale-200.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.scale-200.png delete mode 100644 src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IPriService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs delete mode 100644 src/winapp-CLI/WinApp.Cli/Services/PriService.cs diff --git a/docs/debugging.md b/docs/debugging.md deleted file mode 100644 index 1c0cffbc..00000000 --- a/docs/debugging.md +++ /dev/null @@ -1,141 +0,0 @@ -# Debugging with Package Identity - -Many Windows APIs (push notifications, background tasks, share target, startup tasks, Windows AI APIs) require your app to have **package identity**. During development, you don't want to build a full MSIX installer every time you test — winapp provides two commands to give your app identity on the fly. - -> **Using Visual Studio with a packaging project?** If you are already using Visual Studio for your packaged project, you likely don't need winapp for debugging. Visual Studio already handles package registration, identity, AUMID activation, debugger attachment, and activation-code debugging — all from F5. It also offers **Debug → Other Debug Targets → Debug Installed App Package** for advanced scenarios. The workflows below are most useful for **VS Code users, terminal-based workflows, and frameworks that VS doesn't natively package** (Rust, Flutter, Tauri, Electron, plain C++). - -## Two approaches: `winapp run` vs `create-debug-identity` - -| | `winapp run` | `create-debug-identity` | -|---|---|---| -| **What it registers** | Full loose layout package (entire folder) | Sparse package (single exe) | -| **How the app launches** | Launched by winapp (AUMID activation or execution alias) | You launch the exe yourself (command line, IDE, etc.) | -| **Simulates MSIX install** | Yes — closest to production behavior | No — sparse identity only | -| **Files stay in place** | Copied to an AppX layout directory | Yes — exe stays at its original path | -| **Identity scope** | Entire folder contents (exe, DLLs, assets) | Single executable | -| **Debugger-friendly** | Attach to PID after launch, or use `--no-launch` then launch via alias | Launch directly from your IDE's debugger — the exe has identity regardless | -| **Console app support** | `--with-alias` keeps stdin/stdout in terminal | Run exe directly in terminal | -| **Best for** | Most frameworks (.NET, C++, Rust, Flutter, Tauri) | Electron, or when you need full IDE debugger control (F5) | - -## When to use which - -### Default: `winapp run` - -Use `winapp run` for most development workflows. It simulates a real MSIX install — your app gets the same identity, capabilities, and file associations it would have in production. - -```powershell -# Build your app, then: -winapp run .\build\output -``` - -### Use `create-debug-identity` when: - -- **Your exe is separate from your build output** — e.g., Electron apps where `electron.exe` lives in `node_modules/` -- **You need to debug startup code** and can't attach a debugger fast enough after AUMID launch -- **With some debuggers where you can't launch with AUMID** and need identity on the launched process — `create-debug-identity` registers the exe so it has identity no matter how it's started -- **You're testing sparse package behavior** specifically (AllowExternalContent, TrustedLaunch) - -```powershell -# Register identity for an exe, then launch it however you want: -winapp create-debug-identity .\bin\Debug\myapp.exe -.\bin\Debug\myapp.exe # or F5 in your IDE -``` - -## Debugging scenarios - -### Scenario A: Just run with identity - -The simplest workflow — build, run with identity, done. - -```powershell -winapp run .\build\Debug -``` - -Winapp registers the folder as a loose layout package and launches the app. Identity-requiring APIs work immediately. This covers the majority of development and testing scenarios. - -For **console apps** that need stdin/stdout in the current terminal, add `--with-alias`: - -```powershell -winapp run .\build\Debug --with-alias -``` - -### Scenario B: Attach a debugger to a running app - -Launch with `winapp run`, note the PID, then attach your IDE's debugger. - -```powershell -winapp run .\build\Debug -# Output: Process ID: 12345 -``` - -Then in your IDE: -- **VS Code**: Run and Debug → select "Attach" configuration (see [IDE setup](#ide-setup) below) -- **WinDbg**: `windbg -p 12345` - -> **Limitation:** You'll miss any code that runs before you attach. For startup debugging, use Scenario D (`create-debug-identity`). - -### Scenario C: Register identity, then launch via AUMID or alias from your IDE - -Use `--no-launch` to register the package, then launch the app through its AUMID (reported by `run`) or **execution alias** from your IDE. - -**Step 1:** Register the package without launching: - -```powershell -winapp run .\build\Debug --no-launch -``` - -**Step 2:** Configure your IDE to launch via the AUMID or the **execution alias** (not the exe directly). -* Launching with AUMID: Use the command `start shell:AppsFolder\`. `winapp run` outputs the AUMID when the app is registered. -* Launching with the alias: The alias must be defined in the appxmanifest.xml (or Package.appxmanifest). - -> **Important:** Simply launching the exe in the build folder will **not** give it identity. The app must be started via AUMID activation or its execution alias. This is how loose layout packages work - identity is tied to the activation path, not the exe file. - -### Scenario D: Launch from your IDE with identity (startup debugging) - -This is the best approach for **debugging startup code with full IDE control** - your IDE's debugger controls the process from the very first instruction, and the exe has identity no matter how it's launched. - -```powershell -winapp create-debug-identity .\build\Debug\myapp.exe -``` - -Now launch the exe any way you like — from the terminal, from VS Code's F5, from a script. The exe has identity because Windows registered a **sparse package** pointing directly at it. - -> **How it differs from `winapp run`:** With `create-debug-identity`, identity is tied to the exe itself (via `Add-AppxPackage -ExternalLocation`). With `winapp run`, identity is tied to the loose layout package — the app must be launched through AUMID or an alias. This makes `create-debug-identity` the better choice when you need your IDE to launch and debug the exe directly. - -> This is also the best approach for **Electron apps** where the exe path differs from your source directory. - -### Scenario E: Capture debug output - -Capture `OutputDebugString` messages and first-chance exceptions inline: - -```powershell -winapp run .\build\Debug --debug-output -``` - -> **Important:** This attaches winapp as the debugger. Windows only allows one debugger per process, so you **cannot** also attach Visual Studio, VS Code, or WinDbg. - -## IDE setup - -### VS Code - -**Attach to running process** — add to `.vscode/launch.json`: - -```json -{ - "name": "Attach to Process", - "type": "coreclr", - "request": "attach", - "processId": "${command:pickProcess}" -} -``` - -For C++/Rust, use `"type": "cppvsdbg"` (MSVC) or `"type": "lldb"` (LLDB): - -```json -{ - "name": "Attach (C++)", - "type": "cppvsdbg", - "request": "attach", - "processId": "${command:pickProcess}" -} -``` diff --git a/plugin.json b/plugin.json deleted file mode 100644 index 2e09a949..00000000 --- a/plugin.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "winappcli", - "description": "Windows app development, packaging, and distribution. Helps with creating Windows installers (MSIX), code signing, certificates, Windows SDK and Windows App SDK setup, package identity for Windows APIs (push notifications, background tasks, share target), appxmanifest authoring, and Microsoft Store distribution. Supports Electron, .NET, C++, Rust, Flutter, and Tauri apps.", - "version": "0.2.2", - "author": { - "name": "Microsoft", - "url": "https://github.com/microsoft/WinAppCli" - }, - "license": "MIT", - "keywords": [ - "windows", - "msix", - "packaging", - "installer", - "identity", - "electron", - "winapp", - "windows app sdk", - "windows sdk", - "appxmanifest.xml", - "appxmanifest", - "code signing", - "certificate", - "pfx", - "signtool", - "makeappx", - "windows store", - "microsoft store", - "windows distribution", - "windows packaging", - "windows installer", - "package identity", - "sparse package", - "push notifications", - "background tasks", - "share target", - "startup task", - "tauri", - "flutter", - "rust", - "dotnet", - "wpf", - "winforms", - "cpp", - "cppwinrt", - "windows api", - "winrt", - "uwp", - "desktop app", - "win32" - ], - "category": "windows-development", - "tags": [ - "windows", - "msix", - "packaging", - "installer", - "desktop", - "notifications", - "sdk", - "signing", - "certificate", - "distribution" - ], - "agents": ".github/plugin/agents/", - "skills": ".github/plugin/skills/winapp-cli/" -} diff --git a/samples/cpp-app-winui/.gitignore b/samples/cpp-app-winui/.gitignore deleted file mode 100644 index 6b6ab294..00000000 --- a/samples/cpp-app-winui/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ - -# Development certificate -devcert.pfx - -# Windows SDK packages and generated files -.winapp - -.winapp-tools \ No newline at end of file diff --git a/samples/cpp-app-winui/Assets/LockScreenLogo.png b/samples/cpp-app-winui/Assets/LockScreenLogo.png deleted file mode 100644 index 180ad62f876530c13fba8bcfa18263686cf5441d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;w=>VS)*ZnZyUf=^`LTN8M$7Mie zoFzei!Ihp#4BMBUvHyCZP-HGpu)x#BF+}2WvVj{D&oiEafHMgfR%)hWq3 z(=vMIHZS72$sn?LqONNp-zMk4Jq?d6Q}P$Ha1;eFGT4;pS$_V;{}O07gQu&X%Q~lo FCIDy-QN{oO diff --git a/samples/cpp-app-winui/Assets/LockScreenLogo.scale-200.png b/samples/cpp-app-winui/Assets/LockScreenLogo.scale-200.png deleted file mode 100644 index 7440f0d4bf7c7e26e4e36328738c68e624ee851e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FqV6|IEGZ*x-#9g>~Mkr+x6^F zy~CDX2QIMs&Gcs3RnRBoxBA!*(Mfw0KTCYuYk0WlEIV>qBmPl! zq4ukrvfADX@#p8fbLY(H47N+k`FZ(FZh?cDro7>{8mkBO3>^oaIx`3!Jl)Qq)HI!+ z(S=1{o~eT)&W^=Ea8C`-17(Jv5(nHFJ{dOjGdxLVkY_y6&S1whfuFI4MM0kF0f&cO zPDVpV%nz;Id$>+0Ga5e9625-JcI)oq=#Pa3p^>8BB}21BUw@eN!-6@w%X+^`+Vn?! zryu|3T>kVWNBYyBc=7Y6H#s1Ah!OI_nezW zXTqOdkv2Az6KKBV=$yHdF^R3Fqw(TZEoNSZX>reXJ#bwX42%f|Pgg&ebxsLQ010xn AssI20 diff --git a/samples/cpp-app-winui/Assets/SplashScreen.png b/samples/cpp-app-winui/Assets/SplashScreen.png deleted file mode 100644 index a634fef0fc2ba26e2910c5e8c41f8bf6e1bb994f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1579 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 diff --git a/samples/cpp-app-winui/Assets/Square44x44Logo.png b/samples/cpp-app-winui/Assets/Square44x44Logo.png deleted file mode 100644 index 6135405f8f13c518341e3995b41dfc0fbf86e5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^Iv~u#3?$#IayJ4}iUB?$uI>ds`|$#+%Dw(T#oQ%9 ze!&dV&*qE#sjjJ9ekxDB{uogBq^FBxh{pM;ldbs<8Su0%Z(C}Uo!o}p|QcIwa_ zzr75`QswsiYB9gAFEbIg{Dl><3nv3A}sm=EZ)#l3`NR zpZda3^rNox*D1%NC98Z~L*6zipLw~Gxn&(Y-;KmJ+aR6eLabU-L#y8HW%7P-E_-VlLqIabbHPHKT*)fT@9iWJ7iWgOT9%0}Lrj>lztPxWq6sPw3pi z#-<=#$jjrP_DD*i!RLsn0mIA=>4~N)IMYWIf=j%-zuKCdMG%tHYot70D1| zvWa0wMhauW#S>1CnI_;>!1Q3zMA17@DOVq{MQ+{U7^a&yA+%dMCG;WNPV0i;w$tu; zX^b}UKziPM)(<;)ruW;-`)bBN+rQNM*Zs_>?n$|FVFo-e*PZb*@U7VAd+tHb4e?=Blc~}S6K)wL}r*Gf`BM#QB z+y>N$mCswb4d{^{S9v_!eQj4fTRMOwOCi?lSk9%<=vAz}jM-*PQtH@Odn1LZcd^j#o> hW$4xn+CT+ep9lJ{OAO?njobhL002ovPDHLkV1nYebbkN< diff --git a/samples/cpp-app-winui/Assets/StoreLogo.png b/samples/cpp-app-winui/Assets/StoreLogo.png deleted file mode 100644 index a4586f26bdf7841cad10f39cdffe2aca3af252c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 456 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fF!p=8IEGZ*dUM0H=rDtTTVkd2 z(%lbKn@VS_lUaADVB&;Z6F#LM+mPsa?e>FnHo;HND^!P`-lX%BH~FOg%y&x+t*x!? zg$#_1A1kgsSvO(fw`bOmo;lrJX8byO1j^gf7qohR%mmt z@L)WX;>gqgK|tWJvQ5j;4;=gt4HXVKSMYRv5RhY5vS~TqfK_NAP*r{h!!g^BZ;w4r z7CGdsai)y;fJQc`7{Zc2b==h%o`Op$|bg6a&nL{*m7-=0>k4M4-PXlU;G-?%*(*g>iFt^ U$m#7DfHB12>FVdQ&MBb@0G`#n8vp1KIqEP)`#j%umtt)f(_Qx@)^@hpb;sJa4{hxKS}ERE zEv}h+ly_S)f4r7=+x)z;emj|q|KCb3T*D^XR^2h-YPOS7w(aJH2e)Ob7Q$^;?<}~6 z?Mu4thy_=(Jse866&$#RZ6)8vfos?b`8Ebz%Z6x6!?kRPwj^A~25C#db!?Ef1YFOC zX^X@4Y?!tv+`tBEi@^C^(A^*hawFY{)hr&Spcld2kLJw9SQc*r06=oXdu7v*BDeY?}q=v4Pu6IFAk7 zX2AJu=(Z8gXG6CQa0DB?t%oDn;B6fo#fEQd;V3qITLbq&v1QO!!#z`M8MM`K8;UK5 zwi=FN%b~4-`(9<)Hd$}mPK!L-=7G0d8ML);u9i8r)ofD|4&PQ^67I=EKD@2Hl9EqH z;BD=dlzcjZZtHJAC^&Rme*;3nf!oIWHUbXZHr}@paM(8UcGri)wwbrPJ{+{oJ|%c? z&^G&&;K3o=+{2X%hir2XS1uf|%|DYmaKJYIOzObl+Nh(v4To!^j`B7ftc`pEV#C4O z$R{8+9IB0Gv*A!}G@A_vY74PhaG8n{aQTOJl$_m)e#jmOr#9u#ilemgcRGydjIzg5(>zb)r)iVrgW7F(y& k>2x}sPN&o9bh>}uFDMS%SxTob*#H0l07*qoM6N<$f=8c%l>h($ diff --git a/samples/cpp-app-winui/Assets/Wide310x150Logo.scale-200.png b/samples/cpp-app-winui/Assets/Wide310x150Logo.scale-200.png deleted file mode 100644 index 8b4a5d0dd5f6c6ab408e1edf04a07888859a9eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2097 zcmc&$ZA@EL7(Q+3r2+3&!t4k)FxZ4;Vj0QoMdu<_3Kciz7YR4=A(qT^w)Jxi;?x!9 zuyq6dMF?b>c0sq%A~kJcD9FY~qQRMt?ZR3YyDZt}Od;|mgpc{2dv9AHF){kXU%k({ z=Y8JidEayHTkG@twPZ|U3_^%3ct-OgLSiFAqDN!|tbCX@c@?4P`2x*TMK!+Q4b?k0 ziW7!!KF6dPWcF<%I|iznM~`QJ_V7sHGV_D`dhgpA9Vd@&X}ErK+j~_rdv;Bp?OA@a zFXOk7eWOJe5NcK;6h$FaM&7JxNc#-@QTwzW6x#d_zmQNkz5) zPI;kh;3d;5UCJU+9a(cOxX(|edWoOiAEdGU#kPJ&xnc2||3vDbuhBCkj-pb0as$Zl z5;}4n=**n6(1g`JEtSy;SG6X;#-F~Oz3lESG2b5`j@wAwY4Yp<=4Xeb>iH=6aicF?DxD&q{`!&}ct zBI)aycwuobQAf&678Uf+Mmh-@9RUhyH~>?w0dixO0#jZjEc9R^=5NZw=|a(kcB?9^ zfnTiEFXp-q#B;Tn>(O%$A*ud^Rg&eVH6Y_5Y%!E39RR&s?XpG`gKwU!6FE1 z7X)DC7)*(5g}lh`4`{i~DZcWupZI`K)_4P)VE{@gc7@Xsd^86zl~_mOYH?I4!aGeX z^E(_=L6?PgveDQ+r%P@UISEXrkn`LHJZ##+!-anV>6h)IkKp;E@p8+3&(5%kS2)ld*J*rJccZM0iyaAx7+F~GW1UWFK&3X$PE1^}NH zgAG9ck5K!{07OwU@j@Do>TbH=CDEo#4m0cEyAuXy_<&jlzJVcKweSJ5 z&=q~iIn18$w8yb=rmEmHxVEUA^?RwnB?6Qlp1os8@*dWTGL2bhzZ!s*xqScR?EPL` zo(JwNdKUUYy7GtvZ3asXm)cgFvCx9EmAi;|w=a0iGiv%%VYKh`P0Wma4y`Xyx|T~( zAmfGbgbEEC7)j8b@WA@+5W3a61HJXC1dX@6_T|Czk0I0zBk%tnW~()VWITGI!`$c< gARL?UBrYYkwoDw4eo*CrzXGTrZ@;GF>596)00d&n@&Et; diff --git a/samples/cpp-app-winui/CMakeLists.txt b/samples/cpp-app-winui/CMakeLists.txt deleted file mode 100644 index 3d680319..00000000 --- a/samples/cpp-app-winui/CMakeLists.txt +++ /dev/null @@ -1,100 +0,0 @@ -cmake_minimum_required(VERSION 3.20) -project(cpp-app-winui) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Download winapp CLI if not available in PATH -find_program(WINAPP_CLI winapp) -if(NOT WINAPP_CLI) - set(WINAPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.winapp-tools") - set(WINAPP_CLI "${WINAPP_DIR}/winapp.exe") - - if(NOT EXISTS "${WINAPP_CLI}") - message(STATUS "Downloading winapp CLI...") - - # Determine architecture - if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64") - set(WINAPP_ARCH "arm64") - else() - set(WINAPP_ARCH "x64") - endif() - - # Download and extract - set(WINAPP_ZIP "${CMAKE_CURRENT_BINARY_DIR}/winappcli.zip") - file(DOWNLOAD - "https://github.com/microsoft/WinAppCli/releases/latest/download/winappcli-${WINAPP_ARCH}.zip" - "${WINAPP_ZIP}" - SHOW_PROGRESS - ) - - file(ARCHIVE_EXTRACT INPUT "${WINAPP_ZIP}" DESTINATION "${WINAPP_DIR}") - file(REMOVE "${WINAPP_ZIP}") - message(STATUS "winapp CLI downloaded to ${WINAPP_DIR}") - endif() -endif() - -# Automatically restore Windows App SDK headers and generate certificate if needed -# This runs once during CMake configuration, not on every build -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include") - message(STATUS "Restoring Windows App SDK headers...") - execute_process( - COMMAND "${WINAPP_CLI}" restore - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE RESTORE_RESULT - ) - if(NOT RESTORE_RESULT EQUAL 0) - message(WARNING "Failed to restore Windows App SDK. Run 'winapp restore' manually.") - endif() -endif() - -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/devcert.pfx") - message(STATUS "Generating development certificate...") - execute_process( - COMMAND "${WINAPP_CLI}" cert generate --if-exists skip - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE CERT_RESULT - ) - if(NOT CERT_RESULT EQUAL 0) - message(WARNING "Failed to generate certificate. Run 'winapp cert generate' manually.") - endif() -endif() - -# WinUI 3 windowed application (WIN32 = Windows subsystem, no console) -add_executable(cpp-app-winui WIN32 main.cpp) - -# C++/WinRT + WinUI headers are large; need /bigobj for MSVC -if(MSVC) - target_compile_options(cpp-app-winui PRIVATE /bigobj) -endif() - -# Link Windows Runtime and bootstrapper libraries -target_link_libraries(cpp-app-winui PRIVATE WindowsApp.lib OneCoreUap.lib Microsoft.WindowsAppRuntime.Bootstrap.lib) - -# Add Windows App SDK include and lib directories -target_include_directories(cpp-app-winui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include) - -# Determine build architecture for lib/bin paths -if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64") - set(WINAPP_LIB_ARCH "arm64") -else() - set(WINAPP_LIB_ARCH "x64") -endif() - -target_link_directories(cpp-app-winui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/lib/${WINAPP_LIB_ARCH}) - -# Embed the application manifest for DPI awareness -if(MSVC) - target_sources(cpp-app-winui PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/app.manifest") -endif() - -# Copy Windows App SDK runtime DLLs to the build output directory -set(WINAPP_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.winapp/bin/${WINAPP_LIB_ARCH}") -if(EXISTS "${WINAPP_BIN_DIR}") - add_custom_command(TARGET cpp-app-winui POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - "${WINAPP_BIN_DIR}" - "$" - COMMENT "Copying Windows App SDK runtime DLLs..." - ) -endif() diff --git a/samples/cpp-app-winui/README.md b/samples/cpp-app-winui/README.md deleted file mode 100644 index f51003f8..00000000 --- a/samples/cpp-app-winui/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# C++ WinUI 3 Sample Application - -This sample demonstrates a WinUI 3 desktop window built entirely in C++ with CMake - no XAML files, no MIDL, no MSBuild. The UI is constructed programmatically using C++/WinRT projections. - -For the console-only version, see the [C++ Sample](../cpp-app/). - -## What This Sample Shows - -- **Programmatic WinUI 3** — Window, Button, TextBlock, StackPanel created in code -- Using Windows App Model APIs to retrieve package identity -- Using `winapp run` to run the app packaged with MSIX identity -- MSIX packaging with app manifest and assets -- CMake integration with Windows App SDK headers and runtime DLLs - -## Prerequisites - -- Visual Studio with C++ Desktop development workload -- CMake 3.20 or later -- Windows App SDK runtime installed -- WinApp CLI installed - -## Building and Running - -### Build the Application - -```powershell -cmake -B build -cmake --build build --config Debug -``` - -### Run without Identity - -```powershell -.\build\Debug\cpp-app-winui.exe -``` - -Click the **Check Identity** button — it will show "Not packaged". - -### Run with Identity (Debug) - -```powershell -winapp run .\build\Debug -``` - -This registers a loose layout package (just like a real MSIX install), then launches the WinUI 3 window. Click the **Check Identity** button to see the Package Family Name. - -## Technical Notes - -This sample uses a non-standard approach to WinUI 3: all UI is built programmatically in C++ without XAML files. This avoids the need for XAML compilation (which requires MSBuild), but means: - -- Controls use basic styling (no `XamlControlsResources`) -- The `App` class implements a minimal `IXamlMetadataProvider` (returns null for all types) -- `MddBootstrapInitialize2` handles Windows App SDK initialization for both packaged and unpackaged execution diff --git a/samples/cpp-app-winui/app.manifest b/samples/cpp-app-winui/app.manifest deleted file mode 100644 index d85a4f57..00000000 --- a/samples/cpp-app-winui/app.manifest +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - PerMonitorV2 - UTF-8 - - - diff --git a/samples/cpp-app-winui/appxmanifest.xml b/samples/cpp-app-winui/appxmanifest.xml deleted file mode 100644 index c0b6264b..00000000 --- a/samples/cpp-app-winui/appxmanifest.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - cpp-app-winui - nikolame - Assets\StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/cpp-app-winui/main.cpp b/samples/cpp-app-winui/main.cpp deleted file mode 100644 index f86a55c6..00000000 --- a/samples/cpp-app-winui/main.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Forward-declare bootstrap APIs to avoid header conflicts with C++/WinRT -extern "C" { - HRESULT __stdcall MddBootstrapInitialize2( - UINT32 majorMinorVersion, - PCWSTR versionTag, - PACKAGE_VERSION minVersion, - UINT32 options) noexcept; - void __stdcall MddBootstrapShutdown() noexcept; -} - -// Windows App SDK 1.8 version constants -constexpr UINT32 kWinAppSdkMajorMinor = 0x00010008; -// OnPackageIdentity_NOOP: skip bootstrap when running packaged (via winapp run) -constexpr UINT32 kBootstrapOptions_OnPackageIdentity_NOOP = 0x0010; - -using namespace winrt; -using namespace Microsoft::UI::Xaml; -using namespace Microsoft::UI::Xaml::Controls; - -struct App : ApplicationT -{ - // Minimal IXamlMetadataProvider (no custom XAML types) - Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(Windows::UI::Xaml::Interop::TypeName const&) - { - return nullptr; - } - Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(hstring const&) - { - return nullptr; - } - com_array GetXmlnsDefinitions() - { - return {}; - } - - void OnLaunched(LaunchActivatedEventArgs const&) - { - m_window = Window(); - - auto panel = StackPanel(); - panel.HorizontalAlignment(HorizontalAlignment::Center); - panel.VerticalAlignment(VerticalAlignment::Center); - panel.Spacing(16); - - auto title = TextBlock(); - title.Text(L"WinUI 3 CMAKE C++ Sample"); - title.FontSize(24); - title.HorizontalAlignment(HorizontalAlignment::Center); - - auto infoText = TextBlock(); - infoText.Text(L"Click the button to check package identity"); - infoText.HorizontalAlignment(HorizontalAlignment::Center); - infoText.TextWrapping(TextWrapping::Wrap); - - auto button = Button(); - button.Content(box_value(L"Check Identity")); - button.HorizontalAlignment(HorizontalAlignment::Center); - button.Click([infoText](auto&&, auto&&) { - UINT32 length = 0; - LONG result = GetCurrentPackageFamilyName(&length, nullptr); - - if (result == ERROR_INSUFFICIENT_BUFFER) { - std::wstring familyName(length, L'\0'); - result = GetCurrentPackageFamilyName(&length, familyName.data()); - - if (result == ERROR_SUCCESS) { - familyName.resize(wcslen(familyName.c_str())); - infoText.Text(hstring(L"Package Family Name: ") + hstring(familyName)); - } else { - infoText.Text(L"Error retrieving Package Family Name"); - } - } else { - infoText.Text(L"Not packaged \u2014 run with 'winapp run' for identity"); - } - }); - - panel.Children().Append(title); - panel.Children().Append(button); - panel.Children().Append(infoText); - - m_window.Content(panel); - m_window.Title(L"C++ WinUI 3 Sample"); - m_window.Activate(); - } - -private: - Window m_window{ nullptr }; -}; - -int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) -{ - try - { - // COM must be initialized before the bootstrap - init_apartment(apartment_type::single_threaded); - - // Initialize the Windows App SDK for unpackaged execution. - // When running packaged (winapp run), OnPackageIdentity_NOOP skips this. - PACKAGE_VERSION minVer{}; - minVer.Version = 0x1F4002A304760000u; - HRESULT hr = MddBootstrapInitialize2( - kWinAppSdkMajorMinor, L"", minVer, - kBootstrapOptions_OnPackageIdentity_NOOP); - if (FAILED(hr)) - { - MessageBoxW(nullptr, - L"Failed to initialize Windows App SDK.\n" - L"Make sure the Windows App SDK runtime is installed.", - L"WinUI 3 Error", MB_ICONERROR); - return static_cast(hr); - } - - Application::Start([](auto&&) { make(); }); - - MddBootstrapShutdown(); - } - catch (hresult_error const& ex) - { - MessageBoxW(nullptr, ex.message().c_str(), L"WinUI 3 Error", MB_ICONERROR); - return static_cast(ex.code()); - } - return 0; -} diff --git a/samples/cpp-app-winui/winapp.yaml b/samples/cpp-app-winui/winapp.yaml deleted file mode 100644 index 9f7e3925..00000000 --- a/samples/cpp-app-winui/winapp.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packages: - - name: Microsoft.Windows.CppWinRT - version: 2.0.250303.1 - - name: Microsoft.Windows.SDK.BuildTools - version: 10.0.26100.7705 - - name: Microsoft.WindowsAppSDK - version: 1.8.260317003 - - name: Microsoft.Windows.ImplementationLibrary - version: 1.0.260126.7 - - name: Microsoft.Windows.SDK.CPP - version: 10.0.26100.7705 - - name: Microsoft.Windows.SDK.CPP.x64 - version: 10.0.26100.7705 - - name: Microsoft.Windows.SDK.CPP.arm64 - version: 10.0.26100.7705 diff --git a/samples/electron/Assets/AppList.png b/samples/electron/Assets/AppList.png deleted file mode 100644 index 3eb4d21db03b2e72f286d4bee7cdde25be6c4951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774 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 M07*qoM6N<$g6*0zXaE2J diff --git a/samples/electron/Assets/AppList.scale-150.png b/samples/electron/Assets/AppList.scale-150.png deleted file mode 100644 index 39bf5d07459ec5e4fb7b635291aeebbc79500e32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2462 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 diff --git a/samples/electron/Assets/AppList.scale-200.png b/samples/electron/Assets/AppList.scale-200.png deleted file mode 100644 index a162005dc973ba4122da5775990fc6b11fb99d10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3466 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 R|4|^2lY^^$t!+^1{{YiqfNTH& diff --git a/samples/electron/Assets/AppList.scale-400.png b/samples/electron/Assets/AppList.scale-400.png deleted file mode 100644 index d24dcbbe964fd33881da42c24ee6d2e753d8e494..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8370 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 diff --git a/samples/electron/Assets/AppList.targetsize-20_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-20_altform-unplated.png deleted file mode 100644 index 0fabfe2c1e97be776a5841bee2427642e5edb68b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmV;919AL`P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D10zX9K~y+TeU!;h z(@_+KkI}>x3s>%SXIz@NbK{?2VjQq7ohWpG77*hQ6Nx($T^d);l)*v^Rm#{xnJI&U zMw9AJ1#?3TaSVFQeLv)j7QTLLln50Cho})J>2zO5*to%9Q zOA+pbz^JJL6oYxwJam1W-+%uxz#|P~gez4D^Ck&j7vYX8a8isQ+hAG3KS+27n0^1% zzzp!lDBLxs$n=n`0x;viTZrWAKQev+6YgAaJC9eoKz5n8Ple@CzxhQB*XolZpLO7xBW6cx5&Nr z_BGHN%&G7=QPv_b+0;0#Cm=9jnY6rzC`~dt^?3u9|MHC`z9eC9vjFQbXDi`uBOJ{N z%!s(laaJGifR(int{7pD3e46}L3YB_s24ielCyw;#(Cm& zOL3T^Sp^~NjRLcFG+xExR2t_a(&0&)m!L zU?fG@V!OlajfA6#xbR4UeYz|QVQN%wCsC@iQ4!jAhuNN3b?HL^DtcGUa@2jM>aNdE zv$>H5HF3h4pQ@<>=*$ebX|v_?4_08-wzjRyQTLgyAl(`obj_y5NfV%^RiCLfR818? kXQplH&umWx=kDkK0ho-JKTy=P(f|Me07*qoM6N<$f>|D)JOBUy diff --git a/samples/electron/Assets/AppList.targetsize-24.png b/samples/electron/Assets/AppList.targetsize-24.png deleted file mode 100644 index e86ebf14712ea01c780023cf2e117dd68af109a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1083 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 Ghy4$%V~!C3 diff --git a/samples/electron/Assets/AppList.targetsize-256_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-256_altform-unplated.png deleted file mode 100644 index d8f2f2aabeaf26f4bdb5bdbc0d43da8f585fa6d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13555 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 Ghy4$%V~!C3 diff --git a/samples/electron/Assets/AppList.targetsize-30.png b/samples/electron/Assets/AppList.targetsize-30.png deleted file mode 100644 index eee71aaaaf32fdf1b6ebb83491a82faf315bfaee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1306 zcmV+#1?BpQP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1g}X%K~zXfz1PWa zn?)D~@JCcRqjKQH1UnovnbNoRE+>6>;dPKueb_jqRj~)7Va& zrD>BiOPhwaG!(Vsz%mVLiYRT>2R-xIT}%>x>4A~vFmw2R-#7ElJMRZ{udU;wYX)7b z2A!7-I#&!j)Rzs~mkin$4U!iOk_!gOd4q)doI&EeLEAZlwpoMLvj(j*1})Q0Z=N)0 zE*iuq4C3Pkv4TM?Z_t!;=AvWkK=M@Cvd22d8(h5#5)Gu2rJz647~)=v*N> zmx+!gqGOS0zd*Dv5XpHWIY%VU6Nz&~oBAx#dX{LNCR(P5mPw*{l86_H))}Jl%qHxY z3N1gpLw%WOUn1HUiDdcV^F-olV3ugp!)J(=640z(0^$=ytU$!_M3Z`sh>j7laiU?= zz%K88!T$pqJ>$$bWCgDnyS&eXHct~VEu%}canzZMj2PHJ(w^=X)tAO5`BL|633P!r zW{IXTA~Gy^+1NnZ4XU8=7Z~fk&g|N4B9tfM$1cw$1gg?DK8t5ZJf_ntdsG8UweL4-+o#uhT>y;+&{QZEhb?1q?Q$%E|s4`M*nxAHz z;M(}>K40w`Blh*XN4+N$(S+xifh{+6$5um?20s|!7uj_SzxDMT@op~>-UaGC?ud==ZOdsTv}8dPDX z8c9`y?E}W%r@Gp7%P8S1_bLr4BPluPoa)FZdKF08Q!TH)wEn0;?cw!*0X$_EWWgL{ QZvX%Q07*qoM6N<$g60u?xBvhE diff --git a/samples/electron/Assets/AppList.targetsize-30_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-30_altform-unplated.png deleted file mode 100644 index eee71aaaaf32fdf1b6ebb83491a82faf315bfaee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1306 zcmV+#1?BpQP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1g}X%K~zXfz1PWa zn?)D~@JCcRqjKQH1UnovnbNoRE+>6>;dPKueb_jqRj~)7Va& zrD>BiOPhwaG!(Vsz%mVLiYRT>2R-xIT}%>x>4A~vFmw2R-#7ElJMRZ{udU;wYX)7b z2A!7-I#&!j)Rzs~mkin$4U!iOk_!gOd4q)doI&EeLEAZlwpoMLvj(j*1})Q0Z=N)0 zE*iuq4C3Pkv4TM?Z_t!;=AvWkK=M@Cvd22d8(h5#5)Gu2rJz647~)=v*N> zmx+!gqGOS0zd*Dv5XpHWIY%VU6Nz&~oBAx#dX{LNCR(P5mPw*{l86_H))}Jl%qHxY z3N1gpLw%WOUn1HUiDdcV^F-olV3ugp!)J(=640z(0^$=ytU$!_M3Z`sh>j7laiU?= zz%K88!T$pqJ>$$bWCgDnyS&eXHct~VEu%}canzZMj2PHJ(w^=X)tAO5`BL|633P!r zW{IXTA~Gy^+1NnZ4XU8=7Z~fk&g|N4B9tfM$1cw$1gg?DK8t5ZJf_ntdsG8UweL4-+o#uhT>y;+&{QZEhb?1q?Q$%E|s4`M*nxAHz z;M(}>K40w`Blh*XN4+N$(S+xifh{+6$5um?20s|!7uj_SzxDMT@op~>-UaGC?ud==ZOdsTv}8dPDX z8c9`y?E}W%r@Gp7%P8S1_bLr4BPluPoa)FZdKF08Q!TH)wEn0;?cw!*0X$_EWWgL{ QZvX%Q07*qoM6N<$g60u?xBvhE diff --git a/samples/electron/Assets/AppList.targetsize-32.png b/samples/electron/Assets/AppList.targetsize-32.png deleted file mode 100644 index 8500b46bd335ca6cd8955705049288f1bd9dacfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1378 zcmV-o1)chdP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1oufqK~z{ry_d;v zlUEeQPq&r2DEoF%i9evbivAImDz#EcVmpIlC(eZ91W*UWqAFc-hWzaqt>(@E791&%}W>V!!MsqVp=zd4=e>OmwUg z9hZpM3K3f-VoOB3Y>|jA5Yc&JpKOk3yGXQNAX;aMmKmaDnrNPK)->t-;q&XTkqh1_ zKn@biLE4ucAeteff-DmI778J4IY_G@&x0n3rZmx%A|ew+WSnS}C5eVHB9bD)=M3zz zEAm$fZ#eJ#p|b|)xNPjPze;%cob!be60mCQoxc=(mS~(Jf~lRs!=uhuKVo2CyB^$* z;WI>dl5Zbc;E}OEh{z-n@dyu{b-wyx30N`qweJ+(JWbT4n7V$ScON_-Zv|J_N;)P6CkKZc-^4$)eK0$f|^cw zcte`6w@fm7_i-WJ3iz7{o+CnD;BjYGK)-?QYHBRo!Bu1eNglm$tC(%Sx!>;-{sd90 zdrc6*#P;yo0q3vjlYn_+yV_Rp76<>}=+$EG=J)6QfB4R)Q{2^>*Sy!;Po>EPjpHn}ZZbE_iL9^H=v8 z*kMBbycAruy?&hUex2c$qc?bH_%|LJ_?6f0JSygIes|V4_*VNEQ5`QGx!^Tth+v$k z?va35O*p0DYAnrZm*c)s!grSVFwSH1rQd?GMyv<4>@A>2@*OezD#);^W?}k3A2s=^^U+i9nYGOdFem<>2`W=Nn;n=0ySV)!Rfx zA5kmtHjxWn-Q#@z(*|~cR0?{5htCtfVRmQ!DuCa-_n2=+5`?ec1H9Xr6>v%dri@L& za`1eGs~Rd0pnKnQWF~j-9^rv5=l7j7uw%2PS1<6;C{Z<7AfVmeyY93cJSRZ#z-jmI zJCPSKTOOV-ua$9jExIh|S-M5+6{O6Adp>f({ioc2)o}wv(po6I!Rr%5ML*Nm@6kNm z;2Yr)!q-;@UKF|D{*&(C2}o(1DG$%rc7L27eVgH)uclq}F9Y|TaK6f85-_1P${Sp@ zT~^sgRH>Y92QP8#dARSmQjn`!UuJS5Dc~J!?(dCX^f&>qA6BXaDOF-5> zL@kQi)wC{YW6{!}xm}~PrdQ2xbpytZQJQ2lOsJo$rK+o`v8cAIysE;f$SBXNWXFwt kaJq9HyCRow?|0e%0DP@90jV5@egFUf07*qoM6N<$g0Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1oufqK~z{ry_d;v zlUEeQPq&r2DEoF%i9evbivAImDz#EcVmpIlC(eZ91W*UWqAFc-hWzaqt>(@E791&%}W>V!!MsqVp=zd4=e>OmwUg z9hZpM3K3f-VoOB3Y>|jA5Yc&JpKOk3yGXQNAX;aMmKmaDnrNPK)->t-;q&XTkqh1_ zKn@biLE4ucAeteff-DmI778J4IY_G@&x0n3rZmx%A|ew+WSnS}C5eVHB9bD)=M3zz zEAm$fZ#eJ#p|b|)xNPjPze;%cob!be60mCQoxc=(mS~(Jf~lRs!=uhuKVo2CyB^$* z;WI>dl5Zbc;E}OEh{z-n@dyu{b-wyx30N`qweJ+(JWbT4n7V$ScON_-Zv|J_N;)P6CkKZc-^4$)eK0$f|^cw zcte`6w@fm7_i-WJ3iz7{o+CnD;BjYGK)-?QYHBRo!Bu1eNglm$tC(%Sx!>;-{sd90 zdrc6*#P;yo0q3vjlYn_+yV_Rp76<>}=+$EG=J)6QfB4R)Q{2^>*Sy!;Po>EPjpHn}ZZbE_iL9^H=v8 z*kMBbycAruy?&hUex2c$qc?bH_%|LJ_?6f0JSygIes|V4_*VNEQ5`QGx!^Tth+v$k z?va35O*p0DYAnrZm*c)s!grSVFwSH1rQd?GMyv<4>@A>2@*OezD#);^W?}k3A2s=^^U+i9nYGOdFem<>2`W=Nn;n=0ySV)!Rfx zA5kmtHjxWn-Q#@z(*|~cR0?{5htCtfVRmQ!DuCa-_n2=+5`?ec1H9Xr6>v%dri@L& za`1eGs~Rd0pnKnQWF~j-9^rv5=l7j7uw%2PS1<6;C{Z<7AfVmeyY93cJSRZ#z-jmI zJCPSKTOOV-ua$9jExIh|S-M5+6{O6Adp>f({ioc2)o}wv(po6I!Rr%5ML*Nm@6kNm z;2Yr)!q-;@UKF|D{*&(C2}o(1DG$%rc7L27eVgH)uclq}F9Y|TaK6f85-_1P${Sp@ zT~^sgRH>Y92QP8#dARSmQjn`!UuJS5Dc~J!?(dCX^f&>qA6BXaDOF-5> zL@kQi)wC{YW6{!}xm}~PrdQ2xbpytZQJQ2lOsJo$rK+o`v8cAIysE;f$SBXNWXFwt kaJq9HyCRow?|0e%0DP@90jV5@egFUf07*qoM6N<$g0Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1>#9WK~z{rt=LI# zQ&$uR@Ppa~>Y^3y22^$3?|@YG8&q9YDv&twl*D!>I|HG^f*q>VWj8En3k3oR4kU2~ zk8vhK$UuOk6@#c)Aj*AI5Ku%4`B&%OcOB>X+3zLllg{RPaeqGN4(C0h?$vwLb5BtB zT|r&91$C_m>b#|*V_8tgl8X4Ep!kBI*iAF$1-0EUV@^=pbwRDODq5xmwM+?Wo;0H% zC_14ck{1-o35sL|MKXfI9BDz(yr9PPeWLEwm-PK3P|u&Bocj$Aq`GCybku#dtDv!giiePg-Td9h2rk(Htm}RT0jB!f6#v zDfL^!m_f2koEeE(0d+WGG^iS83e@a`$$*+nOw+g;Zy06JijZZJS#iR+%#0(3Atji` zF;FA}3XK>f7cZ}w|D#M%ml$0pjcFWJW80ur2aH3RTBkta0w`L*zN#2YnKY(h1Qbqz zLP>+<*5=(*5#x{<#xzXelkes*_22~#eLD+k%-LXU$|Nx+h{uD&3|itdXm1$3cmp|% ztvv@nhfd6b8ZzY=yE17^D5=JSgC>a2nKFz+nHa;_smq|L_nySO-h7LLCuTsQw7W8C zOlVk*2d)_;PhdWScAFWFKn|asoX5uII|+LA<}D8POe>I^GHFb3NR2Ddf{^FWt}v6J zNCEpA^O$?|Qew72Q=m}FU6~jY8sbu^G5=MA^?Ibz6=ikw6p_L4zglw%A_%Y9iVx>)!k#7^7x>&fcwuj ztR53o00qXXV|1A`CU8}a`x6Gq_JPl!s+f}7xh{>Zf;O0v79x_z{%{V{ z4_{afWe=l_^*1nHZQ}Ktw>W%i22?X(gVCS_XT}8e86;ay?)0pgDZRBDviS6eSq$A4 z=wJRD{R>YqdRO4zcUxA^j%R#LV84G5jMP_Ob|EZ+yh(7RI!Cf%X8*HJTJ>) z_iSwepP!lreZ&8}6k{oq#`rFS0trz4B?e6ix#g{8s)XSaI5-ARJXaCa-MUs^5uPH^414iFX!BKePSzFNH%2UwMgvy3nO#LN3gGxaBs!|nWX(0k5@WeA^ zpvTWQ;E9faY7_3tq%n02>IZo*GN>Tr8B`Ht(?T#PmM#NrubW+Drmj~7k9#i|B;O|a z461~&Ya#r}GEkRXH@p`1fbm{bW3}f^P%DG1m@1u~e;A(j6oh$d_bomF9pMRi-Rz=F z8soj7#%j+QB;U3946xQ@Z9x>kYpxWQnSj|}mWrf`GPGz$8Gl4-+-2lkT7vmnw#F=4C z?Kw48^BaRQ{JF%rOm;1VJZs!yY|5lDH4M4{I>sRWKEqqysZ4HK2)7trCXJ~%Yl41d z5PvM1u)(6}P=!*bKBoCRON+ai_;f8C=2vbuV%7 zb-*WZ5orS~?y_Fcku&QIdO*>fAwL%J_Zj{IAtPzXcWr*X(esAJ80~rZJ_Q2eB za;L}56}JyEE*bJ(;^6CmPhdXucw2MuZsG;ZYlh3jnc=Y`XCC|q=$NMkwbFkS00000 LNkvXXu0mjf>xkl; diff --git a/samples/electron/Assets/AppList.targetsize-36_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-36_altform-unplated.png deleted file mode 100644 index 9ccdb31259ae55a9dc01e00edaf9c1dccc7d34f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1613 zcmV-T2D15yP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1>#9WK~z{rt=LI# zQ&$uR@Ppa~>Y^3y22^$3?|@YG8&q9YDv&twl*D!>I|HG^f*q>VWj8En3k3oR4kU2~ zk8vhK$UuOk6@#c)Aj*AI5Ku%4`B&%OcOB>X+3zLllg{RPaeqGN4(C0h?$vwLb5BtB zT|r&91$C_m>b#|*V_8tgl8X4Ep!kBI*iAF$1-0EUV@^=pbwRDODq5xmwM+?Wo;0H% zC_14ck{1-o35sL|MKXfI9BDz(yr9PPeWLEwm-PK3P|u&Bocj$Aq`GCybku#dtDv!giiePg-Td9h2rk(Htm}RT0jB!f6#v zDfL^!m_f2koEeE(0d+WGG^iS83e@a`$$*+nOw+g;Zy06JijZZJS#iR+%#0(3Atji` zF;FA}3XK>f7cZ}w|D#M%ml$0pjcFWJW80ur2aH3RTBkta0w`L*zN#2YnKY(h1Qbqz zLP>+<*5=(*5#x{<#xzXelkes*_22~#eLD+k%-LXU$|Nx+h{uD&3|itdXm1$3cmp|% ztvv@nhfd6b8ZzY=yE17^D5=JSgC>a2nKFz+nHa;_smq|L_nySO-h7LLCuTsQw7W8C zOlVk*2d)_;PhdWScAFWFKn|asoX5uII|+LA<}D8POe>I^GHFb3NR2Ddf{^FWt}v6J zNCEpA^O$?|Qew72Q=m}FU6~jY8sbu^G5=MA^?Ibz6=ikw6p_L4zglw%A_%Y9iVx>)!k#7^7x>&fcwuj ztR53o00qXXV|1A`CU8}a`x6Gq_JPl!s+f}7xh{>Zf;O0v79x_z{%{V{ z4_{afWe=l_^*1nHZQ}Ktw>W%i22?X(gVCS_XT}8e86;ay?)0pgDZRBDviS6eSq$A4 z=wJRD{R>YqdRO4zcUxA^j%R#LV84G5jMP_Ob|EZ+yh(7RI!Cf%X8*HJTJ>) z_iSwepP!lreZ&8}6k{oq#`rFS0trz4B?e6ix#g{8s)XSaI5-ARJXaCa-MUs^5uPH^414iFX!BKePSzFNH%2UwMgvy3nO#LN3gGxaBs!|nWX(0k5@WeA^ zpvTWQ;E9faY7_3tq%n02>IZo*GN>Tr8B`Ht(?T#PmM#NrubW+Drmj~7k9#i|B;O|a z461~&Ya#r}GEkRXH@p`1fbm{bW3}f^P%DG1m@1u~e;A(j6oh$d_bomF9pMRi-Rz=F z8soj7#%j+QB;U3946xQ@Z9x>kYpxWQnSj|}mWrf`GPGz$8Gl4-+-2lkT7vmnw#F=4C z?Kw48^BaRQ{JF%rOm;1VJZs!yY|5lDH4M4{I>sRWKEqqysZ4HK2)7trCXJ~%Yl41d z5PvM1u)(6}P=!*bKBoCRON+ai_;f8C=2vbuV%7 zb-*WZ5orS~?y_Fcku&QIdO*>fAwL%J_Zj{IAtPzXcWr*X(esAJ80~rZJ_Q2eB za;L}56}JyEE*bJ(;^6CmPhdXucw2MuZsG;ZYlh3jnc=Y`XCC|q=$NMkwbFkS00000 LNkvXXu0mjf>xkl; diff --git a/samples/electron/Assets/AppList.targetsize-40.png b/samples/electron/Assets/AppList.targetsize-40.png deleted file mode 100644 index 49f9a6a0f7be25901dd27ae796b0a088552aed32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1683 zcmV;E25k9>P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1}I5HK~z{r&DhIt zRCN>w@Izd%P!a~x&h$Z_(8P_Jm|(&NTtE%V8-&v5 zOkXo?r=_%|&?l`(F{05_@CPKdK&+at`Tl-)puL^8cc#SfOHL-acX2;CbMHCl_Y>JS zGb^>TN@}K+R8J|Xo-|Q4p`>bDNhM>FRn<(yAQru@EoG=mURTAz|66!Wl6t^O#B;2bc*flFsGczxr+=0~o0NKyD3E9W^ z9#S)7#WbY)2BdllQauT&VoX4)#vzrAF-Ya8iHhrxiffSa5lHz}6MKgtdxs!pj6q1* z6-X(-pQqpgtxDQgCfP@p~UZ8VcbmnSrt$cVT!+LlHV-!+;9a5fr_y}Y#AD#k` ze0V7zZUKsIfN(D))B_21LqchDf(GP)q)9s-rx+a7 z5ZDbVY_ouT=UyaSpu^lNj4IJzD?fK#Vcifxc$cyyG^fWz_oBG zGDn=h$-uoyAd9q99Lq_#50V$d*MoC**VDE0&?qE_1$Q;vDw!kBM_>fvZBU{IO%`dV zxRO3dZVVsPP1{Y&*1H-QT?MyB=7{q(vPeU`my*CyXE^rOf^KZD8Gu^&+iqI6b{rgm z>|s{gAU?iNQD>PW&es4bXo2Ku;IIKY!G#jKJ4C1N+dV7RfzF?>E!csr<#B8+k6}v* zTbF~(5$C&P?&Z}JIK+L%`Z!J{zBsBHf3#axT@M~D;{L*-{=Ic~0WXJ`&E~Z;$M6!^ z1j)OoL~n4ohgc6+)Qim}2_$Ym#LV0yOwTP~`nNxEcm6NCE9Lq$GK)>_#`QBtoVVTr zULbIgJF~TM$&o4CgH7zM><+;=#M6e|$ENI-6)Q4z4?nuIUeO!(ATA=BR>7^3IpT7c0sDAdkv@(cg21CIfqd^?D{g~$ z8dkxrkvZaW&zpOmvq|7!`Z%^PR|~R^uAFAxIWPd(&A&DkZk@~#mwV1!_nc9p4{>-_ zk}i(D)z!QLsNcJf_>!l>ImjGwIcLp1&uI(DBAqUdjo;OTtRtgOarI+!kZeAj@0}gz zFmpK;kk{P=@(7GYI$azae|95w)tL<5QIGLzQN()h?6@>C$MBpn*WITG?BV%c`Z!J{ zu136f_$nSR{)4x^=!fjG-n)%tjyTV0bKQNCz;2~$U|Kk9D)F~sb1;H8YT5RlQ|5^C zoHAh7QlMj;ZJuL)O^M4ea}0M9ctVLja%By4giB+d%QSPuxlfww&wvRBIERz)Ml(m8 z>!i7!eVo7;546|8{g0U=&UL~9eyc=+UGuuk@Jv{L$=1B5|8fh>gB+rH9KjvG9nK97 zrxn6EXfim@aUx-)gfBren@PAvv&@`_WPdYH;H)k+Jh0}OFpph%aLC~K9FM?w%EWUL z26y{98kOF+a%;%l7dJTELvT#zB+Oxx;}U~SpTXYB_QejtBFs9+Qo^s5?^`zGxYC*9 dwLQmX{{yIfVntfV`6vJY002ovPDHLkV1mQcBhvr? diff --git a/samples/electron/Assets/AppList.targetsize-40_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-40_altform-unplated.png deleted file mode 100644 index 49f9a6a0f7be25901dd27ae796b0a088552aed32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1683 zcmV;E25k9>P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1}I5HK~z{r&DhIt zRCN>w@Izd%P!a~x&h$Z_(8P_Jm|(&NTtE%V8-&v5 zOkXo?r=_%|&?l`(F{05_@CPKdK&+at`Tl-)puL^8cc#SfOHL-acX2;CbMHCl_Y>JS zGb^>TN@}K+R8J|Xo-|Q4p`>bDNhM>FRn<(yAQru@EoG=mURTAz|66!Wl6t^O#B;2bc*flFsGczxr+=0~o0NKyD3E9W^ z9#S)7#WbY)2BdllQauT&VoX4)#vzrAF-Ya8iHhrxiffSa5lHz}6MKgtdxs!pj6q1* z6-X(-pQqpgtxDQgCfP@p~UZ8VcbmnSrt$cVT!+LlHV-!+;9a5fr_y}Y#AD#k` ze0V7zZUKsIfN(D))B_21LqchDf(GP)q)9s-rx+a7 z5ZDbVY_ouT=UyaSpu^lNj4IJzD?fK#Vcifxc$cyyG^fWz_oBG zGDn=h$-uoyAd9q99Lq_#50V$d*MoC**VDE0&?qE_1$Q;vDw!kBM_>fvZBU{IO%`dV zxRO3dZVVsPP1{Y&*1H-QT?MyB=7{q(vPeU`my*CyXE^rOf^KZD8Gu^&+iqI6b{rgm z>|s{gAU?iNQD>PW&es4bXo2Ku;IIKY!G#jKJ4C1N+dV7RfzF?>E!csr<#B8+k6}v* zTbF~(5$C&P?&Z}JIK+L%`Z!J{zBsBHf3#axT@M~D;{L*-{=Ic~0WXJ`&E~Z;$M6!^ z1j)OoL~n4ohgc6+)Qim}2_$Ym#LV0yOwTP~`nNxEcm6NCE9Lq$GK)>_#`QBtoVVTr zULbIgJF~TM$&o4CgH7zM><+;=#M6e|$ENI-6)Q4z4?nuIUeO!(ATA=BR>7^3IpT7c0sDAdkv@(cg21CIfqd^?D{g~$ z8dkxrkvZaW&zpOmvq|7!`Z%^PR|~R^uAFAxIWPd(&A&DkZk@~#mwV1!_nc9p4{>-_ zk}i(D)z!QLsNcJf_>!l>ImjGwIcLp1&uI(DBAqUdjo;OTtRtgOarI+!kZeAj@0}gz zFmpK;kk{P=@(7GYI$azae|95w)tL<5QIGLzQN()h?6@>C$MBpn*WITG?BV%c`Z!J{ zu136f_$nSR{)4x^=!fjG-n)%tjyTV0bKQNCz;2~$U|Kk9D)F~sb1;H8YT5RlQ|5^C zoHAh7QlMj;ZJuL)O^M4ea}0M9ctVLja%By4giB+d%QSPuxlfww&wvRBIERz)Ml(m8 z>!i7!eVo7;546|8{g0U=&UL~9eyc=+UGuuk@Jv{L$=1B5|8fh>gB+rH9KjvG9nK97 zrxn6EXfim@aUx-)gfBren@PAvv&@`_WPdYH;H)k+Jh0}OFpph%aLC~K9FM?w%EWUL z26y{98kOF+a%;%l7dJTELvT#zB+Oxx;}U~SpTXYB_QejtBFs9+Qo^s5?^`zGxYC*9 dwLQmX{{yIfVntfV`6vJY002ovPDHLkV1mQcBhvr? diff --git a/samples/electron/Assets/AppList.targetsize-48.png b/samples/electron/Assets/AppList.targetsize-48.png deleted file mode 100644 index e08734f840b44bf5e819729886977c3792428c12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 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 diff --git a/samples/electron/Assets/AppList.targetsize-60_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-60_altform-unplated.png deleted file mode 100644 index bb700593c201baeed2cb060b7b67d0226a812358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2067 zcmV+u2<-QXP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2dGIzK~!i%-P_Ag zR974a@L#(y>833vU3Jk#w=Vi0bmzjP%eJ<{%rFmzhZY8q+K0QEv|Ti5W4A>`5mA8= z0byof1_gO3DhNt_wKgW29;`uYeZ*h${hf2iJ6A!3d&gu>@(qi77V|mW$M2kbF44A` z%k<6fiW+7VHJmq@Rma~fLkuy4VCiRnw>LwM{p4O>5rKlE}Fd5g0A!8<^CL@Y! ztejL-Gpwi@8G4#8PM8epY(X+Qo71)oD5^sGO;U;~tt55OuOfMwXxq#JZU2Mm>p7jT zE}C4>**0r3qw^(l-ej7o0h!X-dX8!9SzFHN)KA)STBq)m$pll~I8!Y$W-`hYvofMn zbMk4bhfRi%J9<1(N?Ppw1uPfFEzg0P6AMtxPi2A?C-| zA`>PSV4Nv7X5v89SP1)%w*zd5EWm--Jit_i^qZuZDy<}SDiS*ATi&NgPGNux8H=EA z)B@uaE@W&)pY9j!1$f#9E>LW_1wN)o?!ve^6*9KGSNF{U7c#c|nC@5BW59Uq6f!mn zaMA*wHQ)lqMtcmnTai>yC@5YPHkxLt%-9MB53mAGFhvA>MuT4kwj#qHe1DqPemTrk zmGKT6?bdxFT?UMDF`wA#<4mRf{K;>#j8=YNj;S>19kvYMJ_E)i0CjP`u=vlGrTM+> zqkQN7V-0SZV+sK55w@($fI9)6G~hg8HxDvJ2Do>2Ua)A<{Q#SK*9$CyKH)5QIzL#% zpW=r2)ht$nFERyC4S9quJF5GHj~Fl}4*9@_2YB6%34ZYS$s%Bnun{{L6L-|nUSlgV zyrwF{Q#bDAV6$L9%0G{=kxr)a9;R^I2KEw*=W8IzKP0a^tC5sZbK0XL+wmuor{52h(#*WyrwG6Q#bB9O-r^=(|HZ{3X7m$@G!umN?v`e z7K_QdFTp<~|8#m@*tWLKF@2013(7wP&rIB8fO7#GYSVpk!K=gu61;xrS*K-LZTiLn zzrQWRMb$|zilw+HhQCREtEP`BY(ZCn4FVjuz$19;w>oTefZvJ@@-=neX<2r|3~K)F zL!Q5vo66>CrcLIivr=sEFjEBJLyF{erGQt6txEIjkrczzXDaSk9ag{{ zcJRc?uvmkt9N;%%8Sb5#=aH*-a&!3~zIgK5wLmFIq5a5$a z-p{TCD{D|`y;4zzbzcOlgC>DvytY2gxBq?Qw7jsT=MfDInh$4grk$u;4x`_?6>f-{&wjcq! zjKxGCynOJ6pGTQC9`*(sIAFjn7I?&cEG7cs9xkd)y%4OgTRba0!dhUzfCn|$ZLGZ7 z=#koZDX_k7Q3c_;^#~hiVJbbu8>Og;;YI@)clfRA!f z&9mUHshdp2?fSaS2iD)LkNfs2l3k_(b{UIhnIOP@IpDu;-{*I>^f39_^M~~Te83JK zavzIjnP8`YZ`;5h{y4<65s#oeVtxAzxXA*aa33ox{hj=FbuT|O?}NW?pJn>wV1ECz zzzVp9sYJj7*o5sq7Kt3?*Zgt*DRG5&4PR&STi0!#vA(_fxPY;f995RvSZfU`+{xtY zU@8H^c9Y5{w!{MO0l43QJ-}L1JRn1_;54?dB~AKxaTYx29X20D#fz|6@NR%p_;{kQ zvBgdLxPbBHhky$hTfD~!#&T-`V+FjAY11x%@l8*ru(8FvnM!iN>B7ct0(dXerbb0n zo?KYEv^3W?_8VIRB7aWUu@4F+`v}&ZJM<#CH6U1BltHlu z_y46KhJr?8|^AOF1Z^8eMtW%yq8v+Do=002ovPDHLkV1i6N&4B;_ diff --git a/samples/electron/Assets/AppList.targetsize-64.png b/samples/electron/Assets/AppList.targetsize-64.png deleted file mode 100644 index 2c32c2bc5bcb042e9804c145a1ad16054351d3d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2210 zcmV;T2wnGyP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2scSYK~#8N<(tWG z8&w>~e;^^?fS?CXfDkthNc;r|i38#Q+zKU$?KoayH(j!|ltRS`0ph|Y5Q~&_q3N19 zakDhBV>{`ReQ85iDg>0Wlqr=G)Dl{L==3|ea@rIIvgjhTc@!>}!JgH|K4bTB6zJ4dwX#3F6^fobzurp;%V zHlJpC138t>rjsfsWL`(URas!#h#Xfr#W%j3#d5+J$i-3b8|1Yh6Jw0ejs*k=kjQGR$|f+?6_T2}&i1aksHz?>9NF8H;h zOraF7YCFZxd^pb(O6rHN?~`)^y-EOT`*OgqA7%0m^QyL!jIn?2B9k|!A3kUofUXXG zK&jzz+Xs{U_?B^Ayn0OtXdnAk3D685>}3icWC}zfU{(nzG5nelra*!RPyH$2(*n?_ zXog2H$8QN(C@nlv8|OEAzPI2r1!#s39*}eVJxTx?p;E&8hWV*?rg-_`T(8qsO&;XexPZT zfBf^RliM0n0P1wj@cte-&liD!DeU^{g~uz;H_RQ=zdM;TM*G+WCXXpVBfJpM>lB~~ z9=E>w+o0e`8pwBLJ24mJQ7awy{nTv{o|sOG3Q7N z&;hT&MY^PbI!nMz@!+wU;ThtOlINX#w;W#|I?v=nr=}Sm!MwWt5HM~NPy~2yjGy~> z-pQ8N=wG|$27ogyMopMg5fFi&LhxwtFQU3Yz&8xj!J5M`ViZ+)1zQ&Ji zh%tG43Wpa0B6a~&g~8+M2jUz#c9D(0midn*M;4d)dj4<2tmCCmCz-18@G1g)-99P6 zyB7kEDgk#0Z*E{VB>4Wu6hGKHqLShVTU5d^Zrpq4cgK4J-!ZMiRVxxa;_tExm@Ew5 z+`|jwAJ>>n5dXX&gG|-E{PO2>POiMh$XSE$s>OQ6tzRVgy1nvu?;Z#^f)AJqhtIW* zh9P6c^rW7l5y^@G+7uc$BqwDBwk`d))e{+KLH}_&ZGj zhm`` z-;A7MdIb$hnc#(hT}&<^Ac3 z?Q%|yDF9Vq$>B9sc=^C1ZV1>40dbpvQo)z;$|E(~dBAPU76bC<&E*?==PrXUfU zATtnzFEAlV{EcmjI-nAD?Db*y2f-g7D2_}yXplNHCHa&j?Bv)5WnC6FL3r8=K}r&? zyr=_qF;%rKk~l(+Y7hsBah4XRDMbbhajp(0t3>(?amEIxEJUJ)_}(5L9Gk(Xry>y< ze20e*k*stY;@-ZOvf+PNYL+ry=@zbaW!y4N=;PY?Hxz9#49a kc0=6qB5m^b*V@kg2S702EHgig2mk;807*qoM6N<$f+q0!vj6}9 diff --git a/samples/electron/Assets/AppList.targetsize-64_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-64_altform-unplated.png deleted file mode 100644 index 2c32c2bc5bcb042e9804c145a1ad16054351d3d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2210 zcmV;T2wnGyP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2scSYK~#8N<(tWG z8&w>~e;^^?fS?CXfDkthNc;r|i38#Q+zKU$?KoayH(j!|ltRS`0ph|Y5Q~&_q3N19 zakDhBV>{`ReQ85iDg>0Wlqr=G)Dl{L==3|ea@rIIvgjhTc@!>}!JgH|K4bTB6zJ4dwX#3F6^fobzurp;%V zHlJpC138t>rjsfsWL`(URas!#h#Xfr#W%j3#d5+J$i-3b8|1Yh6Jw0ejs*k=kjQGR$|f+?6_T2}&i1aksHz?>9NF8H;h zOraF7YCFZxd^pb(O6rHN?~`)^y-EOT`*OgqA7%0m^QyL!jIn?2B9k|!A3kUofUXXG zK&jzz+Xs{U_?B^Ayn0OtXdnAk3D685>}3icWC}zfU{(nzG5nelra*!RPyH$2(*n?_ zXog2H$8QN(C@nlv8|OEAzPI2r1!#s39*}eVJxTx?p;E&8hWV*?rg-_`T(8qsO&;XexPZT zfBf^RliM0n0P1wj@cte-&liD!DeU^{g~uz;H_RQ=zdM;TM*G+WCXXpVBfJpM>lB~~ z9=E>w+o0e`8pwBLJ24mJQ7awy{nTv{o|sOG3Q7N z&;hT&MY^PbI!nMz@!+wU;ThtOlINX#w;W#|I?v=nr=}Sm!MwWt5HM~NPy~2yjGy~> z-pQ8N=wG|$27ogyMopMg5fFi&LhxwtFQU3Yz&8xj!J5M`ViZ+)1zQ&Ji zh%tG43Wpa0B6a~&g~8+M2jUz#c9D(0midn*M;4d)dj4<2tmCCmCz-18@G1g)-99P6 zyB7kEDgk#0Z*E{VB>4Wu6hGKHqLShVTU5d^Zrpq4cgK4J-!ZMiRVxxa;_tExm@Ew5 z+`|jwAJ>>n5dXX&gG|-E{PO2>POiMh$XSE$s>OQ6tzRVgy1nvu?;Z#^f)AJqhtIW* zh9P6c^rW7l5y^@G+7uc$BqwDBwk`d))e{+KLH}_&ZGj zhm`` z-;A7MdIb$hnc#(hT}&<^Ac3 z?Q%|yDF9Vq$>B9sc=^C1ZV1>40dbpvQo)z;$|E(~dBAPU76bC<&E*?==PrXUfU zATtnzFEAlV{EcmjI-nAD?Db*y2f-g7D2_}yXplNHCHa&j?Bv)5WnC6FL3r8=K}r&? zyr=_qF;%rKk~l(+Y7hsBah4XRDMbbhajp(0t3>(?amEIxEJUJ)_}(5L9Gk(Xry>y< ze20e*k*stY;@-ZOvf+PNYL+ry=@zbaW!y4N=;PY?Hxz9#49a kc0=6qB5m^b*V@kg2S702EHgig2mk;807*qoM6N<$f+q0!vj6}9 diff --git a/samples/electron/Assets/AppList.targetsize-72.png b/samples/electron/Assets/AppList.targetsize-72.png deleted file mode 100644 index 9eb82f00cf892ac94e382d9fc2c92c4866708e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2767 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 Vs@0MM6ubZc002ovPDHLkV1ie43V8qk diff --git a/samples/electron/Assets/AppList.targetsize-72_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-72_altform-unplated.png deleted file mode 100644 index 9eb82f00cf892ac94e382d9fc2c92c4866708e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2767 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 Vs@0MM6ubZc002ovPDHLkV1ie43V8qk diff --git a/samples/electron/Assets/AppList.targetsize-80.png b/samples/electron/Assets/AppList.targetsize-80.png deleted file mode 100644 index df4cea4c5bf34a8787cd44803b30c426453a5ca1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2851 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 diff --git a/samples/electron/Assets/AppList.targetsize-96_altform-unplated.png b/samples/electron/Assets/AppList.targetsize-96_altform-unplated.png deleted file mode 100644 index ad36b4b3d672ef6abbc0872831246843cbc5d71d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3269 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 diff --git a/samples/electron/Assets/MedTile.png b/samples/electron/Assets/MedTile.png deleted file mode 100644 index ee285b96d60bb11828ed1f27f722d3850f940f8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3912 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 diff --git a/samples/electron/Assets/MedTile.scale-125.png b/samples/electron/Assets/MedTile.scale-125.png deleted file mode 100644 index 7b3430d080f1d8758f36ec41a74c50a1c1863e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8537 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 azv_H3iaq2#r?emjzBajeyVk4=&io(Yf?P=e diff --git a/samples/electron/Assets/MedTile.scale-150.png b/samples/electron/Assets/MedTile.scale-150.png deleted file mode 100644 index 5df9f2e3e36ce92eb6f8e461a72f55da74467d6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12278 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 diff --git a/samples/electron/Assets/MedTile.scale-200.png b/samples/electron/Assets/MedTile.scale-200.png deleted file mode 100644 index cc1d81dc61f0902d8b2d0cc647e248e170b41ce9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5401 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 a3x?MJwPnHgI{2K)Te2W^{>iz-_WuAqSR1nd diff --git a/samples/electron/Assets/MedTile.scale-400.png b/samples/electron/Assets/MedTile.scale-400.png deleted file mode 100644 index 60b8401cd176f6f621c248e3bf10cfa4ab6b649c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46433 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 Qaq!Rgi|iL>EFkaxKSuj{00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2)0Q?K~!i%<=e|| z8^;|7@E;2F)CNe}9*U+#&Or~o^qS<*zo01Kz}ABlNl_AI$&ut(vYpn4B1qvLatL}T z(AJLQha$;(OVooB^?q2gt;mrnTgt(4n^#G@wVT>WZ2gMwZ)VBSnxrUlm#*3YzK{UU z<+C%t-Pze)qL$bO9ovjk^Yu72uc;ilrn9Q@`Ugf<;&d3fno1LLMdxyy8j)q4rBq(K zWMoliAx?*od7arf)gv>h)J^M5sT@Qmbtd9eYbB}@8ds^s-!UVRIMqZ{-|EOFQSimA@ZB-24;LiMc~y$^g`-~s_69mK#0 z!hkd(2BLA z8}|qtLEkD9pACLE9USOA2(KB@_~8V}i4FDr{{pWDemosM+f=vX_fUsRG|LnM z)2g`2crDbH<$VAM_Xy|W6r__ZNMx0Ri8j_f@C8CH#NnAhbH;kp$T5R zbuS4|;pdowz&i=oXbWE1XX3A5*QPjdT*ti;J~?)K8+Ntve6xZfPm}sIzcE z8}LA{fyZVe*7a_}v1+Rr<3G=TYBwjF;E&HRRa*Ew;OS0o7TSUfevrxE4g4~8W!;5) zBfS4~%nG*1_R`x|?Y12o@IPvqWb%hI!}G0i6YlR+eSH^!U)1;l z;TZ9%C_mRQ#hdX5cH52(OWt4oafRuZT}&l?OeMWc2YOQR_3Kg3qsC3R5BOdizEC(W zYn~B4z4)2km2>;#9}oGh{!Kp7v%x2NQ#sy!gI_!`#pLVDZK~UA+=TlsYJ3Oq3wWkp z7#w?Uz6kI8%bMNeSbHnH$+RD%Rv=vPm`T9n#egI}Pk42dpKY4vM~~CpuNB^I-+jQ( zH;yn>^yXLNCfwJd@$JCR$K}OStr!Nj}GS$JT%C3#KxFFx?BN@g%$h zXpL`6ki7hZ_g~zABh{mM;s1K~0e`;^v!~mk5!Z0RcUkxujduZ#HM-!#yTU)-+TvGE z&Z)CIR%1@WE7}y^o5U}=0~h>=!pCyQw{)&E?L(bA0k_Z^j-apieT~P`)(yCYugD!Q zbzTaLRp$=WxC!^RsXl_o%YA}(0gk(~s$r&z!JP28o`27(b4>SwMmz)D`#zH&_*M(Q z;0_$4F8BeC#Ij%NwzimGJ~_*@|DwBa1bsb%pHg@?;J7oxU0DwJqn$CPSFr$b8SZUW zeLNR59yc{^z%9JDKP$d}^>e1uPF?3N)VK-vv@%s#c)Y}mb-f#K+>ZytS@9p=dB8uY z3p15>JqdSu!~yqQP*(uYdEh5C-UT>DUGRMw@yR+b797FvA*z=b-Tt*c=>tNNAP&@9_xBH;MjJm z9ANVFY#aHFFTUhws{5HdUBw-7z{}66-?!r(hFhDB8b7_E7CB!%+Tx%8cFL&pBEv0w z*?Yi8<8o6|Fg()(g?^@zF8jZJFBMzFF&h(PvWtzF9^QFzPhiMpFK3h zuV6>U3t*9J+=Q1|_%jKT4{@-xbqkIi7D#*^^0 z_{rVF_fn0UaKWErDme{&1fON*0e5Svy9_UVm#NIcmpFRMIbrDvW5lVyPyh7jZ6NQ?u7b3wZEfy6i;NP2X-)s`|ID9B&yAmQf6I7rxt zkb{)w;K+e74i3)ha&Ytk!idXtzLn{~sr$fxLd_1+RPI-IruK6Y@p`o{dX7blMa0_(;;Gf_-)Duu57J zvgd}~7s>gA+>hht#0r+Hk~1o}j^m_e1=Cz|O8vh7)JOjTOVqf=o~!U100000NkvXX Hu0mjfbnBbH diff --git a/samples/electron/Assets/StoreLogo.scale-150.png b/samples/electron/Assets/StoreLogo.scale-150.png deleted file mode 100644 index 68330b4712cf5de35d359a1c32d543d5db7235e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2416 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 AivR!s diff --git a/samples/electron/Assets/WideTile.png b/samples/electron/Assets/WideTile.png deleted file mode 100644 index fca4b440368d6ec3fb57dc17929503b57dd06bd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4541 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 K)vnTp%=j-0Tx7HW diff --git a/samples/electron/Assets/WideTile.scale-125.png b/samples/electron/Assets/WideTile.scale-125.png deleted file mode 100644 index 8b2f47a6a5c04340d584b5482802f3e91c11a4a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9760 zcma)ic|4SR*#2y0CI*ivBTG+7bP_WX$~GgVC}}yBY$J+xWr-rQwJIr2(g~wQS~*ll zCB~9SB56?v<5ad3g+$?ZKhI2^&iTEc_w)WS86UHJ@8x@4*L~d`^l*1kRi2u88z~-{^V8=ef`QkBey@D#WDmE6y!*o`@)koudeo*nJs2 z;>cjh#r?Zu?7f@cMLpyQRw}XgE}MVxdV=NL)XLA{cI7(@;{%L8GJ`_?w%L+sx%hNh z%Bw{YBf;-y{uMLwu4v!gnwOip6Tg0SjlH*cx}W3f(IwA4+fJuW$@29HANu$v?r8dw zYj!aXw?4Hr+?s5wuG3oF&~Q4HW*QnBV3pE(&Oz5Jw8Y|mP|(~>?tyz!Qv;4Pyg2Eh9@x<&%2hV`t$wODRRv%uM85HzuMCJ?n z=+X+CK7p;-sYam!4!B#r94y^0RZR)SkHMq2JBc z&AsVC;;68QYb!%D2WwnRLvOQJrL_9TTzmLv?un=z_NtyJ&dKF@d9!T?bX(8GSfz$M z++L;kd+L#f*&9z9=H+F~8t`mwD!*b~ZU5=YrfUBxy;G?-8g9K}(Sm{&Z~Bn?P*8rw zs`{|qUQ84?alECOMt^x1z9hBAty*28ukzxL!`3(E&+vH? zIKF$n!~MOLrj4rbJtBIoccl@~V;^qR>Yj@@d`bBG?(Jg>6zS&!1@ z)#dM%Oilm(yr}xogRVtu)}LWdUo`VtO~|J5IWEIG`|kV7Vuv-H8tSX&&OlBhTE)`7 z(ekP?zM+Q}*?+M2F`<1tIi9nW;f*8-h%OQnsu_L9S^|Q6x}xOd&#)mxu@LmYAtDW> zQD1&)D<#s8z;ksfiL#U}BfKf*@TGgXy5gmLe!?sz;ca6RvA~!mxX4cYJDxBT0pE$Gs z7Rmho60n3$oeKvma)xiW?&nbtNtXe%!B=Ak=B}MEJZQeF!2_DB<#mkgRn=DWsY^26Zl# zP>|cjykJKTr{b~|J&GNY%IG{S$oV!HOsH2PYg59@rApj|Oaeu0=f;g(A?1@3Xk?-= ziy1=L!IR90b-=0+Liof(ImkG&8pnV^p*#6wC0-O09E+s2LwdF5gQ#!sYJEJL0uI+;3mCy631 zaOHX!Q{wsN-@8o8@fwN7K2F|o=r2?~z8#Z}Bpnn?=nqXMQ^Z~8QM<_;Dd&H!Mhc#z zA z5o%UL}|b;`JaoM^w-ZA6M#FPtwc;qWD^ShXh{C#rE$;qtWWK)%-o z;Q9XdcPP^9T>7JJ2z2_}%c??EynH=xCn^h>@9ZABFmqG!a)G;I)YDqQ1 znh!fGk;BPa{d@N|xNRP^|LK&!21wC%g1MG~vEkU6gW#-LWNOvG zvfQ-{sQ2dOYH*2pWG;b!l*(De*o@}0SXfi&(#;qQD6y0*=0alZRpFEV*JeAjR!Zc- zO>%I%RXSs;-&+(X2MZ2L(eYecHD1u1UIBJaRZ)^5LKPc|a8YZK< zw#FysG;jMFZs}#^Qh?~p;RNsUq%$}@YjJO zG?i<%B~G6>7Nzfn4pHEdz@6Ih%I!i&khR{q(Cor$)%Gk+KoV7Q;kUNGDNfaF3jWh2rA6g+pJS?(e8fZhCX4>9QJ9 z;7(nP6B}R{RVaq;Gh;xp)3#LL%?3DQLSM^+9}DqRzb(4G-+fpXUf6cmjmu>A@0T9J zN!*5D28B~%FW|m#sm!&7Pk3f^I)snn!iO;NyC@M0hAck`x8EKq42{0ub}01_Hamm4P_>k5HM-@|bBeFQ(&3%|k!$wkIbHmBnoT!8U<}!LT9u ztd%JV#*ozrrJN%}o=|R=4!8O0k$^!J0>RC1gCPW`0C+AE5a3n)QKF3U(1h5YPOs>q zwrfP;oJk>+XgQnHpy_6tCCsL}5%HD>E%h@#C$K#7cI!Hw=kscciU)SJ2!%M`Xkdqf0Bc^NZxmV%7Y!sPW62ZRDt$@! z-PK*a)XXr%8|foZeUXza86lFFAk0ZVem?TmG^fv2dba7_+g&kjL?&+;>}>(DCf7+M zZR%z9+ONq=9^c*)H0aQM+Z~{gjyT`rlRdOVX66YuZjk}de;tqMq(4zCsd6-;(5RJX z#tfxyitqI?IOmB2r~SVyKPtgY1i6TCwWu_Tz{ z-yeDNAmvTOLajB8&hsz8j#DV z_ga*^s5WTi{S^CwHHATXZ>A~vA5#^d3XpdShl`g7T8$pL_TS1-2E1`zv#%r8#&9(Q=E$`_^oNl*>UMU75eFRt$+S=3TA=pO z9Rj>`V}aKw7`oK^%typ;yH#=7do;{q^~|{oN0~midTpb3CLdQ5VzO9%qsH-aoCP`i zkKeiGAaJ4po~eBdHY@IgZrjYqFa$JGkRG%`1t zdF6CwU;{;e=)?5rZOP9}>GH9K&Wz;pAn6DqGQz&ZH=K`}#%`l`#>2%nVAL}XKWQ83 z_TTj`H4ff(Vw^aw6s=RTxDR2x@nqrDsSx-Ej1!CN3N}s5jBAWN8-8s9+I1NLK_QP? zN+QYMqFk~tW^nghgbXxfn9kUD;mIA5i(#W~pe7fD8%?^GNRv#(G3-^yIk)e^*$z9s zI%Dx;)d2_H;V?;^JlIoyiePVXY6%ApV(JK`^98%&Ishbx(vN<$*T0HsDJcWN;ao)ONNy6F-9Bf$Plkvt6z$^Ebgq2yLh~bh0;t#)UHfXtX>-b-C4hF;o zVWdmfV1Y`GI-$hHbW8ML+iV?52`6SlV~=G_V5!gbcY@w%>s56p3~M~<{#?aJwWxXo zZTW+*%nBCe=tB|~3vxQd1Wq;&<-iy`tJh^iZ^i3q^HnEw#1`!Kk1By1s5?Q=rZar9HW&7 zcm9$5#r>M4z8C@N+Vq-vox_9(8fF5?(vK*l&!M$BJN6wf9y4_Rv2lCqFAnjWFKWW; zF}Z^wD~~+MimHRu*TAGQdn`U`0<9}Ap>{r3aoKS1YS0(6YxD9jvd43Z=hcY1m|ky6 zh0*4`KuU~M50K2a?`eH-r=$K!cCU|j9Q5im4bgxM`WjX8H#Q=_g7Q6^gO>(q5tGS{ zJc+}Rg5blS)2}_h%e|?FC}Y^CO|_4(z$TR|s(5bXvshSM@udyBnq?H9&(-_eL3{St zd;fmT_M4?qZv-2$Az8ugp2w!gSeWRD##1^rlc9*e2}8U&=oE$YZ68bSH+gu}dwWhq zaz(-JqOTS)(XX|P+@7APiNkBJj_hh7-6D&(02OpUYu-czX%s!UD%N7bKI4uUQDR^9 zTu0g0KTpK8Hpef`%AZdY%g+;+ zFIkpbL2)u*v{ooCdJ5f%t{8f1XHYL+nC5BWo<4NPu{yT1FQZ#qQ z)0mV7TU)o>y!)&>x_OIc?8Y;c;5)UB^FkRxax(=#CIly*WPONmPs<@uUt{l6`<~3; zqiMTS3k!xBKDQ%;W{oUv^45okhiW!#Pu=bK0B7)Egs%FMo!N+|_U zc8=FH&zl&dT~USjzI@8AtmNEXQSay)JJe5WZ)}P>pH&}`HpgY6fk&M8j8Webj!-J# zDwEvl^jmIRoApxo6igO|?d57x_7=;g48OUw;ncMT?dQKXb`L+9GU4Q}eW7!FpL5I^%eDc4T4Uf#)Kl6gfpaiX{awZTnPESLu z)&sY__-?-}V60s9nD+c(?dzI>raOloY#zASX40{(abmqb=v+Ob3gmJtX3uyf~OZuru|2+I};j z?Mu_ubwQ7Y6ZZ6HXPR{AUYt!GOA+Ar@EH;BBxlUwsvncFpOfDO9X}X%^@!UWHMn|Z zuUsbM=+e5PKR=^gHiM>j{Tg)`J-Y!Tz-=N;Vw=2Pcjoh7 z^DFD#1IPruQ>zxr!YzV@|FSSE*4C2If~c0s!;fd}H%a-^9-)qhHpMCpNCEE~U4wpc z;@#u$!KsQKq&evH5$M^4AJt+;V&j)u#GbSEo%&0EU~L(yL^Ri#^u}F~li`dRrX;As zELuxZW}!>vy3Sd>v!ewM*>HiPZd$33Xi{ytOJ+=+MQtfto?UBfb#zvmxK%7sW=#IB zfX<_T8Kbf|#ejmDw;}!>%aKQ?cc(fO?umW9`_Rh)U_(Ekcc--;omI{Z;j=8qxtO__ zrmk>d6#7;hjLF>_X(dk$k9vF>+(KmK@I9E=y%B814i*0VlvGwu}CT9*sW=JDQZjXA5f;(69MyjIUZxF z<3u?v&-S;+3~!7IC0r_#tkH1stiiXx^V5xq;ImSWg2u4Ub&B7d%4`<_b#56Z5;=i9X@RMx9~M z(;|Tq#h4lem>Cv_KgF8Y5~0vBCkvY#xxwII+m-H&*kB*=A$+LbZ^(I2 zbLZ?Bc*|}F+z!M?!3=C39ydD%B+SL$riul{lrbfPeZwq*oj(O)Brf5F_- zno8XDov+lgueAAsTRp9&E<00(tPpV3y+P;JXE~}s7)zB)?B`JIu>++rvpktJ@%#Jf z83UJX$^DxHKZfy2IUK1jIiAnwUNqMnLyruo6{B%nWf*cd1L%6rl+PlVZJBhCI|XUF z-D5Uf?AW-tQv2o@Bp-?{E0a>3j;1l{Zr%FWO8|rDnO-ff&GA(So3+~4f$kXnj^vZ( zpzRs6L}R~XEyLAbCrQy!xH*P<=425ajo$LjHYwWO{`0Z99!iRumti$e$~J{jSyol# z0yb6G*}#@ZbsU?)`ssafotLN8-vwKF0rCPy*A)l3nk29QXfaJ?rP1 z9#`O#?|joAMP)~bE^u?Ox-+ricneafiYX1w>gRGH{oSOML?c#PVon|}%C|QLlsXNt zjq6*8(ctP<5QabPbAp{b#?mo!aU{HJyX#PW=&`{z)nCda^@&_(#t^jB7wmfGX^BfQ zq*P2mV3^ZdDm2ZPTf{m?7IHU(?E%ID=fDxc81W3~jqa5-t1%}G_#xV&ozOyy&enFl zd#`Uv;jXOhbF#F^V@SGS=R-5lSy1lg*hv@^X;64*jW1{kjW=Qm>q}|u?D{XRo3;UJ z6~GJ2%CUNj^t3DVM1?bsH0we`Ex_zVxl9M+UJr>#-F4!4)GOV%CKa*@-5riqKr>g^ z2$qNwPto7l(8(arQn^j@@9OIf>&V$5CcE zJ2d~l`&7azYqz=%&9~UbJ^}$#;yL%jJe%ea*-8#G?k%6C33#J&Nj$= z)9ba}!bD7Tk&JaP=7Bc|FbOdh;jOi4L*}sMS`XR zePdv!u!=KSiY1COI2|$pmy4uDepDUm@4HoD_%wjE%6BRWB>o~n4p5Ja^k<8nTOzTc zFF8G`F?^zg*L1VhXotM$od=-gdiWUY_1kwnH9N$@gkG{L!f}4(x<6Y2B}AA_3_TE$ z72O&(Jy&LPVVU<&*xY>1hp}R>54%q}oI@9MtA3YH02~Dz7UZR6sw;J?Tu0@5@qdom zioQb_G779ns~2q@dODzWubiibjmq^|>3_i83*7cJ=^w3x;hV{UV%b*L@rNS?tWn+j zy6Sf>juqYdhDoBm*0c6(gH|`x0C6SXfyu2x?oRYu`&AXo(b%KbMc2*b)D@0lM=~C_ ze9aeYmc}^6%u2C#+P%d7zJBmd*&n@051XH@ZePkyK-820(r#2xDG$=4=qJFRwREng*YI_@vkX_jz_KW!^a4skVwrT|8+&9nQauUcF? zcS+Iw?4oc%d`$Pc&e8OMl)W_?IT+_G^P4&rfwPZqdhsO&v{igA8kx#Ch3|BK*j_f& zjrGRyUn6RlWH-1>d}ZnCcw?hE@YFb@hX4(S(O=_Q9ua@)gs;T1m4xP$m9QbhDiQ*w zMNb)O+ex(UEab7x65bwz^=;B&oVJxpPK^af2D``1EWrm|@hMzOPNzQ-Vkg&17ILj* z0C^&5;?N>h+OZW+7N{emf7H=Dc8)iydw-vR&siX+PFRBRkn8IoS0_D$v7o_gb_W&T z<643mh2<yz{iG=UY6bOoZj+(Gos0%<1_7uKVG-gYc zay5l%QVxexf{~~W?f-TWYIzvjjJ6``eGjH+>OGN9su;;RPX&Ne2p+Y9K6= z{&kay#BGdEr`2J-6Ny3s>u`om?__qbdQVQj$=U`~>Dx*b|$gHtY&MYa-m( zC1ft9pmG0(j}4-*SC!H0nRqm~;H_ej{o2KVP zr{7=-@K!u7l@YWnma>CIJu~1SH?+h$`oc9lxFY(vEH1zepm-3Yiy^L{>E-JnE4b}p zqZ28RmC3;USkUGLf6JZ%e~}VT8Zwi1SrDib@Ec~MFvEFdZi$rY^fMpgw-(<7KUg&$ zmRUa^38rjt0wu114cJ!U*R?-ZBEJRvbhJoyUx2iis5ec{_D{dtW(BPFKN1DctDG@y zkLB!puU(>W`Z$yrytD zRR>3YyAk-lG3iWg+QkOX6#f3c)@_)|SmLc&sAib@NBK!Y-TK&oMT8A`+3X3<3`32n z@yCdn@eQ+V^`>n9APH=UZ*?5Zigk3VtkW;aiY>Z3yYqSVV~>~XPhLH;w4IR^dy}`3 zbgp{Kkq&7&_0iQN#sXQs>{iNghgo_CWT(#E2kTm7OxaQD*`JC==oy>mz79(K z?TPu6(tv86)YOcwF6)Pd4W4PKclR|sImvanqndY_UG=5w`a^y5DQ-I_%pcxaV!E|j zt?Dp6FD8PTn^(>9ni2Z7%k+5b%BUf)(71J5(^3n&HfyxLO3h>0T6?yZmz!3wd%cPZ zi!Gj8&yVriu)A>G$#?5rQt4~X?n#e&?od8Dbavy{*jrCR_ZgAJAS~?YANj_mh`Q?iU0YZWT#TPnFM`()mJG+IJ?AT$OWa7NsG}*B9D1n)?<#e8D!@l$D!`d+G!kTe1 zo2rFgKJzJ$$}F!di|O4{7nw1VA3S>UN@l^Zi{|BXE~WR(-Fo{TI=JpR6y6`a_E*n{ z!I03J;Gv19-TD6PJ%?LWSV^5334Nd9{JO$6{GBIcf_*?0@rGD0^_Cs6Qusy-Hf+ojkG=rkj&qd;!bl<(MENZ`Y{)s2k%dYfF2USbQ1tyk_#eFOD(A+*h|` z^jREHu`zfuKW9vUKpg6u%)A~Q6GD(21%Ioe+@Gs^xhwbh9FZU2?Fqzxty?%4%N+HPuSEqZtOlb_Pj!yzLT zzHjOkzh>S%w1=2$9{qOT-igz3$G_Ga$H~U7{(Wb#+SNevzmr8q1gk1>94)uB<28b+ zdo4<5LzVVD-3-jqt(8IRf{YEFzU;Fk2d>y*d9z6TjB1t#!V#k zfa!~+*~GSgBXSWnBrjI^G<7O`-Uq~^0HJqZu5=E{F) zw=EP>wisQAs+?L1zRm^ds-c6twACOi&X?@=>wYHrZRTScHO^B`YEforE5Df}W+V)Z(7>Cd>%^wK&gxaOQj3m4z8PCAyg_=wLAHKV>>S6H|^z950>OpkK zHU|plll@eCuK%Uzgfo!gesZu!n*_oTub2178FGq*wrVbF7dbm#(-3{pO$f&%Z$})$ z6zY1YK^wp9D#?(m7XE1f7-k^6MI6D=i}myjB&*Xdk@Wu&eH4O2I?GVR@n#0WV1Kt! zxi-5{+NZtQx*pJzc?sS@@a4pjBX{q#T97p-LNh4TLHpt&67okCJ95Hnz z9e%P=OdFXctbHJ%=Zl#Nu_Nm(nj)-07y%2O|s!P7x34?>Y3q8+a-(0xW2stW3oj1*F%jDBL47=3Ak4N~*NFv5CG(25K(oNL5( zq$Yh=hbZanpfth=Lo#-8BTZ=xWTO)l6GzBDKZIQ( z*B8mj1pDz87BXsG> zg;bD8RZ>?XQT(eVNS5s7b+RP?h$mom>W#H(+dq6#JG-yl7`GX3CoGL|MK5@AN1<`jWM7XhY-XzRCb|v$NhkqTE(L zzmcV!jU4B-oyL+`-o5Uy11Cd)EZAq%MR@dH95HQS7KKKH8RS^6; z`2&*EnF2(k{_)Jau zB@n7SAVv(=>-Sfv3T=3Qct^7?yU%2#E8yD+JhleG&C-;JxF-Cg>2Y7U^b^v_X#n)_ zt?4zzfdq%rp@JBNyGu8nT^wZUS4)m_>anb|gcEurDNDf#W#waM`1&!eJM2)BUem&zvo zTT9aIKEB?d@h;UBVAGGbmQv z()i53#&2!O!-49Uw78eHA?JA$AKVFs1BV7yjz{bT<4ykp`lPInDI^o4KNg%yV zROhn|ohNTV5sanveATA5^8(dXs4X#xQEb|g@E7+pZ=B1O#VB=%4o^jLuEP*31Hcp? zu$ZIUtoM-pH~4k?#!T$0+_KNtw1|bBAHE`7(=9iHWN`sl1|TCf4iV@dBH#W&yP{W% zK6+eDjCvP6Sz*!X*4!?kxb?gAKab3@DCdS(8G_zZMo#GL?O8 zis8#HzpoB~eo$hN3}IqnSTO`h*e?|rUV{A5Uk|v6YWXo+f<=4M-`X*6*S^ItZY_$c zI0(D;PBAdfKTZzC2#KNtFQ?vHOd^zyGUJTWLYO^BDp}J|l!8w6F-1D9&+tQtx5*1U z63plw9VWy~8A3R_OxN+XXP0`~fZQB<-H33j*d8*q4HVQP$vVIPtH#|>0#qp+$}e_r zL}#D@%UBF#8FU%{a=qYk&!AspR1CtGfJ#}C@?A_x;k%GTh!DvOV)drbsmMBQ3OY@h zQY9MFn`RR5?7VwS?-2X-wSXI^?v{N%vn<~&nuK#%GKcgOaaIx4fS7f0AXEaLr%8jA z2h^yIz3?>e+;9SOWla)He#$B}g*e@MJwFcE3|4KwF~4zLzrX3f)6Q{Drc^!T;uz8- zOU*X?l#}y72UQp8-qkEzR7L(QBhOmC3**(%PkuTL!#Q<`RO<01|*=m3Sd@35c=f}FqiR!s1zHkjy`*c$sEx7n~ ziN?&O0ax{WXMVeQKw`7&9H3@lk3T0c2ir6#Llqjs7dMz5d^A5h~7-ubW|l!fADYNAJNYMnJI5 zNdxNQ+!nLLLuZb+|Al02>CJU@0j+#(NpbDtbG3RtW_W_8I$*(72jFY;w*nB%2YuCe zA#c_xue@8#{=+?dVo7(?g^;$^Dvajm;aQ0Sf>LlrmBKGWDyh;MA*>qy(IqQS$#K~} zox#^a0CLRrInj03@+^LN6x8V2D_OS8VnTepXk{FBmSh|i@D{0C4iMvSfQB6PPfrI3 zZNeJMMFCH%Zo%yf?ho?xNcY3d@%rh(Mz!Vbwff8#L28Cn!06Tpd7~MIWfLkBCn|nms8D-Dg-cpf{vOrT2qZ^iWVhb;VP&JWq=5sZrNIz9tI|Xjtk2D>B2W(a9 z98zKHBT*RDT{^pn&olS7y>&PIXg+fJAUn(JWc+Qt<4ed=5s|a_4Z>5{9UdZ1qSR7Y z8n|PF`5sBZf=5k9so&f5he3_$Gt3S)(=*e$u6m~mBmRW3>*DwlHQ0vh8Z-b`LODa` z0{mI9$I(Q*1sL{k*lBkkvTtdgc)Rvjj5DZDvH}LN-Xbt3+;)1=M(j&LWOe@G9t_H2 zUs=buAlY1FE^J^&I)NXxP>6WzzWL*H`#@&szzz&0 zR2gVp^Nj(--x|;>R#MPF-|B7E^08d%Mp86USRd4@Jk9|*>s;eT^4zX|E#GtQ4YCc)RyuA2;XHgz-cvNi^S-*3hC$>!$he7A}h zpmx-1Spw+HQ@7yVc$YI>*vS=>H?Pqp+b=N4$FzV>n+6xs`U4~zjEulbwa82WubxX= zOY^9RO(5}`jQev(;+qp~_ioKvwM7N63J!sqOZdyGX9I;WL_dH-iVtFel(SGEK$=E1 zlx$=Rfa=*=TYlcg{=^LcAKwenF&XOt$NC3IF56H*fx+xR)jjPgfZ)uFCF*muZdUzl zL_eBtUHg8ZsQDXeygoq|!|y_*5%4nP{{)q3PK)zS(ru1ZlqgH~i+NWwm93ar{d}Bk z{sghGyF-_P1-##oGSU;%&=Upmun=Vc`t(QUffZRZ>FL9k+egCLokzm|dvS3}2ip+W z8sUR z%(w|Yopwvoc1(>R05!jFBxwu7jY}GDu<){;1uN6XPvgPm`>@o)+iw}ssSabqb`@b= zcTPTWeZ>xEp7B^*7~=N{eZGX)f*)3fu1zTc$Q`^^#A;kuh2;2kzlZGV(kra@`Y|i6 zWKYj@4IGZ?F`u$XrujTKCq$o2&eLz{%{|v^p_n&wvV`?IEYaUD@dg@dF&EvS+I|2O z#$_BC{Q$4N-5jYaQ8H9uacWw+VWwrqk$tYrv2-mvuAk8t*A~`1?1$6*@d76~71U>SVWJ#IhS9aqa=ktz$U_rXNp6-fl|P*|c+Ih-=K|3Zr%8tDx^|^J z&Oi|E*q_e6a3C}#Ya-^j3`n_WZ-j@_#N%1tJk2`yPK^vO1nk}u9n0IzHyo3yA!z|V zvq}^Q*;a5!`+Yb9D#+GZt~+O6pu9mb4lb@Oxw3a7u4y#SsE$_03R(JCO92$jn-FNo zUJh3_NERwa&Z%a!-Gq{nP8C<<3I9D7wHuO>CcDq)>HF{PIKy-Pj3Q1VT(JnF(ZsOU z1{>RXEFK{Q%e2G%-nYP@WS#RjJSRKvlzqrcF7aWum_|GR6z2kDM?q{<4D!bqW!%yO z-RryuvK?`^xQA4BxZxbTzf#$CchAYmSH;a<(+$ob+`Rrn^VACvu~PVONFoE#U~M44 zRWfN;2_>iR8h?0v>~ofP?5#e#D9=D<49!d0Q4$7joDxwZ+TBw&LC)U*nst--;;q;q zSX=sY6XRK0j|1y#GR7}Mjp^LO)->zjvpwDCyqdMbc9i;rb|@Ihz)%$Gv`|9r0vh|y zva*WtUP#Otj3k>|f7r$wUV4;~BKtC*DYdbuue?~cVJm`E-VX-;7QCdX)-P=T700rQ zzs3_xrvEKqWY!X5{^-6zSOLl$E58x5<pQvMVSx> zZzL?X4GlA%DarOZ#at`3XSjd+V`8j9#p?7Hmi@^UwySaT#FUz;hh`MFM%S^7hn9uc=9=Z_8kJPTYW`^#GB+~kh_f4(sxhh# z3sQ({y6P=uv_787pZM?=XeGyriETo12-w2_sXQwTK*{8S%ocN84Z!JXztq;3!Yr?X z7%{V+-`oSDtJ8~VHutL6VZXn+g9s({$SaWkl4Qx%YM?4VE0S+PJi_36!-I@kCd~-M z5YG=26ws)aQ?U5wux!s|&oGuf0rb(MrywdP>s}SlYasen4ep-t!?w}7uf!ab5ZrKd0|-{^Kb&LW>`BPOHR*2 z^6_#RoihGvWc|O-MX3a#tk=!qI?&5RsbZY>6d1aNFb$8cAhyZIc3)u^v_8MvdcvBg zc!i{K(;xVUYl6b!uATSdtcmVg?%1)42jk2xN|f^g%zqbn?bmgilQh6_jC}!Y@Yk6O ze&Ub|Y8(gPGbD@xa-vQddE3&Jdl^z9OxD9@Z)+W)M9KEim_FOH#g|ah;)ZZ5m^Okl zP2VJvM*AX)vgd2~2>~-iGDM8gR#xKcBYk;sHLAMcW``q4V9H@us%5>%SpvF4ya`<% zKQYig{m>-qiT5J6HQ+ zyS!1iSwd~g3AwSgzQ5ljfz10Ltq!4g_?EeR)fBeM8fE;K(-SWydk+ws$1j%n_K6uO zQtDT8vk2%G-#(*pzNW&xYU3*#4Y4CF&!=88rU!(Xd~R3|dFwOGUBOg94R5YA0zm-H zh+dWeeal*v{+Y;)*u{6uC7{aGf-`WS9-9yq@qF;4+Y5RY63H}dgNZ!dl`Re`tl47a?5_uFSE)F z&Hz7Hf!F6J0-rw}hfhx1(qSI?{sYm22fDZ*x!?m9=fCa5!j}KV#BM>)=z7;o#lIJ= z(#PSt?f_(wl4F6(_uc#j8n@Y03&UtBE9o7p0SAJs|K z?`t&1$c=3!+(D%F{uRa`jt;}!w=_W+t;ML@;9`s`K z&=K*d<;$Qd?X$lAShQ1FWgDnD$PB}ONbD1cSm_HwEMP?IsLc3HBbe%flAz3TLFU^7QzJeld3PH5&RRBZt3<5OBumdIaS5s{r(!*5B z`_u?}P5+o?W9D%k2WtXlNP^J>YbpZVGxkQ)HEo7t3(VVd>sVG#z_1I9sTtc)z>jj} z5ECYOuQ~3!c^#c<7m;o$^~g7U7G;tJ6&9cM^NDRXNSTg*kia@`_M0j=2(j6wO*mds zt<@$LE(%3JxZIGw|124p%P2L#3<6-QxN5ewa}{(Ml0K3;6&^IUexQ98H-}t6C~bup zKbXDHH-a#m-!(HLaT;j+Xr2KlNJC$RXtWBcqpH(JQU(NDo&#T78%&%4SB*ow4?#

Naq6kdP_v@B2vEvd=VG;I z^!=fjR!>9XwM3nms{zQzJraV;sXBIHjRJEgia3?-0`r8EdTXg|;X$DMi)Al&ggs2J zWpp)aF>C|QlcnyJo}fV8Enc&hIx~>iT2?nX^6XT2&_S-80gx4%0VRt#5XD}Ctq(gu zd@eH{MuA#ru$)U^EWtpA{@iN#*2ap^u9=Z$bpa0Y0Cg1@_p4!w{>2>%H$c4?PpANm zOrUOL=oPs;T5E48vx|Lu5Xi_tEdv-pp(2aM+&q+%VfCUC3Mn zfghGR)p)S`I_*Wwr0&?D?r52hq4^pWev#`8k5$R+lw{a02ReZ$lE*O(A^74|%tITa zPc)s*r~THYKHnp_dszX)*?)`v0$9p%ZuX69uaLX~CLXBL`bWJyMluKdO+R@NN}NH| z9D^q;G&y)hV1DuEpv7-g9S;u%p!bgRif_m13-BJNKpZ2l;&}ZafzFV5V+0s0pt$K_ zrz(OiK2;=`d|6U7i~HtJejbh=K;o&_-zGqtzewa1Z60BG;@o3`oxHN6k*rF1A23XL3qTOHI$2|l7 z0#%#^s`#Q&4k^+qRkxXw^bw<&f*4ed1V@G7w2a#z6K``iJ%TaY{$$(2=_<%`+dz`Q zMoZsaIts=tg1g=Zjy{$I;9~x?n-H|Vl<5YDf_3<%ABIfaM>lnwvH0+_*t@OI%Rju5 zEDJtUvCo8WR?5Rd>mRY?BL6J#2T77m8&5FxC|=~rez49T=LiP8gr-P2}qVF4{3B)FLW z*H6v6xMQ93&LJ54dHQRASY}BNPV)8Pf{KyR$+D90QlIFhKivwp7K;xEXFeE!=S?ED z7#eyQXtHzsWJ#sc){uaiwy%dwSQU>CR5S&DaSi_(Ul9_OagsT8&G&Y*uo1go;~x}9 z!h$UiN3#WdjC4CvGPw`1ho%ryQ|6R zOn@%=Ax7xu5nFJ>QsBU7>C54XWUlQt&oO2@(n0Rn-m$MCp`V_iQ|0-%&-~X2`G2KQ zwy!-4xG7&>?Z+W+W)Mf(yP-mv+PWU(?3?>MzAa>YaG4v_3!&now2=Z}_hHa)yOyXo zO7SM?oB_3{Yl7gG1!kWZ-R?0YkYoP@-@wnr{9*SgLe^UWu!5=MP6tW;FXt#E?Fe^^ zdJ(=3{twQOejW+yxZ+^`d3KVLkJ3NOg7sji1eNTg0@?AmF-|>V4WMLKHXiE+*O!I> z)7D%=Z%zS$z8J=E>%pWt99G##sxmO39VLHYfI*`EMKpjWsVHcMTKgP;MnZ6@$gv4g zjQ?&JVv(L`y(Xs+z3aS|ur>%zpn@x>TH+28O27s7KJan*S-n3GNgrso@~t}*X=87oadhCj+rVbe z0(7+HFwVt+GNQi;#O`@hyr`+!>|`V~>BkuzfT7ep?+~Lv1Ey=J=+w;|SOXl|#iJUF zq)k{%n8g!Kh@R`K(FP+B8^SOG;OUG5WT3g-=L;f|tkhdHZ6#bi&rB(U)r+l)y)0h*ARQ~qj zkBScc7OhgLZ>Y5=Mh_wg%ICDhQPu`8M4`c-MM~COmk|odaikb>;Pma&0lTg!g_`{m ze@&Ss+r0m9yzHuPO1GXVdF);~^k={4?xn#2lA~SwlkN>D3O+6kwf|4?kL07DW13za zt*aCY91Cn!dY}J4VWVBwYY^t|+|Wvc)rKGT##ucQASIJnvi-|jhTP4Uw?QcZzY> z*WdHED7hlpVyy?Psk?1Ob|vXmt%frJKxawFn|XYnS;KnOr~H)74v;A>t90M|E*V|C=GUSv%nKE%(L#Ka%bptN;K2 diff --git a/samples/electron/Assets/WideTile.scale-200.png b/samples/electron/Assets/WideTile.scale-200.png deleted file mode 100644 index 91abeb7241cca1be40699df79911a7a916920b34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6344 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 LHdt#P%~3jD|Bv=-6iZsA(g`l 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 - - // Known publisher → publisherId mappings obtained from Get-AppxPackage on Windows. - // These are the ground truth values computed by the Windows platform. - - [TestMethod] - [DataRow( - "CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", - "cw5n1h2txyewy", - DisplayName = "Microsoft Windows publisher")] - [DataRow( - "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", - "8wekyb3d8bbwe", - DisplayName = "Microsoft Corporation publisher")] - [DataRow( - "CN=CA0D5344-F590-41F9-BE2C-16BE6FCEE1DF", - "rn9aeerfb38dg", - DisplayName = "GUID-style publisher")] - [DataRow( - "CN=83564403-0B26-46B8-9D84-040F43691D31", - "dt26b99r8h8gj", - DisplayName = "GUID-style publisher 2")] - [DataRow( - "CN=Metulev", - "j3adjyj8sqwmw", - DisplayName = "Simple CN publisher")] - public void ComputePackageFamilyName_MatchesWindowsValue(string publisher, string expectedPublisherId) - { - var pfn = _service.ComputePackageFamilyName("TestPackage", publisher); - - Assert.AreEqual($"TestPackage_{expectedPublisherId}", pfn); - } - - [TestMethod] - public void ComputePackageFamilyName_PublisherIsCaseSensitive() - { - // Windows treats publisher DN as case-sensitive for hash computation. - // "CN=Test" and "cn=test" produce different publisher IDs. - var pfn1 = _service.ComputePackageFamilyName("Pkg", "CN=Test"); - var pfn2 = _service.ComputePackageFamilyName("Pkg", "cn=test"); - - Assert.AreNotEqual(pfn1, pfn2, "Publisher comparison should be case-sensitive"); - } - - [TestMethod] - public void ComputePackageFamilyName_PublisherIdIs13Chars() - { - var pfn = _service.ComputePackageFamilyName("Pkg", "CN=AnyPublisher"); - - // Format: {name}_{publisherId} where publisherId is exactly 13 chars - var parts = pfn.Split('_'); - Assert.AreEqual(2, parts.Length, "PFN should have exactly one underscore"); - Assert.AreEqual(13, parts[1].Length, "Publisher ID should be exactly 13 characters"); - } - - [TestMethod] - public void ComputePackageFamilyName_PublisherIdIsLowercase() - { - var pfn = _service.ComputePackageFamilyName("Pkg", "CN=SomePublisher"); - var publisherId = pfn.Split('_')[1]; - - Assert.AreEqual(publisherId, publisherId.ToLowerInvariant(), - "Publisher ID should be lowercase"); - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs deleted file mode 100644 index 07068827..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/AppxManifestDocumentTests.cs +++ /dev/null @@ -1,780 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class AppxManifestDocumentTests -{ - private const string MinimalManifest = """ - - - - - Test App - - - - - - - - - - - - - - """; - - private const string BareMinimalManifest = """ - - - - - """; - - #region Parse / ToXml Round-Trip - - [TestMethod] - public void Parse_AndToXml_RoundTrips_PreservesContent() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - var xml = doc.ToXml(); - - Assert.Contains("TestApp", xml); - Assert.Contains("CN=Test", xml); - Assert.Contains("1.0.0.0", xml); - } - - [TestMethod] - public void Load_FromFile_AndSave_RoundTrips() - { - var tempDir = Path.Combine(Path.GetTempPath(), $"AppxDocTest_{Guid.NewGuid():N}"); - Directory.CreateDirectory(tempDir); - try - { - var filePath = Path.Combine(tempDir, "appxmanifest.xml"); - File.WriteAllText(filePath, MinimalManifest); - - var doc = AppxManifestDocument.Load(filePath); - Assert.AreEqual("TestApp", doc.IdentityName); - - doc.IdentityName = "ModifiedApp"; - var savePath = Path.Combine(tempDir, "saved.xml"); - doc.Save(savePath); - - var reloaded = AppxManifestDocument.Load(savePath); - Assert.AreEqual("ModifiedApp", reloaded.IdentityName); - - // Verify UTF-8 no BOM - var bytes = File.ReadAllBytes(savePath); - Assert.IsFalse(bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF, - "File should not have a UTF-8 BOM"); - } - finally - { - Directory.Delete(tempDir, true); - } - } - - [TestMethod] - public void Load_FromStream_Works() - { - using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(MinimalManifest)); - var doc = AppxManifestDocument.Load(stream); - - Assert.AreEqual("TestApp", doc.IdentityName); - Assert.AreEqual("CN=Test", doc.IdentityPublisher); - } - - #endregion - - #region Identity Properties - - [TestMethod] - public void IdentityName_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("TestApp", doc.IdentityName); - doc.IdentityName = "NewName"; - Assert.AreEqual("NewName", doc.IdentityName); - } - - [TestMethod] - public void IdentityPublisher_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("CN=Test", doc.IdentityPublisher); - doc.IdentityPublisher = "CN=NewPublisher"; - Assert.AreEqual("CN=NewPublisher", doc.IdentityPublisher); - } - - [TestMethod] - public void IdentityVersion_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("1.0.0.0", doc.IdentityVersion); - doc.IdentityVersion = "2.0.0.0"; - Assert.AreEqual("2.0.0.0", doc.IdentityVersion); - } - - [TestMethod] - public void IdentityProcessorArchitecture_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("x64", doc.IdentityProcessorArchitecture); - doc.IdentityProcessorArchitecture = "arm64"; - Assert.AreEqual("arm64", doc.IdentityProcessorArchitecture); - } - - [TestMethod] - public void IdentityProperties_SetNull_RemovesAttribute() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.IdentityProcessorArchitecture = null; - Assert.IsNull(doc.IdentityProcessorArchitecture); - - // Other attributes should be unaffected - Assert.AreEqual("TestApp", doc.IdentityName); - } - - [TestMethod] - public void IdentityProperties_CreatesIdentityElement_WhenMissing() - { - // A manifest with no Identity element - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - Assert.IsNull(doc.IdentityName); - - doc.IdentityName = "CreatedApp"; - Assert.AreEqual("CreatedApp", doc.IdentityName); - Assert.IsNotNull(doc.GetIdentityElement()); - } - - [TestMethod] - public void IdentityProperties_SetNullOnMissingElement_IsNoOp() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - // Should not throw or create an Identity element - doc.IdentityName = null; - Assert.IsNull(doc.GetIdentityElement()); - } - - #endregion - - #region Application Properties - - [TestMethod] - public void ApplicationId_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("App", doc.ApplicationId); - doc.ApplicationId = "NewApp"; - Assert.AreEqual("NewApp", doc.ApplicationId); - } - - [TestMethod] - public void ApplicationExecutable_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("TestApp.exe", doc.ApplicationExecutable); - doc.ApplicationExecutable = "Other.exe"; - Assert.AreEqual("Other.exe", doc.ApplicationExecutable); - } - - [TestMethod] - public void ApplicationEntryPoint_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("Windows.FullTrustApplication", doc.ApplicationEntryPoint); - doc.ApplicationEntryPoint = "App.Main"; - Assert.AreEqual("App.Main", doc.ApplicationEntryPoint); - } - - [TestMethod] - public void ApplicationProperties_ReturnNull_WhenNoApplicationElement() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - Assert.IsNull(doc.ApplicationId); - Assert.IsNull(doc.ApplicationExecutable); - Assert.IsNull(doc.ApplicationEntryPoint); - } - - [TestMethod] - public void ApplicationProperties_SetOnMissing_IsNoOp() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.ApplicationId = "ShouldNotThrow"; - Assert.IsNull(doc.ApplicationId); // still null because there's no Application element - } - - #endregion - - #region VisualElements - - [TestMethod] - public void VisualElementsDisplayName_GetAndSet() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - Assert.AreEqual("Test App", doc.VisualElementsDisplayName); - doc.VisualElementsDisplayName = "New Name"; - Assert.AreEqual("New Name", doc.VisualElementsDisplayName); - } - - [TestMethod] - public void VisualElementsDisplayName_ReturnsNull_WhenMissing() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - Assert.IsNull(doc.VisualElementsDisplayName); - } - - #endregion - - #region Resource Languages - - [TestMethod] - public void GetResourceLanguages_ReturnsList() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - var languages = doc.GetResourceLanguages(); - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("en-US", languages[0]); - } - - [TestMethod] - public void GetResourceLanguages_ReturnsEmpty_WhenNoResources() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - var languages = doc.GetResourceLanguages(); - Assert.AreEqual(0, languages.Count); - } - - [TestMethod] - public void SetResourceLanguages_SingleLanguage() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetResourceLanguages(["fr-FR"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("fr-FR", languages[0]); - } - - [TestMethod] - public void SetResourceLanguages_MultipleLanguages() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetResourceLanguages(["en-US", "fr-FR", "de-DE"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(3, languages.Count); - Assert.AreEqual("en-US", languages[0]); - Assert.AreEqual("fr-FR", languages[1]); - Assert.AreEqual("de-DE", languages[2]); - } - - [TestMethod] - public void SetResourceLanguages_ReplacesExisting() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - // Replace en-US with ja-JP - doc.SetResourceLanguages(["ja-JP"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("ja-JP", languages[0]); - Assert.DoesNotContain("en-US", doc.ToXml()); - } - - [TestMethod] - public void SetResourceLanguages_CreatesResourcesElement_WhenMissing() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.SetResourceLanguages(["en-US"]); - var languages = doc.GetResourceLanguages(); - - Assert.AreEqual(1, languages.Count); - Assert.AreEqual("en-US", languages[0]); - } - - #endregion - - #region Namespace Management - - [TestMethod] - public void AddIgnorableNamespace_AddsNew() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.AddIgnorableNamespace("rescap"); - - var result = doc.ToXml(); - Assert.Contains("IgnorableNamespaces=\"uap rescap\"", result); - } - - [TestMethod] - public void AddIgnorableNamespace_SkipsDuplicate() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.AddIgnorableNamespace("rescap"); - - var result = doc.ToXml(); - // Should not duplicate - Assert.AreEqual(1, CountOccurrences(result, "rescap")); - } - - [TestMethod] - public void AddIgnorableNamespace_CreatesAttribute_WhenMissing() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.AddIgnorableNamespace("build"); - - var result = doc.ToXml(); - Assert.Contains("IgnorableNamespaces=\"build\"", result); - } - - [TestMethod] - public void EnsureNamespace_AddsXmlnsDeclaration() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.EnsureNamespace("build", AppxManifestDocument.BuildNs); - - var result = doc.ToXml(); - Assert.Contains("xmlns:build=\"http://schemas.microsoft.com/developer/appx/2015/build\"", result); - } - - [TestMethod] - public void EnsureNamespace_DoesNotDuplicate() - { - var xml = """ - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.EnsureNamespace("build", AppxManifestDocument.BuildNs); - - var result = doc.ToXml(); - Assert.AreEqual(1, CountOccurrences(result, "xmlns:build=")); - } - - #endregion - - #region Capabilities - - [TestMethod] - public void EnsureCapability_AddsNew() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.EnsureCapability("runFullTrust"); - - var xml = doc.ToXml(); - Assert.Contains("runFullTrust", xml); - Assert.Contains(" - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - // Adding the same capability (case-insensitive match) should not duplicate - doc.EnsureCapability("runFullTrust"); - - var result = doc.ToXml(); - Assert.AreEqual(1, CountOccurrences(result, "runFullTrust")); - } - - [TestMethod] - public void EnsureCapability_WithCustomNamespace() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.EnsureCapability("runFullTrust", AppxManifestDocument.RescapNs); - - var result = doc.ToXml(); - Assert.Contains("runFullTrust", result); - } - - [TestMethod] - public void EnsureCapability_CreatesCapabilitiesElement_WhenMissing() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - Assert.IsNull(doc.GetCapabilitiesElement()); - - doc.EnsureCapability("internetClient"); - - Assert.IsNotNull(doc.GetCapabilitiesElement()); - Assert.Contains("internetClient", doc.ToXml()); - } - - #endregion - - #region Build Metadata - - [TestMethod] - public void SetBuildMetadata_CreatesSection() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - doc.SetBuildMetadata("Microsoft.WinAppCli", "1.0.0"); - - var xml = doc.ToXml(); - Assert.Contains("Microsoft.WinAppCli", xml); - Assert.Contains("1.0.0", xml); - } - - [TestMethod] - public void SetBuildMetadata_UpdatesExisting() - { - var xml = """ - - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.SetBuildMetadata("Microsoft.WinAppCli", "2.0.0"); - - var result = doc.ToXml(); - Assert.Contains("2.0.0", result); - Assert.DoesNotContain("0.0.1", result); - Assert.AreEqual(1, CountOccurrences(result, "Microsoft.WinAppCli")); - } - - [TestMethod] - public void SetBuildMetadata_AddsAlongsideExisting() - { - var xml = """ - - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - doc.SetBuildMetadata("Microsoft.WinAppCli", "3.0.0"); - - var result = doc.ToXml(); - Assert.Contains("OtherTool", result); - Assert.Contains("Microsoft.WinAppCli", result); - } - - #endregion - - #region Package-Level Extensions - - [TestMethod] - public void GetOrCreatePackageLevelExtensionsElement_CreatesAfterApplications() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - Assert.IsNull(doc.GetExtensionsElement()); // no package-level extensions yet - - var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); - Assert.IsNotNull(extensions); - - var xml = doc.ToXml(); - var applicationsClose = xml.IndexOf("", StringComparison.Ordinal); - var extensionsOpen = xml.IndexOf(" applicationsClose, - "Package-level Extensions should be after "); - } - - [TestMethod] - public void GetOrCreatePackageLevelExtensionsElement_ReturnsExisting() - { - var xml = """ - - - - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); - - Assert.IsNotNull(extensions); - Assert.IsTrue(extensions.HasElements, "Should return existing Extensions with children"); - } - - [TestMethod] - public void AddInProcessServerExtension_AddsCorrectStructure() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.AddInProcessServerExtension("MyRuntime.dll", ["My.Namespace.ClassA", "My.Namespace.ClassB"]); - - var xml = doc.ToXml(); - Assert.Contains("MyRuntime.dll", xml); - Assert.Contains("My.Namespace.ClassA", xml); - Assert.Contains("My.Namespace.ClassB", xml); - Assert.Contains("windows.activatableClass.inProcessServer", xml); - Assert.Contains("ThreadingModel=\"both\"", xml); - } - - [TestMethod] - public void GetRegisteredExtensionDllPaths_ReturnsAllPaths() - { - var xml = """ - - - - - RuntimeA.dll - - - - - - RuntimeB.dll - - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - var paths = doc.GetRegisteredExtensionDllPaths(); - - Assert.AreEqual(2, paths.Count); - Assert.IsTrue(paths.Contains("RuntimeA.dll")); - Assert.IsTrue(paths.Contains("RuntimeB.dll")); - } - - [TestMethod] - public void GetRegisteredExtensionDllPaths_IsCaseInsensitive() - { - var xml = """ - - - - - Runtime.dll - - - - - runtime.dll - - - - - """; - var doc = AppxManifestDocument.Parse(xml); - - var paths = doc.GetRegisteredExtensionDllPaths(); - - // Case-insensitive dedup: should be only 1 - Assert.AreEqual(1, paths.Count); - } - - [TestMethod] - public void GetRegisteredExtensionDllPaths_ReturnsEmpty_WhenNoPaths() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - var paths = doc.GetRegisteredExtensionDllPaths(); - Assert.AreEqual(0, paths.Count); - } - - #endregion - - #region Package Dependencies - - [TestMethod] - public void HasPackageDependency_ReturnsFalse_WhenNone() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - Assert.IsFalse(doc.HasPackageDependency("Microsoft.WindowsAppRuntime")); - } - - [TestMethod] - public void SetPackageDependency_AddsNew() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetPackageDependency( - "Microsoft.WindowsAppRuntime.1.5", - "5001.178.1908.0", - "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"); - - Assert.IsTrue(doc.HasPackageDependency("Microsoft.WindowsAppRuntime")); - var xml = doc.ToXml(); - Assert.Contains("Microsoft.WindowsAppRuntime.1.5", xml); - Assert.Contains("5001.178.1908.0", xml); - } - - [TestMethod] - public void SetPackageDependency_UpdatesExisting() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - - doc.SetPackageDependency("MyDep", "1.0.0.0", "CN=Pub"); - doc.SetPackageDependency("MyDep", "2.0.0.0", "CN=Pub"); - - var xml = doc.ToXml(); - Assert.Contains("MinVersion=\"2.0.0.0\"", xml); - // The old MinVersion should be gone (but 1.0.0.0 exists in Identity, so check specifically) - Assert.DoesNotContain("MinVersion=\"1.0.0.0\"", xml); - Assert.AreEqual(1, CountOccurrences(xml, "MyDep")); - } - - [TestMethod] - public void SetPackageDependency_CreatesDependenciesElement_WhenMissing() - { - // BareMinimalManifest has no Dependencies element - var xml = """ - - - - """; - var doc = AppxManifestDocument.Parse(xml); - Assert.IsNull(doc.GetDependenciesElement()); - - doc.SetPackageDependency("TestDep", "1.0.0.0", "CN=Test"); - - Assert.IsNotNull(doc.GetDependenciesElement()); - Assert.IsTrue(doc.HasPackageDependency("TestDep")); - } - - #endregion - - #region Element Accessors - - [TestMethod] - public void GetFirstApplicationElement_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - var app = doc.GetFirstApplicationElement(); - - Assert.IsNotNull(app); - Assert.AreEqual("App", app.Attribute("Id")?.Value); - } - - [TestMethod] - public void GetVisualElements_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - var ve = doc.GetVisualElements(); - - Assert.IsNotNull(ve); - Assert.AreEqual("Test App", ve.Attribute("DisplayName")?.Value); - } - - [TestMethod] - public void GetResourcesElement_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - Assert.IsNotNull(doc.GetResourcesElement()); - } - - [TestMethod] - public void GetDependenciesElement_ReturnsCorrectElement() - { - var doc = AppxManifestDocument.Parse(MinimalManifest); - Assert.IsNotNull(doc.GetDependenciesElement()); - } - - [TestMethod] - public void AllAccessors_ReturnNull_ForBareManifest() - { - var doc = AppxManifestDocument.Parse(BareMinimalManifest); - - Assert.IsNull(doc.GetFirstApplicationElement()); - Assert.IsNull(doc.GetVisualElements()); - Assert.IsNull(doc.GetResourcesElement()); - Assert.IsNull(doc.GetExtensionsElement()); - Assert.IsNull(doc.GetCapabilitiesElement()); - } - - #endregion - - #region Helpers - - private static int CountOccurrences(string text, string pattern) - { - int count = 0; - int index = 0; - while ((index = text.IndexOf(pattern, index, StringComparison.Ordinal)) != -1) - { - count++; - index += pattern.Length; - } - return count; - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs deleted file mode 100644 index 505ef5a4..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakeDebugOutputService.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -///

-/// Fake debug output service that records calls without actually attaching a debugger. -/// -internal class FakeDebugOutputService : IDebugOutputService -{ - public List AttachCalls { get; } = []; - public int FakeExitCode { get; set; } - - public Task RunDebugLoopAsync(uint processId, CancellationToken cancellationToken) - { - AttachCalls.Add(processId); - return Task.FromResult(FakeExitCode); - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs deleted file mode 100644 index 8e8c1fda..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -/// -/// Fake package registration service that records calls without actually -/// registering, unregistering, or installing MSIX packages. -/// -internal class FakePackageRegistrationService : IPackageRegistrationService -{ - public List RegisterLooseLayoutCalls { get; } = []; - public List<(string ManifestPath, string ExternalLocation)> RegisterSparseCalls { get; } = []; - public List UnregisterCalls { get; } = []; - public List InstallPackageCalls { get; } = []; - public List GetInstalledVersionCalls { get; } = []; - public List FindDevPackagesCalls { get; } = []; - - /// - /// When set, returns this value. - /// Defaults to false (no package found). - /// - public bool FakeUnregisterResult { get; set; } - - /// - /// When set, returns this value. - /// Defaults to null (package not installed). - /// - public string? FakeInstalledVersion { get; set; } - - /// - /// When set, returns these values. - /// Defaults to empty list. - /// - public List FakeDevPackages { get; set; } = []; - - public Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default) - { - RegisterLooseLayoutCalls.Add(manifestPath); - return Task.CompletedTask; - } - - public Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default) - { - RegisterSparseCalls.Add((manifestPath, externalLocation)); - return Task.CompletedTask; - } - - public Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) - { - UnregisterCalls.Add(packageName); - return Task.FromResult(FakeUnregisterResult); - } - - public Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default) - { - InstallPackageCalls.Add(packagePath); - return Task.CompletedTask; - } - - public string? GetInstalledVersion(string packageName) - { - GetInstalledVersionCalls.Add(packageName); - return FakeInstalledVersion; - } - - public List FindDevPackages(string packageName) - { - FindDevPackagesCalls.Add(packageName); - return FakeDevPackages; - } -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs deleted file mode 100644 index 19044314..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/IncrementalCopyHelperTests.cs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class IncrementalCopyHelperTests -{ - private DirectoryInfo _tempDir = null!; - - [TestInitialize] - public void Setup() - { - _tempDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"IncrementalCopyTest_{Guid.NewGuid():N}")); - _tempDir.Create(); - } - - [TestCleanup] - public void Cleanup() - { - if (_tempDir.Exists) - { - _tempDir.Delete(recursive: true); - } - } - - private DirectoryInfo CreateSubDir(string name) - { - var dir = new DirectoryInfo(Path.Combine(_tempDir.FullName, name)); - dir.Create(); - return dir; - } - - private static FileInfo WriteFile(DirectoryInfo dir, string relativePath, string content) - { - var path = Path.Combine(dir.FullName, relativePath); - var file = new FileInfo(path); - file.Directory?.Create(); - File.WriteAllText(path, content); - return file; - } - - #region SyncDirectory Tests - - [TestMethod] - public void SyncDirectory_FirstSync_CopiesAllFiles() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe-content"); - WriteFile(source, "app.dll", "dll-content"); - WriteFile(source, "sub\\lib.dll", "lib-content"); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(3, result.Copied); - Assert.AreEqual(0, result.Skipped); - Assert.AreEqual(0, result.Deleted); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.exe"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.dll"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "sub", "lib.dll"))); - } - - [TestMethod] - public void SyncDirectory_UnchangedFiles_AreSkipped() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe-content"); - WriteFile(source, "app.dll", "dll-content"); - - // First sync copies everything - IncrementalCopyHelper.SyncDirectory(source, dest); - - // Second sync should skip everything (no changes) - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(0, result.Copied); - Assert.AreEqual(2, result.Skipped); - Assert.AreEqual(0, result.Deleted); - } - - [TestMethod] - public void SyncDirectory_ModifiedFile_IsCopied() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "original"); - - // First sync - IncrementalCopyHelper.SyncDirectory(source, dest); - - // Modify the source file (different content = different size) - Thread.Sleep(50); // ensure timestamp differs - WriteFile(source, "app.exe", "modified-content-longer"); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(1, result.Copied); - Assert.AreEqual(0, result.Skipped); - Assert.AreEqual(0, result.Deleted); - Assert.AreEqual("modified-content-longer", File.ReadAllText(Path.Combine(dest.FullName, "app.exe"))); - } - - [TestMethod] - public void SyncDirectory_StaleFile_IsDeleted() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe"); - WriteFile(source, "old.dll", "old"); - - // First sync copies both - IncrementalCopyHelper.SyncDirectory(source, dest); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "old.dll"))); - - // Remove old.dll from source - File.Delete(Path.Combine(source.FullName, "old.dll")); - - // Second sync should delete old.dll from dest - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(0, result.Copied); - Assert.AreEqual(1, result.Skipped); - Assert.AreEqual(1, result.Deleted); - Assert.IsFalse(File.Exists(Path.Combine(dest.FullName, "old.dll"))); - } - - [TestMethod] - public void SyncDirectory_ProtectedFiles_AreNotDeleted() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "app.exe", "exe"); - - // First sync - IncrementalCopyHelper.SyncDirectory(source, dest); - - // Manually create protected files in dest that don't exist in source - WriteFile(dest, "appxmanifest.xml", ""); - WriteFile(dest, "resources.pri", "pri-data"); - WriteFile(dest, "stale.dll", "stale"); - - var protectedFiles = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "appxmanifest.xml", - "resources.pri" - }; - - var result = IncrementalCopyHelper.SyncDirectory(source, dest, protectedFiles); - - // stale.dll should be deleted, but protected files should survive - Assert.AreEqual(1, result.Deleted); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "appxmanifest.xml"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "resources.pri"))); - Assert.IsFalse(File.Exists(Path.Combine(dest.FullName, "stale.dll"))); - } - - [TestMethod] - public void SyncDirectory_NestedOutputInsideSource_IsExcluded() - { - var source = CreateSubDir("source"); - var dest = new DirectoryInfo(Path.Combine(source.FullName, "AppX")); - dest.Create(); - - WriteFile(source, "app.exe", "exe"); - // This file is inside the dest folder (nested inside source) - WriteFile(dest, "should-not-recurse.txt", "nested"); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - // Should only copy app.exe, not recurse into dest - Assert.AreEqual(1, result.Copied); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.exe"))); - } - - [TestMethod] - public void SyncDirectory_CreatesDestDirectory_IfNotExists() - { - var source = CreateSubDir("source"); - var dest = new DirectoryInfo(Path.Combine(_tempDir.FullName, "nonexistent_dest")); - WriteFile(source, "app.exe", "exe"); - - Assert.IsFalse(dest.Exists); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(1, result.Copied); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "app.exe"))); - } - - [TestMethod] - public void SyncDirectory_SubdirectoriesInSource_AreCopied() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - WriteFile(source, "root.dll", "root"); - WriteFile(source, "runtimes\\win-x64\\native\\lib.dll", "native-lib"); - WriteFile(source, "wwwroot\\index.html", ""); - - var result = IncrementalCopyHelper.SyncDirectory(source, dest); - - Assert.AreEqual(3, result.Copied); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "runtimes", "win-x64", "native", "lib.dll"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "wwwroot", "index.html"))); - } - - #endregion - - #region CopyFiles Tests - - [TestMethod] - public void CopyFiles_FirstCopy_CopiesAll() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - var file1 = WriteFile(source, "icon.png", "icon-data"); - var file2 = WriteFile(source, "assets\\logo.png", "logo-data"); - - var files = new List<(FileInfo, string)> - { - (file1, "icon.png"), - (file2, "assets\\logo.png"), - }; - - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(files, dest); - - Assert.AreEqual(2, copied); - Assert.AreEqual(0, skipped); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "icon.png"))); - Assert.IsTrue(File.Exists(Path.Combine(dest.FullName, "assets", "logo.png"))); - } - - [TestMethod] - public void CopyFiles_UnchangedFiles_AreSkipped() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - var file1 = WriteFile(source, "icon.png", "icon-data"); - - var files = new List<(FileInfo, string)> { (file1, "icon.png") }; - - // First copy - IncrementalCopyHelper.CopyFiles(files, dest); - - // Second copy should skip - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(files, dest); - - Assert.AreEqual(0, copied); - Assert.AreEqual(1, skipped); - } - - [TestMethod] - public void CopyFiles_ModifiedFile_IsCopied() - { - var source = CreateSubDir("source"); - var dest = CreateSubDir("dest"); - var file1 = WriteFile(source, "icon.png", "original"); - - var files = new List<(FileInfo, string)> { (file1, "icon.png") }; - - // First copy - IncrementalCopyHelper.CopyFiles(files, dest); - - // Modify the source - Thread.Sleep(50); - file1 = WriteFile(source, "icon.png", "modified-content-longer"); - files = [(file1, "icon.png")]; - - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(files, dest); - - Assert.AreEqual(1, copied); - Assert.AreEqual(0, skipped); - Assert.AreEqual("modified-content-longer", File.ReadAllText(Path.Combine(dest.FullName, "icon.png"))); - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs deleted file mode 100644 index aaf74eca..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/MrtAssetHelperTests.cs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class MrtAssetHelperTests -{ - private DirectoryInfo _tempDir = null!; - - [TestInitialize] - public void Setup() - { - _tempDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), $"MrtTest_{Guid.NewGuid():N}")); - _tempDir.Create(); - } - - [TestCleanup] - public void Cleanup() - { - if (_tempDir.Exists) - { - _tempDir.Delete(recursive: true); - } - } - - #region IsSingleQualifierToken - - [TestMethod] - [DataRow("scale-100", DisplayName = "scale-100")] - [DataRow("scale-200", DisplayName = "scale-200")] - [DataRow("scale-400", DisplayName = "scale-400")] - [DataRow("theme-dark", DisplayName = "theme-dark")] - [DataRow("theme-light", DisplayName = "theme-light")] - [DataRow("contrast-standard", DisplayName = "contrast-standard")] - [DataRow("contrast-high", DisplayName = "contrast-high")] - [DataRow("targetsize-16", DisplayName = "targetsize-16")] - [DataRow("targetsize-256", DisplayName = "targetsize-256")] - [DataRow("altform-unplated", DisplayName = "altform-unplated")] - [DataRow("altform-lightunplated", DisplayName = "altform-lightunplated")] - [DataRow("dxfeaturelevel-9", DisplayName = "dxfeaturelevel-9")] - [DataRow("dxfeaturelevel-11", DisplayName = "dxfeaturelevel-11")] - [DataRow("device-family-desktop", DisplayName = "device-family-desktop")] - [DataRow("device-family-xbox", DisplayName = "device-family-xbox")] - [DataRow("homeregion-US", DisplayName = "homeregion-US")] - [DataRow("homeregion-JP", DisplayName = "homeregion-JP")] - [DataRow("configuration-debug", DisplayName = "configuration-debug")] - [DataRow("configuration-retail", DisplayName = "configuration-retail")] - [DataRow("en-US", DisplayName = "en-US (language)")] - [DataRow("fr", DisplayName = "fr (language)")] - [DataRow("zh-Hans", DisplayName = "zh-Hans (language)")] - [DataRow("pt-BR", DisplayName = "pt-BR (language)")] - [DataRow("ltr", DisplayName = "ltr (layout direction)")] - [DataRow("rtl", DisplayName = "rtl (layout direction)")] - public void IsSingleQualifierToken_ReturnsTrue_ForValidTokens(string token) - { - Assert.IsTrue(MrtAssetHelper.IsSingleQualifierToken(token)); - } - - [TestMethod] - [DataRow("", DisplayName = "empty string")] - [DataRow("Logo", DisplayName = "plain name")] - [DataRow("foo-barbaz1234", DisplayName = "arbitrary hyphenated (subtag too long)")] - [DataRow("scale-abc", DisplayName = "scale with non-numeric")] - [DataRow("theme-blue", DisplayName = "invalid theme")] - [DataRow("contrast-low", DisplayName = "invalid contrast")] - [DataRow("device-family-phone", DisplayName = "invalid device family")] - public void IsSingleQualifierToken_ReturnsFalse_ForInvalidTokens(string token) - { - Assert.IsFalse(MrtAssetHelper.IsSingleQualifierToken(token)); - } - - [TestMethod] - public void IsSingleQualifierToken_ReturnsFalse_ForNull() - { - Assert.IsFalse(MrtAssetHelper.IsSingleQualifierToken(null!)); - } - - #endregion - - #region IsQualifierToken (compound) - - [TestMethod] - [DataRow("scale-200", DisplayName = "single qualifier")] - [DataRow("scale-200_theme-dark", DisplayName = "compound: scale + theme")] - [DataRow("targetsize-24_altform-unplated", DisplayName = "compound: targetsize + altform")] - [DataRow("en-US", DisplayName = "language alone")] - public void IsQualifierToken_ReturnsTrue_ForValidTokens(string token) - { - Assert.IsTrue(MrtAssetHelper.IsQualifierToken(token)); - } - - [TestMethod] - [DataRow("", DisplayName = "empty string")] - [DataRow("Logo", DisplayName = "plain name")] - [DataRow("scale-200_Logo", DisplayName = "qualifier + non-qualifier")] - [DataRow("Logo_scale-200", DisplayName = "non-qualifier + qualifier")] - public void IsQualifierToken_ReturnsFalse_ForInvalidTokens(string token) - { - Assert.IsFalse(MrtAssetHelper.IsQualifierToken(token)); - } - - #endregion - - #region IsMrtVariantName - - [TestMethod] - public void IsMrtVariantName_ExactMatch_ReturnsTrue() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo")); - } - - [TestMethod] - public void IsMrtVariantName_CaseInsensitive() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "logo")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("logo", "Logo")); - } - - [TestMethod] - public void IsMrtVariantName_WithSingleQualifier() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.scale-200")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.targetsize-48")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.theme-dark")); - } - - [TestMethod] - public void IsMrtVariantName_WithMultipleQualifiers() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.scale-200.theme-dark")); - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.targetsize-48.altform-unplated")); - } - - [TestMethod] - public void IsMrtVariantName_WithCompoundQualifier() - { - Assert.IsTrue(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.targetsize-24_altform-unplated")); - } - - [TestMethod] - public void IsMrtVariantName_ReturnsFalse_ForNonQualifierSuffix() - { - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "Logo.99invalid")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "LogoExtra")); - } - - [TestMethod] - public void IsMrtVariantName_ReturnsFalse_ForDifferentBaseName() - { - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "Icon.scale-200")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "Banner")); - } - - [TestMethod] - public void IsMrtVariantName_ReturnsFalse_ForEmptyInputs() - { - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("", "Logo")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", "")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("", "")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName(null!, "Logo")); - Assert.IsFalse(MrtAssetHelper.IsMrtVariantName("Logo", null!)); - } - - #endregion - - #region GetMrtVariantBaseName - - [TestMethod] - public void GetMrtVariantBaseName_UnqualifiedName_ReturnsSame() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo")); - } - - [TestMethod] - public void GetMrtVariantBaseName_SingleQualifier_ReturnsBase() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.scale-100")); - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.targetsize-48")); - } - - [TestMethod] - public void GetMrtVariantBaseName_MultipleQualifiers_ReturnsBase() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.scale-200.theme-dark")); - } - - [TestMethod] - public void GetMrtVariantBaseName_CompoundQualifier_ReturnsBase() - { - Assert.AreEqual("Logo", MrtAssetHelper.GetMrtVariantBaseName("Logo.targetsize-24_altform-unplated")); - } - - [TestMethod] - public void GetMrtVariantBaseName_DottedBaseName_PreservesNonQualifierDots() - { - // "Assets.Logo.scale-200" → base should be "Assets.Logo" - Assert.AreEqual("Assets.Logo", MrtAssetHelper.GetMrtVariantBaseName("Assets.Logo.scale-200")); - } - - [TestMethod] - public void GetMrtVariantBaseName_NoQualifiers_ReturnsOriginal() - { - Assert.AreEqual("SomeFile.backup", MrtAssetHelper.GetMrtVariantBaseName("SomeFile.backup")); - } - - [TestMethod] - public void GetMrtVariantBaseName_ThrowsForNull() - { - Assert.ThrowsExactly(() => MrtAssetHelper.GetMrtVariantBaseName(null!)); - } - - [TestMethod] - public void GetMrtVariantBaseName_ThrowsForEmpty() - { - Assert.ThrowsExactly(() => MrtAssetHelper.GetMrtVariantBaseName("")); - } - - [TestMethod] - public void GetMrtVariantBaseName_ThrowsForWhitespace() - { - Assert.ThrowsExactly(() => MrtAssetHelper.GetMrtVariantBaseName(" ")); - } - - #endregion - - #region ExpandManifestReferencedFiles - - [TestMethod] - public void ExpandManifestReferencedFiles_FindsMrtVariants() - { - // Create files: Logo.png, Logo.scale-100.png, Logo.scale-200.png - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-100.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png"], - taskContext: null); - - Assert.AreEqual(3, result.Count); - Assert.IsTrue(result.Any(f => f.RelativePath == "Logo.png")); - Assert.IsTrue(result.Any(f => f.RelativePath == "Logo.scale-100.png")); - Assert.IsTrue(result.Any(f => f.RelativePath == "Logo.scale-200.png")); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_FallsBackToExactFile_WhenNoVariants() - { - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Icon.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Icon.png"], - taskContext: null); - - Assert.AreEqual(1, result.Count); - Assert.AreEqual("Icon.png", result[0].RelativePath); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_MissingDirectory_ReturnsEmpty() - { - // Reference a file in a non-existent subdirectory - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["nonexistent\\Logo.png"], - taskContext: null); - - Assert.AreEqual(0, result.Count); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_MissingFile_NoVariants_ReturnsEmpty() - { - // No files exist at all - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Missing.png"], - taskContext: null); - - Assert.AreEqual(0, result.Count); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_ExcludesNonVariantFiles() - { - // Logo.png, Logo.backup.png (not a qualifier), Logo.scale-200.png - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.backup.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png"], - taskContext: null); - - // Should include Logo.png and Logo.scale-200.png, but NOT Logo.backup.png - Assert.AreEqual(2, result.Count); - Assert.IsFalse(result.Any(f => f.RelativePath.Contains("backup"))); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_HandlesSubdirectories() - { - var assetsDir = Directory.CreateDirectory(Path.Combine(_tempDir.FullName, "Assets")); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(assetsDir.FullName, "Logo.scale-200.png"), [0]); - - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Assets\\Logo.png"], - taskContext: null); - - Assert.AreEqual(2, result.Count); - Assert.IsTrue(result.All(f => f.RelativePath.StartsWith("Assets\\", StringComparison.Ordinal))); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_WithIncludeFilter() - { - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-100.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - // Only include files that end with scale-200 - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png"], - taskContext: null, - includeFile: f => f.Name.Contains("scale-200")); - - Assert.AreEqual(1, result.Count); - Assert.AreEqual("Logo.scale-200.png", result[0].RelativePath); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_EmptyReferencedFiles_ReturnsEmpty() - { - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - [], - taskContext: null); - - Assert.AreEqual(0, result.Count); - } - - [TestMethod] - public void ExpandManifestReferencedFiles_DeduplicatesAcrossReferences() - { - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.png"), [0]); - File.WriteAllBytes(Path.Combine(_tempDir.FullName, "Logo.scale-200.png"), [0]); - - // Reference the same logical file twice - var result = MrtAssetHelper.ExpandManifestReferencedFiles( - _tempDir, - ["Logo.png", "Logo.png"], - taskContext: null); - - // Dictionary-based dedup should prevent duplicates - Assert.AreEqual(2, result.Count); // Logo.png + Logo.scale-200.png - } - - #endregion - - #region PriIncludedExtensions - - [TestMethod] - [DataRow(".png")] - [DataRow(".jpg")] - [DataRow(".jpeg")] - [DataRow(".gif")] - [DataRow(".bmp")] - [DataRow(".ico")] - [DataRow(".svg")] - public void PriIncludedExtensions_ContainsExpected(string ext) - { - Assert.IsTrue(MrtAssetHelper.PriIncludedExtensions.Contains(ext)); - } - - [TestMethod] - [DataRow(".exe")] - [DataRow(".dll")] - [DataRow(".xml")] - [DataRow(".txt")] - public void PriIncludedExtensions_ExcludesNonImage(string ext) - { - Assert.IsFalse(MrtAssetHelper.PriIncludedExtensions.Contains(ext)); - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs deleted file mode 100644 index 21aeca83..00000000 --- a/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.DependencyInjection; -using WinApp.Cli.Commands; -using WinApp.Cli.Services; - -namespace WinApp.Cli.Tests; - -[TestClass] -public class UnregisterCommandTests : BaseCommandTests -{ - private FakePackageRegistrationService _fakePackageRegistrationService = null!; - - private const string TestManifestContent = """ - - - - - Test Package - Test Publisher - Test package - Assets\Logo.png - - - - - - - - - - - - - - """; - - protected override IServiceCollection ConfigureServices(IServiceCollection services) - { - _fakePackageRegistrationService = new FakePackageRegistrationService(); - return services - .AddSingleton(_fakePackageRegistrationService); - } - - private async Task CreateTestManifestAsync(string? directory = null) - { - directory ??= _tempDirectory.FullName; - var manifestPath = Path.Combine(directory, "appxmanifest.xml"); - await File.WriteAllTextAsync(manifestPath, TestManifestContent, TestContext.CancellationToken); - return new FileInfo(manifestPath); - } - - [TestMethod] - public async Task UnregisterCommand_WithManifest_UnregistersDevPackages() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - _tempDirectory.FullName, IsDevelopmentMode: true) - ]; - _fakePackageRegistrationService.FakeUnregisterResult = true; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage")); - Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Contains("TestPackage")); - } - - [TestMethod] - public async Task UnregisterCommand_ChecksBothNameAndDebugVariant() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - _fakePackageRegistrationService.FakeDevPackages = []; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage")); - Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage.debug")); - } - - [TestMethod] - public async Task UnregisterCommand_SkipsNonDevModePackages() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - _tempDirectory.FullName, IsDevelopmentMode: false) - ]; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.AreEqual(0, _fakePackageRegistrationService.UnregisterCalls.Count); - } - - [TestMethod] - public async Task UnregisterCommand_SkipsPackagesFromDifferentTree() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - @"C:\OtherProject\bin\Debug\AppX", IsDevelopmentMode: true) - ]; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.AreEqual(0, _fakePackageRegistrationService.UnregisterCalls.Count); - } - - [TestMethod] - public async Task UnregisterCommand_WithForce_SkipsLocationCheck() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - @"C:\OtherProject\bin\Debug\AppX", IsDevelopmentMode: true) - ]; - _fakePackageRegistrationService.FakeUnregisterResult = true; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName, "--force"]); - - // Assert - Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Contains("TestPackage")); - } - - [TestMethod] - public async Task UnregisterCommand_WithJson_ReturnsJsonOutput() - { - // Arrange - var manifest = await CreateTestManifestAsync(); - var command = GetRequiredService(); - - _fakePackageRegistrationService.FakeDevPackages = - [ - new DevPackageInfo("TestPackage_1.0.0.0_x64__abc123", "TestPackage", "1.0.0.0", - _tempDirectory.FullName, IsDevelopmentMode: true) - ]; - _fakePackageRegistrationService.FakeUnregisterResult = true; - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, ["--manifest", manifest.FullName, "--json"]); - - // Assert - Assert.AreEqual(0, exitCode); - var output = TestAnsiConsole.Output; - Assert.IsTrue(output.Contains("TestPackage_1.0.0.0_x64__abc123")); - } - - [TestMethod] - public async Task UnregisterCommand_NoManifest_ReturnsError() - { - // Arrange — empty temp directory with no manifest - var emptyDir = new DirectoryInfo(Path.Combine(_tempDirectory.FullName, "empty")); - emptyDir.Create(); - var command = GetRequiredService(); - - // Act - var exitCode = await ParseAndInvokeWithCaptureAsync(command, []); - - // Assert - Assert.AreEqual(1, exitCode); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/AppList.png deleted file mode 100644 index 6135405f8f13c518341e3995b41dfc0fbf86e5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^Iv~u#3?$#IayJ4}iUB?$uI>ds`|$#+%Dw(T#oQ%9 ze!&dV&*qE#sjjJ9ekxDB{uogBq^FBxh{pM;ldbs<8Su0%Z(C}Uo!o}p|QcIwa_ zzr75`QswsiYB9gAFEbIg{Dl><3nv3A}sm=EZ)#l3`NR zpZda3^rNox*D1%NC98Z~L*6zipLw~Gxn&(Y-;KmJ+aR6eLabU-L#y8HW%7P-E_-VlLqIabbHPHKT*)fT@9iWJ7iWgOT9%0}Lrj>lztPxWq6sPw3pi z#-<=#$jjrP_DD*i!RLsn0mIA=>4~N)IMYWIf=j%-zuKCdMG%tHYot70D1| zvWa0wMhauW#S>1CnI_;>!1Q3zMA17@DOVq{MQ+{U7^a&yA+%dMCG;WNPV0i;w$tu; zX^b}UKziPM)(<;)ruW;-`)bBN+rQNM*Zs_>?n$|FVFo-e*PZb*@U7VAd+tHb4e?=Blc~}S6K)wL}r*Gf`BM#QB z+y>N$mCswb4d{^{S9v_!eQj4fTRMOwOCi?lSk9%<=vAz}jM-*PQtH@Odn1LZcd^j#o> hW$4xn+CT+ep9lJ{OAO?njobhL002ovPDHLkV1nYebbkN< diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/MedTile.png deleted file mode 100644 index 9c81c0fc0fe12b924da8d6319b04b7957a6e3b0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 733 zcmV<30wVp1P)-A$+fw5jbX5||j z)CE?yu|Zs5 z$xohSNqNeHr&&Ux@`t2Zai;d7I9O4t_5wRtVXpfAdRRfS`dxWg%WVC7cd-`f`a0xd z&GU_A$j6!ijOEeCnh4gi?PQGzYddkW28Owtcv(Hf+~vHij`C*izHU})?A5Fa+^qg| zZWRT7R@XJKiWNVrH)a%38LZZrRowj%3w%cMw+G(|@?8bLA@bV>*Bo*!1lMSC?*R8S za<2tz0J1g#YbLUm18YpO_62Kl@~i-!VaT%;c;+L|qTm^sJiCKE1+v!x_F%}~5ZJRK zdud>gkL*2yJyEh(3-*x7-Zt2CCuafRjDnnvyv%aSq0 z)njao1dV0XNw&c@qmj1e*jgQ$l@_urW5G4RSY#rT1z`#%3;{EB`aJK|TH^lb_3nAT z-_Q4X-(K&IS8UyqsnjYdippfmN-HT!X2MT;Dpcy~-#$k6V z|MR4vU#O&p7TC46pTflb3 zoUJ;ZRf#&8&EwXy5s%!&(q6cN62swD#FH%O-RJsjWPZN3^^@FCIQ&MxXIFo7!I#VI zkpIstuWqUV5uhgs07?k$*!`uiZ=5b#$lI|0c+XJvj(}zSE3MN#EyOK zql(#yA}~Ibl*r(s1}Z^5mmn*-n93g?-ccM+^PN?6HH~h0hjy6@XY*^i<-V)+OZ;p7 z7j`p_sT55xnYsedNIIel^QIIg7i@`2Qi}x5$!tk29$2OQI zs^kQXAKE}5ZJu$)2@Dxn?}}O@f@6@^!%9Tj+o>=jd!^ZuvBE4jb4g}Z5WMBtcmy^~ zoFGVS5|0FA!(1Q%fL?Bj*L+9ZL{mjSO8lzqrQ0UCZ)X zPwk$1HNFgaK%NxGpuXz}#ywXvf2JQ?BQ5uOZM2up4S#ieaxS$!o9o6Z=czNQb} zwAh|xLZ>+WyN%o?^uCAQw&&4o?S$DJ`WP(Hr*grL*qNXlqU0osCQ(Up5F(^$Z5;n&oJIO4uF`k&QL*j{f zU=;#MZ5{@b%qMbjTB3dh-5#mqY>%{0jgS+WdHyG diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.png deleted file mode 100644 index 49a431d6c94028d821bd2b027ac0d1917b38f078..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806 zcmV+>1KIqEP)`#j%umtt)f(_Qx@)^@hpb;sJa4{hxKS}ERE zEv}h+ly_S)f4r7=+x)z;emj|q|KCb3T*D^XR^2h-YPOS7w(aJH2e)Ob7Q$^;?<}~6 z?Mu4thy_=(Jse866&$#RZ6)8vfos?b`8Ebz%Z6x6!?kRPwj^A~25C#db!?Ef1YFOC zX^X@4Y?!tv+`tBEi@^C^(A^*hawFY{)hr&Spcld2kLJw9SQc*r06=oXdu7v*BDeY?}q=v4Pu6IFAk7 zX2AJu=(Z8gXG6CQa0DB?t%oDn;B6fo#fEQd;V3qITLbq&v1QO!!#z`M8MM`K8;UK5 zwi=FN%b~4-`(9<)Hd$}mPK!L-=7G0d8ML);u9i8r)ofD|4&PQ^67I=EKD@2Hl9EqH z;BD=dlzcjZZtHJAC^&Rme*;3nf!oIWHUbXZHr}@paM(8UcGri)wwbrPJ{+{oJ|%c? z&^G&&;K3o=+{2X%hir2XS1uf|%|DYmaKJYIOzObl+Nh(v4To!^j`B7ftc`pEV#C4O z$R{8+9IB0Gv*A!}G@A_vY74PhaG8n{aQTOJl$_m)e#jmOr#9u#ilemgcRGydjIzg5(>zb)r)iVrgW7F(y& k>2x}sPN&o9bh>}uFDMS%SxTob*#H0l07*qoM6N<$f=8c%l>h($ diff --git a/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.scale-200.png b/src/winapp-CLI/WinApp.Cli/Assets/msix_default_assets/WideTile.scale-200.png deleted file mode 100644 index 8b4a5d0dd5f6c6ab408e1edf04a07888859a9eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2097 zcmc&$ZA@EL7(Q+3r2+3&!t4k)FxZ4;Vj0QoMdu<_3Kciz7YR4=A(qT^w)Jxi;?x!9 zuyq6dMF?b>c0sq%A~kJcD9FY~qQRMt?ZR3YyDZt}Od;|mgpc{2dv9AHF){kXU%k({ z=Y8JidEayHTkG@twPZ|U3_^%3ct-OgLSiFAqDN!|tbCX@c@?4P`2x*TMK!+Q4b?k0 ziW7!!KF6dPWcF<%I|iznM~`QJ_V7sHGV_D`dhgpA9Vd@&X}ErK+j~_rdv;Bp?OA@a zFXOk7eWOJe5NcK;6h$FaM&7JxNc#-@QTwzW6x#d_zmQNkz5) zPI;kh;3d;5UCJU+9a(cOxX(|edWoOiAEdGU#kPJ&xnc2||3vDbuhBCkj-pb0as$Zl z5;}4n=**n6(1g`JEtSy;SG6X;#-F~Oz3lESG2b5`j@wAwY4Yp<=4Xeb>iH=6aicF?DxD&q{`!&}ct zBI)aycwuobQAf&678Uf+Mmh-@9RUhyH~>?w0dixO0#jZjEc9R^=5NZw=|a(kcB?9^ zfnTiEFXp-q#B;Tn>(O%$A*ud^Rg&eVH6Y_5Y%!E39RR&s?XpG`gKwU!6FE1 z7X)DC7)*(5g}lh`4`{i~DZcWupZI`K)_4P)VE{@gc7@Xsd^86zl~_mOYH?I4!aGeX z^E(_=L6?PgveDQ+r%P@UISEXrkn`LHJZ##+!-anV>6h)IkKp;E@p8+3&(5%kS2)ld*J*rJccZM0iyaAx7+F~GW1UWFK&3X$PE1^}NH zgAG9ck5K!{07OwU@j@Do>TbH=CDEo#4m0cEyAuXy_<&jlzJVcKweSJ5 z&=q~iIn18$w8yb=rmEmHxVEUA^?RwnB?6Qlp1os8@*dWTGL2bhzZ!s*xqScR?EPL` zo(JwNdKUUYy7GtvZ3asXm)cgFvCx9EmAi;|w=a0iGiv%%VYKh`P0Wma4y`Xyx|T~( zAmfGbgbEEC7)j8b@WA@+5W3a61HJXC1dX@6_T|Czk0I0zBk%tnW~()VWITGI!`$c< gARL?UBrYYkwoDw4eo*CrzXGTrZ@;GF>596)00d&n@&Et; diff --git a/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs deleted file mode 100644 index a9817cee..00000000 --- a/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.Logging; -using Spectre.Console; -using System.CommandLine; -using System.CommandLine.Invocation; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using WinApp.Cli.Helpers; -using WinApp.Cli.Services; - -namespace WinApp.Cli.Commands; - -internal class UnregisterCommand : Command, IShortDescription -{ - public string ShortDescription => "Unregister a sideloaded development package."; - - public static Option ManifestOption { get; } - public static Option ForceOption { get; } - - static UnregisterCommand() - { - ManifestOption = new Option("--manifest") - { - Description = "Path to the appxmanifest.xml (default: auto-detect from current directory)" - }; - ManifestOption.AcceptExistingOnly(); - - ForceOption = new Option("--force") - { - Description = "Skip the install-location directory check and unregister even if the package was registered from a different project tree" - }; - } - - public UnregisterCommand() : base("unregister", "Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity').") - { - Options.Add(ManifestOption); - Options.Add(ForceOption); - Options.Add(WinAppRootCommand.JsonOption); - } - - public class Handler( - IPackageRegistrationService packageRegistrationService, - ICurrentDirectoryProvider currentDirectoryProvider, - IAnsiConsole ansiConsole, - ILogger logger) : AsynchronousCommandLineAction - { - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) - { - var manifest = parseResult.GetValue(ManifestOption); - var force = parseResult.GetValue(ForceOption); - var isJson = parseResult.GetValue(WinAppRootCommand.JsonOption); - - // Resolve manifest - FileInfo resolvedManifest; - if (manifest != null && manifest.Exists) - { - resolvedManifest = manifest; - } - else - { - resolvedManifest = ManifestHelper.FindManifest(currentDirectoryProvider.GetCurrentDirectory()); - if (!resolvedManifest.Exists) - { - var message = "No manifest found in the current directory. Use --manifest to specify the path."; - if (isJson) - { - PrintJson([], [], message); - } - else - { - logger.LogError("{UISymbol} {Message}", UiSymbols.Error, message); - } - return 1; - } - } - - // Parse package name from manifest - var manifestContent = await File.ReadAllTextAsync(resolvedManifest.FullName, Encoding.UTF8, cancellationToken); - var identity = MsixService.ParseAppxManifestAsync(manifestContent); - var packageName = identity.PackageName; - - // Search for both the exact name and the .debug variant - var namesToCheck = new[] { packageName, $"{packageName}.debug" }; - var cwd = Path.GetFullPath(currentDirectoryProvider.GetCurrentDirectory()); - - var unregistered = new List(); - var skipped = new List(); - - foreach (var name in namesToCheck) - { - var packages = packageRegistrationService.FindDevPackages(name); - - foreach (var pkg in packages) - { - if (!pkg.IsDevelopmentMode) - { - if (!isJson) - { - logger.LogInformation("{UISymbol} {FullName}: installed via MSIX/Store, skipping.", UiSymbols.Note, pkg.FullName); - } - skipped.Add(pkg.FullName); - continue; - } - - // Check install location is under current directory tree - if (!force && !string.IsNullOrEmpty(pkg.InstallLocation)) - { - var installPath = Path.GetFullPath(pkg.InstallLocation); - if (!installPath.StartsWith(cwd, StringComparison.OrdinalIgnoreCase)) - { - if (!isJson) - { - logger.LogWarning("{UISymbol} {FullName}: registered from a different project tree ({Location}). Use --force to override.", - UiSymbols.Warning, pkg.FullName, pkg.InstallLocation); - } - skipped.Add(pkg.FullName); - continue; - } - } - - // Unregister - await packageRegistrationService.UnregisterAsync(name, cancellationToken); - - if (!isJson) - { - ansiConsole.MarkupLineInterpolated($"{UiSymbols.Check} Unregistered {pkg.FullName}"); - } - unregistered.Add(pkg.FullName); - } - } - - if (isJson) - { - PrintJson(unregistered, skipped, errorMessage: null); - } - else if (unregistered.Count == 0 && skipped.Count == 0) - { - logger.LogInformation("{UISymbol} No dev-registered package found for '{PackageName}'.", UiSymbols.Note, packageName); - } - - return 0; - } - - private void PrintJson(List unregistered, List skipped, string? errorMessage) - { - var result = new UnregisterResult - { - Unregistered = unregistered.Count > 0 ? unregistered : null, - Skipped = skipped.Count > 0 ? skipped : null, - Error = errorMessage - }; - - var json = JsonSerializer.Serialize(result, UnregisterJsonContext.Default.UnregisterResult); - ansiConsole.WriteLine(json); - } - } -} - -internal sealed class UnregisterResult -{ - public List? Unregistered { get; set; } - public List? Skipped { get; set; } - public string? Error { get; set; } -} - -[JsonSerializable(typeof(UnregisterResult))] -[JsonSourceGenerationOptions( - WriteIndented = true, - NewLine = "\n", - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] -internal partial class UnregisterJsonContext : JsonSerializerContext; diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs b/src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs deleted file mode 100644 index 15919bda..00000000 --- a/src/winapp-CLI/WinApp.Cli/Helpers/ManifestHelper.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Helpers; - -/// -/// Shared helper for locating appxmanifest.xml files. -/// -internal static class ManifestHelper -{ - private static readonly string[] ManifestNames = ["appxmanifest.xml", "Package.appxmanifest"]; - - /// - /// Finds an appxmanifest file in the specified directory. - /// Checks for appxmanifest.xml first, then Package.appxmanifest. - /// - /// A for the manifest. Check before using. - public static FileInfo FindManifest(string directory) - { - foreach (var name in ManifestNames) - { - var path = Path.Combine(directory, name); - if (File.Exists(path)) - { - return new FileInfo(path); - } - } - - // Return a non-existent FileInfo for the primary name so callers can check .Exists - return new FileInfo(Path.Combine(directory, ManifestNames[0])); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs b/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs deleted file mode 100644 index 90f48784..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Text; -using System.Xml; -using System.Xml.Linq; - -namespace WinApp.Cli.Services; - -/// -/// XDocument-based wrapper for reading and manipulating AppxManifest.xml files. -/// This is a pure data class with no DI dependencies. -/// -internal class AppxManifestDocument -{ - // AppxManifest XML namespaces - public static readonly XNamespace DefaultNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10"; - public static readonly XNamespace UapNs = "http://schemas.microsoft.com/appx/manifest/uap/windows10"; - public static readonly XNamespace Uap5Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/5"; - public static readonly XNamespace Uap10Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/10"; - public static readonly XNamespace RescapNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"; - public static readonly XNamespace BuildNs = "http://schemas.microsoft.com/developer/appx/2015/build"; - public static readonly XNamespace DesktopNs = "http://schemas.microsoft.com/appx/manifest/desktop/windows10"; - public static readonly XNamespace Desktop6Ns = "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"; - - private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false); - - private readonly XDocument _document; - - private AppxManifestDocument(XDocument document) - { - _document = document ?? throw new ArgumentNullException(nameof(document)); - } - - /// - /// Direct access to the underlying XDocument for advanced operations. - /// - public XDocument Document => _document; - - #region Static Factory Methods - - /// - /// Loads an AppxManifest from a file path. - /// - public static AppxManifestDocument Load(string path) - { - var doc = XDocument.Load(path); - return new AppxManifestDocument(doc); - } - - /// - /// Loads an AppxManifest from a stream. - /// - public static AppxManifestDocument Load(Stream stream) - { - var doc = XDocument.Load(stream); - return new AppxManifestDocument(doc); - } - - /// - /// Parses an AppxManifest from an XML string. - /// - public static AppxManifestDocument Parse(string xml) - { - var doc = XDocument.Parse(xml); - return new AppxManifestDocument(doc); - } - - #endregion - - #region Save / Serialize - - /// - /// Saves the manifest to a file with UTF-8 (no BOM) encoding. - /// - public void Save(string path) - { - var settings = new XmlWriterSettings - { - Indent = true, - IndentChars = " ", - Encoding = Utf8NoBom, - OmitXmlDeclaration = _document.Declaration == null, - }; - - using var memoryStream = new MemoryStream(); - using (var writer = XmlWriter.Create(memoryStream, settings)) - { - _document.Save(writer); - } - - File.WriteAllBytes(path, memoryStream.ToArray()); - } - - /// - /// Serializes the manifest to an XML string. - /// - public string ToXml() - { - var settings = new XmlWriterSettings - { - Indent = true, - IndentChars = " ", - Encoding = Utf8NoBom, - OmitXmlDeclaration = _document.Declaration == null, - }; - - using var memoryStream = new MemoryStream(); - using (var writer = XmlWriter.Create(memoryStream, settings)) - { - _document.Save(writer); - } - - return Utf8NoBom.GetString(memoryStream.ToArray()); - } - - #endregion - - #region Element Accessors - - /// - /// Gets the Identity element. - /// - public XElement? GetIdentityElement() => - _document.Root?.Element(DefaultNs + "Identity"); - - /// - /// Gets the first Application element. - /// - public XElement? GetFirstApplicationElement() => - _document.Root?.Element(DefaultNs + "Applications")?.Element(DefaultNs + "Application"); - - /// - /// Gets the uap:VisualElements element from the first Application. - /// - public XElement? GetVisualElements() => - GetFirstApplicationElement()?.Element(UapNs + "VisualElements"); - - /// - /// Gets the Resources element. - /// - public XElement? GetResourcesElement() => - _document.Root?.Element(DefaultNs + "Resources"); - - /// - /// Gets the Dependencies element. - /// - public XElement? GetDependenciesElement() => - _document.Root?.Element(DefaultNs + "Dependencies"); - - /// - /// Gets the package-level Extensions element (child of Package, after Applications). - /// - public XElement? GetExtensionsElement() => - _document.Root?.Element(DefaultNs + "Extensions"); - - /// - /// Gets the Capabilities element. - /// - public XElement? GetCapabilitiesElement() => - _document.Root?.Element(DefaultNs + "Capabilities"); - - #endregion - - #region Identity Properties - - /// - /// Gets or sets the Identity Name attribute. - /// - public string? IdentityName - { - get => GetIdentityElement()?.Attribute("Name")?.Value; - set => SetIdentityAttribute("Name", value); - } - - /// - /// Gets or sets the Identity Publisher attribute. - /// - public string? IdentityPublisher - { - get => GetIdentityElement()?.Attribute("Publisher")?.Value; - set => SetIdentityAttribute("Publisher", value); - } - - /// - /// Gets or sets the Identity Version attribute. - /// - public string? IdentityVersion - { - get => GetIdentityElement()?.Attribute("Version")?.Value; - set => SetIdentityAttribute("Version", value); - } - - /// - /// Gets or sets the Identity ProcessorArchitecture attribute. - /// - public string? IdentityProcessorArchitecture - { - get => GetIdentityElement()?.Attribute("ProcessorArchitecture")?.Value; - set => SetIdentityAttribute("ProcessorArchitecture", value); - } - - private void SetIdentityAttribute(string attributeName, string? value) - { - var identity = GetIdentityElement(); - if (identity == null) - { - if (value == null) - { - return; - } - - identity = new XElement(DefaultNs + "Identity"); - _document.Root?.AddFirst(identity); - } - - if (value == null) - { - identity.Attribute(attributeName)?.Remove(); - } - else - { - identity.SetAttributeValue(attributeName, value); - } - } - - #endregion - - #region Application Properties - - /// - /// Gets or sets the first Application's Id attribute. - /// - public string? ApplicationId - { - get => GetFirstApplicationElement()?.Attribute("Id")?.Value; - set => SetApplicationAttribute("Id", value); - } - - /// - /// Gets or sets the first Application's Executable attribute. - /// - public string? ApplicationExecutable - { - get => GetFirstApplicationElement()?.Attribute("Executable")?.Value; - set => SetApplicationAttribute("Executable", value); - } - - /// - /// Gets or sets the first Application's EntryPoint attribute. - /// - public string? ApplicationEntryPoint - { - get => GetFirstApplicationElement()?.Attribute("EntryPoint")?.Value; - set => SetApplicationAttribute("EntryPoint", value); - } - - private void SetApplicationAttribute(string attributeName, string? value) - { - var app = GetFirstApplicationElement(); - if (app == null) - { - return; - } - - if (value == null) - { - app.Attribute(attributeName)?.Remove(); - } - else - { - app.SetAttributeValue(attributeName, value); - } - } - - #endregion - - #region VisualElements Properties - - /// - /// Gets or sets the uap:VisualElements DisplayName attribute. - /// - public string? VisualElementsDisplayName - { - get => GetVisualElements()?.Attribute("DisplayName")?.Value; - set - { - var ve = GetVisualElements(); - if (ve == null) - { - return; - } - - if (value == null) - { - ve.Attribute("DisplayName")?.Remove(); - } - else - { - ve.SetAttributeValue("DisplayName", value); - } - } - } - - #endregion - - #region Resource Languages - - /// - /// Extracts all Resource Language values. - /// - public List GetResourceLanguages() - { - var resources = GetResourcesElement(); - if (resources == null) - { - return []; - } - - return resources.Elements(DefaultNs + "Resource") - .Select(r => r.Attribute("Language")?.Value) - .Where(lang => lang != null) - .Cast() - .ToList(); - } - - /// - /// Replaces the Resources block with the given languages. - /// - public void SetResourceLanguages(IList languages) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var resources = GetResourcesElement(); - if (resources == null) - { - resources = new XElement(DefaultNs + "Resources"); - // Insert after Dependencies if present, otherwise after Identity - var dependencies = GetDependenciesElement(); - if (dependencies != null) - { - dependencies.AddAfterSelf(resources); - } - else - { - var identity = GetIdentityElement(); - if (identity != null) - { - identity.AddAfterSelf(resources); - } - else - { - root.Add(resources); - } - } - } - else - { - resources.RemoveAll(); - } - - foreach (var lang in languages) - { - resources.Add(new XElement(DefaultNs + "Resource", new XAttribute("Language", lang))); - } - } - - #endregion - - #region Namespace Management - - /// - /// Adds a prefix to the IgnorableNamespaces attribute on the Package element if not already present. - /// - public void AddIgnorableNamespace(string prefix) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var ignorableAttr = root.Attribute("IgnorableNamespaces"); - if (ignorableAttr != null) - { - var namespaces = ignorableAttr.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (!namespaces.Contains(prefix, StringComparer.OrdinalIgnoreCase)) - { - ignorableAttr.Value = ignorableAttr.Value + " " + prefix; - } - } - else - { - root.SetAttributeValue("IgnorableNamespaces", prefix); - } - } - - /// - /// Adds an xmlns:prefix declaration to the Package element if not already present. - /// - public void EnsureNamespace(string prefix, XNamespace ns) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var existing = root.Attribute(XNamespace.Xmlns + prefix); - if (existing == null) - { - root.Add(new XAttribute(XNamespace.Xmlns + prefix, ns.NamespaceName)); - } - } - - #endregion - - #region Capabilities - - /// - /// Adds a capability if not already present. Uses the default namespace unless a specific namespace is provided. - /// - public void EnsureCapability(string capabilityName, XNamespace? ns = null) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var capabilities = GetCapabilitiesElement(); - if (capabilities == null) - { - capabilities = new XElement(DefaultNs + "Capabilities"); - root.Add(capabilities); - } - - var targetNs = ns ?? DefaultNs; - - // Check all child elements for a matching Name attribute regardless of namespace - var existing = capabilities.Elements() - .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, capabilityName, StringComparison.OrdinalIgnoreCase)); - - if (existing == null) - { - capabilities.Add(new XElement(targetNs + "Capability", new XAttribute("Name", capabilityName))); - } - } - - #endregion - - #region Build Metadata - - /// - /// Adds or updates a build:Item entry in the build:Metadata section. - /// - public void SetBuildMetadata(string toolName, string version) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var metadata = root.Element(BuildNs + "Metadata"); - if (metadata == null) - { - metadata = new XElement(BuildNs + "Metadata"); - root.Add(metadata); - } - - var existingItem = metadata.Elements(BuildNs + "Item") - .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, toolName, StringComparison.OrdinalIgnoreCase)); - - if (existingItem != null) - { - existingItem.SetAttributeValue("Version", version); - } - else - { - metadata.Add(new XElement(BuildNs + "Item", - new XAttribute("Name", toolName), - new XAttribute("Version", version))); - } - } - - #endregion - - #region Package-level Extensions - - /// - /// Gets or creates the Package-level Extensions element (direct child of Package root). - /// This is distinct from Application-level Extensions which live inside Application elements. - /// - public XElement GetOrCreatePackageLevelExtensionsElement() - { - var root = _document.Root ?? throw new InvalidOperationException("Document has no root element"); - - // root.Element() only returns direct children, so this correctly gets - // Package-level Extensions (not Application > Extensions) - var extensions = root.Element(DefaultNs + "Extensions"); - if (extensions != null) - { - return extensions; - } - - extensions = new XElement(DefaultNs + "Extensions"); - - // Insert after Applications (standard AppxManifest element order) - var applications = root.Element(DefaultNs + "Applications"); - if (applications != null) - { - applications.AddAfterSelf(extensions); - } - else - { - root.Add(extensions); - } - - return extensions; - } - - /// - /// Collects all DLL paths registered in InProcessServer or ProxyStub extensions - /// (from Package-level <Path> elements). Used for dedup when adding new entries. - /// - public HashSet GetRegisteredExtensionDllPaths() - { - var result = new HashSet(StringComparer.OrdinalIgnoreCase); - var root = _document.Root; - if (root == null) - { - return result; - } - - foreach (var path in root.Descendants(DefaultNs + "Path")) - { - var text = path.Value?.Trim(); - if (!string.IsNullOrEmpty(text)) - { - result.Add(text); - } - } - - return result; - } - - /// - /// Adds an InProcessServer extension entry to the Package-level Extensions element. - /// - public void AddInProcessServerExtension(string dllPath, IEnumerable activatableClasses) - { - var extensions = GetOrCreatePackageLevelExtensionsElement(); - - var extension = new XElement(DefaultNs + "Extension", - new XAttribute("Category", "windows.activatableClass.inProcessServer"), - new XElement(DefaultNs + "InProcessServer", - new XElement(DefaultNs + "Path", dllPath), - activatableClasses.Select(cls => - new XElement(DefaultNs + "ActivatableClass", - new XAttribute("ActivatableClassId", cls), - new XAttribute("ThreadingModel", "both"))))); - - extensions.Add(extension); - } - - #endregion - - #region Package Dependencies - - /// - /// Checks if a PackageDependency with the given name prefix exists. - /// - public bool HasPackageDependency(string namePrefix) - { - var dependencies = GetDependenciesElement(); - if (dependencies == null) - { - return false; - } - - return dependencies.Elements(DefaultNs + "PackageDependency") - .Any(e => e.Attribute("Name")?.Value?.StartsWith(namePrefix, StringComparison.OrdinalIgnoreCase) == true); - } - - /// - /// Adds or updates a PackageDependency element. - /// - public void SetPackageDependency(string name, string minVersion, string publisher) - { - var root = _document.Root; - if (root == null) - { - return; - } - - var dependencies = GetDependenciesElement(); - if (dependencies == null) - { - dependencies = new XElement(DefaultNs + "Dependencies"); - root.Add(dependencies); - } - - var existing = dependencies.Elements(DefaultNs + "PackageDependency") - .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, name, StringComparison.Ordinal)); - - if (existing != null) - { - existing.SetAttributeValue("MinVersion", minVersion); - existing.SetAttributeValue("Publisher", publisher); - } - else - { - dependencies.Add(new XElement(DefaultNs + "PackageDependency", - new XAttribute("Name", name), - new XAttribute("MinVersion", minVersion), - new XAttribute("Publisher", publisher))); - } - } - - #endregion -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs b/src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs deleted file mode 100644 index feacd86c..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/DebugOutputService.cs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -// The CLI requires Windows 10+; suppress platform compat warnings for Debug APIs. -#pragma warning disable CA1416 - -using Microsoft.Extensions.Logging; -using Spectre.Console; -using System.Text; -using Windows.Win32; -using Windows.Win32.Foundation; -using Windows.Win32.System.Diagnostics.Debug; -using Windows.Win32.System.Threading; - -namespace WinApp.Cli.Services; - -/// -/// Attaches to a running process via the Win32 Debug API and streams -/// OutputDebugString messages and first-chance exceptions to the console. -/// Only one debugger can attach to a process at a time — using this service -/// prevents other debuggers (Visual Studio, VS Code) from attaching. -/// The debugged process is terminated when the debug session ends (e.g., Ctrl+C) -/// or if the winapp process exits unexpectedly. -/// -internal sealed class DebugOutputService(IAnsiConsole console, ILogger logger) : IDebugOutputService -{ - // Well-known NTSTATUS / exception codes - private const uint STATUS_BREAKPOINT = 0x80000003; - private const uint STATUS_SINGLE_STEP = 0x80000004; - private const uint STATUS_WX86_BREAKPOINT = 0x4000001F; - private const uint THREAD_NAME_EXCEPTION = 0x406D1388; - - /// - public Task RunDebugLoopAsync(uint processId, CancellationToken cancellationToken) - { - // DebugActiveProcess + WaitForDebugEventEx must be called from the same thread, - // so spin up a dedicated thread via Task.Run. - return Task.Run(() => RunDebugLoop(processId, cancellationToken), cancellationToken); - } - - private int RunDebugLoop(uint processId, CancellationToken cancellationToken) - { - // If winapp crashes without cleanup, the OS terminates the debuggee. - PInvoke.DebugSetProcessKillOnExit(true); - - if (!PInvoke.DebugActiveProcess(processId)) - { - logger.LogError( - "Failed to attach debugger to process {PID}. The process may have exited before the debugger could attach. " + - "For short-lived apps, consider using --with-alias instead.", - processId); - return -1; - } - - logger.LogDebug("Attached debugger to process {PID}.", processId); - - try - { - return DebugEventLoop(processId, cancellationToken); - } - finally - { - PInvoke.DebugActiveProcessStop(processId); - logger.LogDebug("Detached debugger from process {PID}.", processId); - } - } - - private int DebugEventLoop(uint processId, CancellationToken cancellationToken) - { - int exitCode = -1; - bool initialBreakpointSeen = false; - - while (!cancellationToken.IsCancellationRequested) - { - // Poll with a short timeout so we can check the cancellation token. - if (!PInvoke.WaitForDebugEventEx(out var debugEvent, 100)) - { - continue; - } - - var continueStatus = NTSTATUS.DBG_CONTINUE; - - switch (debugEvent.dwDebugEventCode) - { - case DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT: - HandleOutputDebugString(in debugEvent); - break; - - case DEBUG_EVENT_CODE.EXCEPTION_DEBUG_EVENT: - HandleException(in debugEvent, ref initialBreakpointSeen, ref continueStatus); - break; - - case DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT: - exitCode = unchecked((int)debugEvent.u.ExitProcess.dwExitCode); - PInvoke.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus); - return exitCode; - - case DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT: - CloseHandleSafe(debugEvent.u.CreateProcessInfo.hFile); - break; - - case DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT: - CloseHandleSafe(debugEvent.u.LoadDll.hFile); - break; - } - - PInvoke.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus); - } - - return exitCode; - } - - private unsafe void HandleOutputDebugString(in DEBUG_EVENT debugEvent) - { - var info = debugEvent.u.DebugString; - int length = Math.Min((int)info.nDebugStringLength, 65534); - if (length == 0) - { - return; - } - - using var processHandle = PInvoke.OpenProcess_SafeHandle( - PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ, false, debugEvent.dwProcessId); - - if (processHandle.IsInvalid) - { - return; - } - - Span buffer = length <= 4096 ? stackalloc byte[length] : new byte[length]; - - if (!PInvoke.ReadProcessMemory(processHandle, info.lpDebugStringData, buffer, out var bytesRead) || bytesRead == 0) - { - return; - } - - var usable = buffer[..(int)bytesRead]; - string message = info.fUnicode != 0 - ? Encoding.Unicode.GetString(usable) - : Encoding.Default.GetString(usable); - - message = message.TrimEnd('\0'); - - if (!string.IsNullOrWhiteSpace(message)) - { - // Trim trailing newline so Spectre doesn't double-space the output. - message = message.TrimEnd('\r', '\n'); - console.MarkupLine($"[dim][[Debug]][/] {message.EscapeMarkup()}"); - } - } - - private unsafe void HandleException( - in DEBUG_EVENT debugEvent, - ref bool initialBreakpointSeen, - ref NTSTATUS continueStatus) - { - var exInfo = debugEvent.u.Exception; - uint code = unchecked((uint)exInfo.ExceptionRecord.ExceptionCode.Value); - bool firstChance = exInfo.dwFirstChance != 0; - - // Suppress the initial breakpoint that the OS sends when we attach. - if (!initialBreakpointSeen && (code is STATUS_BREAKPOINT or STATUS_WX86_BREAKPOINT)) - { - initialBreakpointSeen = true; - continueStatus = NTSTATUS.DBG_CONTINUE; - return; - } - - // Suppress single-step and thread-name exceptions — they are noise. - if (code is STATUS_SINGLE_STEP or THREAD_NAME_EXCEPTION) - { - continueStatus = NTSTATUS.DBG_CONTINUE; - return; - } - - if (firstChance) - { - var name = GetExceptionName(code); - var address = (nuint)exInfo.ExceptionRecord.ExceptionAddress; - console.MarkupLine($"[yellow]First-chance exception:[/] {name} (0x{code:X8}) at 0x{address:X}"); - } - - // Let the target's own exception handling run. For second-chance exceptions - // (firstChance == false), this causes the OS to terminate the process — correct - // behavior for a passive listener that doesn't handle exceptions itself. - continueStatus = NTSTATUS.DBG_EXCEPTION_NOT_HANDLED; - } - - private static unsafe void CloseHandleSafe(HANDLE handle) - { - if (!handle.IsNull && handle.Value != (void*)-1) - { - PInvoke.CloseHandle(handle); - } - } - - private static string GetExceptionName(uint code) => code switch - { - 0xC0000005 => "Access Violation", - 0xC00000FD => "Stack Overflow", - 0xC0000094 => "Integer Division By Zero", - 0xC0000017 => "No Memory", - 0xC000001D => "Illegal Instruction", - 0xC0000025 => "Non-Continuable Exception", - 0xC000008C => "Array Bounds Exceeded", - 0xC0000135 => "DLL Not Found", - 0xC0000142 => "DLL Initialization Failed", - STATUS_BREAKPOINT => "Breakpoint", - STATUS_SINGLE_STEP => "Single Step", - 0xE06D7363 => "C++ Exception", - 0xE0434352 => "CLR Exception", - _ => "Exception", - }; -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs b/src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs deleted file mode 100644 index f31c0312..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IDebugOutputService.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Attaches to a running process as a debugger to capture OutputDebugString -/// messages and first-chance exceptions, similar to Visual Studio's Output window. -/// Only one debugger can attach to a process at a time — using this service -/// prevents other debuggers (Visual Studio, VS Code) from attaching. -/// -internal interface IDebugOutputService -{ - /// - /// Attaches to the specified process using the Win32 Debug API and writes - /// captured debug output and exception information to the console until - /// the process exits or the is signaled. - /// When cancelled, the debugged process is terminated before returning. - /// - /// The ID of the process to attach to. - /// Token to stop the debug loop (e.g. Ctrl+C). The debugged process is terminated when this token is signaled. - /// The exit code of the debugged process, or -1 if terminated early. - Task RunDebugLoopAsync(uint processId, CancellationToken cancellationToken); -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs deleted file mode 100644 index 1e94be56..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Provides methods for registering, unregistering, and querying MSIX packages -/// using the Windows PackageManager API. -/// -internal interface IPackageRegistrationService -{ - /// - /// Registers a loose-layout MSIX package from an AppxManifest.xml path. - /// Uses DevelopmentMode to allow registration without signing. - /// - /// Path to the AppxManifest.xml in the loose layout. - /// Cancellation token. - Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default); - - /// - /// Registers a sparse MSIX package with an external location. - /// The package references files at the external location rather than containing them. - /// - /// Path to the AppxManifest.xml file. - /// External directory containing the app files. - /// Cancellation token. - Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default); - - /// - /// Unregisters an installed package by name. Returns true if a package was found and removed. - /// - /// The package identity name (e.g. MyCompany.MyApp). - /// Cancellation token. - /// True if a package was unregistered, false if no matching package was found. - Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default); - - /// - /// Installs an MSIX/APPX package file, optionally forcing application shutdown. - /// Used for installing framework dependencies like Windows App Runtime. - /// - /// Path to the .msix or .appx file. - /// Cancellation token. - Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default); - - /// - /// Checks if a package with the given name is installed and returns its version, - /// or null if not found. - /// - /// The package identity name. - /// The installed version, or null if not found. - string? GetInstalledVersion(string packageName); - - /// - /// Finds all installed packages matching the given name that were registered in - /// development mode (sideloaded). Returns package metadata including the full name - /// and install location for safety checks. - /// - /// The package identity name to search for. - /// A list of matching dev-mode packages. - List FindDevPackages(string packageName); -} - -/// -/// Information about a development-mode registered package. -/// -internal sealed record DevPackageInfo( - string FullName, - string Name, - string Version, - string? InstallLocation, - bool IsDevelopmentMode); diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs deleted file mode 100644 index 9205f534..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using WinApp.Cli.ConsoleTasks; - -namespace WinApp.Cli.Services; - -internal interface IPriService -{ - Task CreatePriConfigAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - IEnumerable precomputedPriResourceCandidates, - string language = "en-US", - string platformVersion = "10.0.0", - CancellationToken cancellationToken = default); - - Task> GeneratePriFileAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - FileInfo? configPath = null, - FileInfo? outputPath = null, - CancellationToken cancellationToken = default); - - Task> ExtractLanguagesFromPriAsync( - FileInfo priFile, - TaskContext taskContext, - CancellationToken cancellationToken); -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs deleted file mode 100644 index d7b5b226..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -namespace WinApp.Cli.Services; - -/// -/// Static helper for incremental file copy operations. -/// Compares source and destination by file size and last-write timestamp -/// to skip unchanged files and remove stale files. -/// -internal static class IncrementalCopyHelper -{ - internal record SyncResult(int Copied, int Skipped, int Deleted); - - /// - /// Synchronizes files from to incrementally. - /// Only copies files that are new or changed (by size or timestamp). - /// Removes stale files from that no longer exist in source, - /// except for files in . - /// - internal static SyncResult SyncDirectory( - DirectoryInfo sourceDir, - DirectoryInfo destDir, - HashSet? protectedFileNames = null) - { - if (!destDir.Exists) - { - destDir.Create(); - } - - var destFullPath = destDir.FullName.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; - var sourceRelativePaths = new HashSet(StringComparer.OrdinalIgnoreCase); - int copied = 0, skipped = 0; - - foreach (var file in sourceDir.EnumerateFiles("*", SearchOption.AllDirectories)) - { - // Skip files that are inside the dest folder (if dest is nested inside source) - if (file.FullName.StartsWith(destFullPath, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var relativePath = Path.GetRelativePath(sourceDir.FullName, file.FullName); - sourceRelativePaths.Add(relativePath); - var destFile = new FileInfo(Path.Combine(destDir.FullName, relativePath)); - - // Skip copy if destination exists with same size and timestamp - if (destFile.Exists && destFile.Length == file.Length && destFile.LastWriteTimeUtc == file.LastWriteTimeUtc) - { - skipped++; - continue; - } - - destFile.Directory?.Create(); - file.CopyTo(destFile.FullName, overwrite: true); - copied++; - } - - // Remove stale files in dest that no longer exist in source - int deleted = 0; - foreach (var destFile in destDir.EnumerateFiles("*", SearchOption.AllDirectories)) - { - var relativePath = Path.GetRelativePath(destDir.FullName, destFile.FullName); - - if (protectedFileNames != null && protectedFileNames.Contains(relativePath)) - { - continue; - } - - if (!sourceRelativePaths.Contains(relativePath)) - { - destFile.Delete(); - deleted++; - } - } - - return new SyncResult(copied, skipped, deleted); - } - - /// - /// Copies a list of files to a target directory incrementally, - /// skipping files that are unchanged (same size and timestamp). - /// - internal static (int Copied, int Skipped) CopyFiles( - List<(FileInfo SourceFile, string RelativePath)> files, - DirectoryInfo targetDir) - { - int copied = 0, skipped = 0; - - foreach (var (sourceFile, relativePath) in files) - { - var targetFile = new FileInfo(Path.Combine(targetDir.FullName, relativePath)); - - if (targetFile.Exists && targetFile.Length == sourceFile.Length && targetFile.LastWriteTimeUtc == sourceFile.LastWriteTimeUtc) - { - skipped++; - continue; - } - - targetFile.Directory?.Create(); - sourceFile.CopyTo(targetFile.FullName, overwrite: true); - copied++; - } - - return (copied, skipped); - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs deleted file mode 100644 index e48220c6..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Text.RegularExpressions; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; - -namespace WinApp.Cli.Services; - -/// -/// Static helper for MRT (Modern Resource Technology) asset qualification, -/// expansion, and file copying. -/// -internal static partial class MrtAssetHelper -{ - // Language (en, en-US, pt-BR, zh-Hans, etc.) – bare token - [GeneratedRegex(@"^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex LanguageQualifierRegex(); - - [GeneratedRegex(@"^scale-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - // scale-100, scale-200, etc. - private static partial Regex ScaleQualifierRegex(); - - // theme-dark, theme-light - [GeneratedRegex(@"^theme-(light|dark)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ThemeQualifierRegex(); - - // contrast-standard, contrast-high - [GeneratedRegex(@"^contrast-(standard|high)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ContrastQualifierRegex(); - - // dxfeaturelevel-9 / 10 / 11 - [GeneratedRegex(@"^dxfeaturelevel-(9|10|11)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex DxFeatureLevelQualifierRegex(); - - // device-family-desktop / xbox / team / iot / mobile - [GeneratedRegex(@"^device-family-(desktop|mobile|team|xbox|iot)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex DeviceFamilyQualifierRegex(); - - // homeregion-US, homeregion-JP, ... - [GeneratedRegex(@"^homeregion-[A-Za-z]{2}$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex HomeRegionQualifierRegex(); - - // configuration-debug, configuration-retail, etc. - [GeneratedRegex(@"^configuration-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ConfigurationQualifierRegex(); - - // targetsize-16, targetsize-24, targetsize-256, ... - [GeneratedRegex(@"^targetsize-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex TargetSizeQualifierRegex(); - - // altform-unplated, altform-lightunplated, etc. - [GeneratedRegex(@"^altform-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex AltFormQualifierRegex(); - - internal static readonly HashSet PriIncludedExtensions = new(StringComparer.OrdinalIgnoreCase) - { - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".svg" - }; - - internal static List<(FileInfo SourceFile, string RelativePath)> ExpandManifestReferencedFiles( - DirectoryInfo manifestDir, - IEnumerable referencedFiles, - TaskContext? taskContext, - Func? includeFile = null) - { - var expandedFilesByRelativePath = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var relativeFilePath in referencedFiles) - { - var logicalSourceFile = new FileInfo(Path.Combine(manifestDir.FullName, relativeFilePath)); - var sourceDir = logicalSourceFile.Directory; - - if (sourceDir is null || !sourceDir.Exists) - { - taskContext?.AddDebugMessage($"{UiSymbols.Warning} Source directory not found for referenced file: {relativeFilePath}"); - continue; - } - - var logicalBaseName = Path.GetFileNameWithoutExtension(logicalSourceFile.Name); - var variantBaseName = GetMrtVariantBaseName(logicalBaseName); - var extension = logicalSourceFile.Extension; - - var searchPattern = variantBaseName + "*" + extension; - var candidates = sourceDir.EnumerateFiles(searchPattern); - var anyIncludedForLogical = false; - - foreach (var candidateFile in candidates) - { - if (includeFile != null && !includeFile(candidateFile)) - { - continue; - } - - var candidateNameWithoutExtension = Path.GetFileNameWithoutExtension(candidateFile.Name); - if (!IsMrtVariantName(variantBaseName, candidateNameWithoutExtension)) - { - continue; - } - - var relativeDir = Path.GetDirectoryName(relativeFilePath); - var candidateRelativePath = string.IsNullOrEmpty(relativeDir) - ? candidateFile.Name - : Path.Combine(relativeDir, candidateFile.Name); - - expandedFilesByRelativePath[candidateRelativePath] = candidateFile; - anyIncludedForLogical = true; - } - - if (!anyIncludedForLogical && logicalSourceFile.Exists && (includeFile == null || includeFile(logicalSourceFile))) - { - expandedFilesByRelativePath[relativeFilePath] = logicalSourceFile; - } - else if (!anyIncludedForLogical && !logicalSourceFile.Exists) - { - taskContext?.AddDebugMessage($"{UiSymbols.Warning} Referenced file not found (no MRT variants): {logicalSourceFile}"); - } - } - - return [.. expandedFilesByRelativePath - .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase) - .Select(pair => (pair.Value, pair.Key))]; - } - - internal static List<(FileInfo SourceFile, string RelativePath)> GetExpandedManifestReferencedFiles( - FileInfo manifestPath, - TaskContext taskContext) - { - var manifestDir = manifestPath.Directory; - if (manifestDir == null) - { - taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest directory not found for: {manifestPath}"); - return []; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Reading manifest: {manifestPath}"); - - var assetReferences = ManifestService.ExtractAssetReferencesFromManifest(manifestPath, taskContext); - var referencedFiles = assetReferences.Select(a => a.RelativePath); - return ExpandManifestReferencedFiles(manifestDir, referencedFiles, taskContext); - } - - internal static void CopyAllAssets(List<(FileInfo SourceFile, string RelativePath)> expandedFiles, DirectoryInfo targetDir, TaskContext taskContext) - { - var (copied, skipped) = IncrementalCopyHelper.CopyFiles(expandedFiles, targetDir); - taskContext.AddDebugMessage($"{UiSymbols.Note} Manifest resources: {copied} copied, {skipped} unchanged"); - } - - // ltr / rtl - private static bool IsLayoutDirectionQualifier(string token) - { - return token.Equals("ltr", StringComparison.OrdinalIgnoreCase) || - token.Equals("rtl", StringComparison.OrdinalIgnoreCase); - } - - internal static bool IsSingleQualifierToken(string token) - { - if (string.IsNullOrEmpty(token)) - { - return false; - } - - return LanguageQualifierRegex().IsMatch(token) - || ScaleQualifierRegex().IsMatch(token) - || ThemeQualifierRegex().IsMatch(token) - || ContrastQualifierRegex().IsMatch(token) - || DxFeatureLevelQualifierRegex().IsMatch(token) - || DeviceFamilyQualifierRegex().IsMatch(token) - || HomeRegionQualifierRegex().IsMatch(token) - || ConfigurationQualifierRegex().IsMatch(token) - || TargetSizeQualifierRegex().IsMatch(token) - || AltFormQualifierRegex().IsMatch(token) - || IsLayoutDirectionQualifier(token); - } - - internal static bool IsQualifierToken(string token) - { - if (string.IsNullOrEmpty(token)) - { - return false; - } - - var parts = token.Split('_'); - - foreach (var part in parts) - { - if (!IsSingleQualifierToken(part)) - { - return false; - } - } - - return true; - } - - /// - /// Returns true if is a valid MRT - /// variant of the logical base name (dots allowed in base name). - /// - internal static bool IsMrtVariantName(string logicalBaseName, string candidateNameWithoutExtension) - { - if (string.IsNullOrWhiteSpace(logicalBaseName) || string.IsNullOrWhiteSpace(candidateNameWithoutExtension)) - { - return false; - } - - // Split by '.'; "Logo.scale-200.theme-dark" -> ["Logo", "scale-200", "theme-dark"] - var parts = candidateNameWithoutExtension.Split('.'); - - if (parts.Length == 0) - { - return false; - } - - // First token must match logical base name (case-insensitive) - if (!parts[0].Equals(logicalBaseName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - // No qualifiers -> exact logical name, valid - if (parts.Length == 1) - { - return true; - } - - // All remaining tokens must be valid MRT qualifiers - for (int i = 1; i < parts.Length; i++) - { - if (!IsQualifierToken(parts[i])) - { - return false; - } - } - - return true; - } - - /// - /// For a qualified logical name like "Logo.scale-100" or "Logo.targetsize-24_altform-unplated", - /// returns the unqualified asset family base (e.g. "Logo"). - /// If the name has no trailing qualifier tokens, returns the original name unchanged. - /// - internal static string GetMrtVariantBaseName(string logicalBaseName) - { - ArgumentException.ThrowIfNullOrWhiteSpace(logicalBaseName); - - var parts = logicalBaseName.Split('.'); - if (parts.Length <= 1) - { - return logicalBaseName; - } - - // Find the earliest segment where every remaining segment is a valid qualifier token. - for (int i = 1; i < parts.Length; i++) - { - var allRemainingAreQualifiers = true; - for (int j = i; j < parts.Length; j++) - { - if (!IsQualifierToken(parts[j])) - { - allRemainingAreQualifiers = false; - break; - } - } - - if (allRemainingAreQualifiers) - { - return string.Join('.', parts[..i]); - } - } - - return logicalBaseName; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs deleted file mode 100644 index a737ccba..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs +++ /dev/null @@ -1,771 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Security; -using System.Text; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; -using WinApp.Cli.Models; -using WinApp.Cli.Tools; - -namespace WinApp.Cli.Services; - -internal partial class MsixService -{ - public async Task AddSparseIdentityAsync(string? entryPointPath, FileInfo appxManifestPath, bool noInstall, bool keepIdentity, TaskContext taskContext, CancellationToken cancellationToken = default) - { - // Validate inputs - if (!appxManifestPath.Exists) - { - throw new FileNotFoundException($"AppX manifest not found at: {appxManifestPath}. You can generate one using 'winapp manifest generate'."); - } - - if (!devModeService.IsEnabled() && noInstall == false) - { - throw new InvalidOperationException("Developer Mode is not enabled on this machine. Please enable Developer Mode and try again."); - } - - if (entryPointPath == null) - { - var manifestContent = await File.ReadAllTextAsync(appxManifestPath.FullName, Encoding.UTF8, cancellationToken); - - // Parse once to extract the executable path - var doc = AppxManifestDocument.Parse(manifestContent); - - if (PlaceholderHelper.ContainsPlaceholders(manifestContent)) - { - // Without an explicit entrypoint, we can't resolve $targetnametoken$ in the executable - if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) - { - throw new InvalidOperationException( - "The manifest contains a placeholder for the executable. " + - "Provide the entrypoint argument to specify the executable path."); - } - - // Resolve built-in tokens (e.g. $targetentrypoint$) in memory — the executable - // attribute itself has no placeholders, so its value from the initial parse is valid. - manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent); - } - - entryPointPath = doc.ApplicationExecutable ?? entryPointPath; - } - - // Validate inputs - if (!File.Exists(entryPointPath)) - { - throw new FileNotFoundException($"EntryPoint/Executable not found at: {entryPointPath}"); - } - - taskContext.AddDebugMessage($"Processing entryPoint/executable: {entryPointPath}"); - taskContext.AddDebugMessage($"Using AppX manifest: {appxManifestPath}"); - - // Generate sparse package structure - // Fetch dotnet package list once for all downstream operations - var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - - var (debugManifestPath, debugIdentity) = await GenerateSparsePackageStructureAsync( - appxManifestPath, - entryPointPath, - keepIdentity, - dotNetPackageList, - taskContext, - cancellationToken); - - // Update executable with debug identity - if (Path.HasExtension(entryPointPath) && string.Equals(Path.GetExtension(entryPointPath), ".exe", StringComparison.OrdinalIgnoreCase)) - { - var exePath = new FileInfo(entryPointPath); - await EmbedMsixIdentityToExeAsync(exePath, debugIdentity, taskContext, cancellationToken); - } - - if (noInstall) - { - taskContext.AddDebugMessage("Skipping package installation as per --no-install option."); - } - else - { - // Register the debug appxmanifest - var entryPointDir = Path.GetDirectoryName(entryPointPath); - var externalLocation = new DirectoryInfo(string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir); - - // Unregister any existing package first - await UnregisterExistingPackageAsync(debugIdentity.PackageName, taskContext, cancellationToken); - - // Register the new debug manifest with external location - await RegisterSparsePackageAsync(debugManifestPath, externalLocation, taskContext, cancellationToken); - } - - return new MsixIdentityResult(debugIdentity.PackageName, debugIdentity.Publisher, debugIdentity.ApplicationId); - } - - public async Task AddLooseLayoutIdentityAsync(FileInfo appxManifestPath, DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, CancellationToken cancellationToken = default) - { - // Validate inputs - if (!appxManifestPath.Exists) - { - throw new FileNotFoundException($"AppX manifest not found at: {appxManifestPath}. You can generate one using 'winapp manifest generate'."); - } - - if (!devModeService.IsEnabled()) - { - throw new InvalidOperationException("Developer Mode is not enabled on this machine. Please enable Developer Mode and try again."); - } - - taskContext.AddDebugMessage($"Using AppX manifest: {appxManifestPath}"); - - var manifestContent = await File.ReadAllTextAsync(appxManifestPath.FullName, Encoding.UTF8, cancellationToken); - - // Detect whether this manifest was generated by MSBuild (dotnet build). - // MSBuild-generated manifests have build:Metadata with a makepri.exe entry. - // When MSBuild-generated, the build output includes a .appxrecipe file that - // lists all files and their correct source paths for the AppX layout. - var doc = AppxManifestDocument.Parse(manifestContent); - var isMSBuildGenerated = doc.Document.Root? - .Element(AppxManifestDocument.BuildNs + "Metadata")? - .Elements(AppxManifestDocument.BuildNs + "Item") - .Any(e => string.Equals(e.Attribute("Name")?.Value, "makepri.exe", StringComparison.OrdinalIgnoreCase)) == true; - - if (isMSBuildGenerated) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} MSBuild-generated manifest detected"); - - // Look for a .build.appxrecipe file in the input directory - var recipeFile = inputDirectory.EnumerateFiles("*.build.appxrecipe", SearchOption.TopDirectoryOnly).FirstOrDefault(); - - if (recipeFile != null) - { - taskContext.AddDebugMessage($"{UiSymbols.Files} Using appxrecipe for layout: {recipeFile.Name}"); - await CopyFilesFromRecipeAsync(recipeFile, outputAppXDirectory, taskContext, cancellationToken); - } - else - { - // No recipe — fall back to incremental copy from input directory - taskContext.AddDebugMessage($"{UiSymbols.Warning} No .appxrecipe found, falling back to file copy"); - SyncFilesToOutputDirectory(inputDirectory, outputAppXDirectory, appxManifestPath, taskContext); - } - - var identity = ParseAppxManifestAsync(manifestContent); - - // Unregister any existing package first - await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); - - // Register from the AppX layout directory - var registrationManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "AppxManifest.xml")); - if (!registrationManifest.Exists) - { - registrationManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml")); - } - await RegisterLooseLayoutPackageAsync(registrationManifest, taskContext, cancellationToken); - - return new MsixIdentityResult(identity.PackageName, identity.Publisher, identity.ApplicationId); - } - - // --- Non-MSBuild manifest path (raw Package.appxmanifest with unresolved placeholders) --- - - if (!outputAppXDirectory.Exists) - { - outputAppXDirectory.Create(); - } - - SyncFilesToOutputDirectory(inputDirectory, outputAppXDirectory, appxManifestPath, taskContext); - - var copiedAppxManifestPath = new FileInfo(Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name)); - manifestContent = await File.ReadAllTextAsync(copiedAppxManifestPath.FullName, Encoding.UTF8, cancellationToken); - var executableMatch = outputAppXDirectory.EnumerateFiles("*", SearchOption.AllDirectories) - .FirstOrDefault(f => string.Equals(f.Extension, ".exe", StringComparison.OrdinalIgnoreCase)); - - if (executableMatch == null) - { - throw new FileNotFoundException("No executable (.exe) file found in the output directory for token replacement."); - } - - // Fetch dotnet package list once for all downstream operations - var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - - // If there is a pri file named after the executable, rename it to resources.pri - var priFilePath = Path.Combine(outputAppXDirectory.FullName, Path.GetFileNameWithoutExtension(executableMatch.Name) + ".pri"); - if (File.Exists(priFilePath)) - { - var resourcesPriPath = Path.Combine(outputAppXDirectory.FullName, "resources.pri"); - File.Move(priFilePath, resourcesPriPath, overwrite: true); - taskContext.AddDebugMessage($"{UiSymbols.Files} Renamed {Path.GetFileName(priFilePath)} to resources.pri"); - } - - // Generate resources.pri if not present (matches winapp package behavior) - var existingPri = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "resources.pri")); - if (!existingPri.Exists) - { - try - { - var stagingManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml")); - var priExpandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(stagingManifest, taskContext); - var priResourceCandidates = priExpandedFiles.Select(file => file.RelativePath); - await priService.CreatePriConfigAsync( - outputAppXDirectory, - taskContext, - precomputedPriResourceCandidates: priResourceCandidates, - cancellationToken: cancellationToken); - await priService.GeneratePriFileAsync(outputAppXDirectory, taskContext, cancellationToken: cancellationToken); - taskContext.AddDebugMessage($"{UiSymbols.Files} Generated resources.pri"); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to generate PRI: {ex.Message}"); - } - } - - // Resolve $targetnametoken$ and $targetentrypoint$ placeholders - var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - [PlaceholderHelper.TargetNameToken] = Path.GetFileNameWithoutExtension(executableMatch.Name) - }; - manifestContent = PlaceholderHelper.ReplacePlaceholders(manifestContent, replacements); - - // Resolve — falls back to "en-US" if no PRI found - manifestContent = manifestContent.Replace("x-generate", "EN-US"); - - // Unified manifest processing: WinAppSDK dependency, third-party WinRT components, - // ProcessorArchitecture auto-detection, and build metadata - (manifestContent, _) = await UpdateAppxManifestContentAsync( - manifestContent, null, null, executableMatch.FullName, - sparse: false, selfContained: false, - dotNetPackageList, taskContext, cancellationToken); - - await File.WriteAllTextAsync(copiedAppxManifestPath.FullName, manifestContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), cancellationToken); - - // Copy all assets - var originalManifestDir = appxManifestPath.DirectoryName; - - if (!string.Equals(originalManifestDir, outputAppXDirectory.FullName, StringComparison.OrdinalIgnoreCase)) - { - var expandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(appxManifestPath, taskContext); - MrtAssetHelper.CopyAllAssets(expandedFiles, outputAppXDirectory, taskContext); - } - else - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Manifest directory and target directory are the same, skipping assets copy"); - } - - { - var identity = ParseAppxManifestAsync(manifestContent); - - // Install the Windows App Runtime framework packages if not already present - await EnsureWindowsAppRuntimeInstalledAsync(dotNetPackageList, taskContext, cancellationToken); - - // Unregister any existing package first - await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); - - // Register the new debug manifest with external location - await RegisterLooseLayoutPackageAsync(copiedAppxManifestPath, taskContext, cancellationToken); - - return new MsixIdentityResult(identity.PackageName, identity.Publisher, identity.ApplicationId); - } - } - - /// - /// Copies files to the AppX layout directory using the .build.appxrecipe file. - /// The recipe is generated by MSBuild and lists all files with their correct source - /// paths and target PackagePaths. This preserves file metadata that .NET's CopyTo may lose. - /// - private static async Task CopyFilesFromRecipeAsync(FileInfo recipeFile, DirectoryInfo outputDir, TaskContext taskContext, CancellationToken cancellationToken) - { - if (!outputDir.Exists) - { - outputDir.Create(); - } - - var recipeContent = await File.ReadAllTextAsync(recipeFile.FullName, Encoding.UTF8, cancellationToken); - var recipeDoc = System.Xml.Linq.XDocument.Parse(recipeContent); - System.Xml.Linq.XNamespace msbuildNs = "http://schemas.microsoft.com/developer/msbuild/2003"; - - int copied = 0, skipped = 0; - - // Copy the AppxManifest - var manifestEntries = recipeDoc.Descendants(msbuildNs + "AppXManifest"); - foreach (var entry in manifestEntries) - { - var sourcePath = entry.Attribute("Include")?.Value; - var packagePath = entry.Element(msbuildNs + "PackagePath")?.Value; - if (sourcePath != null && packagePath != null && File.Exists(sourcePath)) - { - var destPath = Path.Combine(outputDir.FullName, packagePath); - Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); - File.Copy(sourcePath, destPath, overwrite: true); - copied++; - } - } - - // Copy all AppxPackagedFile entries - var fileEntries = recipeDoc.Descendants(msbuildNs + "AppxPackagedFile"); - foreach (var entry in fileEntries) - { - var sourcePath = entry.Attribute("Include")?.Value; - var packagePath = entry.Element(msbuildNs + "PackagePath")?.Value; - if (sourcePath == null || packagePath == null || !File.Exists(sourcePath)) - { - continue; - } - - var destPath = Path.Combine(outputDir.FullName, packagePath); - var destFile = new FileInfo(destPath); - - // Skip unchanged files (same size and timestamp) - if (destFile.Exists) - { - var srcFile = new FileInfo(sourcePath); - if (destFile.Length == srcFile.Length && destFile.LastWriteTimeUtc == srcFile.LastWriteTimeUtc) - { - skipped++; - continue; - } - } - - Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); - File.Copy(sourcePath, destPath, overwrite: true); - copied++; - } - - taskContext.AddDebugMessage($"{UiSymbols.Check} AppX layout from recipe: {copied} copied, {skipped} unchanged"); - } - - /// - /// Syncs files from the input directory to the output AppX directory using IncrementalCopyHelper. - /// Also handles manifest copy and rename. - /// - private static void SyncFilesToOutputDirectory(DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, FileInfo appxManifestPath, TaskContext taskContext) - { - if (!outputAppXDirectory.Exists) - { - outputAppXDirectory.Create(); - } - - if (inputDirectory != null && !string.Equals(inputDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar), - outputAppXDirectory.FullName.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)) - { - var protectedFiles = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "appxmanifest.xml", - "Package.appxmanifest", - "resources.pri" - }; - - var result = IncrementalCopyHelper.SyncDirectory(inputDirectory, outputAppXDirectory, protectedFiles); - taskContext.AddDebugMessage($"{UiSymbols.Check} Sync to output directory: {result.Copied} copied, {result.Skipped} unchanged, {result.Deleted} deleted"); - } - - // Copy the appxmanifest to the output directory - appxManifestPath.CopyTo(Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name), overwrite: true); - - // If its Package.appxmanifest, rename to appxmanifest.xml - if (string.Equals(appxManifestPath.Name, "Package.appxmanifest", StringComparison.OrdinalIgnoreCase)) - { - var renamedPath = Path.Combine(outputAppXDirectory.FullName, "appxmanifest.xml"); - var originalPath = Path.Combine(outputAppXDirectory.FullName, appxManifestPath.Name); - File.Move(originalPath, renamedPath, true); - taskContext.AddDebugMessage($"{UiSymbols.Files} Renamed Package.appxmanifest to appxmanifest.xml"); - } - } - - /// - /// Ensures that the Windows App Runtime framework MSIX packages are installed on the machine. - /// Locates the runtime MSIX directory from the NuGet package cache and installs any - /// missing or outdated packages (Framework, DDLM, Singleton, Main) via Add-AppxPackage. - /// - private async Task EnsureWindowsAppRuntimeInstalledAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); - if (msixDir == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not locate Windows App Runtime MSIX packages. The runtime may need to be installed manually."); - return; - } - - var (installedCount, errorCount) = await workspaceSetupService.InstallWindowsAppRuntimeAsync(msixDir, taskContext, cancellationToken); - - if (errorCount > 0) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} {errorCount} runtime package(s) failed to install. The app may not launch correctly."); - } - else if (installedCount > 0) - { - taskContext.AddDebugMessage($"{UiSymbols.Check} Installed {installedCount} Windows App Runtime package(s)"); - } - } - - private async Task EmbedMsixIdentityToExeAsync(FileInfo exePath, MsixIdentityResult identityInfo, TaskContext taskContext, CancellationToken cancellationToken) - { - // Create the MSIX element for the win32 manifest - string assemblyIdentity = $@";"; - var existingManifestPath = new FileInfo(Path.Combine(exePath.DirectoryName!, "temp_extracted.manifest")); - - try - { - bool hasExistingManifest = await TryExtractManifestFromExeAsync(exePath, existingManifestPath, taskContext, cancellationToken); - if (!hasExistingManifest) - { - assemblyIdentity = string.Empty; - } - else - { - taskContext.AddDebugMessage("Existing manifest found in executable, checking for AssemblyIdentity..."); - var existingManifestContent = await File.ReadAllTextAsync(existingManifestPath.FullName, Encoding.UTF8, cancellationToken); - var assemblyIdentityMatch = AssemblyIdentityNameRegex().Match(existingManifestContent); - if (assemblyIdentityMatch.Success) - { - taskContext.AddDebugMessage("Existing AssemblyIdentity found in manifest, will not add a new one."); - assemblyIdentity = string.Empty; - } - } - } - finally - { - TryDeleteFile(existingManifestPath); - } - - var manifestContent = $@" - - - {assemblyIdentity} -"; - - // Create a temporary manifest file - var tempManifestPath = new FileInfo(Path.Combine(exePath.DirectoryName!, "msix_identity_temp.manifest")); - - try - { - await File.WriteAllTextAsync(tempManifestPath.FullName, manifestContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), cancellationToken); - - // Use mt.exe to merge manifests - await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - } - finally - { - TryDeleteFile(tempManifestPath); - } - } - - /// - /// Embeds a manifest file into the Win32 manifest of an executable using mt.exe for proper merging. - /// - /// Path to the executable to modify - /// Path to the manifest file to embed - /// Cancellation token - private async Task EmbedManifestFileToExeAsync( - FileInfo exePath, - FileInfo manifestPath, - TaskContext taskContext, - CancellationToken cancellationToken = default) - { - // Validate inputs - if (!exePath.Exists) - { - throw new FileNotFoundException($"Executable not found at: {exePath}"); - } - - if (!manifestPath.Exists) - { - throw new FileNotFoundException($"Manifest file not found at: {manifestPath}"); - } - - taskContext.AddDebugMessage($"Processing executable: {exePath}"); - taskContext.AddDebugMessage($"Embedding manifest: {manifestPath}"); - - var exeDir = exePath.DirectoryName!; - var tempManifestPath = new FileInfo(Path.Combine(exeDir, "temp_extracted.manifest")); - var mergedManifestPath = new FileInfo(Path.Combine(exeDir, "merged.manifest")); - - try - { - bool hasExistingManifest = await TryExtractManifestFromExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - - if (hasExistingManifest) - { - taskContext.AddDebugMessage("Merging with existing manifest using mt.exe..."); - - // Use mt.exe to merge existing manifest with new manifest - await RunMtToolAsync($@"-manifest ""{tempManifestPath}"" ""{manifestPath}"" -out:""{mergedManifestPath}""", true, taskContext, cancellationToken); - } - else - { - taskContext.AddDebugMessage("No existing manifest, using new manifest as-is"); - - // No existing manifest, use the new manifest directly - manifestPath.CopyTo(mergedManifestPath.FullName); - } - - taskContext.AddDebugMessage("Embedding merged manifest into executable..."); - - // Update the executable with merged manifest - await RunMtToolAsync($@"-manifest ""{mergedManifestPath}"" -outputresource:""{exePath}"";#1", true, taskContext, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Successfully embedded manifest into: {exePath}"); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to embed manifest into executable: {ex.Message}", ex); - } - finally - { - // Clean up temporary files - TryDeleteFile(tempManifestPath); - TryDeleteFile(mergedManifestPath); - } - } - - private async Task TryExtractManifestFromExeAsync(FileInfo exePath, FileInfo tempManifestPath, TaskContext taskContext, CancellationToken cancellationToken) - { - taskContext.AddDebugMessage("Extracting current manifest from executable..."); - - // Extract current manifest from the executable - bool hasExistingManifest = false; - try - { - await RunMtToolAsync($@"-inputresource:""{exePath}"";#1 -out:""{tempManifestPath}""", false, taskContext, cancellationToken); - tempManifestPath.Refresh(); - hasExistingManifest = tempManifestPath.Exists; - } - catch - { - taskContext.AddDebugMessage("No existing manifest found in executable"); - } - - return hasExistingManifest; - } - - private async Task RunMtToolAsync(string arguments, bool printErrors, TaskContext taskContext, CancellationToken cancellationToken = default) - { - // Use BuildToolsService to run mt.exe - await buildToolsService.RunBuildToolAsync(new GenericTool("mt.exe"), arguments, taskContext, printErrors, cancellationToken: cancellationToken); - } - - /// Path to the original appxmanifest.xml - /// Path to the entryPoint/executable that the manifest should reference - /// Cancellation token - /// Tuple containing the debug manifest path and modified identity info - public async Task<(FileInfo debugManifestPath, MsixIdentityResult debugIdentity)> GenerateSparsePackageStructureAsync( - FileInfo originalManifestPath, - string entryPointPath, - bool keepIdentity, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken = default) - { - var winappDir = winappDirectoryService.GetLocalWinappDirectory(); - var debugDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "debug")); - - taskContext.AddDebugMessage($"{UiSymbols.Note} Creating sparse package structure in: {debugDir.FullName}"); - - // Step 1: Create debug directory, removing existing one if present - if (debugDir.Exists) - { - taskContext.AddDebugMessage($"{UiSymbols.Trash} Removing existing debug directory..."); - debugDir.Delete(recursive: true); - } - - debugDir.Create(); - taskContext.AddDebugMessage($"{UiSymbols.Folder} Created debug directory"); - - // Step 2: Parse original manifest to get identity and assets - var originalManifestContent = await File.ReadAllTextAsync(originalManifestPath.FullName, Encoding.UTF8, cancellationToken); - - // Resolve placeholders in memory (never write back to the original manifest) - if (PlaceholderHelper.ContainsPlaceholders(originalManifestContent)) - { - var nameWithoutExtension = Path.GetFileNameWithoutExtension(entryPointPath); - var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - [PlaceholderHelper.TargetNameToken] = nameWithoutExtension - }; - - // Also replace the Executable attribute if it has a placeholder - var doc = AppxManifestDocument.Parse(originalManifestContent); - if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) - { - var exeName = Path.GetFileName(entryPointPath); - doc.ApplicationExecutable = exeName; - originalManifestContent = doc.ToXml(); - } - - originalManifestContent = PlaceholderHelper.ReplacePlaceholders(originalManifestContent, replacements); - PlaceholderHelper.ThrowIfUnresolvedPlaceholders(originalManifestContent); - - taskContext.AddDebugMessage($"{UiSymbols.Note} Resolved manifest placeholders for debug identity"); - } - - var originalIdentity = ParseAppxManifestAsync(originalManifestContent); - - // Step 3: Create debug identity (optionally with ".debug" suffix) - var debugIdentity = keepIdentity ? originalIdentity : CreateDebugIdentity(originalIdentity); - - // Step 4: Modify manifest for sparse packaging and debug identity - (var debugManifestContent, _) = await UpdateAppxManifestContentAsync( - originalManifestContent, - debugIdentity, - entryPointPath, - entryPointPath, - sparse: true, - selfContained: false, - dotNetPackageList, - taskContext, - cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Note} Modified manifest for sparse packaging and debug identity"); - - // Step 5: Write debug manifest - var debugManifestPath = new FileInfo(Path.Combine(debugDir.FullName, "appxmanifest.xml")); - await File.WriteAllTextAsync(debugManifestPath.FullName, debugManifestContent, Encoding.UTF8, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Files} Created debug manifest: {debugManifestPath.FullName}"); - - // Step 6: Copy all assets - var entryPointDir = Path.GetDirectoryName(entryPointPath); - if (!string.IsNullOrEmpty(entryPointDir)) - { - var entryPointDirInfo = new DirectoryInfo(entryPointDir); - var originalManifestDir = originalManifestPath.DirectoryName; - - if (!string.Equals(originalManifestDir, entryPointDirInfo.FullName, StringComparison.OrdinalIgnoreCase)) - { - var expandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(originalManifestPath, taskContext); - MrtAssetHelper.CopyAllAssets(expandedFiles, entryPointDirInfo, taskContext); - } - else - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Manifest directory and target directory are the same, skipping assets copy"); - } - } - - return (debugManifestPath, debugIdentity); - } - - /// - /// Auto-detects ProcessorArchitecture from the executable PE header and sets it in the manifest - /// if not already present. Mirrors the logic used by all three code paths (run, create-debug-identity, package). - /// Without this, ARM64 Windows resolves framework dependencies to ARM64 DLLs even for x64 apps. - /// - /// The effective architecture (detected or existing), or null if unknown. - internal static (string manifestContent, string? architecture) AutoDetectProcessorArchitecture(string manifestContent, string exePath, TaskContext taskContext) - { - var detectedArch = PeHelper.DetectPeArchitecture(exePath); - if (detectedArch == null) - { - // Can't detect — return whatever the manifest already has - var existingDoc = AppxManifestDocument.Parse(manifestContent); - return (manifestContent, existingDoc.IdentityProcessorArchitecture); - } - - var doc = AppxManifestDocument.Parse(manifestContent); - var existingArch = doc.IdentityProcessorArchitecture; - - if (existingArch == null) - { - doc.IdentityProcessorArchitecture = detectedArch; - taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-detected ProcessorArchitecture: {detectedArch}"); - return (doc.ToXml(), detectedArch); - } - - if (!string.Equals(existingArch, detectedArch, StringComparison.OrdinalIgnoreCase) - && !string.Equals(existingArch, "neutral", StringComparison.OrdinalIgnoreCase)) - { - taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest ProcessorArchitecture is '{existingArch}' but the executable is {detectedArch}. This may cause runtime failures."); - } - - return (manifestContent, existingArch); - } - - /// - /// Creates a debug version of the identity by appending ".debug" to package name and application ID - /// - private static MsixIdentityResult CreateDebugIdentity(MsixIdentityResult originalIdentity) - { - var debugPackageName = originalIdentity.PackageName.EndsWith(".debug") - ? originalIdentity.PackageName - : $"{originalIdentity.PackageName}.debug"; - - var debugApplicationId = originalIdentity.ApplicationId.EndsWith(".debug") - ? originalIdentity.ApplicationId - : $"{originalIdentity.ApplicationId}.debug"; - - return new MsixIdentityResult(debugPackageName, originalIdentity.Publisher, debugApplicationId); - } - - /// - /// Copies files referenced in the manifest to the target directory. - /// - /// - /// Checks if a package with the given name exists and unregisters it if found - /// - /// The name of the package to check and unregister - /// Cancellation token - /// True if package was found and unregistered, false if no package was found - public async Task UnregisterExistingPackageAsync(string packageName, TaskContext taskContext, CancellationToken cancellationToken = default) - { - taskContext.AddDebugMessage($"{UiSymbols.Trash} Checking for existing package..."); - - try - { - var removed = await packageRegistrationService.UnregisterAsync(packageName, cancellationToken); - - if (removed) - { - taskContext.AddDebugMessage($"{UiSymbols.Check} Existing package unregistered successfully"); - } - else - { - taskContext.AddDebugMessage($"{UiSymbols.Note} No existing package found"); - } - - return removed; - } - catch (Exception ex) - { - // If check fails, package likely doesn't exist or we don't have permission - taskContext.AddDebugMessage($"{UiSymbols.Note} Could not check for existing package: {ex.Message}"); - return false; - } - } - - /// - /// Registers a sparse package with external location using Add-AppxPackage - /// - /// Path to the appxmanifest.xml file - /// External location path (typically the working directory) - /// Cancellation token - public async Task RegisterSparsePackageAsync(FileInfo manifestPath, DirectoryInfo externalLocation, TaskContext taskContext, CancellationToken cancellationToken = default) - { - taskContext.AddDebugMessage($"{UiSymbols.Clipboard} Registering sparse package with external location..."); - - try - { - await packageRegistrationService.RegisterSparseAsync( - manifestPath.FullName, externalLocation.FullName, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Sparse package registered successfully"); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to register sparse package: {ex.Message}", ex); - } - } - - public async Task RegisterLooseLayoutPackageAsync(FileInfo manifestPath, TaskContext taskContext, CancellationToken cancellationToken = default) - { - taskContext.AddDebugMessage($"{UiSymbols.Clipboard} Registering loose layout package..."); - - try - { - await packageRegistrationService.RegisterLooseLayoutAsync( - manifestPath.FullName, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Package registered successfully"); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to register package: {ex.Message}", ex); - } - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs deleted file mode 100644 index 1da10336..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs +++ /dev/null @@ -1,881 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.IO.Compression; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Linq; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; -using WinApp.Cli.Models; -using WinApp.Cli.Tools; - -namespace WinApp.Cli.Services; - -internal partial class MsixService -{ - [GeneratedRegex(@"^Microsoft\.WindowsAppRuntime\.\d+\.\d+.*\.msix$", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex WindowsAppRuntimeMsixRegex(); - [GeneratedRegex(@"]*name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AssemblyIdentityNameRegex(); - - // DLL dedup regex — extract registered file names from SxS manifest StringBuilder - [GeneratedRegex(@" - /// Sets up Windows App SDK for self-contained deployment by extracting MSIX content - /// and preparing the necessary files for embedding in applications. - ///
- public async Task SetupSelfContainedAsync(DirectoryInfo winappDir, string architecture, TaskContext taskContext, DotNetPackageListJson? dotNetPackageList = null, CancellationToken cancellationToken = default) - { - await taskContext.AddSubTaskAsync("Setting up Self Contained", async (taskContext, cancellationToken) => - { - // Look for the Runtime package which contains the MSIX files - var selfContainedDir = winappDir.CreateSubdirectory("self-contained"); - var archSelfContainedDir = selfContainedDir.CreateSubdirectory(architecture); - - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken) ?? throw new DirectoryNotFoundException("Windows App SDK Runtime MSIX directory not found. Ensure Windows App SDK is installed."); - - // Look for the MSIX file in the tools/MSIX folder - var msixToolsDir = new DirectoryInfo(Path.Combine(msixDir.FullName, $"win10-{architecture}")); - if (!msixToolsDir.Exists) - { - throw new DirectoryNotFoundException($"MSIX tools directory not found: {msixToolsDir}"); - } - - // Try to use inventory first for accurate file selection - FileInfo? msixPath = null; - try - { - var packageEntries = await WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken); - if (packageEntries != null) - { - // Look for the base Windows App Runtime package (not Framework, DDLM, or Singleton packages) - var mainRuntimeEntry = packageEntries.FirstOrDefault(entry => - entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && - !entry.PackageIdentity.Contains("Framework") && - !entry.FileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && - !entry.FileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase)); - - if (mainRuntimeEntry != null) - { - msixPath = new FileInfo(Path.Combine(msixToolsDir.FullName, mainRuntimeEntry.FileName)); - taskContext.AddDebugMessage($"{UiSymbols.Package} Found main runtime package from inventory: {mainRuntimeEntry.FileName}"); - } - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Could not parse inventory, falling back to file search: {ex.Message}"); - } - - // Fallback: search for files directly with pattern matching - if (msixPath == null || !msixPath.Exists) - { - var msixFiles = msixToolsDir.GetFiles("Microsoft.WindowsAppRuntime.*.msix"); - if (msixFiles.Length == 0) - { - throw new FileNotFoundException($"No MSIX files found in {msixToolsDir}"); - } - - // Look for the base runtime package (format: Microsoft.WindowsAppRuntime.{version}.msix) - // Exclude files with additional suffixes like DDLM, Singleton, Framework, etc. - msixPath = msixFiles.FirstOrDefault(f => - { - var fileName = f.Name; - return !fileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && - !fileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase) && - !fileName.Contains("Framework", StringComparison.OrdinalIgnoreCase) && - WindowsAppRuntimeMsixRegex().IsMatch(fileName); - }) ?? msixFiles[0]; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Extracting MSIX: {msixPath.FullName}"); - - // Extract MSIX content - var extractedDir = new DirectoryInfo(Path.Combine(archSelfContainedDir.FullName, "extracted")); - if (extractedDir.Exists) - { - extractedDir.Delete(recursive: true); - } - extractedDir.Refresh(); - extractedDir.Create(); - - using (var archive = await ZipFile.OpenReadAsync(msixPath.FullName, cancellationToken)) - { - await archive.ExtractToDirectoryAsync(extractedDir.FullName, cancellationToken); - } - - // Copy relevant files to deployment directory - var deploymentDir = archSelfContainedDir.CreateSubdirectory("deployment"); - - // Copy DLLs, WinMD files, and other runtime assets - await CopyRuntimeFilesAsync(extractedDir, deploymentDir, taskContext, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Self-contained files prepared in: {archSelfContainedDir.FullName}"); - - return 0; - }, cancellationToken); - } - - private async Task EmbedActivationManifestToExeAsync(FileInfo exePath, DirectoryInfo winAppSDKDeploymentDir, FileInfo windowsAppSDKAppXManifestPath, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - // Use applicationLocation for DLL content (where runtime files were copied by PrepareRuntimeForPackagingAsync) - var exeDir = exePath.Directory!; - - taskContext.AddDebugMessage($"{UiSymbols.Note} Generating activation manifest from: {windowsAppSDKAppXManifestPath}"); - taskContext.AddDebugMessage($"{UiSymbols.Package} Using DLL content from: {winAppSDKDeploymentDir}"); - - // Create a temporary manifest file - var tempManifestPath = new FileInfo(Path.Combine(exeDir.FullName, "WindowsAppSDK_temp.manifest")); - - try - { - // Build the entire manifest in memory, then write to disk once - var sb = new StringBuilder(); - sb.AppendLine(""); - sb.AppendLine(""); - - // Collect all AppX manifests (main package + component fragments) and their DLLs - (var packageDependencies, _) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); - if (packageDependencies == null || packageDependencies.Count == 0) - { - throw new InvalidOperationException("No Windows SDK packages found. Please install the Windows SDK or Windows App SDK."); - } - - var architecture = WorkspaceSetupService.GetSystemArchitecture(); - IEnumerable appxFragments = GetComponents(packageDependencies); - - // Combine all manifests: main AppxManifest.xml (Package root) + fragments (Fragment root) - var allManifests = new List { windowsAppSDKAppXManifestPath }; - allManifests.AddRange(appxFragments); - - // Combine all DLL file names from deployment dir and fragment native dirs - var allDllFiles = new List(winAppSDKDeploymentDir.EnumerateFiles("*.dll").Select(di => di.Name)); - allDllFiles.AddRange(appxFragments - .Select(fragment => Path.Combine(fragment.DirectoryName!, $"win-{architecture}\\native")) - .Where(Directory.Exists) - .SelectMany(dir => Directory.EnumerateFiles(dir, "*.dll")) - .Select(Path.GetFileName)!); - - // Single pass: process all AppX manifests (auto-detects Package vs Fragment root) - AppendAppManifestFromAppx( - sb, - redirectDlls: false, - inDllFiles: allDllFiles, - inAppxManifests: allManifests); - - // Phase 3: Discover and register third-party WinRT components (e.g., Win2D, WebView2) - // These packages ship .winmd files + native DLLs but no package.appxfragment - await AppendThirdPartyWinRTManifestEntriesAsync( - sb, architecture, dotNetPackageList, taskContext, cancellationToken); - - sb.AppendLine(""); - - // Single write to disk - await File.WriteAllTextAsync( - tempManifestPath.FullName, - sb.ToString(), - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), - cancellationToken); - - // Use mt.exe to merge manifests - await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - } - finally - { - TryDeleteFile(tempManifestPath); - } - } - - private IEnumerable GetComponents(Dictionary packageDependencies) - { - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - - // Find appx fragments in the NuGet global cache (lowercase-id/version/ layout) - var appxFragments = packageDependencies - .Select(package => new FileInfo(Path.Combine(nugetCacheDir.FullName, package.Key.ToLowerInvariant(), package.Value, "runtimes-framework", "package.appxfragment"))) - .Where(f => f.Exists); - return appxFragments; - } - - /// - /// Collects all user NuGet packages from winapp.yaml or .csproj. - /// Returns the full package dictionary (name → version) for WinRT component scanning. - /// - private async Task> GetAllUserPackagesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var packages = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Path 1: Try winapp.yaml - if (configService.Exists()) - { - var config = configService.Load(); - foreach (var pkg in config.Packages) - { - packages.TryAdd(pkg.Name, pkg.Version); - } - } - else - { - // Path 2: Try .csproj via `dotnet list package --format json` (cached) - try - { - var allPackages = dotNetPackageList?.Projects? - .SelectMany(p => p.Frameworks ?? []) - .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - - if (allPackages != null) - { - foreach (var pkg in allPackages) - { - if (!string.IsNullOrEmpty(pkg.Id) && !string.IsNullOrEmpty(pkg.ResolvedVersion)) - { - packages.TryAdd(pkg.Id, pkg.ResolvedVersion); - } - } - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not retrieve package list from .csproj: {ex.Message}"); - } - } - - return packages; - } - - /// - /// Discovers third-party WinRT components and appends their activatable class - /// entries to the in-memory SxS manifest (for self-contained deployment). - /// - private async Task AppendThirdPartyWinRTManifestEntriesAsync( - StringBuilder sb, - string architecture, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) - { - var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); - if (allPackages.Count == 0) - { - return; - } - - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - - // DiscoverWinRTComponents filters out packages that have a package.appxfragment - // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. - // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 - // are transitive WinAppSDK deps but need their own InProcessServer entries. - var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); - if (components.Count == 0) - { - return; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found {components.Count} third-party WinRT component(s) to register"); - - // Build a set of DLL names already registered in the manifest (from WinAppSDK fragments) - // so we can do exact-name dedup instead of substring matching. - var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in SxsFileNameRegex().Matches(sb.ToString())) - { - registeredDlls.Add(match.Groups[1].Value); - } - - foreach (var component in components) - { - var classes = winmdService.GetActivatableClasses(component.WinmdPath); - if (classes.Count == 0) - { - continue; - } - - // Skip components whose DLL is already in the manifest (from WinAppSDK fragments - // or a previous iteration) to avoid duplicate activatableClass entries. - if (!registeredDlls.Add(component.ImplementationDll)) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); - continue; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Registering {classes.Count} activatable class(es) from {component.ImplementationDll}"); - - sb.AppendLine($" "); - foreach (var className in classes) - { - sb.AppendLine($" "); - } - sb.AppendLine(" "); - } - } - - /// - /// Discovers third-party WinRT components and generates InProcessServer - /// extension entries for AppxManifest.xml (for packaged apps). - /// - private async Task AddThirdPartyWinRTExtensionsToAppxManifestAsync( - string manifestContent, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) - { - var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); - if (allPackages.Count == 0) - { - return manifestContent; - } - - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - var architecture = WorkspaceSetupService.GetSystemArchitecture(); - - // DiscoverWinRTComponents filters out packages that have a package.appxfragment - // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. - // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 - // are transitive WinAppSDK deps but need their own InProcessServer entries. - var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); - if (components.Count == 0) - { - return manifestContent; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Adding InProcessServer entries for {components.Count} third-party WinRT component(s)"); - - var doc = AppxManifestDocument.Parse(manifestContent); - - // Build a set of DLL names already registered in the manifest - // so we can do exact-name dedup instead of substring matching. - var registeredDlls = doc.GetRegisteredExtensionDllPaths(); - - var addedAny = false; - foreach (var component in components) - { - var classes = winmdService.GetActivatableClasses(component.WinmdPath); - if (classes.Count == 0) - { - continue; - } - - // Skip components whose DLL is already in the manifest or in entries we've already generated - if (!registeredDlls.Add(component.ImplementationDll)) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); - continue; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Adding {classes.Count} activatable class(es) for {component.ImplementationDll}"); - - doc.AddInProcessServerExtension(component.ImplementationDll, classes); - addedAny = true; - } - - return addedAny ? doc.ToXml() : manifestContent; - } - - /// - /// Inserts Package-level extension entries (e.g. InProcessServer) into a manifest string. - /// Correctly distinguishes Package-level <Extensions> from Application-level ones. - /// - internal static string InsertPackageLevelExtensions(string manifestContent, string extensionEntries) - { - var doc = AppxManifestDocument.Parse(manifestContent); - var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); - - // Parse the raw extension entries as XML fragments and add them - var wrapper = XElement.Parse($"<_wrap xmlns=\"{AppxManifestDocument.DefaultNs}\">{extensionEntries}"); - foreach (var entry in wrapper.Elements()) - { - extensions.Add(entry); - } - - return doc.ToXml(); - } - - /// - /// Generates Win32 SxS manifest entries from AppX manifests (Package or Fragment format). - /// Auto-detects the root element name (Package vs Fragment) per document. - /// - /// StringBuilder to append manifest entries to - /// Whether to redirect DLLs to %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% - /// List of DLL file names to track - /// List of paths to the input AppX manifest files or fragments - internal static void AppendAppManifestFromAppx( - StringBuilder sb, - bool redirectDlls, - IEnumerable inDllFiles, - IEnumerable inAppxManifests) - { - var dllFileFormat = redirectDlls ? - @" " : - @" "; - - var dllFiles = inDllFiles.ToList(); - var hasPackageManifest = false; - - foreach (var inAppxManifest in inAppxManifests) - { - XmlDocument doc = new(); - doc.Load(inAppxManifest.FullName); - - // Auto-detect root element name (Package or Fragment) - var prefix = doc.DocumentElement?.LocalName ?? "Package"; - var isPackage = prefix == "Package"; - if (isPackage) - { - hasPackageManifest = true; - } - - var nsmgr = new XmlNamespaceManager(doc.NameTable); - nsmgr.AddNamespace("m", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); - // Add InProcessServer elements to the generated appxmanifest - var xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:InProcessServer"; - XmlNodeList? inProcessServers = doc.SelectNodes(xQuery, nsmgr); - if (inProcessServers != null) - { - foreach (XmlNode winRTFactory in inProcessServers) - { - var dllFileNode = winRTFactory.SelectSingleNode("./m:Path", nsmgr); - if (dllFileNode == null) - { - continue; - } - - var dllFile = dllFileNode.InnerText; - var typesNames = winRTFactory.SelectNodes("./m:ActivatableClass", nsmgr)?.OfType(); - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(); - if (typesNames != null) - { - foreach (var typeNode in typesNames) - { - var attribs = typeNode.Attributes?.OfType().ToArray(); - var typeName = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "ActivatableClassId") - ?.InnerText; - var xmlEntryFormat = - @" "; - sb.AppendFormat(xmlEntryFormat, typeName); - sb.AppendLine(); - dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); - } - } - sb.AppendLine(@" "); - } - } - - // Only for Package manifests with redirect - if (isPackage && redirectDlls) - { - foreach (var dllFile in dllFiles) - { - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(@""); - } - } - // Add ProxyStub elements to the generated appxmanifest - dllFiles = [.. inDllFiles]; - - xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:ProxyStub"; - var inProcessProxystubs = doc.SelectNodes(xQuery, nsmgr); - if (inProcessProxystubs != null) - { - foreach (XmlNode proxystub in inProcessProxystubs) - { - var classIDAdded = false; - - var dllFileNode = proxystub.SelectSingleNode("./m:Path", nsmgr); - var dllFile = dllFileNode?.InnerText; - // exclude PushNotificationsLongRunningTask, which requires the Singleton (which is unavailable for self-contained apps) - // exclude Widgets entries unless/until they have been tested and verified by the Widgets team - if (dllFile == null || dllFile == "PushNotificationsLongRunningTask.ProxyStub.dll" || dllFile == "Microsoft.Windows.Widgets.dll") - { - continue; - } - var typesNamesForProxy = proxystub.SelectNodes("./m:Interface", nsmgr)?.OfType(); - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(); - if (typesNamesForProxy != null) - { - foreach (var typeNode in typesNamesForProxy) - { - if (!classIDAdded) - { - var classIdAttribute = proxystub.Attributes?.OfType().ToArray(); - var classID = classIdAttribute - ?.OfType() - ?.SingleOrDefault(x => x.Name == "ClassId") - ?.InnerText; - - if (classID != null) - { - var xmlEntryFormat = @" "; - sb.AppendFormat(xmlEntryFormat, classID); - classIDAdded = true; - } - } - var attribs = typeNode.Attributes?.OfType().ToArray(); - var typeID = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "InterfaceId") - ?.InnerText; - var typeNames = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "Name") - ?.InnerText; - var xmlEntryFormatForStubs = @" "; - if (typeNames != null && typeID != null) - { - sb.AppendFormat(xmlEntryFormatForStubs, typeNames, typeID); - sb.AppendLine(); - dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); - } - } - } - sb.AppendLine(@" "); - } - } - } - - if (hasPackageManifest && redirectDlls) - { - foreach (var dllFile in dllFiles) - { - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(@""); - } - } - } - - /// - /// Updates or inserts the Windows App SDK dependency in the manifest - /// - /// The manifest content to modify - /// The modified manifest content - private async Task UpdateWindowsAppSdkDependencyAsync(string manifestContent, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var winAppSdkInfo = await GetWindowsAppSdkDependencyInfoAsync(dotNetPackageList, taskContext, cancellationToken); - - if (winAppSdkInfo == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not determine Windows App SDK version, skipping dependency update"); - return manifestContent; - } - - var doc = AppxManifestDocument.Parse(manifestContent); - const string publisher = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"; - - var dependencies = doc.GetDependenciesElement(); - if (dependencies == null) - { - // Create Dependencies element and insert before Applications (per AppxManifest schema order) - dependencies = new XElement(AppxManifestDocument.DefaultNs + "Dependencies"); - var applications = doc.Document.Root?.Element(AppxManifestDocument.DefaultNs + "Applications"); - if (applications != null) - { - applications.AddBeforeSelf(dependencies); - } - else - { - doc.Document.Root?.Add(dependencies); - } - - dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", - new XAttribute("Name", winAppSdkInfo.RuntimeName), - new XAttribute("MinVersion", winAppSdkInfo.MinVersion), - new XAttribute("Publisher", publisher))); - - taskContext.AddDebugMessage($"{UiSymbols.Package} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} (v{winAppSdkInfo.MinVersion})"); - } - else - { - // Check for existing WindowsAppRuntime dependency (prefix match for version-specific names) - var existing = dependencies.Elements(AppxManifestDocument.DefaultNs + "PackageDependency") - .FirstOrDefault(e => e.Attribute("Name")?.Value?.StartsWith("Microsoft.WindowsAppRuntime.", StringComparison.OrdinalIgnoreCase) == true); - - if (existing != null) - { - existing.SetAttributeValue("Name", winAppSdkInfo.RuntimeName); - existing.SetAttributeValue("MinVersion", winAppSdkInfo.MinVersion); - existing.SetAttributeValue("Publisher", publisher); - - taskContext.AddDebugMessage($"{UiSymbols.Sync} Updated Windows App SDK dependency to {winAppSdkInfo.RuntimeName} v{winAppSdkInfo.MinVersion}"); - } - else - { - dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", - new XAttribute("Name", winAppSdkInfo.RuntimeName), - new XAttribute("MinVersion", winAppSdkInfo.MinVersion), - new XAttribute("Publisher", publisher))); - - taskContext.AddDebugMessage($"{UiSymbols.Add} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} to existing Dependencies section (v{winAppSdkInfo.MinVersion})"); - } - } - - return doc.ToXml(); - } - - /// - /// Gets the Windows App SDK dependency information from the locked winapp.yaml config and package source - /// - /// The dependency information, or null if not found - private async Task GetWindowsAppSdkDependencyInfoAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - try - { - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); - if (msixDir == null) - { - return null; - } - - // Get the runtime package information from the MSIX inventory - var runtimeInfo = GetWindowsAppRuntimePackageInfo(taskContext, msixDir, cancellationToken); - if (runtimeInfo == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not parse Windows App Runtime package information from MSIX inventory"); - return null; - } - - return runtimeInfo; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Error getting Windows App SDK dependency info: {ex.Message}"); - return null; - } - } - - private async Task GetRuntimeMsixDirAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - (var packageDependencies, var mainVersion) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); - if (packageDependencies == null || mainVersion == null) - { - return null; - } - - // Look for the runtime package in the package dependencies - var runtimePackage = packageDependencies.FirstOrDefault(kvp => - kvp.Key.StartsWith(BuildToolsService.WINAPP_SDK_RUNTIME_PACKAGE, StringComparison.OrdinalIgnoreCase)); - - // Create a dictionary with versions for FindWindowsAppSdkMsixDirectory - var usedVersions = new Dictionary - { - [BuildToolsService.WINAPP_SDK_PACKAGE] = mainVersion - }; - - if (runtimePackage.Key != null) - { - // For Windows App SDK 1.8+, there's a separate runtime package - var runtimeVersion = runtimePackage.Value; - usedVersions[runtimePackage.Key] = runtimeVersion; - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found runtime package: {runtimePackage.Key} v{runtimeVersion}"); - } - else - { - // For Windows App SDK 1.7 and earlier, runtime is included in the main package - taskContext.AddDebugMessage($"{UiSymbols.Note} No separate runtime package found - using main package (Windows App SDK 1.7 or earlier)"); - taskContext.AddDebugMessage($"{UiSymbols.Note} Available package dependencies: {string.Join(", ", packageDependencies.Keys)}"); - } - - // Find the MSIX directory with the runtime package - var msixDir = workspaceSetupService.FindWindowsAppSdkMsixDirectory(usedVersions); - if (msixDir == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Windows App SDK MSIX directory not found for dependent runtime package"); - return null; - } - - return msixDir; - } - - private async Task<(Dictionary? CachedPackages, string? MainVersion)> GetWinAppSDKPackageDependenciesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - string? mainVersion = null; - // Path 1: Try winapp.yaml (C++ / native projects) - if (configService.Exists()) - { - var config = configService.Load(); - mainVersion = config.GetVersion(BuildToolsService.WINAPP_SDK_PACKAGE); - } - else - { - // Path 2: Try .csproj via `dotnet list package --format json` - taskContext.AddDebugMessage($"{UiSymbols.Package} Querying NuGet package list..."); - - var allPackages = dotNetPackageList?.Projects? - .SelectMany(p => p.Frameworks ?? []) - .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - - var winAppSdkPkg = allPackages? - .FirstOrDefault(p => string.Equals(p.Id, BuildToolsService.WINAPP_SDK_PACKAGE, StringComparison.OrdinalIgnoreCase)); - - if (winAppSdkPkg != null && !string.IsNullOrEmpty(winAppSdkPkg.ResolvedVersion)) - { - mainVersion = winAppSdkPkg.ResolvedVersion; - } - } - - if (string.IsNullOrEmpty(mainVersion)) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} No {BuildToolsService.WINAPP_SDK_PACKAGE} package found in winapp.yaml"); - return (null, null); - } - taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App SDK main package: v{mainVersion}"); - try - { - // Query NuGet API for the dependency tree of this package - var deps = await nugetService.GetPackageDependenciesAsync(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion, cancellationToken); - - // Include the main package itself in the result - deps.TryAdd(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion); - - return (deps, mainVersion); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} {BuildToolsService.WINAPP_SDK_PACKAGE} v{mainVersion} not found in package source: {ex.Message}"); - } - - return (null, null); - } - - /// - /// Parses the MSIX inventory file to extract Windows App Runtime package information - /// - /// The MSIX directory containing the inventory file - /// Package information, or null if not found - private static WindowsAppRuntimePackageInfo? GetWindowsAppRuntimePackageInfo(TaskContext taskContext, DirectoryInfo msixDir, CancellationToken cancellationToken) - { - try - { - // Use the shared inventory parsing logic (synchronous version) - var packageEntries = WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken).GetAwaiter().GetResult(); - - if (packageEntries == null || packageEntries.Count == 0) - { - return null; - } - - // Look for the Windows App Runtime main package (not Framework packages) - var mainRuntimeEntry = packageEntries - .FirstOrDefault(entry => entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && - !entry.PackageIdentity.Contains("Framework")); - - if (mainRuntimeEntry != null) - { - // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) - var identityParts = mainRuntimeEntry.PackageIdentity.Split('_'); - if (identityParts.Length >= 2) - { - var runtimeName = identityParts[0]; - var version = identityParts[1]; - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App Runtime: {runtimeName} v{version}"); - - return new WindowsAppRuntimePackageInfo - { - RuntimeName = runtimeName, - MinVersion = version - }; - } - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} No Windows App Runtime main package found in inventory"); - taskContext.AddDebugMessage($"{UiSymbols.Note} Available packages: {string.Join(", ", packageEntries.Select(e => e.PackageIdentity))}"); - - return null; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Error parsing MSIX inventory: {ex.Message}"); - return null; - } - } - - private static readonly string[] patterns = new[] { "*.dll", "workloads*.json", "restartAgent.exe", "map.html", "*.mui", "*.png", "*.winmd", "*.xaml", "*.xbf", "*.pri" }; - - private static async Task CopyRuntimeFilesAsync(DirectoryInfo extractedDir, DirectoryInfo deploymentDir, TaskContext taskContext, CancellationToken cancellationToken) - { - await taskContext.AddSubTaskAsync("Copying Runtime Files", (taskContext, cancellationToken) => - { - foreach (var pattern in patterns) - { - var files = extractedDir.GetFiles(pattern, SearchOption.AllDirectories); - foreach (var file in files) - { - var relativePath = Path.GetRelativePath(extractedDir.FullName, file.FullName); - var destPath = Path.Combine(deploymentDir.FullName, relativePath); - - // Create destination directory if needed - var destDir = Path.GetDirectoryName(destPath); - if (!string.IsNullOrEmpty(destDir)) - { - Directory.CreateDirectory(destDir); - } - - file.CopyTo(destPath, overwrite: true); - - taskContext.AddDebugMessage($"{UiSymbols.Files} {relativePath}"); - } - } - - return Task.FromResult(0); - }, cancellationToken); - } - - /// - /// Prepares Windows App SDK runtime files for packaging into an MSIX by extracting them to the input folder - /// - /// The folder where runtime files should be copied - /// Cancellation token - /// The path to the self-contained deployment directory - private async Task PrepareRuntimeForPackagingAsync(DirectoryInfo inputFolder, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var arch = WorkspaceSetupService.GetSystemArchitecture(); - - var winappDir = winappDirectoryService.GetLocalWinappDirectory(); - - // Extract runtime files using the existing method - await SetupSelfContainedAsync(winappDir, arch, taskContext, dotNetPackageList, cancellationToken); - - // Copy runtime files from .winapp/self-contained to input folder - var runtimeSourceDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "self-contained", arch, "deployment")); - - if (runtimeSourceDir.Exists) - { - // Copy files recursively to maintain directory structure - foreach (var file in runtimeSourceDir.GetFiles("*", SearchOption.AllDirectories)) - { - var relativePath = Path.GetRelativePath(runtimeSourceDir.FullName, file.FullName); - var destFile = Path.Combine(inputFolder.FullName, relativePath); - - // Create destination directory if needed - var destDir = Path.GetDirectoryName(destFile); - if (!string.IsNullOrEmpty(destDir)) - { - Directory.CreateDirectory(destDir); - } - - file.CopyTo(destFile, overwrite: true); - - taskContext.AddDebugMessage($"{UiSymbols.Folder} Bundled runtime: {relativePath}"); - } - - taskContext.AddDebugMessage($"{UiSymbols.Check} Windows App SDK runtime bundled into package"); - } - else - { - throw new DirectoryNotFoundException($"Runtime files not found at {runtimeSourceDir}"); - } - - return runtimeSourceDir; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs deleted file mode 100644 index f61b4601..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.Logging; -using Windows.Management.Deployment; - -namespace WinApp.Cli.Services; - -/// -/// Manages MSIX package registration, unregistration, and installation using -/// the Windows WinRT API directly, without -/// shelling out to PowerShell. -/// -internal sealed class PackageRegistrationService(ILogger logger) : IPackageRegistrationService -{ - // HRESULT 0x80073CFB = ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD (developer mode not enabled) - // HRESULT 0x800704EC = ERROR_ACCESS_DISABLED_BY_POLICY (group policy blocks sideloading) - private const int ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD = unchecked((int)0x80073CFB); - private const int ERROR_ACCESS_DISABLED_BY_POLICY = unchecked((int)0x800704EC); - - /// - public async Task RegisterLooseLayoutAsync(string manifestPath, CancellationToken cancellationToken = default) - { - var manifestUri = new Uri(Path.GetFullPath(manifestPath)); - var pm = new PackageManager(); - - try - { - var result = await pm.RegisterPackageAsync( - manifestUri, - null, - DeploymentOptions.DevelopmentMode | DeploymentOptions.ForceApplicationShutdown - ).AsTask(cancellationToken); - - if (!result.IsRegistered) - { - var errorText = result.ErrorText ?? "Unknown error"; - throw new InvalidOperationException( - $"Failed to register package: {errorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); - } - - logger.LogDebug("Package registered from loose layout: {ManifestPath}", manifestPath); - } - catch (Exception ex) when (IsDeveloperModeError(ex)) - { - throw new InvalidOperationException( - "Windows Developer Mode is required to register MSIX packages from loose files. " + - "Open Settings > System > For Developers and enable Developer Mode.", ex); - } - } - - /// - public async Task RegisterSparseAsync(string manifestPath, string externalLocation, CancellationToken cancellationToken = default) - { - var manifestUri = new Uri(Path.GetFullPath(manifestPath)); - var externalUri = new Uri(Path.GetFullPath(externalLocation) + Path.DirectorySeparatorChar); - var pm = new PackageManager(); - - try - { - var options = new RegisterPackageOptions - { - ExternalLocationUri = externalUri, - DeveloperMode = true, - ForceUpdateFromAnyVersion = true, - }; - - var result = await pm.RegisterPackageByUriAsync( - manifestUri, - options - ).AsTask(cancellationToken); - - if (!result.IsRegistered) - { - var errorText = result.ErrorText ?? "Unknown error"; - throw new InvalidOperationException( - $"Failed to register sparse package: {errorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); - } - - logger.LogDebug("Sparse package registered: {ManifestPath} (external: {ExternalLocation})", manifestPath, externalLocation); - } - catch (Exception ex) when (IsDeveloperModeError(ex)) - { - throw new InvalidOperationException( - "Windows Developer Mode is required to register MSIX packages from loose files. " + - "Open Settings > System > For Developers and enable Developer Mode.", ex); - } - } - - /// - public async Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) - { - var pm = new PackageManager(); - - // FindPackagesForUser with name+publisher requires both to match. - // Use the single-string overload to find by family name prefix, then filter by name. - var allUserPackages = pm.FindPackagesForUser(string.Empty); - var matchingPackages = allUserPackages - .Where(p => string.Equals(p.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - if (matchingPackages.Count == 0) - { - return false; - } - - foreach (var pkg in matchingPackages) - { - var fullName = pkg.Id.FullName; - logger.LogDebug("Removing package: {PackageFullName}", fullName); - - var result = await pm.RemovePackageAsync(fullName).AsTask(cancellationToken); - - if (!string.IsNullOrEmpty(result.ErrorText)) - { - logger.LogWarning("Warning removing package {PackageFullName}: {Error}", fullName, result.ErrorText); - } - } - - return true; - } - - /// - public async Task InstallPackageAsync(string packagePath, CancellationToken cancellationToken = default) - { - var packageUri = new Uri(Path.GetFullPath(packagePath)); - var pm = new PackageManager(); - - var result = await pm.AddPackageAsync( - packageUri, - null, - DeploymentOptions.ForceApplicationShutdown - ).AsTask(cancellationToken); - - if (!string.IsNullOrEmpty(result.ErrorText)) - { - throw new InvalidOperationException( - $"Failed to install package '{Path.GetFileName(packagePath)}': {result.ErrorText} (0x{result.ExtendedErrorCode?.HResult:X8})"); - } - - logger.LogDebug("Installed package: {PackagePath}", packagePath); - } - - /// - public string? GetInstalledVersion(string packageName) - { - var pm = new PackageManager(); - // Use the single-parameter overload and filter manually. - // The (userId, name, publisher) overload rejects empty/null publisher - // because string.Empty marshals as null HSTRING in WinRT interop. - var allUserPackages = pm.FindPackagesForUser(string.Empty); - - foreach (var pkg in allUserPackages) - { - if (string.Equals(pkg.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) - { - var v = pkg.Id.Version; - return $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}"; - } - } - - return null; - } - - /// - public List FindDevPackages(string packageName) - { - var pm = new PackageManager(); - var allUserPackages = pm.FindPackagesForUser(string.Empty); - var results = new List(); - - foreach (var pkg in allUserPackages) - { - if (!string.Equals(pkg.Id.Name, packageName, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - string? installLocation = null; - try - { - installLocation = pkg.InstalledLocation?.Path; - } - catch - { - // InstalledLocation can throw if the path no longer exists - } - - var v = pkg.Id.Version; - results.Add(new DevPackageInfo( - FullName: pkg.Id.FullName, - Name: pkg.Id.Name, - Version: $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}", - InstallLocation: installLocation, - IsDevelopmentMode: pkg.IsDevelopmentMode)); - } - - return results; - } - - private static bool IsDeveloperModeError(Exception ex) - { - return ex.HResult == ERROR_PACKAGE_NOT_REGISTERED_FOR_SIDELOAD - || ex.HResult == ERROR_ACCESS_DISABLED_BY_POLICY; - } -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs deleted file mode 100644 index d7340d6d..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Reflection.PortableExecutable; - -namespace WinApp.Cli.Services; - -/// -/// Static helper for detecting executable architecture from PE headers. -/// -internal static class PeHelper -{ - /// - /// Detects the architecture of a PE file and returns an MSIX-style architecture string: - /// "x86", "x64", "arm", "arm64", or "neutral". - /// - /// Rules: - /// - Native PE images are classified from the COFF Machine field. - /// - Managed .NET IL-only images are classified using COR flags: - /// * I386 + ILOnly + Requires32Bit => x86 - /// * I386 + ILOnly + !Requires32Bit => neutral - /// - Mixed-mode / native-hosted managed images fall back to the native Machine field. - /// - /// Returns null if the file is not a valid PE image or uses an unsupported architecture. - /// - internal static string? DetectPeArchitecture(string filePath) - { - try - { - using var stream = File.OpenRead(filePath); - using var peReader = new PEReader(stream); - - var headers = peReader.PEHeaders; - var coff = headers.CoffHeader; - var cor = headers.CorHeader; - - ushort machine = (ushort)coff.Machine; - - // Native or mixed-mode case: use the PE machine directly. - if (cor is null) - { - return MapNativeMachine(machine); - } - - CorFlags flags = cor.Flags; - bool isIlOnly = (flags & CorFlags.ILOnly) != 0; - bool requires32Bit = (flags & CorFlags.Requires32Bit) != 0; - - // Managed IL-only assemblies need special handling. - // In particular, IL-only I386 without Requires32Bit is effectively AnyCPU/neutral. - if (isIlOnly) - { - return machine switch - { - 0x014C => requires32Bit ? "x86" : "neutral", // I386 - 0x8664 => "x64", // unusual for pure IL-only, but valid to preserve - 0x01C0 => "arm", // ARM - 0x01C4 => "arm", // ARMNT - 0xAA64 => "arm64", // ARM64 - _ => null - }; - } - - // Mixed-mode / native-entry managed image: machine matters. - return MapNativeMachine(machine); - } - catch - { - return null; - } - } - - private static string? MapNativeMachine(ushort machine) => machine switch - { - 0x014C => "x86", // IMAGE_FILE_MACHINE_I386 - 0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64 - 0xAA64 => "arm64", // IMAGE_FILE_MACHINE_ARM64 - 0x01C4 => "arm", // IMAGE_FILE_MACHINE_ARMNT - 0x01C0 => "arm", // IMAGE_FILE_MACHINE_ARM - _ => null - }; -} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PriService.cs b/src/winapp-CLI/WinApp.Cli/Services/PriService.cs deleted file mode 100644 index 8f3286aa..00000000 --- a/src/winapp-CLI/WinApp.Cli/Services/PriService.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. -// Licensed under the MIT License. - -using System.Text.RegularExpressions; -using System.Xml; -using WinApp.Cli.ConsoleTasks; -using WinApp.Cli.Helpers; -using WinApp.Cli.Tools; - -namespace WinApp.Cli.Services; - -/// -/// Handles PRI (Package Resource Index) configuration, generation, and language extraction -/// via MakePri.exe. -/// -internal partial class PriService( - IBuildToolsService buildToolsService) : IPriService -{ - // Extracts language tag from PRI dump qualifier strings like 'Language-en-US' - [GeneratedRegex(@"qualifiers=""[^""]*Language-([a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex PriDumpLanguageQualifierRegex(); - - /// - /// Creates a PRI configuration file for the given package directory - /// - public async Task CreatePriConfigAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - IEnumerable precomputedPriResourceCandidates, - string language = "en-US", - string platformVersion = "10.0.0", - CancellationToken cancellationToken = default) - { - if (!packageDir.Exists) - { - throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); - } - - ArgumentNullException.ThrowIfNull(precomputedPriResourceCandidates); - - var resfilesPath = Path.Combine(packageDir.FullName, "pri.resfiles"); - var priResourceCandidates = precomputedPriResourceCandidates.ToList(); - - priResourceCandidates = [.. priResourceCandidates - .Where(path => MrtAssetHelper.PriIncludedExtensions.Contains(Path.GetExtension(path))) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; - - taskContext.AddDebugMessage($"PRI resource candidates discovered: {priResourceCandidates.Count}"); - - using (var writer = new StreamWriter(resfilesPath)) - { - foreach (var priFile in priResourceCandidates) - { - await writer.WriteLineAsync(priFile); - } - } - - var configPath = new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); - var arguments = $@"createconfig /cf ""{configPath}"" /dq lang-{language}_scale-200 /pv {platformVersion} /o"; - - taskContext.AddDebugMessage("Creating PRI configuration file..."); - - try - { - await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - taskContext.AddDebugMessage($"PRI configuration created: {configPath}"); - - var xmlDoc = new XmlDocument(); - xmlDoc.Load(configPath.FullName); - var resourcesNode = xmlDoc.SelectSingleNode("/resources"); - if (resourcesNode != null) - { - var indexNode = resourcesNode.SelectSingleNode("index"); - if (indexNode != null) - { - if (indexNode.Attributes?["startIndexAt"]?.Value != null) - { - // set to relative path - indexNode.Attributes["startIndexAt"]!.Value = ".\\pri.resfiles"; - } - - var resfilesIndexerNode = xmlDoc.CreateElement("indexer-config"); - var typeAttr = xmlDoc.CreateAttribute("type"); - typeAttr.Value = "resfiles"; - resfilesIndexerNode.Attributes.Append(typeAttr); - - var delimiterAttr = xmlDoc.CreateAttribute("qualifierDelimiter"); - delimiterAttr.Value = "."; - resfilesIndexerNode.Attributes.Append(delimiterAttr); - - indexNode.AppendChild(resfilesIndexerNode); - - // Ensure folder-based indexer is configured to parse qualifiers from - // both folder names and file names (e.g. targetsize-48_altform-unplated). - var folderIndexerNode = indexNode - .SelectNodes("indexer-config") - ?.OfType() - .FirstOrDefault(node => - node.Attributes?["type"]?.Value?.Equals("folder", StringComparison.OrdinalIgnoreCase) == true); - - if (folderIndexerNode?.Attributes != null) - { - var folderAttributes = folderIndexerNode.Attributes; - - var folderNameAsQualifierAttr = folderAttributes["foldernameAsQualifier"]; - if (folderNameAsQualifierAttr == null) - { - folderNameAsQualifierAttr = xmlDoc.CreateAttribute("foldernameAsQualifier"); - folderAttributes.Append(folderNameAsQualifierAttr); - } - folderNameAsQualifierAttr.Value = "true"; - - var fileNameAsQualifierAttr = folderAttributes["filenameAsQualifier"]; - if (fileNameAsQualifierAttr == null) - { - fileNameAsQualifierAttr = xmlDoc.CreateAttribute("filenameAsQualifier"); - folderAttributes.Append(fileNameAsQualifierAttr); - } - fileNameAsQualifierAttr.Value = "true"; - } - - xmlDoc.Save(configPath.FullName); - } - } - - return configPath; - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to create PRI configuration: {ex.Message}", ex); - } - } - - /// - /// Generates a PRI file from the configuration - /// - public async Task> GeneratePriFileAsync(DirectoryInfo packageDir, TaskContext taskContext, FileInfo? configPath = null, FileInfo? outputPath = null, CancellationToken cancellationToken = default) - { - if (!packageDir.Exists) - { - throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); - } - - var priConfigPath = configPath ?? new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); - var priOutputPath = outputPath ?? new FileInfo(Path.Combine(packageDir.FullName, "resources.pri")); - - if (!priConfigPath.Exists) - { - throw new FileNotFoundException($"PRI configuration file not found: {priConfigPath}"); - } - - var arguments = $@"new /pr ""{Path.TrimEndingDirectorySeparator(packageDir.FullName)}"" /cf ""{priConfigPath.FullName}"" /of ""{priOutputPath.FullName}"" /o"; - - taskContext.AddDebugMessage("Generating PRI file..."); - - try - { - var (stdout, stderr) = await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - // Parse the output to extract resource files - var resourceFiles = new List(); - var lines = stdout.Replace("\0", "").Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); - - foreach (var line in lines) - { - // Look for lines that match the pattern "Resource File: *" - const string resourceFileStr = "Resource File: "; - if (line.StartsWith(resourceFileStr, StringComparison.OrdinalIgnoreCase)) - { - var fileName = line[resourceFileStr.Length..].Trim(); - if (!string.IsNullOrEmpty(fileName)) - { - resourceFiles.Add(new FileInfo(Path.Combine(packageDir.FullName, fileName))); - } - } - } - - taskContext.AddDebugMessage($"PRI file generated: {priOutputPath}"); - if (resourceFiles.Count > 0) - { - taskContext.AddDebugMessage($"Processed {resourceFiles.Count} resource files"); - } - - return resourceFiles; - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to generate PRI file: {ex.Message}", ex); - } - } - - /// - /// Extracts language qualifiers from a PRI file using makepri dump. - /// Returns a distinct, sorted list of BCP-47 language tags found in the PRI resource map. - /// - public async Task> ExtractLanguagesFromPriAsync( - FileInfo priFile, - TaskContext taskContext, - CancellationToken cancellationToken) - { - try - { - var dumpOutputFile = Path.Combine(Path.GetTempPath(), $"winapp-pri-dump-{Guid.NewGuid():N}.xml"); - var arguments = $@"dump /if ""{priFile.FullName}"" /of ""{dumpOutputFile}"" /o"; - - await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - if (!File.Exists(dumpOutputFile)) - { - return []; - } - - try - { - var dumpContent = await File.ReadAllTextAsync(dumpOutputFile, cancellationToken); - - // Extract language qualifiers from Candidate elements: - // or multi-qualifier like "Language-en-US, Scale-200" - var languages = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in PriDumpLanguageQualifierRegex().Matches(dumpContent)) - { - languages.Add(match.Groups[1].Value); - } - - return languages.OrderBy(l => l, StringComparer.OrdinalIgnoreCase).ToList(); - } - finally - { - File.Delete(dumpOutputFile); - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to extract languages from PRI: {ex.Message}"); - return []; - } - } -} From 4aa2b111cf4c39ab8dd84c49979d057659d37a12 Mon Sep 17 00:00:00 2001 From: Nikola Metulev Date: Tue, 7 Apr 2026 13:34:37 -0700 Subject: [PATCH 6/7] Preserve MSIX app data (LocalState) across winapp run re-deploys (#403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem `winapp run` called `RemovePackageAsync` without `RemovalOptions`, which wiped the package's `LocalState`, `RoamingState`, and `Settings` folders on every re-deploy. Apps writing to `ApplicationData.Current.LocalFolder` or `LocalApplicationData` would lose all persisted data between runs. This was reported ~15 times as a pain point — developers waste 10-30 minutes debugging why data doesn't persist across restarts. ## Fix - `UnregisterAsync` now passes `RemovalOptions.PreserveApplicationData` by default when removing dev-mode packages before re-registration. This is a Windows API flag specifically designed for packages registered in development mode. - Added `--clean` flag to `winapp run` for when a fresh start is needed (e.g., reset corrupted state or test first-run behavior). - Explicit unregister (`winapp unregister`, `--unregister-on-exit`) still removes application data, since those are intentional cleanup actions. ## Changes | Area | Files | |------|-------| | Core fix | `PackageRegistrationService.cs`, `IPackageRegistrationService.cs` | | Plumbing | `MsixService.Identity.cs`, `IMsixService.cs`, `RunCommand.cs`, `UnregisterCommand.cs` | | npm SDK | `winapp-commands.ts` | | Tests | `FakeMsixService.cs`, `FakePackageRegistrationService.cs` | | Docs | `usage.md` + autogenerated `cli-schema.json`, `npm-usage.md`, `SKILL.md` | ## Behavior matrix | Scenario | App data | |----------|----------| | `winapp run ./bin/Debug` (re-deploy) | **Preserved** ✅ (new default) | | `winapp run ./bin/Debug --clean` | Wiped | | `winapp unregister` | Wiped | | `winapp run --unregister-on-exit` | Wiped on exit | --------- Co-authored-by: Nikola Metulev <711864+nmetulev@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../plugin/skills/winapp-cli/setup/SKILL.md | 1 + docs/cli-schema.json | 13 ++++ docs/npm-usage.md | 2 + docs/usage.md | 10 +++ .../WinApp.Cli.Tests/FakeMsixService.cs | 5 +- .../FakePackageRegistrationService.cs | 6 +- .../WinApp.Cli.Tests/RunCommandTests.cs | 65 ++++++++++++++++++- .../UnregisterCommandTests.cs | 4 +- .../WinApp.Cli/Commands/RunCommand.cs | 11 +++- .../WinApp.Cli/Commands/UnregisterCommand.cs | 4 +- .../WinApp.Cli/Services/IMsixService.cs | 1 + .../Services/IPackageRegistrationService.cs | 6 +- .../Services/MsixService.Identity.cs | 22 ++++--- .../Services/PackageRegistrationService.cs | 10 ++- src/winapp-npm/src/winapp-commands.ts | 3 + 15 files changed, 137 insertions(+), 26 deletions(-) diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md index 4b329c9a..02fceaca 100644 --- a/.github/plugin/skills/winapp-cli/setup/SKILL.md +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -214,6 +214,7 @@ Creates packaged layout, registers the Application, and launches the packaged ap | Option | Description | Default | |--------|-------------|---------| | `--args` | Command-line arguments to pass to the application | (none) | +| `--clean` | Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. | (none) | | `--debug-output` | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | (none) | | `--json` | Format output as JSON | (none) | | `--manifest` | Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) | (none) | diff --git a/docs/cli-schema.json b/docs/cli-schema.json index f19730b4..4b5ddd41 100644 --- a/docs/cli-schema.json +++ b/docs/cli-schema.json @@ -1356,6 +1356,19 @@ "required": false, "recursive": false }, + "--clean": { + "description": "Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments.", + "hidden": false, + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, "--debug-output": { "description": "Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json.", "hidden": false, diff --git a/docs/npm-usage.md b/docs/npm-usage.md index dc5319c8..08f9485e 100644 --- a/docs/npm-usage.md +++ b/docs/npm-usage.md @@ -336,6 +336,7 @@ function run(options: RunOptions): Promise |----------|------|----------|-------------| | `inputFolder` | `string` | Yes | Input folder containing the app to run | | `args` | `string \| undefined` | No | Command-line arguments to pass to the application | +| `clean` | `boolean \| undefined` | No | Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. | | `debugOutput` | `boolean \| undefined` | No | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | | `json` | `boolean \| undefined` | No | Format output as JSON | | `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) | @@ -911,6 +912,7 @@ type ManifestTemplates = "packaged" | "sparse" |----------|------|----------|-------------| | `inputFolder` | `string` | Yes | Input folder containing the app to run | | `args` | `string \| undefined` | No | Command-line arguments to pass to the application | +| `clean` | `boolean \| undefined` | No | Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. | | `debugOutput` | `boolean \| undefined` | No | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | | `json` | `boolean \| undefined` | No | Format output as JSON | | `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) | diff --git a/docs/usage.md b/docs/usage.md index 73e3af4a..97d36671 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -328,6 +328,13 @@ winapp run [options] - `--with-alias` - Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a `uap5:ExecutionAlias` in the manifest (use `winapp manifest add-alias` to add one). Cannot be combined with `--no-launch`. Cannot be combined with `--json`. - `--debug-output` - Capture `OutputDebugString` messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use `--no-launch` instead if you need to attach a different debugger. Cannot be combined with `--no-launch`. Cannot be combined with `--json`. - `--unregister-on-exit` - Unregister the development package after the application exits. Only removes packages registered in development mode. Cannot be combined with `--no-launch`. +- `--clean` - Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. + +**Application data persistence:** + +By default, `winapp run` preserves your application's data (`LocalState`, `RoamingState`, `Settings`, etc.) when re-deploying. If your app writes data to `ApplicationData.Current.LocalFolder` or `Environment.GetFolderPath(SpecialFolder.LocalApplicationData)` within the package context, that data will survive across `winapp run` invocations. + +Use `--clean` when you need a fresh start (e.g., to reset corrupted state or test first-run behavior). **What it does:** @@ -363,6 +370,9 @@ winapp run ./bin/Debug --with-alias --debug-output # Run and automatically clean up registration on exit winapp run ./bin/Debug --with-alias --unregister-on-exit + +# Wipe application data (LocalState, settings) and start fresh +winapp run ./bin/Debug --clean ``` **MSBuild properties (NuGet package):** diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakeMsixService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakeMsixService.cs index 7773eb03..6e7f9a3e 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakeMsixService.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/FakeMsixService.cs @@ -13,7 +13,7 @@ namespace WinApp.Cli.Tests; internal class FakeMsixService : IMsixService { public MsixIdentityResult FakeIdentityResult { get; set; } = new("TestPackage", "CN=TestPublisher", "TestApp"); - public List AddLooseLayoutCalls { get; } = []; + public List<(string ManifestPath, bool Clean)> AddLooseLayoutCalls { get; } = []; public Exception? ExceptionToThrow { get; set; } public Task AddLooseLayoutIdentityAsync( @@ -21,9 +21,10 @@ public Task AddLooseLayoutIdentityAsync( DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, + bool clean = false, CancellationToken cancellationToken = default) { - AddLooseLayoutCalls.Add(appxManifestPath.FullName); + AddLooseLayoutCalls.Add((appxManifestPath.FullName, clean)); if (ExceptionToThrow != null) { throw ExceptionToThrow; diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs index 8e8c1fda..aeebe287 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/FakePackageRegistrationService.cs @@ -13,7 +13,7 @@ internal class FakePackageRegistrationService : IPackageRegistrationService { public List RegisterLooseLayoutCalls { get; } = []; public List<(string ManifestPath, string ExternalLocation)> RegisterSparseCalls { get; } = []; - public List UnregisterCalls { get; } = []; + public List<(string PackageName, bool PreserveAppData)> UnregisterCalls { get; } = []; public List InstallPackageCalls { get; } = []; public List GetInstalledVersionCalls { get; } = []; public List FindDevPackagesCalls { get; } = []; @@ -48,9 +48,9 @@ public Task RegisterSparseAsync(string manifestPath, string externalLocation, Ca return Task.CompletedTask; } - public Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) + public Task UnregisterAsync(string packageName, bool preserveAppData = true, CancellationToken cancellationToken = default) { - UnregisterCalls.Add(packageName); + UnregisterCalls.Add((packageName, preserveAppData)); return Task.FromResult(FakeUnregisterResult); } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs index 86c18b1a..12672efe 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs @@ -152,6 +152,34 @@ public async Task ParseOptions_AllOptions_AreParsedCorrectly() Assert.AreEqual(_tempDirectory.FullName, folder.FullName); } + [TestMethod] + public void ParseOptions_Clean_IsParsedCorrectly() + { + // Arrange + var command = GetRequiredService(); + + // Act + var parseResult = command.Parse([_tempDirectory.FullName, "--clean"]); + + // Assert + Assert.IsEmpty(parseResult.Errors, "There should be no parsing errors"); + Assert.IsTrue(parseResult.GetValue(RunCommand.CleanOption)); + } + + [TestMethod] + public void ParseOptions_CleanNotSpecified_DefaultsToFalse() + { + // Arrange + var command = GetRequiredService(); + + // Act + var parseResult = command.Parse([_tempDirectory.FullName]); + + // Assert + Assert.IsEmpty(parseResult.Errors, "There should be no parsing errors"); + Assert.IsFalse(parseResult.GetValue(RunCommand.CleanOption)); + } + #endregion #region Handler tests @@ -169,9 +197,42 @@ public async Task RunCommand_WithNoLaunch_RegistersIdentityButDoesNotLaunch() // Assert Assert.AreEqual(0, exitCode, "Command should succeed"); Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); + Assert.IsFalse(_fakeMsixService.AddLooseLayoutCalls[0].Clean, "Default run should preserve app data (clean=false)"); Assert.AreEqual(0, _fakeAppLauncherService.LaunchCalls.Count, "Application should NOT be launched with --no-launch"); } + [TestMethod] + public async Task RunCommand_WithClean_PassesCleanThroughToMsixService() + { + // Arrange + await CreateTestManifestAsync(); + var command = GetRequiredService(); + + // Act + var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--no-launch", "--clean"]); + + // Assert + Assert.AreEqual(0, exitCode, "Command should succeed"); + Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); + Assert.IsTrue(_fakeMsixService.AddLooseLayoutCalls[0].Clean, "--clean should be passed through to MSIX service"); + } + + [TestMethod] + public async Task RunCommand_WithoutClean_DefaultsToPreservingAppData() + { + // Arrange + await CreateTestManifestAsync(); + var command = GetRequiredService(); + + // Act + var exitCode = await ParseAndInvokeWithCaptureAsync(command, [_tempDirectory.FullName, "--no-launch"]); + + // Assert + Assert.AreEqual(0, exitCode, "Command should succeed"); + Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); + Assert.IsFalse(_fakeMsixService.AddLooseLayoutCalls[0].Clean, "Without --clean, app data should be preserved"); + } + [TestMethod] public async Task RunCommand_WithNoLaunchAndManifest_RegistersIdentityButDoesNotLaunch() { @@ -202,7 +263,7 @@ public async Task RunCommand_WithInputFolder_ResolvesManifestFromFolder() // Assert Assert.AreEqual(0, exitCode, "Command should succeed"); Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); - StringAssert.Contains(_fakeMsixService.AddLooseLayoutCalls[0], subFolder.FullName, + StringAssert.Contains(_fakeMsixService.AddLooseLayoutCalls[0].ManifestPath, subFolder.FullName, "Manifest should be resolved from the input folder"); } @@ -220,7 +281,7 @@ public async Task RunCommand_WithInputFolderAndManifest_UsesExplicitManifest() // Assert Assert.AreEqual(0, exitCode, "Command should succeed"); Assert.AreEqual(1, _fakeMsixService.AddLooseLayoutCalls.Count, "Debug identity should be created"); - StringAssert.Contains(_fakeMsixService.AddLooseLayoutCalls[0], manifest.FullName, + StringAssert.Contains(_fakeMsixService.AddLooseLayoutCalls[0].ManifestPath, manifest.FullName, "Explicit --manifest should take priority"); } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs index 21aeca83..ea6e70b4 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/UnregisterCommandTests.cs @@ -77,7 +77,7 @@ public async Task UnregisterCommand_WithManifest_UnregistersDevPackages() // Assert Assert.AreEqual(0, exitCode); Assert.IsTrue(_fakePackageRegistrationService.FindDevPackagesCalls.Contains("TestPackage")); - Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Contains("TestPackage")); + Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Any(c => c.PackageName == "TestPackage")); } [TestMethod] @@ -158,7 +158,7 @@ public async Task UnregisterCommand_WithForce_SkipsLocationCheck() // Assert Assert.AreEqual(0, exitCode); - Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Contains("TestPackage")); + Assert.IsTrue(_fakePackageRegistrationService.UnregisterCalls.Any(c => c.PackageName == "TestPackage")); } [TestMethod] diff --git a/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs index c6517879..22e06895 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs @@ -26,6 +26,7 @@ internal partial class RunCommand : Command, IShortDescription public static Option WithAliasOption { get; } public static Option DebugOutputOption { get; } public static Option UnregisterOnExitOption { get; } + public static Option CleanOption { get; } static RunCommand() { @@ -71,6 +72,11 @@ static RunCommand() { Description = "Unregister the development package after the application exits. Only removes packages registered in development mode." }; + + CleanOption = new Option("--clean") + { + Description = "Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments." + }; } public RunCommand() : base("run", "Creates packaged layout, registers the Application, and launches the packaged application.") @@ -83,6 +89,7 @@ public RunCommand() : base("run", "Creates packaged layout, registers the Applic Options.Add(WithAliasOption); Options.Add(DebugOutputOption); Options.Add(UnregisterOnExitOption); + Options.Add(CleanOption); Options.Add(WinAppRootCommand.JsonOption); } @@ -106,6 +113,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio var withAlias = parseResult.GetValue(WithAliasOption); var debugOutput = parseResult.GetValue(DebugOutputOption); var unregisterOnExit = parseResult.GetValue(UnregisterOnExitOption); + var clean = parseResult.GetValue(CleanOption); var isJson = parseResult.GetValue(WinAppRootCommand.JsonOption); // Validate mutually exclusive options @@ -194,6 +202,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio inputFolder, outputAppXDirectory, taskContext, + clean, cancellationToken); packageFamilyName = appLauncherService.ComputePackageFamilyName( @@ -354,7 +363,7 @@ private async Task UnregisterDevPackageAsync(string packageName, CancellationTok continue; } - await packageRegistrationService.UnregisterAsync(pkg.Name, cancellationToken); + await packageRegistrationService.UnregisterAsync(pkg.Name, preserveAppData: false, cancellationToken); logger.LogDebug("Unregistered package {FullName} on exit.", pkg.FullName); } } diff --git a/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs b/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs index a9817cee..1eeb9b89 100644 --- a/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs +++ b/src/winapp-CLI/WinApp.Cli/Commands/UnregisterCommand.cs @@ -121,8 +121,8 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio } } - // Unregister - await packageRegistrationService.UnregisterAsync(name, cancellationToken); + // Explicit unregister command — remove package and its data + await packageRegistrationService.UnregisterAsync(name, preserveAppData: false, cancellationToken); if (!isJson) { diff --git a/src/winapp-CLI/WinApp.Cli/Services/IMsixService.cs b/src/winapp-CLI/WinApp.Cli/Services/IMsixService.cs index ae129fcb..032b2725 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/IMsixService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/IMsixService.cs @@ -38,5 +38,6 @@ public Task AddLooseLayoutIdentityAsync( DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, + bool clean = false, CancellationToken cancellationToken = default); } diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs index 1e94be56..344a2110 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/IPackageRegistrationService.cs @@ -30,9 +30,13 @@ internal interface IPackageRegistrationService /// Unregisters an installed package by name. Returns true if a package was found and removed. ///
/// The package identity name (e.g. MyCompany.MyApp). + /// + /// When true, preserves the package's application data (LocalState, RoamingState, Settings, etc.) + /// during removal. Only supported for packages registered in development mode. + /// /// Cancellation token. /// True if a package was unregistered, false if no matching package was found. - Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default); + Task UnregisterAsync(string packageName, bool preserveAppData = true, CancellationToken cancellationToken = default); /// /// Installs an MSIX/APPX package file, optionally forcing application shutdown. diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs index b6dac4f6..e128d97e 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs @@ -88,8 +88,8 @@ public async Task AddSparseIdentityAsync(string? entryPointP var entryPointDir = Path.GetDirectoryName(entryPointPath); var externalLocation = new DirectoryInfo(string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir); - // Unregister any existing package first - await UnregisterExistingPackageAsync(debugIdentity.PackageName, taskContext, cancellationToken); + // Unregister any existing package first (preserving app data by default) + await UnregisterExistingPackageAsync(debugIdentity.PackageName, taskContext, cancellationToken: cancellationToken); // Register the new debug manifest with external location await RegisterSparsePackageAsync(debugManifestPath, externalLocation, taskContext, cancellationToken); @@ -98,7 +98,7 @@ public async Task AddSparseIdentityAsync(string? entryPointP return new MsixIdentityResult(debugIdentity.PackageName, debugIdentity.Publisher, debugIdentity.ApplicationId); } - public async Task AddLooseLayoutIdentityAsync(FileInfo appxManifestPath, DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, CancellationToken cancellationToken = default) + public async Task AddLooseLayoutIdentityAsync(FileInfo appxManifestPath, DirectoryInfo inputDirectory, DirectoryInfo outputAppXDirectory, TaskContext taskContext, bool clean = false, CancellationToken cancellationToken = default) { // Validate inputs if (!appxManifestPath.Exists) @@ -146,8 +146,8 @@ public async Task AddLooseLayoutIdentityAsync(FileInfo appxM var identity = ParseAppxManifestAsync(manifestContent); - // Unregister any existing package first - await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); + // Unregister any existing package first (preserving app data by default) + await UnregisterExistingPackageAsync(identity.PackageName, taskContext, preserveAppData: !clean, cancellationToken); // Register from the AppX layout directory var registrationManifest = new FileInfo(Path.Combine(outputAppXDirectory.FullName, "AppxManifest.xml")); @@ -252,8 +252,8 @@ await priService.CreatePriConfigAsync( // Install the Windows App Runtime framework packages if not already present await EnsureWindowsAppRuntimeInstalledAsync(dotNetPackageList, taskContext, cancellationToken); - // Unregister any existing package first - await UnregisterExistingPackageAsync(identity.PackageName, taskContext, cancellationToken); + // Unregister any existing package first (preserving app data by default) + await UnregisterExistingPackageAsync(identity.PackageName, taskContext, preserveAppData: !clean, cancellationToken); // Register the new debug manifest with external location await RegisterLooseLayoutPackageAsync(copiedAppxManifestPath, taskContext, cancellationToken); @@ -767,19 +767,21 @@ private static MsixIdentityResult CreateDebugIdentity(MsixIdentityResult origina /// Checks if a package with the given name exists and unregisters it if found /// /// The name of the package to check and unregister + /// Task context for debug output + /// When true, preserves the package's application data during removal /// Cancellation token /// True if package was found and unregistered, false if no package was found - public async Task UnregisterExistingPackageAsync(string packageName, TaskContext taskContext, CancellationToken cancellationToken = default) + public async Task UnregisterExistingPackageAsync(string packageName, TaskContext taskContext, bool preserveAppData = true, CancellationToken cancellationToken = default) { taskContext.AddDebugMessage($"{UiSymbols.Trash} Checking for existing package..."); try { - var removed = await packageRegistrationService.UnregisterAsync(packageName, cancellationToken); + var removed = await packageRegistrationService.UnregisterAsync(packageName, preserveAppData, cancellationToken); if (removed) { - taskContext.AddDebugMessage($"{UiSymbols.Check} Existing package unregistered successfully"); + taskContext.AddDebugMessage($"{UiSymbols.Check} Existing package unregistered successfully{(preserveAppData ? " (app data preserved)" : "")}"); } else { diff --git a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs b/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs index f61b4601..9bb74f60 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/PackageRegistrationService.cs @@ -88,7 +88,7 @@ public async Task RegisterSparseAsync(string manifestPath, string externalLocati } /// - public async Task UnregisterAsync(string packageName, CancellationToken cancellationToken = default) + public async Task UnregisterAsync(string packageName, bool preserveAppData = true, CancellationToken cancellationToken = default) { var pm = new PackageManager(); @@ -107,9 +107,13 @@ public async Task UnregisterAsync(string packageName, CancellationToken ca foreach (var pkg in matchingPackages) { var fullName = pkg.Id.FullName; - logger.LogDebug("Removing package: {PackageFullName}", fullName); + logger.LogDebug("Removing package: {PackageFullName} (preserveAppData={PreserveAppData})", fullName, preserveAppData); - var result = await pm.RemovePackageAsync(fullName).AsTask(cancellationToken); + var removalOptions = preserveAppData + ? RemovalOptions.PreserveApplicationData + : RemovalOptions.None; + + var result = await pm.RemovePackageAsync(fullName, removalOptions).AsTask(cancellationToken); if (!string.IsNullOrEmpty(result.ErrorText)) { diff --git a/src/winapp-npm/src/winapp-commands.ts b/src/winapp-npm/src/winapp-commands.ts index c0af1d71..2b2965bc 100644 --- a/src/winapp-npm/src/winapp-commands.ts +++ b/src/winapp-npm/src/winapp-commands.ts @@ -441,6 +441,8 @@ export interface RunOptions extends CommonOptions { inputFolder: string; /** Command-line arguments to pass to the application */ args?: string; + /** Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. */ + clean?: boolean; /** Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. */ debugOutput?: boolean; /** Format output as JSON */ @@ -464,6 +466,7 @@ export async function run(options: RunOptions): Promise { const args: string[] = ['run']; args.push(options.inputFolder); if (options.args) args.push('--args', options.args); + if (options.clean) args.push('--clean'); if (options.debugOutput) args.push('--debug-output'); if (options.json) args.push('--json'); if (options.manifest) args.push('--manifest', options.manifest); From ba4e91965b1904415322c9c6d82c7cff9cf9f497 Mon Sep 17 00:00:00 2001 From: Chiara Mooney <34109996+chiaramooney@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:26:11 -0700 Subject: [PATCH 7/7] Merge --- .../skills/winapp-cli/frameworks/SKILL.md | 2 +- .../plugin/skills/winapp-cli/setup/SKILL.md | 9 +- docs/cli-schema.json | 89 + docs/npm-usage.md | 76 +- .../FakeAppLauncherService.cs | 12 + .../WinApp.Cli.Tests/MsixServiceTests.cs | 243 ++- .../WinApp.Cli.Tests/WinApp.Cli.Tests.csproj | 2 +- .../Helpers/HostBuilderExtensions.cs | 1 + .../Services/AppxManifestDocument.cs | 626 ++++++ .../WinApp.Cli/Services/IPriService.cs | 29 + .../Services/IncrementalCopyHelper.cs | 107 + .../WinApp.Cli/Services/MrtAssetHelper.cs | 281 +++ .../Services/MsixService.Runtime.cs | 881 ++++++++ .../WinApp.Cli/Services/MsixService.cs | 1925 ++--------------- .../WinApp.Cli/Services/PeHelper.cs | 82 + .../WinApp.Cli/Services/PriService.cs | 240 ++ src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj | 2 +- src/winapp-npm/scripts/generate-commands.mjs | 1 + src/winapp-npm/src/winapp-commands.ts | 90 +- 19 files changed, 2868 insertions(+), 1830 deletions(-) create mode 100644 src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/IPriService.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs create mode 100644 src/winapp-CLI/WinApp.Cli/Services/PriService.cs diff --git a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md index 064a3f34..4672f2be 100644 --- a/.github/plugin/skills/winapp-cli/frameworks/SKILL.md +++ b/.github/plugin/skills/winapp-cli/frameworks/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-frameworks description: Framework-specific Windows development guidance for Electron, .NET (WPF, WinForms), C++, Rust, Flutter, and Tauri. Use when packaging or adding Windows features to an Electron app, .NET desktop app, Flutter app, Tauri app, Rust app, or C++ app. -version: 0.2.2 +version: 0.2.1 --- ## When to use diff --git a/.github/plugin/skills/winapp-cli/setup/SKILL.md b/.github/plugin/skills/winapp-cli/setup/SKILL.md index fd1cd567..0a3e305b 100644 --- a/.github/plugin/skills/winapp-cli/setup/SKILL.md +++ b/.github/plugin/skills/winapp-cli/setup/SKILL.md @@ -1,7 +1,7 @@ --- name: winapp-setup description: Set up a Windows app project for MSIX packaging, Windows SDK access, or Windows API usage. Use when adding Windows support to an Electron, .NET, C++, Rust, Flutter, or Tauri project, or restoring SDK packages after cloning. -version: 0.2.2 +version: 0.2.1 --- ## When to use @@ -24,8 +24,6 @@ npm install --save-dev @microsoft/winappcli You need an **existing app project** — `winapp init` does **not** create new projects, it adds Windows platform files to your existing codebase. -> **Already have a `Package.appxmanifest`?** .NET projects that already have a packaging manifest (e.g., WinUI 3 apps or projects with an existing MSIX packaging setup) likely **don't need `winapp init`**. Ensure your `.csproj` references the `Microsoft.WindowsAppSDK` NuGet package and has the right properties for packaged builds (e.g., `MSIX`). WinUI 3 apps created from Visual Studio templates are typically already fully configured — you can go straight to building and using `winapp run` or `winapp package`. - ## Key concepts **`appxmanifest.xml`** is the most important file winapp creates — it declares your app's identity, capabilities, and visual assets. Most winapp commands require it (`package`, `run`, `cert generate --manifest`). @@ -94,10 +92,6 @@ winapp run ./dist --manifest ./out/AppxManifest.xml --args "--my-flag value" # Register identity without launching (useful for attaching a debugger manually) winapp run ./bin/Debug --no-launch - -# Launch and capture OutputDebugString messages and first-chance exceptions -# Note: prevents other debuggers (VS, VS Code) from attaching — use --no-launch if you need those instead -winapp run ./bin/Debug --debug-output ``` Use `winapp run` during iterative development — it creates a loose layout package, registers a debug identity, and launches the app in one step. For identity-only registration without loose layout, use `winapp create-debug-identity` instead. @@ -121,7 +115,6 @@ For console apps, add `--with-alias` to preserve stdin/stdout in the current ter For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md). - ## Recommended workflow 1. **Initialize** — `winapp init --use-defaults` in your existing project diff --git a/docs/cli-schema.json b/docs/cli-schema.json index 6046d357..a3ce54b7 100644 --- a/docs/cli-schema.json +++ b/docs/cli-schema.json @@ -1436,6 +1436,19 @@ "required": false, "recursive": false }, + "--unregister-on-exit": { + "description": "Unregister the development package after the application exits. Only removes packages registered in development mode.", + "hidden": false, + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, "--verbose": { "description": "Enable verbose output", "hidden": false, @@ -1599,6 +1612,82 @@ } } }, + "unregister": { + "description": "Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity').", + "hidden": false, + "options": { + "--force": { + "description": "Skip the install-location directory check and unregister even if the package was registered from a different project tree", + "hidden": false, + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--json": { + "description": "Format output as JSON", + "hidden": false, + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--manifest": { + "description": "Path to the appxmanifest.xml (default: auto-detect from current directory)", + "hidden": false, + "valueType": "System.IO.FileInfo", + "hasDefaultValue": false, + "arity": { + "minimum": 1, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--quiet": { + "description": "Suppress progress messages", + "hidden": false, + "aliases": [ + "-q" + ], + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + }, + "--verbose": { + "description": "Enable verbose output", + "hidden": false, + "aliases": [ + "-v" + ], + "valueType": "System.Boolean", + "hasDefaultValue": true, + "defaultValue": false, + "arity": { + "minimum": 0, + "maximum": 1 + }, + "required": false, + "recursive": false + } + } + }, "update": { "description": "Check for and install newer SDK versions. Updates winapp.yaml with latest versions and reinstalls packages. Requires existing winapp.yaml (created by 'init'). Use --setup-sdks preview for preview SDKs. To reinstall current versions without updating, use 'restore' instead.", "hidden": false, diff --git a/docs/npm-usage.md b/docs/npm-usage.md index fc76d9ee..0de47d62 100644 --- a/docs/npm-usage.md +++ b/docs/npm-usage.md @@ -72,7 +72,7 @@ function certGenerate(options?: CertGenerateOptions): Promise | `ifExists` | `IfExists \| undefined` | No | Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) | | `install` | `boolean \| undefined` | No | Install the certificate to the local machine store after generation | | `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml file to extract publisher information from | +| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml or Package.appxmanifest file to extract publisher information from | | `output` | `string \| undefined` | No | Output path for the generated PFX file | | `password` | `string \| undefined` | No | Password for the generated PFX file | | `publisher` | `string \| undefined` | No | Publisher name for the generated certificate. If not specified, will be inferred from manifest. | @@ -208,6 +208,26 @@ function init(options?: InitOptions): Promise --- +### `manifestAddAlias()` + +Add an execution alias (uap5:AppExecutionAlias) to an appxmanifest.xml. This allows launching the packaged app from the command line by typing the alias name. By default, the alias is inferred from the Executable attribute (e.g. $targetnametoken$.exe becomes $targetnametoken$.exe alias). + +```typescript +function manifestAddAlias(options?: ManifestAddAliasOptions): Promise +``` + +**Options:** + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `appId` | `string \| undefined` | No | Application Id to add the alias to (default: first Application element) | +| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | +| `name` | `string \| undefined` | No | Alias name (e.g. 'myapp.exe'). Default: inferred from the Executable attribute in the manifest. | + +*Also accepts [CommonOptions](#commonoptions) (`quiet`, `verbose`, `cwd`).* + +--- + ### `manifestGenerate()` Create appxmanifest.xml without full project setup. Use when you only need a manifest and image assets (no SDKs, no certificate). For full setup, use 'init' instead. Templates: 'packaged' (full MSIX), 'sparse' (desktop app needing Windows APIs). @@ -247,7 +267,7 @@ function manifestUpdateAssets(options: ManifestUpdateAssetsOptions): Promise @@ -301,12 +321,12 @@ function restore(options?: RestoreOptions): Promise --- -### `run()` +### `runApp()` Creates packaged layout, registers the Application, and launches the packaged application. ```typescript -function run(options: RunOptions): Promise +function runApp(options: RunOptions): Promise ``` **Options:** @@ -385,6 +405,26 @@ function tool(options?: ToolOptions): Promise --- +### `unregister()` + +Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity'). + +```typescript +function unregister(options?: UnregisterOptions): Promise +``` + +**Options:** + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `force` | `boolean \| undefined` | No | Skip the install-location directory check and unregister even if the package was registered from a different project tree | +| `json` | `boolean \| undefined` | No | Format output as JSON | +| `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from current directory) | + +*Also accepts [CommonOptions](#commonoptions) (`quiet`, `verbose`, `cwd`).* + +--- + ### `update()` Check for and install newer SDK versions. Updates winapp.yaml with latest versions and reinstalls packages. Requires existing winapp.yaml (created by 'init'). Use --setup-sdks preview for preview SDKs. To reinstall current versions without updating, use 'restore' instead. @@ -715,7 +755,7 @@ type ManifestTemplates = "packaged" | "sparse" | `ifExists` | `IfExists \| undefined` | No | Behavior when output file exists: 'error' (fail, default), 'skip' (keep existing), or 'overwrite' (replace) | | `install` | `boolean \| undefined` | No | Install the certificate to the local machine store after generation | | `json` | `boolean \| undefined` | No | Format output as JSON | -| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml file to extract publisher information from | +| `manifest` | `string \| undefined` | No | Path to appxmanifest.xml or Package.appxmanifest file to extract publisher information from | | `output` | `string \| undefined` | No | Output path for the generated PFX file | | `password` | `string \| undefined` | No | Password for the generated PFX file | | `publisher` | `string \| undefined` | No | Publisher name for the generated certificate. If not specified, will be inferred from manifest. | @@ -796,6 +836,17 @@ type ManifestTemplates = "packaged" | "sparse" | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | +### `ManifestAddAliasOptions` + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `appId` | `string \| undefined` | No | Application Id to add the alias to (default: first Application element) | +| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | +| `name` | `string \| undefined` | No | Alias name (e.g. 'myapp.exe'). Default: inferred from the Executable attribute in the manifest. | +| `quiet` | `boolean \| undefined` | No | Suppress progress messages. | +| `verbose` | `boolean \| undefined` | No | Enable verbose output. | +| `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | + ### `ManifestGenerateOptions` | Property | Type | Required | Description | @@ -818,7 +869,7 @@ type ManifestTemplates = "packaged" | "sparse" | Property | Type | Required | Description | |----------|------|----------|-------------| | `imagePath` | `string` | Yes | Path to source image file | -| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml file (default: search current directory) | +| `manifest` | `string \| undefined` | No | Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) | | `quiet` | `boolean \| undefined` | No | Suppress progress messages. | | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | @@ -901,6 +952,17 @@ type ManifestTemplates = "packaged" | "sparse" | `verbose` | `boolean \| undefined` | No | Enable verbose output. | | `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | +### `UnregisterOptions` + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `force` | `boolean \| undefined` | No | Skip the install-location directory check and unregister even if the package was registered from a different project tree | +| `json` | `boolean \| undefined` | No | Format output as JSON | +| `manifest` | `string \| undefined` | No | Path to the appxmanifest.xml (default: auto-detect from current directory) | +| `quiet` | `boolean \| undefined` | No | Suppress progress messages. | +| `verbose` | `boolean \| undefined` | No | Enable verbose output. | +| `cwd` | `string \| undefined` | No | Working directory for the CLI process (defaults to process.cwd()). | + ### `UpdateOptions` | Property | Type | Required | Description | diff --git a/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs b/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs index dd2fa42e..e3d3966f 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/FakeAppLauncherService.cs @@ -11,7 +11,9 @@ namespace WinApp.Cli.Tests; internal class FakeAppLauncherService : IAppLauncherService { public List<(string Aumid, string? Arguments)> LaunchCalls { get; } = []; + public List<(string? PackageFullName, uint ProcessId)> TerminateCalls { get; } = []; public uint FakeProcessId { get; set; } = 12345; + public string? FakePackageFullName { get; set; } = "FakePackage_1.0.0.0_x64__fakefamily"; public uint LaunchByAumid(string aumid, string? arguments = null) { @@ -23,4 +25,14 @@ public string ComputePackageFamilyName(string packageName, string publisher) { return $"{packageName}_fakefamily"; } + + public string? GetPackageFullName(string packageFamilyName) + { + return FakePackageFullName; + } + + public void TerminatePackageProcesses(string? packageFullName, uint processId) + { + TerminateCalls.Add((packageFullName, processId)); + } } diff --git a/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs b/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs index 15fcc645..57662258 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs +++ b/src/winapp-CLI/WinApp.Cli.Tests/MsixServiceTests.cs @@ -3,7 +3,9 @@ using System.Reflection; using System.Text; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using WinApp.Cli.ConsoleTasks; using WinApp.Cli.Services; namespace WinApp.Cli.Tests; @@ -740,7 +742,7 @@ public void FindManifestInDirectory_ReturnsNull_WhenNoManifest() public void DetectPeArchitecture_ReturnsNull_ForNonExistentFile() { // Act - var result = MsixService.DetectPeArchitecture(Path.Combine(_tempDir.FullName, "nonexistent.exe")); + var result = PeHelper.DetectPeArchitecture(Path.Combine(_tempDir.FullName, "nonexistent.exe")); // Assert Assert.IsNull(result); @@ -754,7 +756,7 @@ public void DetectPeArchitecture_ReturnsNull_ForNonPeFile() File.WriteAllText(path, "This is not a PE file"); // Act - var result = MsixService.DetectPeArchitecture(path); + var result = PeHelper.DetectPeArchitecture(path); // Assert Assert.IsNull(result); @@ -768,7 +770,7 @@ public void DetectPeArchitecture_ReturnsNull_ForTruncatedFile() File.WriteAllBytes(path, [0x4D, 0x5A]); // Just MZ header, nothing else // Act - var result = MsixService.DetectPeArchitecture(path); + var result = PeHelper.DetectPeArchitecture(path); // Assert Assert.IsNull(result); @@ -786,7 +788,7 @@ public void DetectPeArchitecture_ReturnsExpected_ForValidPeHeader(ushort machine File.WriteAllBytes(path, pe); // Act - var result = MsixService.DetectPeArchitecture(path); + var result = PeHelper.DetectPeArchitecture(path); // Assert Assert.AreEqual(expected, result); @@ -801,7 +803,7 @@ public void DetectPeArchitecture_ReturnsNull_ForUnknownMachineType() File.WriteAllBytes(path, pe); // Act - var result = MsixService.DetectPeArchitecture(path); + var result = PeHelper.DetectPeArchitecture(path); // Assert Assert.IsNull(result); @@ -845,14 +847,161 @@ private static byte[] BuildMinimalNativePe(ushort machineType) #endregion + #region AutoDetectProcessorArchitecture Tests + + private const string ManifestWithoutArch = """ + + + + + """; + + private const string ManifestWithX86Arch = """ + + + + + """; + + private const string ManifestWithNeutralArch = """ + + + + + """; + + private static TaskContext CreateTestTaskContext() + { + var task = new GroupableTask("test", null); + var console = new Spectre.Console.Testing.TestConsole(); + var logger = NullLogger.Instance; + var renderLock = new Lock(); + return new TaskContext(task, null, console, logger, renderLock); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_SetsArch_WhenMissingFromManifest() + { + // Arrange — x64 PE, manifest without ProcessorArchitecture + var exePath = Path.Combine(_tempDir.FullName, "test.exe"); + File.WriteAllBytes(exePath, BuildMinimalNativePe(0x8664)); // x64 + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithoutArch, exePath, taskContext); + + // Assert + Assert.AreEqual("x64", arch); + StringAssert.Contains(content, "ProcessorArchitecture=\"x64\""); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_SetsArm64_WhenMissingFromManifest() + { + // Arrange — arm64 PE, manifest without ProcessorArchitecture + var exePath = Path.Combine(_tempDir.FullName, "test.exe"); + File.WriteAllBytes(exePath, BuildMinimalNativePe(0xAA64)); // arm64 + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithoutArch, exePath, taskContext); + + // Assert + Assert.AreEqual("arm64", arch); + StringAssert.Contains(content, "ProcessorArchitecture=\"arm64\""); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenMatchesExe() + { + // Arrange — x86 PE, manifest already has x86 + var exePath = Path.Combine(_tempDir.FullName, "test.exe"); + File.WriteAllBytes(exePath, BuildMinimalNativePe(0x014C)); // x86 + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithX86Arch, exePath, taskContext); + + // Assert — manifest unchanged, architecture returned + Assert.AreEqual("x86", arch); + Assert.AreEqual(ManifestWithX86Arch, content, "Manifest should not be modified when arch matches"); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenMismatch() + { + // Arrange — x64 PE, manifest says x86 → should warn but keep existing + var exePath = Path.Combine(_tempDir.FullName, "test.exe"); + File.WriteAllBytes(exePath, BuildMinimalNativePe(0x8664)); // x64 + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithX86Arch, exePath, taskContext); + + // Assert — returns existing arch, does not modify manifest + Assert.AreEqual("x86", arch); + Assert.AreEqual(ManifestWithX86Arch, content, "Manifest should not be modified on mismatch"); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_SkipsWarning_WhenNeutral() + { + // Arrange — x64 PE, manifest says "neutral" → should not warn + var exePath = Path.Combine(_tempDir.FullName, "test.exe"); + File.WriteAllBytes(exePath, BuildMinimalNativePe(0x8664)); // x64 + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithNeutralArch, exePath, taskContext); + + // Assert — returns "neutral", no modification + Assert.AreEqual("neutral", arch); + Assert.AreEqual(ManifestWithNeutralArch, content); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenExeNotFound() + { + // Arrange — non-existent exe path, manifest has no arch + var exePath = Path.Combine(_tempDir.FullName, "nonexistent.exe"); + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithoutArch, exePath, taskContext); + + // Assert — returns null arch (can't detect), manifest unchanged + Assert.IsNull(arch); + Assert.AreEqual(ManifestWithoutArch, content); + } + + [TestMethod] + public void AutoDetectProcessorArchitecture_ReturnsExistingArch_WhenExeIsNotPe() + { + // Arrange — non-PE file, manifest already has arch + var exePath = Path.Combine(_tempDir.FullName, "notape.exe"); + File.WriteAllText(exePath, "not a PE file"); + var taskContext = CreateTestTaskContext(); + + // Act + var (content, arch) = MsixService.AutoDetectProcessorArchitecture(ManifestWithX86Arch, exePath, taskContext); + + // Assert — returns existing manifest arch when PE detection fails + Assert.AreEqual("x86", arch); + Assert.AreEqual(ManifestWithX86Arch, content); + } + + #endregion + #region ContainsXGenerateLanguage / ReplaceXGenerateLanguage Tests [TestMethod] public void ContainsXGenerateLanguage_ReturnsTrueForXGenerateManifest() { - var manifest = @" + var manifest = @" + - "; + +
"; Assert.IsTrue(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -860,9 +1009,11 @@ public void ContainsXGenerateLanguage_ReturnsTrueForXGenerateManifest() [TestMethod] public void ContainsXGenerateLanguage_ReturnsFalseForConcreteLanguage() { - var manifest = @" + var manifest = @" + - "; + +
"; Assert.IsFalse(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -870,7 +1021,7 @@ public void ContainsXGenerateLanguage_ReturnsFalseForConcreteLanguage() [TestMethod] public void ContainsXGenerateLanguage_ReturnsFalseForNoResources() { - var manifest = @""; + var manifest = @""; Assert.IsFalse(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -878,28 +1029,32 @@ public void ContainsXGenerateLanguage_ReturnsFalseForNoResources() [TestMethod] public void ReplaceXGenerateLanguage_ReplacesSingleLanguage() { - var manifest = @" + var manifest = @" + - "; + +"; var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US"]); - Assert.Contains(@"", result); + Assert.Contains(@"Language=""en-US""", result); Assert.DoesNotContain("x-generate", result); } [TestMethod] public void ReplaceXGenerateLanguage_ReplacesMultipleLanguages() { - var manifest = @" + var manifest = @" + - "; + +"; var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US", "fr-FR", "de-DE"]); - Assert.Contains(@"", result); - Assert.Contains(@"", result); - Assert.Contains(@"", result); + Assert.Contains(@"Language=""en-US""", result); + Assert.Contains(@"Language=""fr-FR""", result); + Assert.Contains(@"Language=""de-DE""", result); Assert.DoesNotContain("x-generate", result); } @@ -907,7 +1062,7 @@ public void ReplaceXGenerateLanguage_ReplacesMultipleLanguages() public void ReplaceXGenerateLanguage_PreservesRestOfManifest() { var manifest = @" - + @@ -918,29 +1073,30 @@ public void ReplaceXGenerateLanguage_PreservesRestOfManifest() var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US"]); Assert.Contains(@"", result); - Assert.Contains(@"", result); + Assert.Contains("Applications", result); + Assert.Contains(@"Language=""en-US""", result); Assert.DoesNotContain("x-generate", result); } [TestMethod] public void ReplaceXGenerateLanguage_HandlesVariousWhitespace() { - // x-generate with different whitespace patterns - var manifest = "\n\n"; + var manifest = "\n\n\n\n"; var result = MsixService.ReplaceXGenerateLanguage(manifest, ["en-US"]); - Assert.Contains(@"", result); + Assert.Contains(@"Language=""en-US""", result); Assert.DoesNotContain("x-generate", result); } [TestMethod] public void ContainsXGenerateLanguage_HandlesSingleQuotes() { - var manifest = @" + var manifest = @" + - "; + +"; Assert.IsTrue(MsixService.ContainsXGenerateLanguage(manifest)); } @@ -1026,9 +1182,10 @@ private MsixService CreateMsixServiceForManifestRewriteTests() null!, null!, null!, + null!, + null!, NullLogger.Instance, - new CurrentDirectoryProvider(_tempDir.FullName), - null!); + new CurrentDirectoryProvider(_tempDir.FullName)); } private static async Task InvokeUpdateAppxManifestContentAsync(MsixService service, string manifest) @@ -1037,20 +1194,24 @@ private static async Task InvokeUpdateAppxManifestContentAsync(MsixServi Assert.IsNotNull(updateMethod, "Could not locate UpdateAppxManifestContentAsync via reflection"); // selfContained=true and executable=.dll avoid dependency mutation paths, keeping this test focused + // exePath=null skips ProcessorArchitecture detection var resultTask = updateMethod.Invoke(service, [ manifest, null, "TestApp.dll", + null, true, true, null, null!, CancellationToken.None - ]) as Task; + ]) as dynamic; - Assert.IsNotNull(resultTask, "Reflection call did not return Task"); - return await resultTask; + Assert.IsNotNull(resultTask, "Reflection call did not return a Task"); + var result = await resultTask; + // Named tuple members aren't available via reflection; use Item1 (Content) + return result.Item1; } #endregion @@ -1176,11 +1337,11 @@ public void ExtractExecutionAliases_NoAliases_ReturnsEmptyList() public void InsertPackageLevelExtensions_WithExistingPackageLevelExtensions_InsertsBeforeClose() { // Arrange — manifest has both Application-level and Package-level - var manifest = @" + var manifest = @" - + @@ -1188,7 +1349,7 @@ public void InsertPackageLevelExtensions_WithExistingPackageLevelExtensions_Inse "; - var newEntry = " \n"; + var newEntry = @""; // Act var result = MsixService.InsertPackageLevelExtensions(manifest, newEntry); @@ -1206,16 +1367,16 @@ public void InsertPackageLevelExtensions_WithExistingPackageLevelExtensions_Inse public void InsertPackageLevelExtensions_WithOnlyApplicationLevelExtensions_CreatesNewPackageLevelBlock() { // Arrange — manifest has ONLY Application-level (the regression scenario) - var manifest = @" + var manifest = @" - + "; - var newEntry = " \n"; + var newEntry = @""; // Act var result = MsixService.InsertPackageLevelExtensions(manifest, newEntry); @@ -1230,7 +1391,7 @@ public void InsertPackageLevelExtensions_WithOnlyApplicationLevelExtensions_Crea "InProcessServer must be outside (Package-level), not inside Application-level Extensions"); // Should have two separate blocks - Assert.AreEqual(2, CountOccurrences(result, ""), + Assert.AreEqual(2, CountOccurrences(result, " blocks"); } @@ -1238,12 +1399,12 @@ public void InsertPackageLevelExtensions_WithOnlyApplicationLevelExtensions_Crea public void InsertPackageLevelExtensions_WithNoExtensions_CreatesNewPackageLevelBlock() { // Arrange — manifest has no at all - var manifest = @" + var manifest = @" "; - var newEntry = " \n"; + var newEntry = @""; // Act var result = MsixService.InsertPackageLevelExtensions(manifest, newEntry); @@ -1251,7 +1412,7 @@ public void InsertPackageLevelExtensions_WithNoExtensions_CreatesNewPackageLevel // Assert Assert.IsTrue(result.Contains("inProcessServer"), "Should contain the new entry"); var packageCloseIndex = result.IndexOf("", StringComparison.Ordinal); - var extensionsIndex = result.IndexOf("", StringComparison.Ordinal); + var extensionsIndex = result.IndexOf(" result.IndexOf("", StringComparison.Ordinal), "New block should be after "); Assert.IsTrue(extensionsIndex < packageCloseIndex, diff --git a/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj b/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj index 6d15b9bf..bc981218 100644 --- a/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj +++ b/src/winapp-CLI/WinApp.Cli.Tests/WinApp.Cli.Tests.csproj @@ -1,7 +1,7 @@  - net10.0-windows + net10.0-windows10.0.19041.0 enable enable diff --git a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs index 8fa58450..d7934a31 100644 --- a/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs +++ b/src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs @@ -41,6 +41,7 @@ public static IServiceCollection ConfigureServices(this IServiceCollection servi .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(AnsiConsole.Console) .AddSingleton() .AddSingleton(); diff --git a/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs b/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs new file mode 100644 index 00000000..90f48784 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/AppxManifestDocument.cs @@ -0,0 +1,626 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace WinApp.Cli.Services; + +/// +/// XDocument-based wrapper for reading and manipulating AppxManifest.xml files. +/// This is a pure data class with no DI dependencies. +/// +internal class AppxManifestDocument +{ + // AppxManifest XML namespaces + public static readonly XNamespace DefaultNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10"; + public static readonly XNamespace UapNs = "http://schemas.microsoft.com/appx/manifest/uap/windows10"; + public static readonly XNamespace Uap5Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/5"; + public static readonly XNamespace Uap10Ns = "http://schemas.microsoft.com/appx/manifest/uap/windows10/10"; + public static readonly XNamespace RescapNs = "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"; + public static readonly XNamespace BuildNs = "http://schemas.microsoft.com/developer/appx/2015/build"; + public static readonly XNamespace DesktopNs = "http://schemas.microsoft.com/appx/manifest/desktop/windows10"; + public static readonly XNamespace Desktop6Ns = "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"; + + private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false); + + private readonly XDocument _document; + + private AppxManifestDocument(XDocument document) + { + _document = document ?? throw new ArgumentNullException(nameof(document)); + } + + /// + /// Direct access to the underlying XDocument for advanced operations. + /// + public XDocument Document => _document; + + #region Static Factory Methods + + /// + /// Loads an AppxManifest from a file path. + /// + public static AppxManifestDocument Load(string path) + { + var doc = XDocument.Load(path); + return new AppxManifestDocument(doc); + } + + /// + /// Loads an AppxManifest from a stream. + /// + public static AppxManifestDocument Load(Stream stream) + { + var doc = XDocument.Load(stream); + return new AppxManifestDocument(doc); + } + + /// + /// Parses an AppxManifest from an XML string. + /// + public static AppxManifestDocument Parse(string xml) + { + var doc = XDocument.Parse(xml); + return new AppxManifestDocument(doc); + } + + #endregion + + #region Save / Serialize + + /// + /// Saves the manifest to a file with UTF-8 (no BOM) encoding. + /// + public void Save(string path) + { + var settings = new XmlWriterSettings + { + Indent = true, + IndentChars = " ", + Encoding = Utf8NoBom, + OmitXmlDeclaration = _document.Declaration == null, + }; + + using var memoryStream = new MemoryStream(); + using (var writer = XmlWriter.Create(memoryStream, settings)) + { + _document.Save(writer); + } + + File.WriteAllBytes(path, memoryStream.ToArray()); + } + + /// + /// Serializes the manifest to an XML string. + /// + public string ToXml() + { + var settings = new XmlWriterSettings + { + Indent = true, + IndentChars = " ", + Encoding = Utf8NoBom, + OmitXmlDeclaration = _document.Declaration == null, + }; + + using var memoryStream = new MemoryStream(); + using (var writer = XmlWriter.Create(memoryStream, settings)) + { + _document.Save(writer); + } + + return Utf8NoBom.GetString(memoryStream.ToArray()); + } + + #endregion + + #region Element Accessors + + /// + /// Gets the Identity element. + /// + public XElement? GetIdentityElement() => + _document.Root?.Element(DefaultNs + "Identity"); + + /// + /// Gets the first Application element. + /// + public XElement? GetFirstApplicationElement() => + _document.Root?.Element(DefaultNs + "Applications")?.Element(DefaultNs + "Application"); + + /// + /// Gets the uap:VisualElements element from the first Application. + /// + public XElement? GetVisualElements() => + GetFirstApplicationElement()?.Element(UapNs + "VisualElements"); + + /// + /// Gets the Resources element. + /// + public XElement? GetResourcesElement() => + _document.Root?.Element(DefaultNs + "Resources"); + + /// + /// Gets the Dependencies element. + /// + public XElement? GetDependenciesElement() => + _document.Root?.Element(DefaultNs + "Dependencies"); + + /// + /// Gets the package-level Extensions element (child of Package, after Applications). + /// + public XElement? GetExtensionsElement() => + _document.Root?.Element(DefaultNs + "Extensions"); + + /// + /// Gets the Capabilities element. + /// + public XElement? GetCapabilitiesElement() => + _document.Root?.Element(DefaultNs + "Capabilities"); + + #endregion + + #region Identity Properties + + /// + /// Gets or sets the Identity Name attribute. + /// + public string? IdentityName + { + get => GetIdentityElement()?.Attribute("Name")?.Value; + set => SetIdentityAttribute("Name", value); + } + + /// + /// Gets or sets the Identity Publisher attribute. + /// + public string? IdentityPublisher + { + get => GetIdentityElement()?.Attribute("Publisher")?.Value; + set => SetIdentityAttribute("Publisher", value); + } + + /// + /// Gets or sets the Identity Version attribute. + /// + public string? IdentityVersion + { + get => GetIdentityElement()?.Attribute("Version")?.Value; + set => SetIdentityAttribute("Version", value); + } + + /// + /// Gets or sets the Identity ProcessorArchitecture attribute. + /// + public string? IdentityProcessorArchitecture + { + get => GetIdentityElement()?.Attribute("ProcessorArchitecture")?.Value; + set => SetIdentityAttribute("ProcessorArchitecture", value); + } + + private void SetIdentityAttribute(string attributeName, string? value) + { + var identity = GetIdentityElement(); + if (identity == null) + { + if (value == null) + { + return; + } + + identity = new XElement(DefaultNs + "Identity"); + _document.Root?.AddFirst(identity); + } + + if (value == null) + { + identity.Attribute(attributeName)?.Remove(); + } + else + { + identity.SetAttributeValue(attributeName, value); + } + } + + #endregion + + #region Application Properties + + /// + /// Gets or sets the first Application's Id attribute. + /// + public string? ApplicationId + { + get => GetFirstApplicationElement()?.Attribute("Id")?.Value; + set => SetApplicationAttribute("Id", value); + } + + /// + /// Gets or sets the first Application's Executable attribute. + /// + public string? ApplicationExecutable + { + get => GetFirstApplicationElement()?.Attribute("Executable")?.Value; + set => SetApplicationAttribute("Executable", value); + } + + /// + /// Gets or sets the first Application's EntryPoint attribute. + /// + public string? ApplicationEntryPoint + { + get => GetFirstApplicationElement()?.Attribute("EntryPoint")?.Value; + set => SetApplicationAttribute("EntryPoint", value); + } + + private void SetApplicationAttribute(string attributeName, string? value) + { + var app = GetFirstApplicationElement(); + if (app == null) + { + return; + } + + if (value == null) + { + app.Attribute(attributeName)?.Remove(); + } + else + { + app.SetAttributeValue(attributeName, value); + } + } + + #endregion + + #region VisualElements Properties + + /// + /// Gets or sets the uap:VisualElements DisplayName attribute. + /// + public string? VisualElementsDisplayName + { + get => GetVisualElements()?.Attribute("DisplayName")?.Value; + set + { + var ve = GetVisualElements(); + if (ve == null) + { + return; + } + + if (value == null) + { + ve.Attribute("DisplayName")?.Remove(); + } + else + { + ve.SetAttributeValue("DisplayName", value); + } + } + } + + #endregion + + #region Resource Languages + + /// + /// Extracts all Resource Language values. + /// + public List GetResourceLanguages() + { + var resources = GetResourcesElement(); + if (resources == null) + { + return []; + } + + return resources.Elements(DefaultNs + "Resource") + .Select(r => r.Attribute("Language")?.Value) + .Where(lang => lang != null) + .Cast() + .ToList(); + } + + /// + /// Replaces the Resources block with the given languages. + /// + public void SetResourceLanguages(IList languages) + { + var root = _document.Root; + if (root == null) + { + return; + } + + var resources = GetResourcesElement(); + if (resources == null) + { + resources = new XElement(DefaultNs + "Resources"); + // Insert after Dependencies if present, otherwise after Identity + var dependencies = GetDependenciesElement(); + if (dependencies != null) + { + dependencies.AddAfterSelf(resources); + } + else + { + var identity = GetIdentityElement(); + if (identity != null) + { + identity.AddAfterSelf(resources); + } + else + { + root.Add(resources); + } + } + } + else + { + resources.RemoveAll(); + } + + foreach (var lang in languages) + { + resources.Add(new XElement(DefaultNs + "Resource", new XAttribute("Language", lang))); + } + } + + #endregion + + #region Namespace Management + + /// + /// Adds a prefix to the IgnorableNamespaces attribute on the Package element if not already present. + /// + public void AddIgnorableNamespace(string prefix) + { + var root = _document.Root; + if (root == null) + { + return; + } + + var ignorableAttr = root.Attribute("IgnorableNamespaces"); + if (ignorableAttr != null) + { + var namespaces = ignorableAttr.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (!namespaces.Contains(prefix, StringComparer.OrdinalIgnoreCase)) + { + ignorableAttr.Value = ignorableAttr.Value + " " + prefix; + } + } + else + { + root.SetAttributeValue("IgnorableNamespaces", prefix); + } + } + + /// + /// Adds an xmlns:prefix declaration to the Package element if not already present. + /// + public void EnsureNamespace(string prefix, XNamespace ns) + { + var root = _document.Root; + if (root == null) + { + return; + } + + var existing = root.Attribute(XNamespace.Xmlns + prefix); + if (existing == null) + { + root.Add(new XAttribute(XNamespace.Xmlns + prefix, ns.NamespaceName)); + } + } + + #endregion + + #region Capabilities + + /// + /// Adds a capability if not already present. Uses the default namespace unless a specific namespace is provided. + /// + public void EnsureCapability(string capabilityName, XNamespace? ns = null) + { + var root = _document.Root; + if (root == null) + { + return; + } + + var capabilities = GetCapabilitiesElement(); + if (capabilities == null) + { + capabilities = new XElement(DefaultNs + "Capabilities"); + root.Add(capabilities); + } + + var targetNs = ns ?? DefaultNs; + + // Check all child elements for a matching Name attribute regardless of namespace + var existing = capabilities.Elements() + .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, capabilityName, StringComparison.OrdinalIgnoreCase)); + + if (existing == null) + { + capabilities.Add(new XElement(targetNs + "Capability", new XAttribute("Name", capabilityName))); + } + } + + #endregion + + #region Build Metadata + + /// + /// Adds or updates a build:Item entry in the build:Metadata section. + /// + public void SetBuildMetadata(string toolName, string version) + { + var root = _document.Root; + if (root == null) + { + return; + } + + var metadata = root.Element(BuildNs + "Metadata"); + if (metadata == null) + { + metadata = new XElement(BuildNs + "Metadata"); + root.Add(metadata); + } + + var existingItem = metadata.Elements(BuildNs + "Item") + .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, toolName, StringComparison.OrdinalIgnoreCase)); + + if (existingItem != null) + { + existingItem.SetAttributeValue("Version", version); + } + else + { + metadata.Add(new XElement(BuildNs + "Item", + new XAttribute("Name", toolName), + new XAttribute("Version", version))); + } + } + + #endregion + + #region Package-level Extensions + + /// + /// Gets or creates the Package-level Extensions element (direct child of Package root). + /// This is distinct from Application-level Extensions which live inside Application elements. + /// + public XElement GetOrCreatePackageLevelExtensionsElement() + { + var root = _document.Root ?? throw new InvalidOperationException("Document has no root element"); + + // root.Element() only returns direct children, so this correctly gets + // Package-level Extensions (not Application > Extensions) + var extensions = root.Element(DefaultNs + "Extensions"); + if (extensions != null) + { + return extensions; + } + + extensions = new XElement(DefaultNs + "Extensions"); + + // Insert after Applications (standard AppxManifest element order) + var applications = root.Element(DefaultNs + "Applications"); + if (applications != null) + { + applications.AddAfterSelf(extensions); + } + else + { + root.Add(extensions); + } + + return extensions; + } + + /// + /// Collects all DLL paths registered in InProcessServer or ProxyStub extensions + /// (from Package-level <Path> elements). Used for dedup when adding new entries. + /// + public HashSet GetRegisteredExtensionDllPaths() + { + var result = new HashSet(StringComparer.OrdinalIgnoreCase); + var root = _document.Root; + if (root == null) + { + return result; + } + + foreach (var path in root.Descendants(DefaultNs + "Path")) + { + var text = path.Value?.Trim(); + if (!string.IsNullOrEmpty(text)) + { + result.Add(text); + } + } + + return result; + } + + /// + /// Adds an InProcessServer extension entry to the Package-level Extensions element. + /// + public void AddInProcessServerExtension(string dllPath, IEnumerable activatableClasses) + { + var extensions = GetOrCreatePackageLevelExtensionsElement(); + + var extension = new XElement(DefaultNs + "Extension", + new XAttribute("Category", "windows.activatableClass.inProcessServer"), + new XElement(DefaultNs + "InProcessServer", + new XElement(DefaultNs + "Path", dllPath), + activatableClasses.Select(cls => + new XElement(DefaultNs + "ActivatableClass", + new XAttribute("ActivatableClassId", cls), + new XAttribute("ThreadingModel", "both"))))); + + extensions.Add(extension); + } + + #endregion + + #region Package Dependencies + + /// + /// Checks if a PackageDependency with the given name prefix exists. + /// + public bool HasPackageDependency(string namePrefix) + { + var dependencies = GetDependenciesElement(); + if (dependencies == null) + { + return false; + } + + return dependencies.Elements(DefaultNs + "PackageDependency") + .Any(e => e.Attribute("Name")?.Value?.StartsWith(namePrefix, StringComparison.OrdinalIgnoreCase) == true); + } + + /// + /// Adds or updates a PackageDependency element. + /// + public void SetPackageDependency(string name, string minVersion, string publisher) + { + var root = _document.Root; + if (root == null) + { + return; + } + + var dependencies = GetDependenciesElement(); + if (dependencies == null) + { + dependencies = new XElement(DefaultNs + "Dependencies"); + root.Add(dependencies); + } + + var existing = dependencies.Elements(DefaultNs + "PackageDependency") + .FirstOrDefault(e => string.Equals(e.Attribute("Name")?.Value, name, StringComparison.Ordinal)); + + if (existing != null) + { + existing.SetAttributeValue("MinVersion", minVersion); + existing.SetAttributeValue("Publisher", publisher); + } + else + { + dependencies.Add(new XElement(DefaultNs + "PackageDependency", + new XAttribute("Name", name), + new XAttribute("MinVersion", minVersion), + new XAttribute("Publisher", publisher))); + } + } + + #endregion +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs b/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs new file mode 100644 index 00000000..9205f534 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/IPriService.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using WinApp.Cli.ConsoleTasks; + +namespace WinApp.Cli.Services; + +internal interface IPriService +{ + Task CreatePriConfigAsync( + DirectoryInfo packageDir, + TaskContext taskContext, + IEnumerable precomputedPriResourceCandidates, + string language = "en-US", + string platformVersion = "10.0.0", + CancellationToken cancellationToken = default); + + Task> GeneratePriFileAsync( + DirectoryInfo packageDir, + TaskContext taskContext, + FileInfo? configPath = null, + FileInfo? outputPath = null, + CancellationToken cancellationToken = default); + + Task> ExtractLanguagesFromPriAsync( + FileInfo priFile, + TaskContext taskContext, + CancellationToken cancellationToken); +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs new file mode 100644 index 00000000..d7b5b226 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +namespace WinApp.Cli.Services; + +/// +/// Static helper for incremental file copy operations. +/// Compares source and destination by file size and last-write timestamp +/// to skip unchanged files and remove stale files. +/// +internal static class IncrementalCopyHelper +{ + internal record SyncResult(int Copied, int Skipped, int Deleted); + + /// + /// Synchronizes files from to incrementally. + /// Only copies files that are new or changed (by size or timestamp). + /// Removes stale files from that no longer exist in source, + /// except for files in . + /// + internal static SyncResult SyncDirectory( + DirectoryInfo sourceDir, + DirectoryInfo destDir, + HashSet? protectedFileNames = null) + { + if (!destDir.Exists) + { + destDir.Create(); + } + + var destFullPath = destDir.FullName.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; + var sourceRelativePaths = new HashSet(StringComparer.OrdinalIgnoreCase); + int copied = 0, skipped = 0; + + foreach (var file in sourceDir.EnumerateFiles("*", SearchOption.AllDirectories)) + { + // Skip files that are inside the dest folder (if dest is nested inside source) + if (file.FullName.StartsWith(destFullPath, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var relativePath = Path.GetRelativePath(sourceDir.FullName, file.FullName); + sourceRelativePaths.Add(relativePath); + var destFile = new FileInfo(Path.Combine(destDir.FullName, relativePath)); + + // Skip copy if destination exists with same size and timestamp + if (destFile.Exists && destFile.Length == file.Length && destFile.LastWriteTimeUtc == file.LastWriteTimeUtc) + { + skipped++; + continue; + } + + destFile.Directory?.Create(); + file.CopyTo(destFile.FullName, overwrite: true); + copied++; + } + + // Remove stale files in dest that no longer exist in source + int deleted = 0; + foreach (var destFile in destDir.EnumerateFiles("*", SearchOption.AllDirectories)) + { + var relativePath = Path.GetRelativePath(destDir.FullName, destFile.FullName); + + if (protectedFileNames != null && protectedFileNames.Contains(relativePath)) + { + continue; + } + + if (!sourceRelativePaths.Contains(relativePath)) + { + destFile.Delete(); + deleted++; + } + } + + return new SyncResult(copied, skipped, deleted); + } + + /// + /// Copies a list of files to a target directory incrementally, + /// skipping files that are unchanged (same size and timestamp). + /// + internal static (int Copied, int Skipped) CopyFiles( + List<(FileInfo SourceFile, string RelativePath)> files, + DirectoryInfo targetDir) + { + int copied = 0, skipped = 0; + + foreach (var (sourceFile, relativePath) in files) + { + var targetFile = new FileInfo(Path.Combine(targetDir.FullName, relativePath)); + + if (targetFile.Exists && targetFile.Length == sourceFile.Length && targetFile.LastWriteTimeUtc == sourceFile.LastWriteTimeUtc) + { + skipped++; + continue; + } + + targetFile.Directory?.Create(); + sourceFile.CopyTo(targetFile.FullName, overwrite: true); + copied++; + } + + return (copied, skipped); + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs new file mode 100644 index 00000000..e48220c6 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/MrtAssetHelper.cs @@ -0,0 +1,281 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Text.RegularExpressions; +using WinApp.Cli.ConsoleTasks; +using WinApp.Cli.Helpers; + +namespace WinApp.Cli.Services; + +/// +/// Static helper for MRT (Modern Resource Technology) asset qualification, +/// expansion, and file copying. +/// +internal static partial class MrtAssetHelper +{ + // Language (en, en-US, pt-BR, zh-Hans, etc.) – bare token + [GeneratedRegex(@"^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex LanguageQualifierRegex(); + + [GeneratedRegex(@"^scale-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + // scale-100, scale-200, etc. + private static partial Regex ScaleQualifierRegex(); + + // theme-dark, theme-light + [GeneratedRegex(@"^theme-(light|dark)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex ThemeQualifierRegex(); + + // contrast-standard, contrast-high + [GeneratedRegex(@"^contrast-(standard|high)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex ContrastQualifierRegex(); + + // dxfeaturelevel-9 / 10 / 11 + [GeneratedRegex(@"^dxfeaturelevel-(9|10|11)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex DxFeatureLevelQualifierRegex(); + + // device-family-desktop / xbox / team / iot / mobile + [GeneratedRegex(@"^device-family-(desktop|mobile|team|xbox|iot)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex DeviceFamilyQualifierRegex(); + + // homeregion-US, homeregion-JP, ... + [GeneratedRegex(@"^homeregion-[A-Za-z]{2}$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex HomeRegionQualifierRegex(); + + // configuration-debug, configuration-retail, etc. + [GeneratedRegex(@"^configuration-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex ConfigurationQualifierRegex(); + + // targetsize-16, targetsize-24, targetsize-256, ... + [GeneratedRegex(@"^targetsize-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex TargetSizeQualifierRegex(); + + // altform-unplated, altform-lightunplated, etc. + [GeneratedRegex(@"^altform-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] + private static partial Regex AltFormQualifierRegex(); + + internal static readonly HashSet PriIncludedExtensions = new(StringComparer.OrdinalIgnoreCase) + { + ".png", + ".jpg", + ".jpeg", + ".gif", + ".bmp", + ".ico", + ".svg" + }; + + internal static List<(FileInfo SourceFile, string RelativePath)> ExpandManifestReferencedFiles( + DirectoryInfo manifestDir, + IEnumerable referencedFiles, + TaskContext? taskContext, + Func? includeFile = null) + { + var expandedFilesByRelativePath = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var relativeFilePath in referencedFiles) + { + var logicalSourceFile = new FileInfo(Path.Combine(manifestDir.FullName, relativeFilePath)); + var sourceDir = logicalSourceFile.Directory; + + if (sourceDir is null || !sourceDir.Exists) + { + taskContext?.AddDebugMessage($"{UiSymbols.Warning} Source directory not found for referenced file: {relativeFilePath}"); + continue; + } + + var logicalBaseName = Path.GetFileNameWithoutExtension(logicalSourceFile.Name); + var variantBaseName = GetMrtVariantBaseName(logicalBaseName); + var extension = logicalSourceFile.Extension; + + var searchPattern = variantBaseName + "*" + extension; + var candidates = sourceDir.EnumerateFiles(searchPattern); + var anyIncludedForLogical = false; + + foreach (var candidateFile in candidates) + { + if (includeFile != null && !includeFile(candidateFile)) + { + continue; + } + + var candidateNameWithoutExtension = Path.GetFileNameWithoutExtension(candidateFile.Name); + if (!IsMrtVariantName(variantBaseName, candidateNameWithoutExtension)) + { + continue; + } + + var relativeDir = Path.GetDirectoryName(relativeFilePath); + var candidateRelativePath = string.IsNullOrEmpty(relativeDir) + ? candidateFile.Name + : Path.Combine(relativeDir, candidateFile.Name); + + expandedFilesByRelativePath[candidateRelativePath] = candidateFile; + anyIncludedForLogical = true; + } + + if (!anyIncludedForLogical && logicalSourceFile.Exists && (includeFile == null || includeFile(logicalSourceFile))) + { + expandedFilesByRelativePath[relativeFilePath] = logicalSourceFile; + } + else if (!anyIncludedForLogical && !logicalSourceFile.Exists) + { + taskContext?.AddDebugMessage($"{UiSymbols.Warning} Referenced file not found (no MRT variants): {logicalSourceFile}"); + } + } + + return [.. expandedFilesByRelativePath + .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase) + .Select(pair => (pair.Value, pair.Key))]; + } + + internal static List<(FileInfo SourceFile, string RelativePath)> GetExpandedManifestReferencedFiles( + FileInfo manifestPath, + TaskContext taskContext) + { + var manifestDir = manifestPath.Directory; + if (manifestDir == null) + { + taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest directory not found for: {manifestPath}"); + return []; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Reading manifest: {manifestPath}"); + + var assetReferences = ManifestService.ExtractAssetReferencesFromManifest(manifestPath, taskContext); + var referencedFiles = assetReferences.Select(a => a.RelativePath); + return ExpandManifestReferencedFiles(manifestDir, referencedFiles, taskContext); + } + + internal static void CopyAllAssets(List<(FileInfo SourceFile, string RelativePath)> expandedFiles, DirectoryInfo targetDir, TaskContext taskContext) + { + var (copied, skipped) = IncrementalCopyHelper.CopyFiles(expandedFiles, targetDir); + taskContext.AddDebugMessage($"{UiSymbols.Note} Manifest resources: {copied} copied, {skipped} unchanged"); + } + + // ltr / rtl + private static bool IsLayoutDirectionQualifier(string token) + { + return token.Equals("ltr", StringComparison.OrdinalIgnoreCase) || + token.Equals("rtl", StringComparison.OrdinalIgnoreCase); + } + + internal static bool IsSingleQualifierToken(string token) + { + if (string.IsNullOrEmpty(token)) + { + return false; + } + + return LanguageQualifierRegex().IsMatch(token) + || ScaleQualifierRegex().IsMatch(token) + || ThemeQualifierRegex().IsMatch(token) + || ContrastQualifierRegex().IsMatch(token) + || DxFeatureLevelQualifierRegex().IsMatch(token) + || DeviceFamilyQualifierRegex().IsMatch(token) + || HomeRegionQualifierRegex().IsMatch(token) + || ConfigurationQualifierRegex().IsMatch(token) + || TargetSizeQualifierRegex().IsMatch(token) + || AltFormQualifierRegex().IsMatch(token) + || IsLayoutDirectionQualifier(token); + } + + internal static bool IsQualifierToken(string token) + { + if (string.IsNullOrEmpty(token)) + { + return false; + } + + var parts = token.Split('_'); + + foreach (var part in parts) + { + if (!IsSingleQualifierToken(part)) + { + return false; + } + } + + return true; + } + + /// + /// Returns true if is a valid MRT + /// variant of the logical base name (dots allowed in base name). + /// + internal static bool IsMrtVariantName(string logicalBaseName, string candidateNameWithoutExtension) + { + if (string.IsNullOrWhiteSpace(logicalBaseName) || string.IsNullOrWhiteSpace(candidateNameWithoutExtension)) + { + return false; + } + + // Split by '.'; "Logo.scale-200.theme-dark" -> ["Logo", "scale-200", "theme-dark"] + var parts = candidateNameWithoutExtension.Split('.'); + + if (parts.Length == 0) + { + return false; + } + + // First token must match logical base name (case-insensitive) + if (!parts[0].Equals(logicalBaseName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // No qualifiers -> exact logical name, valid + if (parts.Length == 1) + { + return true; + } + + // All remaining tokens must be valid MRT qualifiers + for (int i = 1; i < parts.Length; i++) + { + if (!IsQualifierToken(parts[i])) + { + return false; + } + } + + return true; + } + + /// + /// For a qualified logical name like "Logo.scale-100" or "Logo.targetsize-24_altform-unplated", + /// returns the unqualified asset family base (e.g. "Logo"). + /// If the name has no trailing qualifier tokens, returns the original name unchanged. + /// + internal static string GetMrtVariantBaseName(string logicalBaseName) + { + ArgumentException.ThrowIfNullOrWhiteSpace(logicalBaseName); + + var parts = logicalBaseName.Split('.'); + if (parts.Length <= 1) + { + return logicalBaseName; + } + + // Find the earliest segment where every remaining segment is a valid qualifier token. + for (int i = 1; i < parts.Length; i++) + { + var allRemainingAreQualifiers = true; + for (int j = i; j < parts.Length; j++) + { + if (!IsQualifierToken(parts[j])) + { + allRemainingAreQualifiers = false; + break; + } + } + + if (allRemainingAreQualifiers) + { + return string.Join('.', parts[..i]); + } + } + + return logicalBaseName; + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs new file mode 100644 index 00000000..1da10336 --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/MsixService.Runtime.cs @@ -0,0 +1,881 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.IO.Compression; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Linq; +using WinApp.Cli.ConsoleTasks; +using WinApp.Cli.Helpers; +using WinApp.Cli.Models; +using WinApp.Cli.Tools; + +namespace WinApp.Cli.Services; + +internal partial class MsixService +{ + [GeneratedRegex(@"^Microsoft\.WindowsAppRuntime\.\d+\.\d+.*\.msix$", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex WindowsAppRuntimeMsixRegex(); + [GeneratedRegex(@"]*name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex AssemblyIdentityNameRegex(); + + // DLL dedup regex — extract registered file names from SxS manifest StringBuilder + [GeneratedRegex(@" + /// Sets up Windows App SDK for self-contained deployment by extracting MSIX content + /// and preparing the necessary files for embedding in applications. + /// + public async Task SetupSelfContainedAsync(DirectoryInfo winappDir, string architecture, TaskContext taskContext, DotNetPackageListJson? dotNetPackageList = null, CancellationToken cancellationToken = default) + { + await taskContext.AddSubTaskAsync("Setting up Self Contained", async (taskContext, cancellationToken) => + { + // Look for the Runtime package which contains the MSIX files + var selfContainedDir = winappDir.CreateSubdirectory("self-contained"); + var archSelfContainedDir = selfContainedDir.CreateSubdirectory(architecture); + + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken) ?? throw new DirectoryNotFoundException("Windows App SDK Runtime MSIX directory not found. Ensure Windows App SDK is installed."); + + // Look for the MSIX file in the tools/MSIX folder + var msixToolsDir = new DirectoryInfo(Path.Combine(msixDir.FullName, $"win10-{architecture}")); + if (!msixToolsDir.Exists) + { + throw new DirectoryNotFoundException($"MSIX tools directory not found: {msixToolsDir}"); + } + + // Try to use inventory first for accurate file selection + FileInfo? msixPath = null; + try + { + var packageEntries = await WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken); + if (packageEntries != null) + { + // Look for the base Windows App Runtime package (not Framework, DDLM, or Singleton packages) + var mainRuntimeEntry = packageEntries.FirstOrDefault(entry => + entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && + !entry.PackageIdentity.Contains("Framework") && + !entry.FileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && + !entry.FileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase)); + + if (mainRuntimeEntry != null) + { + msixPath = new FileInfo(Path.Combine(msixToolsDir.FullName, mainRuntimeEntry.FileName)); + taskContext.AddDebugMessage($"{UiSymbols.Package} Found main runtime package from inventory: {mainRuntimeEntry.FileName}"); + } + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Could not parse inventory, falling back to file search: {ex.Message}"); + } + + // Fallback: search for files directly with pattern matching + if (msixPath == null || !msixPath.Exists) + { + var msixFiles = msixToolsDir.GetFiles("Microsoft.WindowsAppRuntime.*.msix"); + if (msixFiles.Length == 0) + { + throw new FileNotFoundException($"No MSIX files found in {msixToolsDir}"); + } + + // Look for the base runtime package (format: Microsoft.WindowsAppRuntime.{version}.msix) + // Exclude files with additional suffixes like DDLM, Singleton, Framework, etc. + msixPath = msixFiles.FirstOrDefault(f => + { + var fileName = f.Name; + return !fileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && + !fileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase) && + !fileName.Contains("Framework", StringComparison.OrdinalIgnoreCase) && + WindowsAppRuntimeMsixRegex().IsMatch(fileName); + }) ?? msixFiles[0]; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Extracting MSIX: {msixPath.FullName}"); + + // Extract MSIX content + var extractedDir = new DirectoryInfo(Path.Combine(archSelfContainedDir.FullName, "extracted")); + if (extractedDir.Exists) + { + extractedDir.Delete(recursive: true); + } + extractedDir.Refresh(); + extractedDir.Create(); + + using (var archive = await ZipFile.OpenReadAsync(msixPath.FullName, cancellationToken)) + { + await archive.ExtractToDirectoryAsync(extractedDir.FullName, cancellationToken); + } + + // Copy relevant files to deployment directory + var deploymentDir = archSelfContainedDir.CreateSubdirectory("deployment"); + + // Copy DLLs, WinMD files, and other runtime assets + await CopyRuntimeFilesAsync(extractedDir, deploymentDir, taskContext, cancellationToken); + + taskContext.AddDebugMessage($"{UiSymbols.Check} Self-contained files prepared in: {archSelfContainedDir.FullName}"); + + return 0; + }, cancellationToken); + } + + private async Task EmbedActivationManifestToExeAsync(FileInfo exePath, DirectoryInfo winAppSDKDeploymentDir, FileInfo windowsAppSDKAppXManifestPath, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + // Use applicationLocation for DLL content (where runtime files were copied by PrepareRuntimeForPackagingAsync) + var exeDir = exePath.Directory!; + + taskContext.AddDebugMessage($"{UiSymbols.Note} Generating activation manifest from: {windowsAppSDKAppXManifestPath}"); + taskContext.AddDebugMessage($"{UiSymbols.Package} Using DLL content from: {winAppSDKDeploymentDir}"); + + // Create a temporary manifest file + var tempManifestPath = new FileInfo(Path.Combine(exeDir.FullName, "WindowsAppSDK_temp.manifest")); + + try + { + // Build the entire manifest in memory, then write to disk once + var sb = new StringBuilder(); + sb.AppendLine(""); + sb.AppendLine(""); + + // Collect all AppX manifests (main package + component fragments) and their DLLs + (var packageDependencies, _) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); + if (packageDependencies == null || packageDependencies.Count == 0) + { + throw new InvalidOperationException("No Windows SDK packages found. Please install the Windows SDK or Windows App SDK."); + } + + var architecture = WorkspaceSetupService.GetSystemArchitecture(); + IEnumerable appxFragments = GetComponents(packageDependencies); + + // Combine all manifests: main AppxManifest.xml (Package root) + fragments (Fragment root) + var allManifests = new List { windowsAppSDKAppXManifestPath }; + allManifests.AddRange(appxFragments); + + // Combine all DLL file names from deployment dir and fragment native dirs + var allDllFiles = new List(winAppSDKDeploymentDir.EnumerateFiles("*.dll").Select(di => di.Name)); + allDllFiles.AddRange(appxFragments + .Select(fragment => Path.Combine(fragment.DirectoryName!, $"win-{architecture}\\native")) + .Where(Directory.Exists) + .SelectMany(dir => Directory.EnumerateFiles(dir, "*.dll")) + .Select(Path.GetFileName)!); + + // Single pass: process all AppX manifests (auto-detects Package vs Fragment root) + AppendAppManifestFromAppx( + sb, + redirectDlls: false, + inDllFiles: allDllFiles, + inAppxManifests: allManifests); + + // Phase 3: Discover and register third-party WinRT components (e.g., Win2D, WebView2) + // These packages ship .winmd files + native DLLs but no package.appxfragment + await AppendThirdPartyWinRTManifestEntriesAsync( + sb, architecture, dotNetPackageList, taskContext, cancellationToken); + + sb.AppendLine(""); + + // Single write to disk + await File.WriteAllTextAsync( + tempManifestPath.FullName, + sb.ToString(), + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + cancellationToken); + + // Use mt.exe to merge manifests + await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); + } + finally + { + TryDeleteFile(tempManifestPath); + } + } + + private IEnumerable GetComponents(Dictionary packageDependencies) + { + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + + // Find appx fragments in the NuGet global cache (lowercase-id/version/ layout) + var appxFragments = packageDependencies + .Select(package => new FileInfo(Path.Combine(nugetCacheDir.FullName, package.Key.ToLowerInvariant(), package.Value, "runtimes-framework", "package.appxfragment"))) + .Where(f => f.Exists); + return appxFragments; + } + + /// + /// Collects all user NuGet packages from winapp.yaml or .csproj. + /// Returns the full package dictionary (name → version) for WinRT component scanning. + /// + private async Task> GetAllUserPackagesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var packages = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Path 1: Try winapp.yaml + if (configService.Exists()) + { + var config = configService.Load(); + foreach (var pkg in config.Packages) + { + packages.TryAdd(pkg.Name, pkg.Version); + } + } + else + { + // Path 2: Try .csproj via `dotnet list package --format json` (cached) + try + { + var allPackages = dotNetPackageList?.Projects? + .SelectMany(p => p.Frameworks ?? []) + .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); + + if (allPackages != null) + { + foreach (var pkg in allPackages) + { + if (!string.IsNullOrEmpty(pkg.Id) && !string.IsNullOrEmpty(pkg.ResolvedVersion)) + { + packages.TryAdd(pkg.Id, pkg.ResolvedVersion); + } + } + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not retrieve package list from .csproj: {ex.Message}"); + } + } + + return packages; + } + + /// + /// Discovers third-party WinRT components and appends their activatable class + /// entries to the in-memory SxS manifest (for self-contained deployment). + /// + private async Task AppendThirdPartyWinRTManifestEntriesAsync( + StringBuilder sb, + string architecture, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); + if (allPackages.Count == 0) + { + return; + } + + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + + // DiscoverWinRTComponents filters out packages that have a package.appxfragment + // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. + // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 + // are transitive WinAppSDK deps but need their own InProcessServer entries. + var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); + if (components.Count == 0) + { + return; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found {components.Count} third-party WinRT component(s) to register"); + + // Build a set of DLL names already registered in the manifest (from WinAppSDK fragments) + // so we can do exact-name dedup instead of substring matching. + var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in SxsFileNameRegex().Matches(sb.ToString())) + { + registeredDlls.Add(match.Groups[1].Value); + } + + foreach (var component in components) + { + var classes = winmdService.GetActivatableClasses(component.WinmdPath); + if (classes.Count == 0) + { + continue; + } + + // Skip components whose DLL is already in the manifest (from WinAppSDK fragments + // or a previous iteration) to avoid duplicate activatableClass entries. + if (!registeredDlls.Add(component.ImplementationDll)) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); + continue; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Registering {classes.Count} activatable class(es) from {component.ImplementationDll}"); + + sb.AppendLine($" "); + foreach (var className in classes) + { + sb.AppendLine($" "); + } + sb.AppendLine(" "); + } + } + + /// + /// Discovers third-party WinRT components and generates InProcessServer + /// extension entries for AppxManifest.xml (for packaged apps). + /// + private async Task AddThirdPartyWinRTExtensionsToAppxManifestAsync( + string manifestContent, + DotNetPackageListJson? dotNetPackageList, + TaskContext taskContext, + CancellationToken cancellationToken) + { + var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); + if (allPackages.Count == 0) + { + return manifestContent; + } + + var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); + var architecture = WorkspaceSetupService.GetSystemArchitecture(); + + // DiscoverWinRTComponents filters out packages that have a package.appxfragment + // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. + // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 + // are transitive WinAppSDK deps but need their own InProcessServer entries. + var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); + if (components.Count == 0) + { + return manifestContent; + } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Adding InProcessServer entries for {components.Count} third-party WinRT component(s)"); + + var doc = AppxManifestDocument.Parse(manifestContent); + + // Build a set of DLL names already registered in the manifest + // so we can do exact-name dedup instead of substring matching. + var registeredDlls = doc.GetRegisteredExtensionDllPaths(); + + var addedAny = false; + foreach (var component in components) + { + var classes = winmdService.GetActivatableClasses(component.WinmdPath); + if (classes.Count == 0) + { + continue; + } + + // Skip components whose DLL is already in the manifest or in entries we've already generated + if (!registeredDlls.Add(component.ImplementationDll)) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); + continue; + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} Adding {classes.Count} activatable class(es) for {component.ImplementationDll}"); + + doc.AddInProcessServerExtension(component.ImplementationDll, classes); + addedAny = true; + } + + return addedAny ? doc.ToXml() : manifestContent; + } + + /// + /// Inserts Package-level extension entries (e.g. InProcessServer) into a manifest string. + /// Correctly distinguishes Package-level <Extensions> from Application-level ones. + /// + internal static string InsertPackageLevelExtensions(string manifestContent, string extensionEntries) + { + var doc = AppxManifestDocument.Parse(manifestContent); + var extensions = doc.GetOrCreatePackageLevelExtensionsElement(); + + // Parse the raw extension entries as XML fragments and add them + var wrapper = XElement.Parse($"<_wrap xmlns=\"{AppxManifestDocument.DefaultNs}\">{extensionEntries}"); + foreach (var entry in wrapper.Elements()) + { + extensions.Add(entry); + } + + return doc.ToXml(); + } + + /// + /// Generates Win32 SxS manifest entries from AppX manifests (Package or Fragment format). + /// Auto-detects the root element name (Package vs Fragment) per document. + /// + /// StringBuilder to append manifest entries to + /// Whether to redirect DLLs to %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% + /// List of DLL file names to track + /// List of paths to the input AppX manifest files or fragments + internal static void AppendAppManifestFromAppx( + StringBuilder sb, + bool redirectDlls, + IEnumerable inDllFiles, + IEnumerable inAppxManifests) + { + var dllFileFormat = redirectDlls ? + @" " : + @" "; + + var dllFiles = inDllFiles.ToList(); + var hasPackageManifest = false; + + foreach (var inAppxManifest in inAppxManifests) + { + XmlDocument doc = new(); + doc.Load(inAppxManifest.FullName); + + // Auto-detect root element name (Package or Fragment) + var prefix = doc.DocumentElement?.LocalName ?? "Package"; + var isPackage = prefix == "Package"; + if (isPackage) + { + hasPackageManifest = true; + } + + var nsmgr = new XmlNamespaceManager(doc.NameTable); + nsmgr.AddNamespace("m", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); + // Add InProcessServer elements to the generated appxmanifest + var xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:InProcessServer"; + XmlNodeList? inProcessServers = doc.SelectNodes(xQuery, nsmgr); + if (inProcessServers != null) + { + foreach (XmlNode winRTFactory in inProcessServers) + { + var dllFileNode = winRTFactory.SelectSingleNode("./m:Path", nsmgr); + if (dllFileNode == null) + { + continue; + } + + var dllFile = dllFileNode.InnerText; + var typesNames = winRTFactory.SelectNodes("./m:ActivatableClass", nsmgr)?.OfType(); + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(); + if (typesNames != null) + { + foreach (var typeNode in typesNames) + { + var attribs = typeNode.Attributes?.OfType().ToArray(); + var typeName = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "ActivatableClassId") + ?.InnerText; + var xmlEntryFormat = + @" "; + sb.AppendFormat(xmlEntryFormat, typeName); + sb.AppendLine(); + dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); + } + } + sb.AppendLine(@" "); + } + } + + // Only for Package manifests with redirect + if (isPackage && redirectDlls) + { + foreach (var dllFile in dllFiles) + { + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(@""); + } + } + // Add ProxyStub elements to the generated appxmanifest + dllFiles = [.. inDllFiles]; + + xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:ProxyStub"; + var inProcessProxystubs = doc.SelectNodes(xQuery, nsmgr); + if (inProcessProxystubs != null) + { + foreach (XmlNode proxystub in inProcessProxystubs) + { + var classIDAdded = false; + + var dllFileNode = proxystub.SelectSingleNode("./m:Path", nsmgr); + var dllFile = dllFileNode?.InnerText; + // exclude PushNotificationsLongRunningTask, which requires the Singleton (which is unavailable for self-contained apps) + // exclude Widgets entries unless/until they have been tested and verified by the Widgets team + if (dllFile == null || dllFile == "PushNotificationsLongRunningTask.ProxyStub.dll" || dllFile == "Microsoft.Windows.Widgets.dll") + { + continue; + } + var typesNamesForProxy = proxystub.SelectNodes("./m:Interface", nsmgr)?.OfType(); + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(); + if (typesNamesForProxy != null) + { + foreach (var typeNode in typesNamesForProxy) + { + if (!classIDAdded) + { + var classIdAttribute = proxystub.Attributes?.OfType().ToArray(); + var classID = classIdAttribute + ?.OfType() + ?.SingleOrDefault(x => x.Name == "ClassId") + ?.InnerText; + + if (classID != null) + { + var xmlEntryFormat = @" "; + sb.AppendFormat(xmlEntryFormat, classID); + classIDAdded = true; + } + } + var attribs = typeNode.Attributes?.OfType().ToArray(); + var typeID = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "InterfaceId") + ?.InnerText; + var typeNames = attribs + ?.OfType() + ?.SingleOrDefault(x => x.Name == "Name") + ?.InnerText; + var xmlEntryFormatForStubs = @" "; + if (typeNames != null && typeID != null) + { + sb.AppendFormat(xmlEntryFormatForStubs, typeNames, typeID); + sb.AppendLine(); + dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); + } + } + } + sb.AppendLine(@" "); + } + } + } + + if (hasPackageManifest && redirectDlls) + { + foreach (var dllFile in dllFiles) + { + sb.AppendFormat(dllFileFormat, dllFile); + sb.AppendLine(@""); + } + } + } + + /// + /// Updates or inserts the Windows App SDK dependency in the manifest + /// + /// The manifest content to modify + /// The modified manifest content + private async Task UpdateWindowsAppSdkDependencyAsync(string manifestContent, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var winAppSdkInfo = await GetWindowsAppSdkDependencyInfoAsync(dotNetPackageList, taskContext, cancellationToken); + + if (winAppSdkInfo == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not determine Windows App SDK version, skipping dependency update"); + return manifestContent; + } + + var doc = AppxManifestDocument.Parse(manifestContent); + const string publisher = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"; + + var dependencies = doc.GetDependenciesElement(); + if (dependencies == null) + { + // Create Dependencies element and insert before Applications (per AppxManifest schema order) + dependencies = new XElement(AppxManifestDocument.DefaultNs + "Dependencies"); + var applications = doc.Document.Root?.Element(AppxManifestDocument.DefaultNs + "Applications"); + if (applications != null) + { + applications.AddBeforeSelf(dependencies); + } + else + { + doc.Document.Root?.Add(dependencies); + } + + dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", + new XAttribute("Name", winAppSdkInfo.RuntimeName), + new XAttribute("MinVersion", winAppSdkInfo.MinVersion), + new XAttribute("Publisher", publisher))); + + taskContext.AddDebugMessage($"{UiSymbols.Package} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} (v{winAppSdkInfo.MinVersion})"); + } + else + { + // Check for existing WindowsAppRuntime dependency (prefix match for version-specific names) + var existing = dependencies.Elements(AppxManifestDocument.DefaultNs + "PackageDependency") + .FirstOrDefault(e => e.Attribute("Name")?.Value?.StartsWith("Microsoft.WindowsAppRuntime.", StringComparison.OrdinalIgnoreCase) == true); + + if (existing != null) + { + existing.SetAttributeValue("Name", winAppSdkInfo.RuntimeName); + existing.SetAttributeValue("MinVersion", winAppSdkInfo.MinVersion); + existing.SetAttributeValue("Publisher", publisher); + + taskContext.AddDebugMessage($"{UiSymbols.Sync} Updated Windows App SDK dependency to {winAppSdkInfo.RuntimeName} v{winAppSdkInfo.MinVersion}"); + } + else + { + dependencies.Add(new XElement(AppxManifestDocument.DefaultNs + "PackageDependency", + new XAttribute("Name", winAppSdkInfo.RuntimeName), + new XAttribute("MinVersion", winAppSdkInfo.MinVersion), + new XAttribute("Publisher", publisher))); + + taskContext.AddDebugMessage($"{UiSymbols.Add} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} to existing Dependencies section (v{winAppSdkInfo.MinVersion})"); + } + } + + return doc.ToXml(); + } + + /// + /// Gets the Windows App SDK dependency information from the locked winapp.yaml config and package source + /// + /// The dependency information, or null if not found + private async Task GetWindowsAppSdkDependencyInfoAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + try + { + var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); + if (msixDir == null) + { + return null; + } + + // Get the runtime package information from the MSIX inventory + var runtimeInfo = GetWindowsAppRuntimePackageInfo(taskContext, msixDir, cancellationToken); + if (runtimeInfo == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not parse Windows App Runtime package information from MSIX inventory"); + return null; + } + + return runtimeInfo; + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Error getting Windows App SDK dependency info: {ex.Message}"); + return null; + } + } + + private async Task GetRuntimeMsixDirAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + (var packageDependencies, var mainVersion) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); + if (packageDependencies == null || mainVersion == null) + { + return null; + } + + // Look for the runtime package in the package dependencies + var runtimePackage = packageDependencies.FirstOrDefault(kvp => + kvp.Key.StartsWith(BuildToolsService.WINAPP_SDK_RUNTIME_PACKAGE, StringComparison.OrdinalIgnoreCase)); + + // Create a dictionary with versions for FindWindowsAppSdkMsixDirectory + var usedVersions = new Dictionary + { + [BuildToolsService.WINAPP_SDK_PACKAGE] = mainVersion + }; + + if (runtimePackage.Key != null) + { + // For Windows App SDK 1.8+, there's a separate runtime package + var runtimeVersion = runtimePackage.Value; + usedVersions[runtimePackage.Key] = runtimeVersion; + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found runtime package: {runtimePackage.Key} v{runtimeVersion}"); + } + else + { + // For Windows App SDK 1.7 and earlier, runtime is included in the main package + taskContext.AddDebugMessage($"{UiSymbols.Note} No separate runtime package found - using main package (Windows App SDK 1.7 or earlier)"); + taskContext.AddDebugMessage($"{UiSymbols.Note} Available package dependencies: {string.Join(", ", packageDependencies.Keys)}"); + } + + // Find the MSIX directory with the runtime package + var msixDir = workspaceSetupService.FindWindowsAppSdkMsixDirectory(usedVersions); + if (msixDir == null) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Windows App SDK MSIX directory not found for dependent runtime package"); + return null; + } + + return msixDir; + } + + private async Task<(Dictionary? CachedPackages, string? MainVersion)> GetWinAppSDKPackageDependenciesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + string? mainVersion = null; + // Path 1: Try winapp.yaml (C++ / native projects) + if (configService.Exists()) + { + var config = configService.Load(); + mainVersion = config.GetVersion(BuildToolsService.WINAPP_SDK_PACKAGE); + } + else + { + // Path 2: Try .csproj via `dotnet list package --format json` + taskContext.AddDebugMessage($"{UiSymbols.Package} Querying NuGet package list..."); + + var allPackages = dotNetPackageList?.Projects? + .SelectMany(p => p.Frameworks ?? []) + .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); + + var winAppSdkPkg = allPackages? + .FirstOrDefault(p => string.Equals(p.Id, BuildToolsService.WINAPP_SDK_PACKAGE, StringComparison.OrdinalIgnoreCase)); + + if (winAppSdkPkg != null && !string.IsNullOrEmpty(winAppSdkPkg.ResolvedVersion)) + { + mainVersion = winAppSdkPkg.ResolvedVersion; + } + } + + if (string.IsNullOrEmpty(mainVersion)) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} No {BuildToolsService.WINAPP_SDK_PACKAGE} package found in winapp.yaml"); + return (null, null); + } + taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App SDK main package: v{mainVersion}"); + try + { + // Query NuGet API for the dependency tree of this package + var deps = await nugetService.GetPackageDependenciesAsync(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion, cancellationToken); + + // Include the main package itself in the result + deps.TryAdd(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion); + + return (deps, mainVersion); + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} {BuildToolsService.WINAPP_SDK_PACKAGE} v{mainVersion} not found in package source: {ex.Message}"); + } + + return (null, null); + } + + /// + /// Parses the MSIX inventory file to extract Windows App Runtime package information + /// + /// The MSIX directory containing the inventory file + /// Package information, or null if not found + private static WindowsAppRuntimePackageInfo? GetWindowsAppRuntimePackageInfo(TaskContext taskContext, DirectoryInfo msixDir, CancellationToken cancellationToken) + { + try + { + // Use the shared inventory parsing logic (synchronous version) + var packageEntries = WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken).GetAwaiter().GetResult(); + + if (packageEntries == null || packageEntries.Count == 0) + { + return null; + } + + // Look for the Windows App Runtime main package (not Framework packages) + var mainRuntimeEntry = packageEntries + .FirstOrDefault(entry => entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && + !entry.PackageIdentity.Contains("Framework")); + + if (mainRuntimeEntry != null) + { + // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) + var identityParts = mainRuntimeEntry.PackageIdentity.Split('_'); + if (identityParts.Length >= 2) + { + var runtimeName = identityParts[0]; + var version = identityParts[1]; + + taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App Runtime: {runtimeName} v{version}"); + + return new WindowsAppRuntimePackageInfo + { + RuntimeName = runtimeName, + MinVersion = version + }; + } + } + + taskContext.AddDebugMessage($"{UiSymbols.Note} No Windows App Runtime main package found in inventory"); + taskContext.AddDebugMessage($"{UiSymbols.Note} Available packages: {string.Join(", ", packageEntries.Select(e => e.PackageIdentity))}"); + + return null; + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} Error parsing MSIX inventory: {ex.Message}"); + return null; + } + } + + private static readonly string[] patterns = new[] { "*.dll", "workloads*.json", "restartAgent.exe", "map.html", "*.mui", "*.png", "*.winmd", "*.xaml", "*.xbf", "*.pri" }; + + private static async Task CopyRuntimeFilesAsync(DirectoryInfo extractedDir, DirectoryInfo deploymentDir, TaskContext taskContext, CancellationToken cancellationToken) + { + await taskContext.AddSubTaskAsync("Copying Runtime Files", (taskContext, cancellationToken) => + { + foreach (var pattern in patterns) + { + var files = extractedDir.GetFiles(pattern, SearchOption.AllDirectories); + foreach (var file in files) + { + var relativePath = Path.GetRelativePath(extractedDir.FullName, file.FullName); + var destPath = Path.Combine(deploymentDir.FullName, relativePath); + + // Create destination directory if needed + var destDir = Path.GetDirectoryName(destPath); + if (!string.IsNullOrEmpty(destDir)) + { + Directory.CreateDirectory(destDir); + } + + file.CopyTo(destPath, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Files} {relativePath}"); + } + } + + return Task.FromResult(0); + }, cancellationToken); + } + + /// + /// Prepares Windows App SDK runtime files for packaging into an MSIX by extracting them to the input folder + /// + /// The folder where runtime files should be copied + /// Cancellation token + /// The path to the self-contained deployment directory + private async Task PrepareRuntimeForPackagingAsync(DirectoryInfo inputFolder, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + { + var arch = WorkspaceSetupService.GetSystemArchitecture(); + + var winappDir = winappDirectoryService.GetLocalWinappDirectory(); + + // Extract runtime files using the existing method + await SetupSelfContainedAsync(winappDir, arch, taskContext, dotNetPackageList, cancellationToken); + + // Copy runtime files from .winapp/self-contained to input folder + var runtimeSourceDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "self-contained", arch, "deployment")); + + if (runtimeSourceDir.Exists) + { + // Copy files recursively to maintain directory structure + foreach (var file in runtimeSourceDir.GetFiles("*", SearchOption.AllDirectories)) + { + var relativePath = Path.GetRelativePath(runtimeSourceDir.FullName, file.FullName); + var destFile = Path.Combine(inputFolder.FullName, relativePath); + + // Create destination directory if needed + var destDir = Path.GetDirectoryName(destFile); + if (!string.IsNullOrEmpty(destDir)) + { + Directory.CreateDirectory(destDir); + } + + file.CopyTo(destFile, overwrite: true); + + taskContext.AddDebugMessage($"{UiSymbols.Folder} Bundled runtime: {relativePath}"); + } + + taskContext.AddDebugMessage($"{UiSymbols.Check} Windows App SDK runtime bundled into package"); + } + else + { + throw new DirectoryNotFoundException($"Runtime files not found at {runtimeSourceDir}"); + } + + return runtimeSourceDir; + } +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs b/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs index 2ee037b4..4938757e 100644 --- a/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs +++ b/src/winapp-CLI/WinApp.Cli/Services/MsixService.cs @@ -4,14 +4,13 @@ using Microsoft.Extensions.Logging; using System.IO.Compression; using System.Security; -using System.Reflection.PortableExecutable; using System.Text; using System.Text.RegularExpressions; using System.Xml; +using System.Xml.Linq; using WinApp.Cli.ConsoleTasks; using WinApp.Cli.Helpers; using WinApp.Cli.Models; -using WinApp.Cli.Services; using WinApp.Cli.Tools; namespace WinApp.Cli.Services; @@ -20,232 +19,17 @@ internal partial class MsixService( IWinappDirectoryService winappDirectoryService, IConfigService configService, IBuildToolsService buildToolsService, - IPowerShellService powerShellService, ICertificateService certificateService, IWorkspaceSetupService workspaceSetupService, IDevModeService devModeService, + IDotNetService dotNetService, INugetService nugetService, IWinmdService winmdService, + IPriService priService, + IPackageRegistrationService packageRegistrationService, ILogger logger, - ICurrentDirectoryProvider currentDirectoryProvider, - IDotNetService dotNetService) : IMsixService + ICurrentDirectoryProvider currentDirectoryProvider) : IMsixService { - [GeneratedRegex(@"^Microsoft\.WindowsAppRuntime\.\d+\.\d+.*\.msix$", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex WindowsAppRuntimeMsixRegex(); - [GeneratedRegex(@"]*>", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex IdentityElementRegex(); - [GeneratedRegex(@"Name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageNameRegex(); - [GeneratedRegex(@"Publisher\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackagePublisherRegex(); - [GeneratedRegex(@"]*\sId\s*=\s*[""']([^""']*)[""'][^>]*>", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxApplicationIdRegex(); - [GeneratedRegex(@"]*Name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageIdentityNameRegex(); - [GeneratedRegex(@"]*Publisher\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageIdentityPublisherRegex(); - [GeneratedRegex(@"]*Version\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageIdentityVersionRegex(); - [GeneratedRegex(@"]*Executable\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageApplicationExecutableRegex(); - [GeneratedRegex(@"(]*Name\s*=\s*)[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageIdentityNameAssignmentRegex(); - [GeneratedRegex(@"(]*\sId\s*=\s*)[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxApplicationIdAssignmentRegex(); - [GeneratedRegex(@"(]*Executable\s*=\s*)[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageApplicationExecutableAssignmentRegex(); - [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageElementOpenTagRegex(); - [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageOpenTagRegex(); - [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackagePropertiesCloseTagRegex(); - [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxApplicationOpenTagRegex(); - [GeneratedRegex(@"\s*EntryPoint\s*=\s*[""'][^""']*[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageEntryPointRegex(); - [GeneratedRegex(@"(]*?)(\s*/?>)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageVisualElementsOpenTagRegex(); - [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageRunFullTrustCapabilityRegex(); - [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageApplicationsTagRegex(); - [GeneratedRegex(@"(\s*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageDependenciesCloseTagRegex(); - [GeneratedRegex(@"]*name\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AssemblyIdentityNameRegex(); - - // DLL dedup regexes — extract registered file/path names for HashSet-based dedup - [GeneratedRegex(@"([^<]+)
", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxManifestPathElementRegex(); - - [GeneratedRegex(@"]*ProcessorArchitecture\s*=", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageIdentityArchitectureCheckRegex(); - [GeneratedRegex(@"]*ProcessorArchitecture\s*=\s*[""']([^""']*)[""']", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex AppxPackageIdentityArchitectureValueRegex(); - - // Matches the entire ... block containing x-generate - [GeneratedRegex(@"\s*\s*", RegexOptions.IgnoreCase | RegexOptions.Singleline, "en-US")] - private static partial Regex ResourceLanguageXGenerateBlockRegex(); - - // Extracts language tag from PRI dump qualifier strings like 'Language-en-US' - [GeneratedRegex(@"qualifiers=""[^""]*Language-([a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex PriDumpLanguageQualifierRegex(); - - // build:Metadata regexes - [GeneratedRegex(@"(]*)(>)", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataPackageOpenTagRegex(); - [GeneratedRegex(@"IgnorableNamespaces\s*=\s*""[^""]*\bbuild\b", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataIgnorableNamespacesCheckRegex(); - [GeneratedRegex(@"(IgnorableNamespaces\s*=\s*""[^""]*)("")", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataIgnorableNamespacesAssignRegex(); - [GeneratedRegex(@"]*Name\s*=\s*""Microsoft\.WinAppCli""", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataWinAppCliItemCheckRegex(); - [GeneratedRegex(@"]*Name\s*=\s*""Microsoft\.WinAppCli""[^/]*/\s*>", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataWinAppCliItemReplaceRegex(); - [GeneratedRegex(@"([ \t]*)()", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataCloseTagRegex(); - [GeneratedRegex(@"([ \t]*)()", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex BuildMetadataPackageCloseTagRegex(); - - // Language (en, en-US, pt-BR, zh-Hans, etc.) – bare token - [GeneratedRegex(@"^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex LanguageQualifierRegex(); - - [GeneratedRegex(@"^scale-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - // scale-100, scale-200, etc. - private static partial Regex ScaleQualifierRegex(); - - // theme-dark, theme-light - [GeneratedRegex(@"^theme-(light|dark)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ThemeQualifierRegex(); - - // contrast-standard, contrast-high - [GeneratedRegex(@"^contrast-(standard|high)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ContrastQualifierRegex(); - - // dxfeaturelevel-9 / 10 / 11 - [GeneratedRegex(@"^dxfeaturelevel-(9|10|11)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex DxFeatureLevelQualifierRegex(); - - // device-family-desktop / xbox / team / iot / mobile - [GeneratedRegex(@"^device-family-(desktop|mobile|team|xbox|iot)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex DeviceFamilyQualifierRegex(); - - // homeregion-US, homeregion-JP, ... - [GeneratedRegex(@"^homeregion-[A-Za-z]{2}$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex HomeRegionQualifierRegex(); - - // configuration-debug, configuration-retail, etc. - [GeneratedRegex(@"^configuration-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex ConfigurationQualifierRegex(); - - // targetsize-16, targetsize-24, targetsize-256, ... - [GeneratedRegex(@"^targetsize-(\d+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex TargetSizeQualifierRegex(); - - // altform-unplated, altform-lightunplated, etc. - [GeneratedRegex(@"^altform-[A-Za-z0-9]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant)] - private static partial Regex AltFormQualifierRegex(); - - /// - /// Sets up Windows App SDK for self-contained deployment by extracting MSIX content - /// and preparing the necessary files for embedding in applications. - /// - public async Task SetupSelfContainedAsync(DirectoryInfo winappDir, string architecture, TaskContext taskContext, DotNetPackageListJson? dotNetPackageList = null, CancellationToken cancellationToken = default) - { - await taskContext.AddSubTaskAsync("Setting up Self Contained", async (taskContext, cancellationToken) => - { - // Look for the Runtime package which contains the MSIX files - var selfContainedDir = winappDir.CreateSubdirectory("self-contained"); - var archSelfContainedDir = selfContainedDir.CreateSubdirectory(architecture); - - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken) ?? throw new DirectoryNotFoundException("Windows App SDK Runtime MSIX directory not found. Ensure Windows App SDK is installed."); - - // Look for the MSIX file in the tools/MSIX folder - var msixToolsDir = new DirectoryInfo(Path.Combine(msixDir.FullName, $"win10-{architecture}")); - if (!msixToolsDir.Exists) - { - throw new DirectoryNotFoundException($"MSIX tools directory not found: {msixToolsDir}"); - } - - // Try to use inventory first for accurate file selection - FileInfo? msixPath = null; - try - { - var packageEntries = await WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken); - if (packageEntries != null) - { - // Look for the base Windows App Runtime package (not Framework, DDLM, or Singleton packages) - var mainRuntimeEntry = packageEntries.FirstOrDefault(entry => - entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && - !entry.PackageIdentity.Contains("Framework") && - !entry.FileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && - !entry.FileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase)); - - if (mainRuntimeEntry != null) - { - msixPath = new FileInfo(Path.Combine(msixToolsDir.FullName, mainRuntimeEntry.FileName)); - taskContext.AddDebugMessage($"{UiSymbols.Package} Found main runtime package from inventory: {mainRuntimeEntry.FileName}"); - } - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Could not parse inventory, falling back to file search: {ex.Message}"); - } - - // Fallback: search for files directly with pattern matching - if (msixPath == null || !msixPath.Exists) - { - var msixFiles = msixToolsDir.GetFiles("Microsoft.WindowsAppRuntime.*.msix"); - if (msixFiles.Length == 0) - { - throw new FileNotFoundException($"No MSIX files found in {msixToolsDir}"); - } - - // Look for the base runtime package (format: Microsoft.WindowsAppRuntime.{version}.msix) - // Exclude files with additional suffixes like DDLM, Singleton, Framework, etc. - msixPath = msixFiles.FirstOrDefault(f => - { - var fileName = f.Name; - return !fileName.Contains("DDLM", StringComparison.OrdinalIgnoreCase) && - !fileName.Contains("Singleton", StringComparison.OrdinalIgnoreCase) && - !fileName.Contains("Framework", StringComparison.OrdinalIgnoreCase) && - WindowsAppRuntimeMsixRegex().IsMatch(fileName); - }) ?? msixFiles[0]; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Extracting MSIX: {msixPath.FullName}"); - - // Extract MSIX content - var extractedDir = new DirectoryInfo(Path.Combine(archSelfContainedDir.FullName, "extracted")); - if (extractedDir.Exists) - { - extractedDir.Delete(recursive: true); - } - extractedDir.Refresh(); - extractedDir.Create(); - - using (var archive = await ZipFile.OpenReadAsync(msixPath.FullName, cancellationToken)) - { - await archive.ExtractToDirectoryAsync(extractedDir.FullName, cancellationToken); - } - - // Copy relevant files to deployment directory - var deploymentDir = archSelfContainedDir.CreateSubdirectory("deployment"); - - // Copy DLLs, WinMD files, and other runtime assets - await CopyRuntimeFilesAsync(extractedDir, deploymentDir, taskContext, cancellationToken); - - taskContext.AddDebugMessage($"{UiSymbols.Check} Self-contained files prepared in: {archSelfContainedDir.FullName}"); - - return 0; - }, cancellationToken); - } - /// /// Parses an AppX manifest file and extracts the package identity information /// @@ -275,35 +59,19 @@ public static async Task ParseAppxManifestFromPathAsync(File /// Thrown when the manifest is invalid or missing required elements public static MsixIdentityResult ParseAppxManifestAsync(string appxManifestContent) { - // Extract Package Identity information - var identityMatch = IdentityElementRegex().Match(appxManifestContent); - if (!identityMatch.Success) - { - throw new InvalidOperationException("No Identity element found in AppX manifest"); - } + var doc = AppxManifestDocument.Parse(appxManifestContent); - var identityElement = identityMatch.Value; + var identity = doc.GetIdentityElement() + ?? throw new InvalidOperationException("No Identity element found in AppX manifest"); - // Extract attributes from Identity element - var nameMatch = AppxPackageNameRegex().Match(identityElement); - var publisherMatch = AppxPackagePublisherRegex().Match(identityElement); + var packageName = identity.Attribute("Name")?.Value + ?? throw new InvalidOperationException("AppX manifest Identity element missing required Name or Publisher attributes"); - if (!nameMatch.Success || !publisherMatch.Success) - { - throw new InvalidOperationException("AppX manifest Identity element missing required Name or Publisher attributes"); - } - - var packageName = nameMatch.Groups[1].Value; - var publisher = publisherMatch.Groups[1].Value; - - // Extract Application ID from Applications/Application element - var applicationMatch = AppxApplicationIdRegex().Match(appxManifestContent); - if (!applicationMatch.Success) - { - throw new InvalidOperationException("No Application element with Id attribute found in AppX manifest"); - } + var publisher = identity.Attribute("Publisher")?.Value + ?? throw new InvalidOperationException("AppX manifest Identity element missing required Name or Publisher attributes"); - var applicationId = applicationMatch.Groups[1].Value; + var applicationId = doc.ApplicationId + ?? throw new InvalidOperationException("No Application element with Id attribute found in AppX manifest"); return new MsixIdentityResult(packageName, publisher, applicationId); } @@ -316,292 +84,26 @@ public static MsixIdentityResult ParseAppxManifestAsync(string appxManifestConte /// List of alias names (e.g. "myapp.exe") public static List ExtractExecutionAliases(string manifestContent) { + var doc = AppxManifestDocument.Parse(manifestContent); var aliases = new List(); - var matches = ExecutionAliasRegex().Matches(manifestContent); - foreach (Match match in matches) - { - aliases.Add(match.Groups[1].Value); - } - return aliases; - } - - [GeneratedRegex(@"<(?:uap5|desktop):ExecutionAlias\s+Alias\s*=\s*[""']([^""']*)[""']\s*/>", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex ExecutionAliasRegex(); - - /// - /// Creates a PRI configuration file for the given package directory - /// - /// Path to the package directory - /// Task context for logging and progress reporting - /// Default language qualifier (default: 'en-US') - /// Platform version (default: '10.0.0') - /// Pre-computed list of manifest-referenced resource file paths (relative to the package directory) to include in the PRI. Must be provided by the caller via . - /// Cancellation token - /// Path to the created configuration file - public async Task CreatePriConfigAsync( - DirectoryInfo packageDir, - TaskContext taskContext, - string language = "en-US", - string platformVersion = "10.0.0", - IEnumerable precomputedPriResourceCandidates = null!, - CancellationToken cancellationToken = default) - { - if (!packageDir.Exists) - { - throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); - } - - ArgumentNullException.ThrowIfNull(precomputedPriResourceCandidates); - - var resfilesPath = Path.Combine(packageDir.FullName, "pri.resfiles"); - var priResourceCandidates = precomputedPriResourceCandidates.ToList(); - - priResourceCandidates = [.. priResourceCandidates - .Where(path => PriIncludedExtensions.Contains(Path.GetExtension(path))) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; - - taskContext.AddDebugMessage($"PRI resource candidates discovered: {priResourceCandidates.Count}"); - - using (var writer = new StreamWriter(resfilesPath)) - { - foreach (var priFile in priResourceCandidates) - { - await writer.WriteLineAsync(priFile); - } - } - - var configPath = new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); - var arguments = $@"createconfig /cf ""{configPath}"" /dq lang-{language}_scale-200 /pv {platformVersion} /o"; - - taskContext.AddDebugMessage("Creating PRI configuration file..."); - - try - { - await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - taskContext.AddDebugMessage($"PRI configuration created: {configPath}"); - - var xmlDoc = new XmlDocument(); - xmlDoc.Load(configPath.FullName); - var resourcesNode = xmlDoc.SelectSingleNode("/resources"); - if (resourcesNode != null) - { - var indexNode = resourcesNode.SelectSingleNode("index"); - if (indexNode != null) - { - if (indexNode.Attributes?["startIndexAt"]?.Value != null) - { - // set to relative path - indexNode.Attributes["startIndexAt"]!.Value = ".\\pri.resfiles"; - } - - var resfilesIndexerNode = xmlDoc.CreateElement("indexer-config"); - var typeAttr = xmlDoc.CreateAttribute("type"); - typeAttr.Value = "resfiles"; - resfilesIndexerNode.Attributes.Append(typeAttr); - - var delimiterAttr = xmlDoc.CreateAttribute("qualifierDelimiter"); - delimiterAttr.Value = "."; - resfilesIndexerNode.Attributes.Append(delimiterAttr); - - indexNode.AppendChild(resfilesIndexerNode); - - // Ensure folder-based indexer is configured to parse qualifiers from - // both folder names and file names (e.g. targetsize-48_altform-unplated). - var folderIndexerNode = indexNode - .SelectNodes("indexer-config") - ?.OfType() - .FirstOrDefault(node => - node.Attributes?["type"]?.Value?.Equals("folder", StringComparison.OrdinalIgnoreCase) == true); - - if (folderIndexerNode?.Attributes != null) - { - var folderAttributes = folderIndexerNode.Attributes; - - var folderNameAsQualifierAttr = folderAttributes["foldernameAsQualifier"]; - if (folderNameAsQualifierAttr == null) - { - folderNameAsQualifierAttr = xmlDoc.CreateAttribute("foldernameAsQualifier"); - folderAttributes.Append(folderNameAsQualifierAttr); - } - folderNameAsQualifierAttr.Value = "true"; - - var fileNameAsQualifierAttr = folderAttributes["filenameAsQualifier"]; - if (fileNameAsQualifierAttr == null) - { - fileNameAsQualifierAttr = xmlDoc.CreateAttribute("filenameAsQualifier"); - folderAttributes.Append(fileNameAsQualifierAttr); - } - fileNameAsQualifierAttr.Value = "true"; - } - - xmlDoc.Save(configPath.FullName); - } - } - - return configPath; - } - catch (Exception ex) + var root = doc.Document.Root; + if (root == null) { - throw new InvalidOperationException($"Failed to create PRI configuration: {ex.Message}", ex); + return aliases; } - } - - private static readonly HashSet PriIncludedExtensions = new(StringComparer.OrdinalIgnoreCase) - { - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".svg" - }; - - private static List<(FileInfo SourceFile, string RelativePath)> ExpandManifestReferencedFiles( - DirectoryInfo manifestDir, - IEnumerable referencedFiles, - TaskContext? taskContext, - Func? includeFile = null) - { - var expandedFilesByRelativePath = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var relativeFilePath in referencedFiles) + foreach (var element in root.Descendants() + .Where(e => e.Name.LocalName == "ExecutionAlias" + && (e.Name.Namespace == AppxManifestDocument.Uap5Ns || e.Name.Namespace == AppxManifestDocument.DesktopNs))) { - var logicalSourceFile = new FileInfo(Path.Combine(manifestDir.FullName, relativeFilePath)); - var sourceDir = logicalSourceFile.Directory; - - if (sourceDir is null || !sourceDir.Exists) - { - taskContext?.AddDebugMessage($"{UiSymbols.Warning} Source directory not found for referenced file: {relativeFilePath}"); - continue; - } - - var logicalBaseName = Path.GetFileNameWithoutExtension(logicalSourceFile.Name); - var variantBaseName = GetMrtVariantBaseName(logicalBaseName); - var extension = logicalSourceFile.Extension; - - var searchPattern = variantBaseName + "*" + extension; - var candidates = sourceDir.EnumerateFiles(searchPattern); - var anyIncludedForLogical = false; - - foreach (var candidateFile in candidates) - { - if (includeFile != null && !includeFile(candidateFile)) - { - continue; - } - - var candidateNameWithoutExtension = Path.GetFileNameWithoutExtension(candidateFile.Name); - if (!IsMrtVariantName(variantBaseName, candidateNameWithoutExtension)) - { - continue; - } - - var relativeDir = Path.GetDirectoryName(relativeFilePath); - var candidateRelativePath = string.IsNullOrEmpty(relativeDir) - ? candidateFile.Name - : Path.Combine(relativeDir, candidateFile.Name); - - expandedFilesByRelativePath[candidateRelativePath] = candidateFile; - anyIncludedForLogical = true; - } - - if (!anyIncludedForLogical && logicalSourceFile.Exists && (includeFile == null || includeFile(logicalSourceFile))) - { - expandedFilesByRelativePath[relativeFilePath] = logicalSourceFile; - } - else if (!anyIncludedForLogical && !logicalSourceFile.Exists) + var alias = element.Attribute("Alias")?.Value; + if (alias != null) { - taskContext?.AddDebugMessage($"{UiSymbols.Warning} Referenced file not found (no MRT variants): {logicalSourceFile}"); + aliases.Add(alias); } } - return [.. expandedFilesByRelativePath - .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase) - .Select(pair => (pair.Value, pair.Key))]; - } - - private static List<(FileInfo SourceFile, string RelativePath)> GetExpandedManifestReferencedFiles( - FileInfo manifestPath, - TaskContext taskContext) - { - var manifestDir = manifestPath.Directory; - if (manifestDir == null) - { - taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest directory not found for: {manifestPath}"); - return []; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Reading manifest: {manifestPath}"); - - var assetReferences = ManifestService.ExtractAssetReferencesFromManifest(manifestPath, taskContext); - var referencedFiles = assetReferences.Select(a => a.RelativePath); - return ExpandManifestReferencedFiles(manifestDir, referencedFiles, taskContext); - } - - /// - /// Generates a PRI file from the configuration - /// - /// Path to the package directory - /// Path to PRI config file (default: packageDir/priconfig.xml) - /// Output path for PRI file (default: packageDir/resources.pri) - /// Cancellation token - /// List of resource files that were processed - public async Task> GeneratePriFileAsync(DirectoryInfo packageDir, TaskContext taskContext, FileInfo? configPath = null, FileInfo? outputPath = null, CancellationToken cancellationToken = default) - { - if (!packageDir.Exists) - { - throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); - } - - var priConfigPath = configPath ?? new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); - var priOutputPath = outputPath ?? new FileInfo(Path.Combine(packageDir.FullName, "resources.pri")); - - if (!priConfigPath.Exists) - { - throw new FileNotFoundException($"PRI configuration file not found: {priConfigPath}"); - } - - var arguments = $@"new /pr ""{Path.TrimEndingDirectorySeparator(packageDir.FullName)}"" /cf ""{priConfigPath.FullName}"" /of ""{priOutputPath.FullName}"" /o"; - - taskContext.AddDebugMessage("Generating PRI file..."); - - try - { - var (stdout, stderr) = await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - // Parse the output to extract resource files - var resourceFiles = new List(); - var lines = stdout.Replace("\0", "").Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); - - foreach (var line in lines) - { - // Look for lines that match the pattern "Resource File: *" - const string resourceFileStr = "Resource File: "; - if (line.StartsWith(resourceFileStr, StringComparison.OrdinalIgnoreCase)) - { - var fileName = line[resourceFileStr.Length..].Trim(); - if (!string.IsNullOrEmpty(fileName)) - { - resourceFiles.Add(new FileInfo(Path.Combine(packageDir.FullName, fileName))); - } - } - } - - taskContext.AddDebugMessage($"PRI file generated: {priOutputPath}"); - if (resourceFiles.Count > 0) - { - taskContext.AddDebugMessage($"Processed {resourceFiles.Count} resource files"); - } - - return resourceFiles; - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to generate PRI file: {ex.Message}", ex); - } + return aliases; } /// @@ -627,11 +129,11 @@ private static string ResolveManifestPlaceholders(string manifestContent, string replacements[PlaceholderHelper.TargetNameToken] = nameWithoutExtension; // Also replace the Executable attribute value if it contains a placeholder - var execMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); - if (execMatch.Success && PlaceholderHelper.ContainsPlaceholders(execMatch.Groups[1].Value)) + var doc = AppxManifestDocument.Parse(manifestContent); + if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) { - manifestContent = AppxPackageApplicationExecutableAssignmentRegex().Replace( - manifestContent, $"${{1}}\"{executable}\""); + doc.ApplicationExecutable = executable; + manifestContent = doc.ToXml(); } taskContext.AddDebugMessage($"{UiSymbols.Note} Using specified executable: {executable}"); @@ -639,8 +141,8 @@ private static string ResolveManifestPlaceholders(string manifestContent, string else { // Check if the Executable attribute in the manifest has a placeholder - var execMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); - if (execMatch.Success && PlaceholderHelper.ContainsPlaceholders(execMatch.Groups[1].Value)) + var doc = AppxManifestDocument.Parse(manifestContent); + if (doc.ApplicationExecutable != null && PlaceholderHelper.ContainsPlaceholders(doc.ApplicationExecutable)) { // Try to auto-infer by finding .exe files in the input folder root var exeFiles = inputFolder.Exists @@ -655,8 +157,8 @@ private static string ResolveManifestPlaceholders(string manifestContent, string var nameWithoutExtension = Path.GetFileNameWithoutExtension(inferredExe); replacements[PlaceholderHelper.TargetNameToken] = nameWithoutExtension; - manifestContent = AppxPackageApplicationExecutableAssignmentRegex().Replace( - manifestContent, $"${{1}}\"{inferredExe}\""); + doc.ApplicationExecutable = inferredExe; + manifestContent = doc.ToXml(); taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-inferred executable: {inferredExe}"); } @@ -703,7 +205,7 @@ private async Task ResolveResourceLanguageXGenerateAsync( var priFile = new FileInfo(Path.Combine(inputFolder.FullName, "resources.pri")); if (priFile.Exists) { - languages = await ExtractLanguagesFromPriAsync(priFile, taskContext, cancellationToken); + languages = await priService.ExtractLanguagesFromPriAsync(priFile, taskContext, cancellationToken); } if (languages.Count == 0) @@ -724,67 +226,20 @@ private async Task ResolveResourceLanguageXGenerateAsync( /// internal static bool ContainsXGenerateLanguage(string manifestContent) { - return ResourceLanguageXGenerateBlockRegex().IsMatch(manifestContent); + var doc = AppxManifestDocument.Parse(manifestContent); + return doc.GetResourceLanguages() + .Any(lang => string.Equals(lang, "x-generate", StringComparison.OrdinalIgnoreCase)); } /// - /// Replaces the <Resources><Resource Language="x-generate"/></Resources> - /// block with concrete <Resource Language="..."/> entries for each specified language. + /// Replaces <Resource Language="x-generate"/> with concrete + /// <Resource Language="..."/> entries for each specified language. /// internal static string ReplaceXGenerateLanguage(string manifestContent, IList languages) { - var indent = " "; - var resourceEntries = string.Join(Environment.NewLine, languages.Select(lang => $"{indent}{indent}")); - var replacement = $"{indent}{Environment.NewLine}{resourceEntries}{Environment.NewLine}{indent}"; - - return ResourceLanguageXGenerateBlockRegex().Replace(manifestContent, replacement); - } - - /// - /// Extracts language qualifiers from a PRI file using makepri dump. - /// Returns a distinct, sorted list of BCP-47 language tags found in the PRI resource map. - /// - private async Task> ExtractLanguagesFromPriAsync( - FileInfo priFile, - TaskContext taskContext, - CancellationToken cancellationToken) - { - try - { - var dumpOutputFile = Path.Combine(Path.GetTempPath(), $"winapp-pri-dump-{Guid.NewGuid():N}.xml"); - var arguments = $@"dump /if ""{priFile.FullName}"" /of ""{dumpOutputFile}"" /o"; - - await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); - - if (!File.Exists(dumpOutputFile)) - { - return []; - } - - try - { - var dumpContent = await File.ReadAllTextAsync(dumpOutputFile, cancellationToken); - - // Extract language qualifiers from Candidate elements: - // or multi-qualifier like "Language-en-US, Scale-200" - var languages = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in PriDumpLanguageQualifierRegex().Matches(dumpContent)) - { - languages.Add(match.Groups[1].Value); - } - - return languages.OrderBy(l => l, StringComparer.OrdinalIgnoreCase).ToList(); - } - finally - { - File.Delete(dumpOutputFile); - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to extract languages from PRI: {ex.Message}"); - return []; - } + var doc = AppxManifestDocument.Parse(manifestContent); + doc.SetResourceLanguages(languages); + return doc.ToXml(); } /// @@ -831,6 +286,16 @@ public async Task CreateMsixPackageAsync( } } + // Check for an AppX subdirectory, which is a build artifact that should not be + // included in the package. Exclude it from staging and warn the user. + var excludedDirectories = new HashSet(StringComparer.OrdinalIgnoreCase); + var appxDir = new DirectoryInfo(Path.Combine(inputFolder.FullName, "AppX")); + if (appxDir.Exists) + { + excludedDirectories.Add("AppX"); + taskContext.AddStatusMessage($"{UiSymbols.Warning} Found 'AppX' directory in input folder. It will be excluded from the package."); + } + // Determine manifest path based on priority: // 1. Use provided manifestPath parameter // 2. Check for appxmanifest.xml or package.appxmanifest in input folder @@ -879,26 +344,37 @@ public async Task CreateMsixPackageAsync( // Fetch dotnet package list once for all downstream operations var dotNetPackageList = await FetchDotNetPackageListAsync(cancellationToken); - manifestContent = await UpdateAppxManifestContentAsync(manifestContent, null, null, sparse: false, selfContained: selfContained, dotNetPackageList, taskContext, cancellationToken); + // Determine executable path for ProcessorArchitecture auto-detection + string? resolvedExePath = null; + { + var tempDoc = AppxManifestDocument.Parse(manifestContent); + var appExe = tempDoc.ApplicationExecutable; + if (appExe != null) + { + resolvedExePath = Path.Combine(inputFolder.FullName, appExe); + } + } + + (manifestContent, var packageArch) = await UpdateAppxManifestContentAsync(manifestContent, null, null, resolvedExePath, sparse: false, selfContained: selfContained, dotNetPackageList, taskContext, cancellationToken); + + // Parse the manifest to extract identity, executable, and architecture info + var manifestDoc = AppxManifestDocument.Parse(manifestContent); try { if (string.IsNullOrWhiteSpace(finalPackageName)) { - var nameMatch = AppxPackageIdentityNameRegex().Match(manifestContent); - finalPackageName = nameMatch.Success ? nameMatch.Groups[1].Value : "Package"; + finalPackageName = manifestDoc.IdentityName ?? "Package"; } if (string.IsNullOrWhiteSpace(extractedPublisher)) { - var publisherMatch = AppxPackageIdentityPublisherRegex().Match(manifestContent); - extractedPublisher = publisherMatch.Success ? publisherMatch.Groups[1].Value : null; + extractedPublisher = manifestDoc.IdentityPublisher; } if (string.IsNullOrWhiteSpace(extractedVersion)) { - var versionMatch = AppxPackageIdentityVersionRegex().Match(manifestContent); - extractedVersion = versionMatch.Success ? versionMatch.Groups[1].Value : null; + extractedVersion = manifestDoc.IdentityVersion; } } catch @@ -906,47 +382,6 @@ public async Task CreateMsixPackageAsync( finalPackageName ??= "Package"; } - var executableMatch = AppxPackageApplicationExecutableRegex().Match(manifestContent); - - // If manifest Identity lacks ProcessorArchitecture, detect it from the executable PE header. - // If it already has one, warn when it doesn't match the actual executable. - string? packageArch = null; - if (executableMatch.Success) - { - var exePath = Path.Combine(inputFolder.FullName, executableMatch.Groups[1].Value); - var detectedArch = DetectPeArchitecture(exePath); - - var existingArchMatch = AppxPackageIdentityArchitectureValueRegex().Match(manifestContent); - if (!existingArchMatch.Success) - { - if (detectedArch != null) - { - manifestContent = IdentityElementRegex().Replace(manifestContent, m => - { - var tag = m.Value; - // Handle both self-closing () and open () tags - var insertPos = tag.EndsWith("/>") ? tag.Length - 2 : tag.Length - 1; - return tag.Insert(insertPos, $@" ProcessorArchitecture=""{detectedArch}"""); - }); - taskContext.AddDebugMessage($"{UiSymbols.Note} Auto-detected ProcessorArchitecture: {detectedArch}"); - packageArch = detectedArch; - } - } - else - { - packageArch = existingArchMatch.Groups[1].Value; - if (detectedArch != null) - { - var manifestArch = existingArchMatch.Groups[1].Value; - if (!string.Equals(manifestArch, detectedArch, StringComparison.OrdinalIgnoreCase) - && !string.Equals(manifestArch, "neutral", StringComparison.OrdinalIgnoreCase)) - { - taskContext.AddStatusMessage($"{UiSymbols.Warning} Manifest ProcessorArchitecture is '{manifestArch}' but the executable is {detectedArch}. This will likely cause runtime failures."); - } - } - } - } - // Clean the resolved package name to ensure it meets MSIX schema requirements finalPackageName = ManifestService.CleanPackageName(finalPackageName); @@ -995,16 +430,40 @@ public async Task CreateMsixPackageAsync( try { - // Copy input folder contents to staging directory - CopyDirectoryRecursive(inputFolder, stagingDir); - taskContext.AddDebugMessage($"{UiSymbols.Files} Copied input folder to staging directory"); + // Check if the manifest was generated by MSBuild and a .build.appxrecipe is available. + // When present, the recipe lists exactly which files belong in the package and their + // correct PackagePaths, producing a cleaner MSIX without build artifacts. + var isMSBuildGenerated = manifestDoc.Document.Root? + .Element(AppxManifestDocument.BuildNs + "Metadata")? + .Elements(AppxManifestDocument.BuildNs + "Item") + .Any(e => string.Equals(e.Attribute("Name")?.Value, "makepri.exe", StringComparison.OrdinalIgnoreCase)) == true; + + FileInfo? recipeFile = null; + if (isMSBuildGenerated) + { + recipeFile = inputFolder.EnumerateFiles("*.build.appxrecipe", SearchOption.TopDirectoryOnly).FirstOrDefault(); + } + + if (recipeFile != null) + { + taskContext.AddDebugMessage($"{UiSymbols.Note} MSBuild-generated manifest detected"); + taskContext.AddDebugMessage($"{UiSymbols.Files} Using appxrecipe for staging: {recipeFile.Name}"); + await CopyFilesFromRecipeAsync(recipeFile, stagingDir, taskContext, cancellationToken); + } + else + { + // No recipe available — copy the entire input folder to staging + CopyDirectoryRecursive(inputFolder, stagingDir); + taskContext.AddDebugMessage($"{UiSymbols.Files} Copied input folder to staging directory"); + } // Write the updated manifest into the staging directory var updatedManifestPath = Path.Combine(stagingDir.FullName, "appxmanifest.xml"); await File.WriteAllTextAsync(updatedManifestPath, manifestContent, Encoding.UTF8, cancellationToken); // Resolve executable path relative to the staging directory - FileInfo? executablePath = executableMatch.Success ? new FileInfo(Path.Combine(stagingDir.FullName, executableMatch.Groups[1].Value)) : null; + var applicationExecutable = manifestDoc.ApplicationExecutable; + FileInfo? executablePath = applicationExecutable != null ? new FileInfo(Path.Combine(stagingDir.FullName, applicationExecutable)) : null; // Pre-compute expanded manifest resources from the original manifest var manifestIsOutsideInputFolder = !inputFolder.FullName.TrimEnd(Path.DirectorySeparatorChar) @@ -1013,8 +472,8 @@ public async Task CreateMsixPackageAsync( // If manifest is outside input folder, copy its referenced assets into the staging directory if (manifestIsOutsideInputFolder) { - var externalAssets = GetExpandedManifestReferencedFiles(resolvedManifestPath, taskContext); - CopyAllAssets(externalAssets, stagingDir, taskContext); + var externalAssets = MrtAssetHelper.GetExpandedManifestReferencedFiles(resolvedManifestPath, taskContext); + MrtAssetHelper.CopyAllAssets(externalAssets, stagingDir, taskContext); } taskContext.AddDebugMessage($"Creating MSIX package from staging: {stagingDir.FullName}"); @@ -1029,14 +488,14 @@ public async Task CreateMsixPackageAsync( // Expand manifest-referenced files from the staging manifest so that // assets from both the input folder and external manifest are discovered. var stagingManifest = new FileInfo(Path.Combine(stagingDir.FullName, "appxmanifest.xml")); - var priExpandedFiles = GetExpandedManifestReferencedFiles(stagingManifest, taskContext); + var priExpandedFiles = MrtAssetHelper.GetExpandedManifestReferencedFiles(stagingManifest, taskContext); var priResourceCandidates = priExpandedFiles.Select(file => file.RelativePath); - await CreatePriConfigAsync( + await priService.CreatePriConfigAsync( stagingDir, taskContext, precomputedPriResourceCandidates: priResourceCandidates, cancellationToken: cancellationToken); - var resourceFiles = await GeneratePriFileAsync(stagingDir, taskContext, cancellationToken: cancellationToken); + var resourceFiles = await priService.GeneratePriFileAsync(stagingDir, taskContext, cancellationToken: cancellationToken); if (resourceFiles.Count > 0 && logger.IsEnabled(LogLevel.Debug)) { taskContext.AddDebugMessage($"Resource files included in PRI:"); @@ -1106,502 +565,38 @@ await taskContext.AddSubTaskAsync("Pri Resources", async (taskContext, cancellat return new CreateMsixPackageResult(outputMsixPath, autoSign); } - private async Task EmbedActivationManifestToExeAsync(FileInfo exePath, DirectoryInfo winAppSDKDeploymentDir, FileInfo windowsAppSDKAppXManifestPath, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + private async Task FetchDotNetPackageListAsync(CancellationToken cancellationToken) { - // Use applicationLocation for DLL content (where runtime files were copied by PrepareRuntimeForPackagingAsync) - var exeDir = exePath.Directory!; - - taskContext.AddDebugMessage($"{UiSymbols.Note} Generating activation manifest from: {windowsAppSDKAppXManifestPath}"); - taskContext.AddDebugMessage($"{UiSymbols.Package} Using DLL content from: {winAppSDKDeploymentDir}"); - - // Create a temporary manifest file - var tempManifestPath = new FileInfo(Path.Combine(exeDir.FullName, "WindowsAppSDK_temp.manifest")); - - try - { - // Build the entire manifest in memory, then write to disk once - var sb = new StringBuilder(); - sb.AppendLine(""); - sb.AppendLine(""); - - // Collect all AppX manifests (main package + component fragments) and their DLLs - (var packageDependencies, _) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); - if (packageDependencies == null || packageDependencies.Count == 0) - { - throw new InvalidOperationException("No Windows SDK packages found. Please install the Windows SDK or Windows App SDK."); - } - - var architecture = WorkspaceSetupService.GetSystemArchitecture(); - IEnumerable appxFragments = GetComponents(packageDependencies); - - // Combine all manifests: main AppxManifest.xml (Package root) + fragments (Fragment root) - var allManifests = new List { windowsAppSDKAppXManifestPath }; - allManifests.AddRange(appxFragments); - - // Combine all DLL file names from deployment dir and fragment native dirs - var allDllFiles = new List(winAppSDKDeploymentDir.EnumerateFiles("*.dll").Select(di => di.Name)); - allDllFiles.AddRange(appxFragments - .Select(fragment => Path.Combine(fragment.DirectoryName!, $"win-{architecture}\\native")) - .Where(Directory.Exists) - .SelectMany(dir => Directory.EnumerateFiles(dir, "*.dll")) - .Select(Path.GetFileName)!); - - // Single pass: process all AppX manifests (auto-detects Package vs Fragment root) - AppendAppManifestFromAppx( - sb, - redirectDlls: false, - inDllFiles: allDllFiles, - inAppxManifests: allManifests); - - // Phase 3: Discover and register third-party WinRT components (e.g., Win2D, WebView2) - // These packages ship .winmd files + native DLLs but no package.appxfragment - await AppendThirdPartyWinRTManifestEntriesAsync( - sb, architecture, dotNetPackageList, taskContext, cancellationToken); - - sb.AppendLine(""); - - // Single write to disk - await File.WriteAllTextAsync( - tempManifestPath.FullName, - sb.ToString(), - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), - cancellationToken); - - // Use mt.exe to merge manifests - await EmbedManifestFileToExeAsync(exePath, tempManifestPath, taskContext, cancellationToken); - } - finally + var cwd = new DirectoryInfo(currentDirectoryProvider.GetCurrentDirectory()); + var csprojFiles = dotNetService.FindCsproj(cwd); + var csproj = csprojFiles.Count > 0 ? csprojFiles[0] : null; + if (csproj == null) { - TryDeleteFile(tempManifestPath); + return null; } - } - - private IEnumerable GetComponents(Dictionary packageDependencies) - { - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - // Find appx fragments in the NuGet global cache (lowercase-id/version/ layout) - var appxFragments = packageDependencies - .Select(package => new FileInfo(Path.Combine(nugetCacheDir.FullName, package.Key.ToLowerInvariant(), package.Value, "runtimes-framework", "package.appxfragment"))) - .Where(f => f.Exists); - return appxFragments; + return await dotNetService.GetPackageListAsync(csproj, cancellationToken: cancellationToken); } - /// - /// Collects all user NuGet packages from winapp.yaml or .csproj. - /// Returns the full package dictionary (name → version) for WinRT component scanning. - /// - private async Task> GetAllUserPackagesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) + private async Task SignMsixPackageAsync(DirectoryInfo outputFolder, string certificatePassword, bool generateDevCert, bool installDevCert, string finalPackageName, string? extractedPublisher, FileInfo outputMsixPath, FileInfo? certPath, FileInfo resolvedManifestPath, TaskContext taskContext, CancellationToken cancellationToken) { - var packages = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Path 1: Try winapp.yaml - if (configService.Exists()) + if (certPath == null && generateDevCert) { - var config = configService.Load(); - foreach (var pkg in config.Packages) + if (string.IsNullOrWhiteSpace(extractedPublisher)) { - packages.TryAdd(pkg.Name, pkg.Version); + throw new InvalidOperationException("Publisher name required for certificate generation. Provide publisher option or ensure it exists in manifest."); } + + taskContext.AddDebugMessage($"{UiSymbols.Package} Generating certificate for publisher: {extractedPublisher}"); + + certPath = new FileInfo(Path.Combine(outputFolder.FullName, $"{finalPackageName}_cert.pfx")); + await certificateService.GenerateDevCertificateAsync(extractedPublisher, certPath, taskContext, certificatePassword, cancellationToken: cancellationToken); } - else + + if (certPath == null) { - // Path 2: Try .csproj via `dotnet list package --format json` (cached) - try - { - var allPackages = dotNetPackageList?.Projects? - .SelectMany(p => p.Frameworks ?? []) - .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - - if (allPackages != null) - { - foreach (var pkg in allPackages) - { - if (!string.IsNullOrEmpty(pkg.Id) && !string.IsNullOrEmpty(pkg.ResolvedVersion)) - { - packages.TryAdd(pkg.Id, pkg.ResolvedVersion); - } - } - } - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not retrieve package list from .csproj: {ex.Message}"); - } - } - - return packages; - } - - private async Task FetchDotNetPackageListAsync(CancellationToken cancellationToken) - { - var cwd = new DirectoryInfo(currentDirectoryProvider.GetCurrentDirectory()); - var csprojFiles = dotNetService.FindCsproj(cwd); - var csproj = csprojFiles.Count > 0 ? csprojFiles[0] : null; - if (csproj == null) - { - return null; - } - - return await dotNetService.GetPackageListAsync(csproj, cancellationToken: cancellationToken); - } - - /// - /// Discovers third-party WinRT components and appends their activatable class - /// entries to the in-memory SxS manifest (for self-contained deployment). - /// - private async Task AppendThirdPartyWinRTManifestEntriesAsync( - StringBuilder sb, - string architecture, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) - { - var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); - if (allPackages.Count == 0) - { - return; - } - - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - - // DiscoverWinRTComponents filters out packages that have a package.appxfragment - // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. - // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 - // are transitive WinAppSDK deps but need their own InProcessServer entries. - var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); - if (components.Count == 0) - { - return; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found {components.Count} third-party WinRT component(s) to register"); - - // Build a set of DLL names already registered in the manifest (from WinAppSDK fragments) - // so we can do exact-name dedup instead of substring matching. - var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in SxsFileNameRegex().Matches(sb.ToString())) - { - registeredDlls.Add(match.Groups[1].Value); - } - - foreach (var component in components) - { - var classes = winmdService.GetActivatableClasses(component.WinmdPath); - if (classes.Count == 0) - { - continue; - } - - // Skip components whose DLL is already in the manifest (from WinAppSDK fragments - // or a previous iteration) to avoid duplicate activatableClass entries. - if (!registeredDlls.Add(component.ImplementationDll)) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); - continue; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Registering {classes.Count} activatable class(es) from {component.ImplementationDll}"); - - sb.AppendLine($" "); - foreach (var className in classes) - { - sb.AppendLine($" "); - } - sb.AppendLine(" "); - } - } - - /// - /// Discovers third-party WinRT components and generates InProcessServer - /// extension entries for AppxManifest.xml (for packaged apps). - /// - private async Task AddThirdPartyWinRTExtensionsToAppxManifestAsync( - string manifestContent, - DotNetPackageListJson? dotNetPackageList, - TaskContext taskContext, - CancellationToken cancellationToken) - { - var allPackages = await GetAllUserPackagesAsync(dotNetPackageList, taskContext, cancellationToken); - if (allPackages.Count == 0) - { - return manifestContent; - } - - var nugetCacheDir = nugetService.GetNuGetGlobalPackagesDir(); - var architecture = WorkspaceSetupService.GetSystemArchitecture(); - - // DiscoverWinRTComponents filters out packages that have a package.appxfragment - // (WinAppSDK sub-packages), and only returns packages with both a .winmd and a matching DLL. - // We do NOT exclude the full WinAppSDK dependency tree because packages like WebView2 - // are transitive WinAppSDK deps but need their own InProcessServer entries. - var components = winmdService.DiscoverWinRTComponents(nugetCacheDir, allPackages, architecture); - if (components.Count == 0) - { - return manifestContent; - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Adding InProcessServer entries for {components.Count} third-party WinRT component(s)"); - - // Build a set of DLL names already registered in the manifest - // so we can do exact-name dedup instead of substring matching. - var registeredDlls = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in AppxManifestPathElementRegex().Matches(manifestContent)) - { - registeredDlls.Add(match.Groups[1].Value); - } - - var extensionsSb = new StringBuilder(); - foreach (var component in components) - { - var classes = winmdService.GetActivatableClasses(component.WinmdPath); - if (classes.Count == 0) - { - continue; - } - - // Skip components whose DLL is already in the manifest or in entries we've already generated - if (!registeredDlls.Add(component.ImplementationDll)) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Skipping {component.ImplementationDll} — already in manifest"); - continue; - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Adding {classes.Count} activatable class(es) for {component.ImplementationDll}"); - - extensionsSb.AppendLine(@" "); - extensionsSb.AppendLine(@" "); - extensionsSb.AppendLine($@" {component.ImplementationDll}"); - foreach (var className in classes) - { - extensionsSb.AppendLine($@" "); - } - extensionsSb.AppendLine(@" "); - extensionsSb.AppendLine(@" "); - } - - if (extensionsSb.Length == 0) - { - return manifestContent; - } - - return InsertPackageLevelExtensions(manifestContent, extensionsSb.ToString()); - } - - /// - /// Inserts Package-level extension entries (e.g. InProcessServer) into a manifest string. - /// Correctly distinguishes Package-level <Extensions> from Application-level ones. - /// - internal static string InsertPackageLevelExtensions(string manifestContent, string extensionEntries) - { - // IMPORTANT: These are Package-level extensions (e.g. windows.activatableClass.inProcessServer), - // NOT Application-level extensions. We must find a Package-level block - // (after ), not an Application-level one (inside ). - var extensionsCloseTag = ""; - var applicationsCloseTag = ""; - var applicationsCloseIndex = manifestContent.IndexOf(applicationsCloseTag, StringComparison.OrdinalIgnoreCase); - - // Look for AFTER — that's the Package-level one - var extensionsCloseIndex = applicationsCloseIndex >= 0 - ? manifestContent.IndexOf(extensionsCloseTag, applicationsCloseIndex, StringComparison.OrdinalIgnoreCase) - : -1; - - if (extensionsCloseIndex >= 0) - { - // Insert before the Package-level - return manifestContent.Insert(extensionsCloseIndex, extensionEntries); - } - - // No Package-level block exists — create one before - var packageCloseTag = ""; - var packageCloseIndex = manifestContent.LastIndexOf(packageCloseTag, StringComparison.OrdinalIgnoreCase); - if (packageCloseIndex >= 0) - { - var extensionsBlock = $" \n{extensionEntries} \n"; - return manifestContent.Insert(packageCloseIndex, extensionsBlock); - } - - return manifestContent; - } - - /// - /// Generates Win32 SxS manifest entries from AppX manifests (Package or Fragment format). - /// Auto-detects the root element name (Package vs Fragment) per document. - /// - /// StringBuilder to append manifest entries to - /// Whether to redirect DLLs to %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% - /// List of DLL file names to track - /// List of paths to the input AppX manifest files or fragments - internal static void AppendAppManifestFromAppx( - StringBuilder sb, - bool redirectDlls, - IEnumerable inDllFiles, - IEnumerable inAppxManifests) - { - var dllFileFormat = redirectDlls ? - @" " : - @" "; - - var dllFiles = inDllFiles.ToList(); - var hasPackageManifest = false; - - foreach (var inAppxManifest in inAppxManifests) - { - XmlDocument doc = new(); - doc.Load(inAppxManifest.FullName); - - // Auto-detect root element name (Package or Fragment) - var prefix = doc.DocumentElement?.LocalName ?? "Package"; - var isPackage = prefix == "Package"; - if (isPackage) - { - hasPackageManifest = true; - } - - var nsmgr = new XmlNamespaceManager(doc.NameTable); - nsmgr.AddNamespace("m", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); - // Add InProcessServer elements to the generated appxmanifest - var xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:InProcessServer"; - XmlNodeList? inProcessServers = doc.SelectNodes(xQuery, nsmgr); - if (inProcessServers != null) - { - foreach (XmlNode winRTFactory in inProcessServers) - { - var dllFileNode = winRTFactory.SelectSingleNode("./m:Path", nsmgr); - if (dllFileNode == null) - { - continue; - } - - var dllFile = dllFileNode.InnerText; - var typesNames = winRTFactory.SelectNodes("./m:ActivatableClass", nsmgr)?.OfType(); - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(); - if (typesNames != null) - { - foreach (var typeNode in typesNames) - { - var attribs = typeNode.Attributes?.OfType().ToArray(); - var typeName = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "ActivatableClassId") - ?.InnerText; - var xmlEntryFormat = - @" "; - sb.AppendFormat(xmlEntryFormat, typeName); - sb.AppendLine(); - dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); - } - } - sb.AppendLine(@" "); - } - } - - // Only for Package manifests with redirect - if (isPackage && redirectDlls) - { - foreach (var dllFile in dllFiles) - { - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(@""); - } - } - // Add ProxyStub elements to the generated appxmanifest - dllFiles = [.. inDllFiles]; - - xQuery = $"./m:{prefix}/m:Extensions/m:Extension/m:ProxyStub"; - var inProcessProxystubs = doc.SelectNodes(xQuery, nsmgr); - if (inProcessProxystubs != null) - { - foreach (XmlNode proxystub in inProcessProxystubs) - { - var classIDAdded = false; - - var dllFileNode = proxystub.SelectSingleNode("./m:Path", nsmgr); - var dllFile = dllFileNode?.InnerText; - // exclude PushNotificationsLongRunningTask, which requires the Singleton (which is unavailable for self-contained apps) - // exclude Widgets entries unless/until they have been tested and verified by the Widgets team - if (dllFile == null || dllFile == "PushNotificationsLongRunningTask.ProxyStub.dll" || dllFile == "Microsoft.Windows.Widgets.dll") - { - continue; - } - var typesNamesForProxy = proxystub.SelectNodes("./m:Interface", nsmgr)?.OfType(); - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(); - if (typesNamesForProxy != null) - { - foreach (var typeNode in typesNamesForProxy) - { - if (!classIDAdded) - { - var classIdAttribute = proxystub.Attributes?.OfType().ToArray(); - var classID = classIdAttribute - ?.OfType() - ?.SingleOrDefault(x => x.Name == "ClassId") - ?.InnerText; - - if (classID != null) - { - var xmlEntryFormat = @" "; - sb.AppendFormat(xmlEntryFormat, classID); - classIDAdded = true; - } - } - var attribs = typeNode.Attributes?.OfType().ToArray(); - var typeID = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "InterfaceId") - ?.InnerText; - var typeNames = attribs - ?.OfType() - ?.SingleOrDefault(x => x.Name == "Name") - ?.InnerText; - var xmlEntryFormatForStubs = @" "; - if (typeNames != null && typeID != null) - { - sb.AppendFormat(xmlEntryFormatForStubs, typeNames, typeID); - sb.AppendLine(); - dllFiles.RemoveAll(e => e.Equals(dllFile, StringComparison.OrdinalIgnoreCase)); - } - } - } - sb.AppendLine(@" "); - } - } - } - - if (hasPackageManifest && redirectDlls) - { - foreach (var dllFile in dllFiles) - { - sb.AppendFormat(dllFileFormat, dllFile); - sb.AppendLine(@""); - } - } - } - - private async Task SignMsixPackageAsync(DirectoryInfo outputFolder, string certificatePassword, bool generateDevCert, bool installDevCert, string finalPackageName, string? extractedPublisher, FileInfo outputMsixPath, FileInfo? certPath, FileInfo resolvedManifestPath, TaskContext taskContext, CancellationToken cancellationToken) - { - if (certPath == null && generateDevCert) - { - if (string.IsNullOrWhiteSpace(extractedPublisher)) - { - throw new InvalidOperationException("Publisher name required for certificate generation. Provide publisher option or ensure it exists in manifest."); - } - - taskContext.AddDebugMessage($"{UiSymbols.Package} Generating certificate for publisher: {extractedPublisher}"); - - certPath = new FileInfo(Path.Combine(outputFolder.FullName, $"{finalPackageName}_cert.pfx")); - await certificateService.GenerateDevCertificateAsync(extractedPublisher, certPath, taskContext, certificatePassword, cancellationToken: cancellationToken); - } - - if (certPath == null) - { - throw new InvalidOperationException("Certificate path required for signing. Provide certificatePath or set generateDevCert to true."); - } + throw new InvalidOperationException("Certificate path required for signing. Provide certificatePath or set generateDevCert to true."); + } // Validate that the certificate publisher matches the manifest publisher taskContext.AddDebugMessage($"{UiSymbols.Note} Validating certificate and manifest publishers match..."); @@ -1655,9 +650,10 @@ private static void TryDeleteFile(FileInfo path) } /// - /// Recursively copies all files and subdirectories from source to destination. + /// Recursively copies all files and subdirectories from source to destination, + /// skipping any top-level directories whose names appear in . /// - private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo destination) + private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo destination, HashSet? excludedDirectories = null) { destination.Create(); @@ -1668,6 +664,11 @@ private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo d foreach (var subDir in source.EnumerateDirectories()) { + if (excludedDirectories != null && excludedDirectories.Contains(subDir.Name)) + { + continue; + } + var destSubDir = new DirectoryInfo(Path.Combine(destination.FullName, subDir.Name)); CopyDirectoryRecursive(subDir, destSubDir); } @@ -1689,7 +690,7 @@ private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo d { return found; } - + directory = directory.Parent; } @@ -1716,172 +717,95 @@ private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo d return null; } - /// - /// Detects the architecture of a PE file and returns an MSIX-style architecture string: - /// "x86", "x64", "arm", "arm64", or "neutral". - /// - /// Rules: - /// - Native PE images are classified from the COFF Machine field. - /// - Managed .NET IL-only images are classified using COR flags: - /// * I386 + ILOnly + Requires32Bit => x86 - /// * I386 + ILOnly + !Requires32Bit => neutral - /// - Mixed-mode / native-hosted managed images fall back to the native Machine field. - /// - /// Returns null if the file is not a valid PE image or uses an unsupported architecture. - /// - internal static string? DetectPeArchitecture(string filePath) - { - try - { - using var stream = File.OpenRead(filePath); - using var peReader = new PEReader(stream); - - var headers = peReader.PEHeaders; - var coff = headers.CoffHeader; - var cor = headers.CorHeader; - - ushort machine = (ushort)coff.Machine; - - // Native or mixed-mode case: use the PE machine directly. - if (cor is null) - { - return MapNativeMachine(machine); - } - - CorFlags flags = cor.Flags; - bool isIlOnly = (flags & CorFlags.ILOnly) != 0; - bool requires32Bit = (flags & CorFlags.Requires32Bit) != 0; - - // Managed IL-only assemblies need special handling. - // In particular, IL-only I386 without Requires32Bit is effectively AnyCPU/neutral. - if (isIlOnly) - { - return machine switch - { - 0x014C => requires32Bit ? "x86" : "neutral", // I386 - 0x8664 => "x64", // unusual for pure IL-only, but valid to preserve - 0x01C0 => "arm", // ARM - 0x01C4 => "arm", // ARMNT - 0xAA64 => "arm64", // ARM64 - _ => null - }; - } - - // Mixed-mode / native-entry managed image: machine matters. - return MapNativeMachine(machine); - } - catch - { - return null; - } - } - - private static string? MapNativeMachine(ushort machine) => machine switch - { - 0x014C => "x86", // IMAGE_FILE_MACHINE_I386 - 0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64 - 0xAA64 => "arm64", // IMAGE_FILE_MACHINE_ARM64 - 0x01C4 => "arm", // IMAGE_FILE_MACHINE_ARMNT - 0x01C0 => "arm", // IMAGE_FILE_MACHINE_ARM - _ => null - }; - /// /// Updates the manifest identity, application ID, and executable path for sparse packaging /// - private async Task UpdateAppxManifestContentAsync( + private async Task<(string Content, string? DetectedArchitecture)> UpdateAppxManifestContentAsync( string originalAppxManifestContent, MsixIdentityResult? identity, string? entryPointPath, + string? exePath, bool sparse, bool selfContained, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) { - var modifiedContent = originalAppxManifestContent; + var doc = AppxManifestDocument.Parse(originalAppxManifestContent); if (identity != null) { - // Replace package identity attributes - modifiedContent = AppxPackageIdentityNameAssignmentRegex().Replace(modifiedContent, $@"$1""{identity.PackageName}"""); - - // Replace application ID - modifiedContent = AppxApplicationIdAssignmentRegex().Replace(modifiedContent, $@"$1""{identity.ApplicationId}"""); + doc.IdentityName = identity.PackageName; + doc.ApplicationId = identity.ApplicationId; } if (entryPointPath != null) { - // Replace executable path with relative path from package root var entryPointDir = Path.GetDirectoryName(entryPointPath); var workingDir = string.IsNullOrEmpty(entryPointDir) ? currentDirectoryProvider.GetCurrentDirectory() : entryPointDir; string relativeExecutablePath; try { - // Calculate relative path from the working directory (package root) to the executable relativeExecutablePath = Path.GetRelativePath(workingDir, entryPointPath); - - // Ensure we use forward slashes for consistency in manifest relativeExecutablePath = relativeExecutablePath.Replace('\\', '/'); } catch { - // Fallback to just the filename if relative path calculation fails relativeExecutablePath = Path.GetFileName(entryPointPath); } - modifiedContent = AppxPackageApplicationExecutableAssignmentRegex().Replace(modifiedContent, $@"$1""{relativeExecutablePath}"""); + doc.ApplicationExecutable = relativeExecutablePath; } bool isExe = Path.HasExtension(entryPointPath) && string.Equals(Path.GetExtension(entryPointPath), ".exe", StringComparison.OrdinalIgnoreCase); - // Only apply sparse packaging modifications if sparse is true if (sparse) { // Add required namespaces for sparse packaging - if (!modifiedContent.Contains("xmlns:uap10")) - { - modifiedContent = AppxPackageElementOpenTagRegex().Replace(modifiedContent, @"$1 xmlns:uap10=""http://schemas.microsoft.com/appx/manifest/uap/windows10/10""$2"); - } - - if (!modifiedContent.Contains("xmlns:desktop6")) - { - modifiedContent = AppxPackageOpenTagRegex().Replace(modifiedContent, @"$1 xmlns:desktop6=""http://schemas.microsoft.com/appx/manifest/desktop/windows10/6""$2"); - } + doc.EnsureNamespace("uap10", AppxManifestDocument.Uap10Ns); + doc.EnsureNamespace("desktop6", AppxManifestDocument.Desktop6Ns); // Add sparse package properties - if (!modifiedContent.Contains("")) + var properties = doc.Document.Root?.Element(AppxManifestDocument.DefaultNs + "Properties"); + if (properties != null && properties.Element(AppxManifestDocument.Uap10Ns + "AllowExternalContent") == null) { - modifiedContent = AppxPackagePropertiesCloseTagRegex().Replace(modifiedContent, @" true - disabled -$1"); + properties.Add(new XElement(AppxManifestDocument.Uap10Ns + "AllowExternalContent", "true")); + properties.Add(new XElement(AppxManifestDocument.Desktop6Ns + "RegistryWriteVirtualization", "disabled")); } // Ensure Application has sparse packaging attributes - if (!modifiedContent.Contains("uap10:TrustLevel") && isExe) + var app = doc.GetFirstApplicationElement(); + if (app != null && isExe && app.Attribute(AppxManifestDocument.Uap10Ns + "TrustLevel") == null) { - modifiedContent = AppxApplicationOpenTagRegex().Replace(modifiedContent, @"$1 uap10:TrustLevel=""mediumIL"" uap10:RuntimeBehavior=""packagedClassicApp""$2"); + app.SetAttributeValue(AppxManifestDocument.Uap10Ns + "TrustLevel", "mediumIL"); + app.SetAttributeValue(AppxManifestDocument.Uap10Ns + "RuntimeBehavior", "packagedClassicApp"); } // Remove EntryPoint if present (not needed for sparse packages) - modifiedContent = AppxPackageEntryPointRegex().Replace(modifiedContent, ""); + doc.ApplicationEntryPoint = null; // Add AppListEntry="none" to VisualElements if not present - if (!modifiedContent.Contains("AppListEntry")) + var ve = doc.GetVisualElements(); + if (ve != null && ve.Attribute("AppListEntry") == null) { - modifiedContent = AppxPackageVisualElementsOpenTagRegex().Replace(modifiedContent, @"$1 AppListEntry=""none""$2"); + ve.SetAttributeValue("AppListEntry", "none"); } // Add sparse-specific capabilities if not present - if (!modifiedContent.Contains("unvirtualizedResources")) + var capsElement = doc.GetCapabilitiesElement(); + bool hasUnvirtualizedResources = capsElement?.Elements() + .Any(e => string.Equals(e.Attribute("Name")?.Value, "unvirtualizedResources", StringComparison.OrdinalIgnoreCase)) == true; + if (!hasUnvirtualizedResources) { - modifiedContent = AppxPackageRunFullTrustCapabilityRegex().Replace(modifiedContent, @"$1 - - "); + doc.EnsureCapability("unvirtualizedResources", AppxManifestDocument.RescapNs); + doc.EnsureCapability("allowElevation", AppxManifestDocument.RescapNs); } } + // Convert to string for remaining string-based operations + var modifiedContent = doc.ToXml(); + // Update or insert Windows App SDK dependency (skip for self-contained packages) if (!selfContained && (entryPointPath == null || isExe)) { @@ -1899,7 +823,15 @@ private async Task UpdateAppxManifestContentAsync( // Stamp build metadata with CLI version modifiedContent = AddBuildMetadata(modifiedContent); - return modifiedContent; + // Auto-detect ProcessorArchitecture from the executable PE header if not already set. + // Without this, ARM64 Windows resolves framework dependencies to ARM64 DLLs even for x64 apps. + string? detectedArch = null; + if (exePath != null) + { + (modifiedContent, detectedArch) = AutoDetectProcessorArchitecture(modifiedContent, exePath, taskContext); + } + + return (modifiedContent, detectedArch); } /// @@ -1910,519 +842,12 @@ internal static string AddBuildMetadata(string manifestContent) { var version = VersionHelper.GetVersionString(); - // Add xmlns:build namespace to if not present - if (!manifestContent.Contains("xmlns:build")) - { - manifestContent = BuildMetadataPackageOpenTagRegex().Replace(manifestContent, - @"$1 xmlns:build=""http://schemas.microsoft.com/developer/appx/2015/build""$2"); - } - - // Add 'build' to IgnorableNamespaces if not already listed - if (!BuildMetadataIgnorableNamespacesCheckRegex().IsMatch(manifestContent)) - { - if (manifestContent.Contains("IgnorableNamespaces")) - { - // Append 'build' to the existing IgnorableNamespaces value - manifestContent = BuildMetadataIgnorableNamespacesAssignRegex().Replace(manifestContent, - @"$1 build"""); - } - else - { - // No IgnorableNamespaces attribute exists — add one to - manifestContent = BuildMetadataPackageOpenTagRegex().Replace(manifestContent, - @"$1 IgnorableNamespaces=""build""$2"); - } - } - - var buildItemEntry = $@""; + var doc = AppxManifestDocument.Parse(manifestContent); - if (manifestContent.Contains(" - manifestContent = BuildMetadataPackageCloseTagRegex().Replace(manifestContent, - $"\n$1\n$1 {buildItemEntry}\n$1\n$2"); - } - - return manifestContent; - } - - /// - /// Updates or inserts the Windows App SDK dependency in the manifest - /// - /// The manifest content to modify - /// The modified manifest content - private async Task UpdateWindowsAppSdkDependencyAsync(string manifestContent, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - // Get the Windows App SDK version from the locked winapp.yaml config - var winAppSdkInfo = await GetWindowsAppSdkDependencyInfoAsync(dotNetPackageList, taskContext, cancellationToken); - - if (winAppSdkInfo == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not determine Windows App SDK version, skipping dependency update"); - return manifestContent; - } - - // Check if Dependencies section exists - if (!manifestContent.Contains("")) - { - // Add Dependencies section before Applications - manifestContent = AppxPackageApplicationsTagRegex().Replace(manifestContent, $@" - - -$1"); - - taskContext.AddDebugMessage($"{UiSymbols.Package} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} (v{winAppSdkInfo.MinVersion})"); - } - else - { - // Check if Windows App SDK dependency already exists - var existingDependencyPattern = @"]*Name\s*=\s*[""']Microsoft\.WindowsAppRuntime\.[^""']*[""'][^>]*>"; - var existingMatch = Regex.Match(manifestContent, existingDependencyPattern, RegexOptions.IgnoreCase); - - if (existingMatch.Success) - { - // Update existing dependency - var newDependency = $@""; - manifestContent = Regex.Replace( - manifestContent, - existingDependencyPattern, - newDependency, - RegexOptions.IgnoreCase); - - taskContext.AddDebugMessage($"{UiSymbols.Sync} Updated Windows App SDK dependency to {winAppSdkInfo.RuntimeName} v{winAppSdkInfo.MinVersion}"); - } - else - { - // Add new dependency to existing Dependencies section - manifestContent = AppxPackageDependenciesCloseTagRegex().Replace(manifestContent, $@" - $1"); - - taskContext.AddDebugMessage($"{UiSymbols.Add} Added Windows App SDK dependency {winAppSdkInfo.RuntimeName} to existing Dependencies section (v{winAppSdkInfo.MinVersion})"); - } - } - - return manifestContent; - } - - /// - /// Gets the Windows App SDK dependency information from the locked winapp.yaml config and package source - /// - /// The dependency information, or null if not found - private async Task GetWindowsAppSdkDependencyInfoAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - try - { - var msixDir = await GetRuntimeMsixDirAsync(dotNetPackageList, taskContext, cancellationToken); - if (msixDir == null) - { - return null; - } - - // Get the runtime package information from the MSIX inventory - var runtimeInfo = GetWindowsAppRuntimePackageInfo(taskContext, msixDir, cancellationToken); - if (runtimeInfo == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Could not parse Windows App Runtime package information from MSIX inventory"); - return null; - } - - return runtimeInfo; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Error getting Windows App SDK dependency info: {ex.Message}"); - return null; - } - } - - private async Task GetRuntimeMsixDirAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - (var packageDependencies, var mainVersion) = await GetWinAppSDKPackageDependenciesAsync(dotNetPackageList, taskContext, cancellationToken); - if (packageDependencies == null || mainVersion == null) - { - return null; - } - - // Look for the runtime package in the package dependencies - var runtimePackage = packageDependencies.FirstOrDefault(kvp => - kvp.Key.StartsWith(BuildToolsService.WINAPP_SDK_RUNTIME_PACKAGE, StringComparison.OrdinalIgnoreCase)); - - // Create a dictionary with versions for FindWindowsAppSdkMsixDirectory - var usedVersions = new Dictionary - { - [BuildToolsService.WINAPP_SDK_PACKAGE] = mainVersion - }; - - if (runtimePackage.Key != null) - { - // For Windows App SDK 1.8+, there's a separate runtime package - var runtimeVersion = runtimePackage.Value; - usedVersions[runtimePackage.Key] = runtimeVersion; - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found runtime package: {runtimePackage.Key} v{runtimeVersion}"); - } - else - { - // For Windows App SDK 1.7 and earlier, runtime is included in the main package - taskContext.AddDebugMessage($"{UiSymbols.Note} No separate runtime package found - using main package (Windows App SDK 1.7 or earlier)"); - taskContext.AddDebugMessage($"{UiSymbols.Note} Available package dependencies: {string.Join(", ", packageDependencies.Keys)}"); - } - - // Find the MSIX directory with the runtime package - var msixDir = workspaceSetupService.FindWindowsAppSdkMsixDirectory(usedVersions); - if (msixDir == null) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} Windows App SDK MSIX directory not found for dependent runtime package"); - return null; - } - - return msixDir; - } - - private async Task<(Dictionary? CachedPackages, string? MainVersion)> GetWinAppSDKPackageDependenciesAsync(DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - string? mainVersion = null; - // Path 1: Try winapp.yaml (C++ / native projects) - if (configService.Exists()) - { - var config = configService.Load(); - mainVersion = config.GetVersion(BuildToolsService.WINAPP_SDK_PACKAGE); - } - else - { - // Path 2: Try .csproj via `dotnet list package --format json` - taskContext.AddDebugMessage($"{UiSymbols.Package} Querying NuGet package list..."); - - var allPackages = dotNetPackageList?.Projects? - .SelectMany(p => p.Frameworks ?? []) - .SelectMany(f => (f.TopLevelPackages ?? []).Concat(f.TransitivePackages ?? [])); - - var winAppSdkPkg = allPackages? - .FirstOrDefault(p => string.Equals(p.Id, BuildToolsService.WINAPP_SDK_PACKAGE, StringComparison.OrdinalIgnoreCase)); - - if (winAppSdkPkg != null && !string.IsNullOrEmpty(winAppSdkPkg.ResolvedVersion)) - { - mainVersion = winAppSdkPkg.ResolvedVersion; - } - } - - if (string.IsNullOrEmpty(mainVersion)) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} No {BuildToolsService.WINAPP_SDK_PACKAGE} package found in winapp.yaml"); - return (null, null); - } - taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App SDK main package: v{mainVersion}"); - try - { - // Query NuGet API for the dependency tree of this package - var deps = await nugetService.GetPackageDependenciesAsync(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion, cancellationToken); - - // Include the main package itself in the result - deps.TryAdd(BuildToolsService.WINAPP_SDK_PACKAGE, mainVersion); - - return (deps, mainVersion); - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Warning} {BuildToolsService.WINAPP_SDK_PACKAGE} v{mainVersion} not found in package source: {ex.Message}"); - } - - return (null, null); - } - - /// - /// Parses the MSIX inventory file to extract Windows App Runtime package information - /// - /// The MSIX directory containing the inventory file - /// Package information, or null if not found - private static WindowsAppRuntimePackageInfo? GetWindowsAppRuntimePackageInfo(TaskContext taskContext, DirectoryInfo msixDir, CancellationToken cancellationToken) - { - try - { - // Use the shared inventory parsing logic (synchronous version) - var packageEntries = WorkspaceSetupService.ParseMsixInventoryAsync(taskContext, msixDir, cancellationToken).GetAwaiter().GetResult(); - - if (packageEntries == null || packageEntries.Count == 0) - { - return null; - } - - // Look for the Windows App Runtime main package (not Framework packages) - var mainRuntimeEntry = packageEntries - .FirstOrDefault(entry => entry.PackageIdentity.StartsWith("Microsoft.WindowsAppRuntime.") && - !entry.PackageIdentity.Contains("Framework")); - - if (mainRuntimeEntry != null) - { - // Parse the PackageIdentity (format: Name_Version_Architecture_PublisherId) - var identityParts = mainRuntimeEntry.PackageIdentity.Split('_'); - if (identityParts.Length >= 2) - { - var runtimeName = identityParts[0]; - var version = identityParts[1]; - - taskContext.AddDebugMessage($"{UiSymbols.Package} Found Windows App Runtime: {runtimeName} v{version}"); - - return new WindowsAppRuntimePackageInfo - { - RuntimeName = runtimeName, - MinVersion = version - }; - } - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} No Windows App Runtime main package found in inventory"); - taskContext.AddDebugMessage($"{UiSymbols.Note} Available packages: {string.Join(", ", packageEntries.Select(e => e.PackageIdentity))}"); - - return null; - } - catch (Exception ex) - { - taskContext.AddDebugMessage($"{UiSymbols.Note} Error parsing MSIX inventory: {ex.Message}"); - return null; - } - } - - /// - /// Copies files referenced in the manifest to the target directory. - /// - private static void CopyAllAssets(List<(FileInfo SourceFile, string RelativePath)> expandedFiles, DirectoryInfo targetDir, TaskContext taskContext) - { - var filesCopied = 0; - - foreach (var (sourceFile, relativePath) in expandedFiles) - { - var targetFile = new FileInfo(Path.Combine(targetDir.FullName, relativePath)); - - targetFile.Directory?.Create(); - sourceFile.CopyTo(targetFile.FullName, overwrite: true); - filesCopied++; - - taskContext.AddDebugMessage($"{UiSymbols.Files} Copied manifest resource: {relativePath}"); - } - - taskContext.AddDebugMessage($"{UiSymbols.Note} Copied {filesCopied} files to target directory"); - } - - // ltr / rtl - private static bool IsLayoutDirectionQualifier(string token) - { - return token.Equals("ltr", StringComparison.OrdinalIgnoreCase) || - token.Equals("rtl", StringComparison.OrdinalIgnoreCase); - } - - private static bool IsSingleQualifierToken(string token) - { - if (string.IsNullOrEmpty(token)) - { - return false; - } - - return LanguageQualifierRegex().IsMatch(token) - || ScaleQualifierRegex().IsMatch(token) - || ThemeQualifierRegex().IsMatch(token) - || ContrastQualifierRegex().IsMatch(token) - || DxFeatureLevelQualifierRegex().IsMatch(token) - || DeviceFamilyQualifierRegex().IsMatch(token) - || HomeRegionQualifierRegex().IsMatch(token) - || ConfigurationQualifierRegex().IsMatch(token) - || TargetSizeQualifierRegex().IsMatch(token) - || AltFormQualifierRegex().IsMatch(token) - || IsLayoutDirectionQualifier(token); - } - - private static bool IsQualifierToken(string token) - { - if (string.IsNullOrEmpty(token)) - { - return false; - } - - var parts = token.Split('_'); - - foreach (var part in parts) - { - if (!IsSingleQualifierToken(part)) - { - return false; - } - } - - return true; - } - - /// - /// Returns true if is a valid MRT - /// variant of the logical base name (dots allowed in base name). - /// - private static bool IsMrtVariantName(string logicalBaseName, string candidateNameWithoutExtension) - { - if (string.IsNullOrWhiteSpace(logicalBaseName) || string.IsNullOrWhiteSpace(candidateNameWithoutExtension)) - { - return false; - } - - // Split by '.'; "Logo.scale-200.theme-dark" -> ["Logo", "scale-200", "theme-dark"] - var parts = candidateNameWithoutExtension.Split('.'); - - if (parts.Length == 0) - { - return false; - } - - // First token must match logical base name (case-insensitive) - if (!parts[0].Equals(logicalBaseName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - // No qualifiers -> exact logical name, valid - if (parts.Length == 1) - { - return true; - } - - // All remaining tokens must be valid MRT qualifiers - for (int i = 1; i < parts.Length; i++) - { - if (!IsQualifierToken(parts[i])) - { - return false; - } - } - - return true; - } - - /// - /// For a qualified logical name like "Logo.scale-100" or "Logo.targetsize-24_altform-unplated", - /// returns the unqualified asset family base (e.g. "Logo"). - /// If the name has no trailing qualifier tokens, returns the original name unchanged. - /// - private static string GetMrtVariantBaseName(string logicalBaseName) - { - ArgumentException.ThrowIfNullOrWhiteSpace(logicalBaseName); - - var parts = logicalBaseName.Split('.'); - if (parts.Length <= 1) - { - return logicalBaseName; - } - - // Find the earliest segment where every remaining segment is a valid qualifier token. - for (int i = 1; i < parts.Length; i++) - { - var allRemainingAreQualifiers = true; - for (int j = i; j < parts.Length; j++) - { - if (!IsQualifierToken(parts[j])) - { - allRemainingAreQualifiers = false; - break; - } - } - - if (allRemainingAreQualifiers) - { - return string.Join('.', parts[..i]); - } - } - - return logicalBaseName; - } - - private static readonly string[] patterns = new[] { "*.dll", "workloads*.json", "restartAgent.exe", "map.html", "*.mui", "*.png", "*.winmd", "*.xaml", "*.xbf", "*.pri" }; - - private static async Task CopyRuntimeFilesAsync(DirectoryInfo extractedDir, DirectoryInfo deploymentDir, TaskContext taskContext, CancellationToken cancellationToken) - { - await taskContext.AddSubTaskAsync("Copying Runtime Files", (taskContext, cancellationToken) => - { - foreach (var pattern in patterns) - { - var files = extractedDir.GetFiles(pattern, SearchOption.AllDirectories); - foreach (var file in files) - { - var relativePath = Path.GetRelativePath(extractedDir.FullName, file.FullName); - var destPath = Path.Combine(deploymentDir.FullName, relativePath); - - // Create destination directory if needed - var destDir = Path.GetDirectoryName(destPath); - if (!string.IsNullOrEmpty(destDir)) - { - Directory.CreateDirectory(destDir); - } - - file.CopyTo(destPath, overwrite: true); - - taskContext.AddDebugMessage($"{UiSymbols.Files} {relativePath}"); - } - } - - return Task.FromResult(0); - }, cancellationToken); - } - - /// - /// Prepares Windows App SDK runtime files for packaging into an MSIX by extracting them to the input folder - /// - /// The folder where runtime files should be copied - /// Cancellation token - /// The path to the self-contained deployment directory - private async Task PrepareRuntimeForPackagingAsync(DirectoryInfo inputFolder, DotNetPackageListJson? dotNetPackageList, TaskContext taskContext, CancellationToken cancellationToken) - { - var arch = WorkspaceSetupService.GetSystemArchitecture(); - - var winappDir = winappDirectoryService.GetLocalWinappDirectory(); - - // Extract runtime files using the existing method - await SetupSelfContainedAsync(winappDir, arch, taskContext, dotNetPackageList, cancellationToken); - - // Copy runtime files from .winapp/self-contained to input folder - var runtimeSourceDir = new DirectoryInfo(Path.Combine(winappDir.FullName, "self-contained", arch, "deployment")); - - if (runtimeSourceDir.Exists) - { - // Copy files recursively to maintain directory structure - foreach (var file in runtimeSourceDir.GetFiles("*", SearchOption.AllDirectories)) - { - var relativePath = Path.GetRelativePath(runtimeSourceDir.FullName, file.FullName); - var destFile = Path.Combine(inputFolder.FullName, relativePath); - - // Create destination directory if needed - var destDir = Path.GetDirectoryName(destFile); - if (!string.IsNullOrEmpty(destDir)) - { - Directory.CreateDirectory(destDir); - } - - file.CopyTo(destFile, overwrite: true); - - taskContext.AddDebugMessage($"{UiSymbols.Folder} Bundled runtime: {relativePath}"); - } - - taskContext.AddDebugMessage($"{UiSymbols.Check} Windows App SDK runtime bundled into package"); - } - else - { - throw new DirectoryNotFoundException($"Runtime files not found at {runtimeSourceDir}"); - } + doc.EnsureNamespace("build", AppxManifestDocument.BuildNs); + doc.AddIgnorableNamespace("build"); + doc.SetBuildMetadata("Microsoft.WinAppCli", version); - return runtimeSourceDir; + return doc.ToXml(); } } diff --git a/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs b/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs new file mode 100644 index 00000000..d7340d6d --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Reflection.PortableExecutable; + +namespace WinApp.Cli.Services; + +/// +/// Static helper for detecting executable architecture from PE headers. +/// +internal static class PeHelper +{ + /// + /// Detects the architecture of a PE file and returns an MSIX-style architecture string: + /// "x86", "x64", "arm", "arm64", or "neutral". + /// + /// Rules: + /// - Native PE images are classified from the COFF Machine field. + /// - Managed .NET IL-only images are classified using COR flags: + /// * I386 + ILOnly + Requires32Bit => x86 + /// * I386 + ILOnly + !Requires32Bit => neutral + /// - Mixed-mode / native-hosted managed images fall back to the native Machine field. + /// + /// Returns null if the file is not a valid PE image or uses an unsupported architecture. + /// + internal static string? DetectPeArchitecture(string filePath) + { + try + { + using var stream = File.OpenRead(filePath); + using var peReader = new PEReader(stream); + + var headers = peReader.PEHeaders; + var coff = headers.CoffHeader; + var cor = headers.CorHeader; + + ushort machine = (ushort)coff.Machine; + + // Native or mixed-mode case: use the PE machine directly. + if (cor is null) + { + return MapNativeMachine(machine); + } + + CorFlags flags = cor.Flags; + bool isIlOnly = (flags & CorFlags.ILOnly) != 0; + bool requires32Bit = (flags & CorFlags.Requires32Bit) != 0; + + // Managed IL-only assemblies need special handling. + // In particular, IL-only I386 without Requires32Bit is effectively AnyCPU/neutral. + if (isIlOnly) + { + return machine switch + { + 0x014C => requires32Bit ? "x86" : "neutral", // I386 + 0x8664 => "x64", // unusual for pure IL-only, but valid to preserve + 0x01C0 => "arm", // ARM + 0x01C4 => "arm", // ARMNT + 0xAA64 => "arm64", // ARM64 + _ => null + }; + } + + // Mixed-mode / native-entry managed image: machine matters. + return MapNativeMachine(machine); + } + catch + { + return null; + } + } + + private static string? MapNativeMachine(ushort machine) => machine switch + { + 0x014C => "x86", // IMAGE_FILE_MACHINE_I386 + 0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64 + 0xAA64 => "arm64", // IMAGE_FILE_MACHINE_ARM64 + 0x01C4 => "arm", // IMAGE_FILE_MACHINE_ARMNT + 0x01C0 => "arm", // IMAGE_FILE_MACHINE_ARM + _ => null + }; +} diff --git a/src/winapp-CLI/WinApp.Cli/Services/PriService.cs b/src/winapp-CLI/WinApp.Cli/Services/PriService.cs new file mode 100644 index 00000000..8f3286aa --- /dev/null +++ b/src/winapp-CLI/WinApp.Cli/Services/PriService.cs @@ -0,0 +1,240 @@ +// Copyright (c) Microsoft Corporation and Contributors. All rights reserved. +// Licensed under the MIT License. + +using System.Text.RegularExpressions; +using System.Xml; +using WinApp.Cli.ConsoleTasks; +using WinApp.Cli.Helpers; +using WinApp.Cli.Tools; + +namespace WinApp.Cli.Services; + +/// +/// Handles PRI (Package Resource Index) configuration, generation, and language extraction +/// via MakePri.exe. +/// +internal partial class PriService( + IBuildToolsService buildToolsService) : IPriService +{ + // Extracts language tag from PRI dump qualifier strings like 'Language-en-US' + [GeneratedRegex(@"qualifiers=""[^""]*Language-([a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*)", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex PriDumpLanguageQualifierRegex(); + + /// + /// Creates a PRI configuration file for the given package directory + /// + public async Task CreatePriConfigAsync( + DirectoryInfo packageDir, + TaskContext taskContext, + IEnumerable precomputedPriResourceCandidates, + string language = "en-US", + string platformVersion = "10.0.0", + CancellationToken cancellationToken = default) + { + if (!packageDir.Exists) + { + throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); + } + + ArgumentNullException.ThrowIfNull(precomputedPriResourceCandidates); + + var resfilesPath = Path.Combine(packageDir.FullName, "pri.resfiles"); + var priResourceCandidates = precomputedPriResourceCandidates.ToList(); + + priResourceCandidates = [.. priResourceCandidates + .Where(path => MrtAssetHelper.PriIncludedExtensions.Contains(Path.GetExtension(path))) + .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; + + taskContext.AddDebugMessage($"PRI resource candidates discovered: {priResourceCandidates.Count}"); + + using (var writer = new StreamWriter(resfilesPath)) + { + foreach (var priFile in priResourceCandidates) + { + await writer.WriteLineAsync(priFile); + } + } + + var configPath = new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); + var arguments = $@"createconfig /cf ""{configPath}"" /dq lang-{language}_scale-200 /pv {platformVersion} /o"; + + taskContext.AddDebugMessage("Creating PRI configuration file..."); + + try + { + await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + taskContext.AddDebugMessage($"PRI configuration created: {configPath}"); + + var xmlDoc = new XmlDocument(); + xmlDoc.Load(configPath.FullName); + var resourcesNode = xmlDoc.SelectSingleNode("/resources"); + if (resourcesNode != null) + { + var indexNode = resourcesNode.SelectSingleNode("index"); + if (indexNode != null) + { + if (indexNode.Attributes?["startIndexAt"]?.Value != null) + { + // set to relative path + indexNode.Attributes["startIndexAt"]!.Value = ".\\pri.resfiles"; + } + + var resfilesIndexerNode = xmlDoc.CreateElement("indexer-config"); + var typeAttr = xmlDoc.CreateAttribute("type"); + typeAttr.Value = "resfiles"; + resfilesIndexerNode.Attributes.Append(typeAttr); + + var delimiterAttr = xmlDoc.CreateAttribute("qualifierDelimiter"); + delimiterAttr.Value = "."; + resfilesIndexerNode.Attributes.Append(delimiterAttr); + + indexNode.AppendChild(resfilesIndexerNode); + + // Ensure folder-based indexer is configured to parse qualifiers from + // both folder names and file names (e.g. targetsize-48_altform-unplated). + var folderIndexerNode = indexNode + .SelectNodes("indexer-config") + ?.OfType() + .FirstOrDefault(node => + node.Attributes?["type"]?.Value?.Equals("folder", StringComparison.OrdinalIgnoreCase) == true); + + if (folderIndexerNode?.Attributes != null) + { + var folderAttributes = folderIndexerNode.Attributes; + + var folderNameAsQualifierAttr = folderAttributes["foldernameAsQualifier"]; + if (folderNameAsQualifierAttr == null) + { + folderNameAsQualifierAttr = xmlDoc.CreateAttribute("foldernameAsQualifier"); + folderAttributes.Append(folderNameAsQualifierAttr); + } + folderNameAsQualifierAttr.Value = "true"; + + var fileNameAsQualifierAttr = folderAttributes["filenameAsQualifier"]; + if (fileNameAsQualifierAttr == null) + { + fileNameAsQualifierAttr = xmlDoc.CreateAttribute("filenameAsQualifier"); + folderAttributes.Append(fileNameAsQualifierAttr); + } + fileNameAsQualifierAttr.Value = "true"; + } + + xmlDoc.Save(configPath.FullName); + } + } + + return configPath; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to create PRI configuration: {ex.Message}", ex); + } + } + + /// + /// Generates a PRI file from the configuration + /// + public async Task> GeneratePriFileAsync(DirectoryInfo packageDir, TaskContext taskContext, FileInfo? configPath = null, FileInfo? outputPath = null, CancellationToken cancellationToken = default) + { + if (!packageDir.Exists) + { + throw new DirectoryNotFoundException($"Package directory not found: {packageDir}"); + } + + var priConfigPath = configPath ?? new FileInfo(Path.Combine(packageDir.FullName, "priconfig.xml")); + var priOutputPath = outputPath ?? new FileInfo(Path.Combine(packageDir.FullName, "resources.pri")); + + if (!priConfigPath.Exists) + { + throw new FileNotFoundException($"PRI configuration file not found: {priConfigPath}"); + } + + var arguments = $@"new /pr ""{Path.TrimEndingDirectorySeparator(packageDir.FullName)}"" /cf ""{priConfigPath.FullName}"" /of ""{priOutputPath.FullName}"" /o"; + + taskContext.AddDebugMessage("Generating PRI file..."); + + try + { + var (stdout, stderr) = await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + // Parse the output to extract resource files + var resourceFiles = new List(); + var lines = stdout.Replace("\0", "").Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) + { + // Look for lines that match the pattern "Resource File: *" + const string resourceFileStr = "Resource File: "; + if (line.StartsWith(resourceFileStr, StringComparison.OrdinalIgnoreCase)) + { + var fileName = line[resourceFileStr.Length..].Trim(); + if (!string.IsNullOrEmpty(fileName)) + { + resourceFiles.Add(new FileInfo(Path.Combine(packageDir.FullName, fileName))); + } + } + } + + taskContext.AddDebugMessage($"PRI file generated: {priOutputPath}"); + if (resourceFiles.Count > 0) + { + taskContext.AddDebugMessage($"Processed {resourceFiles.Count} resource files"); + } + + return resourceFiles; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to generate PRI file: {ex.Message}", ex); + } + } + + /// + /// Extracts language qualifiers from a PRI file using makepri dump. + /// Returns a distinct, sorted list of BCP-47 language tags found in the PRI resource map. + /// + public async Task> ExtractLanguagesFromPriAsync( + FileInfo priFile, + TaskContext taskContext, + CancellationToken cancellationToken) + { + try + { + var dumpOutputFile = Path.Combine(Path.GetTempPath(), $"winapp-pri-dump-{Guid.NewGuid():N}.xml"); + var arguments = $@"dump /if ""{priFile.FullName}"" /of ""{dumpOutputFile}"" /o"; + + await buildToolsService.RunBuildToolAsync(new MakePriTool(), arguments, taskContext, cancellationToken: cancellationToken); + + if (!File.Exists(dumpOutputFile)) + { + return []; + } + + try + { + var dumpContent = await File.ReadAllTextAsync(dumpOutputFile, cancellationToken); + + // Extract language qualifiers from Candidate elements: + // or multi-qualifier like "Language-en-US, Scale-200" + var languages = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in PriDumpLanguageQualifierRegex().Matches(dumpContent)) + { + languages.Add(match.Groups[1].Value); + } + + return languages.OrderBy(l => l, StringComparer.OrdinalIgnoreCase).ToList(); + } + finally + { + File.Delete(dumpOutputFile); + } + } + catch (Exception ex) + { + taskContext.AddDebugMessage($"{UiSymbols.Warning} Failed to extract languages from PRI: {ex.Message}"); + return []; + } + } +} diff --git a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj index 92a069e2..98c2bf9e 100644 --- a/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj +++ b/src/winapp-CLI/WinApp.Cli/WinApp.Cli.csproj @@ -2,7 +2,7 @@ Exe - net10.0-windows + net10.0-windows10.0.19041.0 enable enable winapp diff --git a/src/winapp-npm/scripts/generate-commands.mjs b/src/winapp-npm/scripts/generate-commands.mjs index 5c78531d..d51b26b8 100644 --- a/src/winapp-npm/scripts/generate-commands.mjs +++ b/src/winapp-npm/scripts/generate-commands.mjs @@ -355,6 +355,7 @@ function generate(schema) { // --------------------------------------------------------------------------- const FN_NAME_OVERRIDES = { 'package': 'packageApp', // `package` is a TS reserved-ish word + 'run': 'runApp', // `run` collides with the internal run() helper }; function getFunctionName(cmdPath) { diff --git a/src/winapp-npm/src/winapp-commands.ts b/src/winapp-npm/src/winapp-commands.ts index 19dfc5f2..b43db4ab 100644 --- a/src/winapp-npm/src/winapp-commands.ts +++ b/src/winapp-npm/src/winapp-commands.ts @@ -60,7 +60,7 @@ function captureOpts(opts: CommonOptions): CallWinappCliCaptureOptions { return opts.cwd ? { cwd: opts.cwd } : {}; } -async function execCommand(args: string[], opts: CommonOptions): Promise { +async function run(args: string[], opts: CommonOptions): Promise { pushCommon(args, opts); const result: CallWinappCliCaptureResult = await callWinappCliCapture(args, captureOpts(opts)); return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr }; @@ -79,7 +79,7 @@ export interface CertGenerateOptions extends CommonOptions { install?: boolean; /** Format output as JSON */ json?: boolean; - /** Path to appxmanifest.xml file to extract publisher information from */ + /** Path to appxmanifest.xml or Package.appxmanifest file to extract publisher information from */ manifest?: string; /** Output path for the generated PFX file */ output?: string; @@ -105,7 +105,7 @@ export async function certGenerate(options: CertGenerateOptions = {}): Promise args.push(options.certPath); if (options.json) args.push('--json'); if (options.password) args.push('--password', options.password); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -153,7 +153,7 @@ export async function certInstall(options: CertInstallOptions): Promise { const args: string[] = ['get-winapp-path']; if (options.global) args.push('--global'); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -267,7 +267,31 @@ export async function init(options: InitOptions = {}): Promise { if (options.noGitignore) args.push('--no-gitignore'); if (options.setupSdks) args.push('--setup-sdks', options.setupSdks); if (options.useDefaults) args.push('--use-defaults'); - return execCommand(args, options); + return run(args, options); +} + +// --------------------------------------------------------------------------- +// manifest add-alias +// --------------------------------------------------------------------------- + +export interface ManifestAddAliasOptions extends CommonOptions { + /** Application Id to add the alias to (default: first Application element) */ + appId?: string; + /** Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) */ + manifest?: string; + /** Alias name (e.g. 'myapp.exe'). Default: inferred from the Executable attribute in the manifest. */ + name?: string; +} + +/** + * Add an execution alias (uap5:AppExecutionAlias) to an appxmanifest.xml. This allows launching the packaged app from the command line by typing the alias name. By default, the alias is inferred from the Executable attribute (e.g. $targetnametoken$.exe becomes $targetnametoken$.exe alias). + */ +export async function manifestAddAlias(options: ManifestAddAliasOptions = {}): Promise { + const args: string[] = ['manifest', 'add-alias']; + if (options.appId) args.push('--app-id', options.appId); + if (options.manifest) args.push('--manifest', options.manifest); + if (options.name) args.push('--name', options.name); + return run(args, options); } // --------------------------------------------------------------------------- @@ -309,7 +333,7 @@ export async function manifestGenerate(options: ManifestGenerateOptions = {}): P if (options.publisherName) args.push('--publisher-name', options.publisherName); if (options.template) args.push('--template', options.template); if (options.version) args.push('--version', options.version); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -319,7 +343,7 @@ export async function manifestGenerate(options: ManifestGenerateOptions = {}): P export interface ManifestUpdateAssetsOptions extends CommonOptions { /** Path to source image file */ imagePath: string; - /** Path to AppxManifest.xml file (default: search current directory) */ + /** Path to AppxManifest.xml or Package.appxmanifest file (default: search current directory) */ manifest?: string; } @@ -330,7 +354,7 @@ export async function manifestUpdateAssets(options: ManifestUpdateAssetsOptions) const args: string[] = ['manifest', 'update-assets']; args.push(options.imagePath); if (options.manifest) args.push('--manifest', options.manifest); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -365,7 +389,7 @@ export interface PackageOptions extends CommonOptions { } /** - * Create MSIX installer from your built app. Run after building your app. appxmanifest.xml is required for packaging - it must be in current working directory, passed as --manifest or be in the input folder. Use --cert devcert.pfx to sign for testing. Example: winapp package ./dist --manifest appxmanifest.xml --cert ./devcert.pfx + * Create MSIX installer from your built app. Run after building your app. A manifest (appxmanifest.xml or package.appxmanifest) is required for packaging - it must be in current working directory, passed as --manifest or be in the input folder. Use --cert devcert.pfx to sign for testing. Example: winapp package ./dist --manifest appxmanifest.xml --cert ./devcert.pfx */ export async function packageApp(options: PackageOptions): Promise { const args: string[] = ['package']; @@ -381,7 +405,7 @@ export async function packageApp(options: PackageOptions): Promise if (options.publisher) args.push('--publisher', options.publisher); if (options.selfContained) args.push('--self-contained'); if (options.skipPri) args.push('--skip-pri'); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -402,7 +426,7 @@ export async function restore(options: RestoreOptions = {}): Promise { +export async function runApp(options: RunOptions): Promise { const args: string[] = ['run']; args.push(options.inputFolder); if (options.args) args.push('--args', options.args); @@ -447,7 +471,7 @@ export async function run(options: RunOptions): Promise { if (options.outputAppxDirectory) args.push('--output-appx-directory', options.outputAppxDirectory); if (options.unregisterOnExit) args.push('--unregister-on-exit'); if (options.withAlias) args.push('--with-alias'); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -474,7 +498,7 @@ export async function sign(options: SignOptions): Promise { args.push(options.certPath); if (options.password) args.push('--password', options.password); if (options.timestamp) args.push('--timestamp', options.timestamp); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -492,7 +516,7 @@ export interface StoreOptions extends CommonOptions { export async function store(options: StoreOptions = {}): Promise { const args: string[] = ['store']; if (options.storeArgs) args.push(...options.storeArgs); - return execCommand(args, options); + return run(args, options); } // --------------------------------------------------------------------------- @@ -512,7 +536,31 @@ export async function tool(options: ToolOptions = {}): Promise { if (options.toolArgs && options.toolArgs.length > 0) { args.push('--', ...options.toolArgs); } - return execCommand(args, options); + return run(args, options); +} + +// --------------------------------------------------------------------------- +// unregister +// --------------------------------------------------------------------------- + +export interface UnregisterOptions extends CommonOptions { + /** Skip the install-location directory check and unregister even if the package was registered from a different project tree */ + force?: boolean; + /** Format output as JSON */ + json?: boolean; + /** Path to the appxmanifest.xml (default: auto-detect from current directory) */ + manifest?: string; +} + +/** + * Unregisters a sideloaded development package. Only removes packages registered in development mode (e.g., via 'winapp run' or 'create-debug-identity'). + */ +export async function unregister(options: UnregisterOptions = {}): Promise { + const args: string[] = ['unregister']; + if (options.force) args.push('--force'); + if (options.json) args.push('--json'); + if (options.manifest) args.push('--manifest', options.manifest); + return run(args, options); } // --------------------------------------------------------------------------- @@ -530,5 +578,5 @@ export interface UpdateOptions extends CommonOptions { export async function update(options: UpdateOptions = {}): Promise { const args: string[] = ['update']; if (options.setupSdks) args.push('--setup-sdks', options.setupSdks); - return execCommand(args, options); + return run(args, options); }