create-vidra-app 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +39 -0
  2. package/bin/create-vidra-app.mjs +2 -0
  3. package/bin/vidra.mjs +2 -0
  4. package/dist/cli.js +777 -0
  5. package/dist/index.js +241 -0
  6. package/package.json +56 -0
  7. package/templates/react-vite/NuGet.Config +8 -0
  8. package/templates/react-vite/README.md +57 -0
  9. package/templates/react-vite/_gitignore +16 -0
  10. package/templates/react-vite/package.json +14 -0
  11. package/templates/react-vite/src/{{projectName}}.Host/App.xaml +5 -0
  12. package/templates/react-vite/src/{{projectName}}.Host/App.xaml.cs +14 -0
  13. package/templates/react-vite/src/{{projectName}}.Host/MainPage.cs +28 -0
  14. package/templates/react-vite/src/{{projectName}}.Host/MauiProgram.cs +26 -0
  15. package/templates/react-vite/src/{{projectName}}.Host/Platforms/MacCatalyst/AppDelegate.cs +9 -0
  16. package/templates/react-vite/src/{{projectName}}.Host/Platforms/MacCatalyst/Info.plist +11 -0
  17. package/templates/react-vite/src/{{projectName}}.Host/Platforms/MacCatalyst/Program.cs +11 -0
  18. package/templates/react-vite/src/{{projectName}}.Host/Platforms/Windows/App.xaml +8 -0
  19. package/templates/react-vite/src/{{projectName}}.Host/Platforms/Windows/App.xaml.cs +11 -0
  20. package/templates/react-vite/src/{{projectName}}.Host/{{projectName}}.Host.csproj +57 -0
  21. package/templates/react-vite/ui/index.html +12 -0
  22. package/templates/react-vite/ui/package.json +23 -0
  23. package/templates/react-vite/ui/src/App.tsx +240 -0
  24. package/templates/react-vite/ui/src/index.css +109 -0
  25. package/templates/react-vite/ui/src/main.tsx +10 -0
  26. package/templates/react-vite/ui/src/vite-env.d.ts +1 -0
  27. package/templates/react-vite/ui/tsconfig.json +21 -0
  28. package/templates/react-vite/ui/vite.config.ts +33 -0
package/dist/index.js ADDED
@@ -0,0 +1,241 @@
1
+ // src/index.ts
2
+ import prompts from "prompts";
3
+ import chalk2 from "chalk";
4
+ import fs2 from "fs-extra";
5
+ import path2 from "path";
6
+ import { randomUUID } from "crypto";
7
+ import { fileURLToPath } from "url";
8
+
9
+ // src/utils.ts
10
+ var toPascalCase = (str) => {
11
+ return str.replace(
12
+ /[-_]+(.)?/g,
13
+ (_, c) => c ? c.toUpperCase() : ""
14
+ ).replace(/^(.)/, (_, c) => c.toUpperCase());
15
+ };
16
+ var toKebabCase = (str) => {
17
+ return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
18
+ };
19
+ var toTitleCase = (str) => {
20
+ return str.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
21
+ };
22
+ var toTextPath = (p) => p.replaceAll("\\", "/");
23
+ var parseArgs = (argv) => {
24
+ const args = { _: [] };
25
+ for (let i = 2; i < argv.length; i++) {
26
+ const arg = argv[i];
27
+ if (arg.startsWith("--")) {
28
+ const [key, val] = arg.slice(2).split("=");
29
+ args[key] = val ?? argv[++i] ?? true;
30
+ } else {
31
+ args._.push(arg);
32
+ }
33
+ }
34
+ return args;
35
+ };
36
+
37
+ // src/exec.ts
38
+ import { execSync } from "child_process";
39
+ import chalk from "chalk";
40
+ var exec = (cmd, cwd) => {
41
+ try {
42
+ execSync(cmd, { cwd, stdio: "pipe" });
43
+ } catch (e) {
44
+ const err = e;
45
+ console.error(chalk.red(` Command failed: ${cmd}`));
46
+ console.error(chalk.dim(err.stderr?.toString() || err.message));
47
+ process.exit(1);
48
+ }
49
+ };
50
+ var tryExec = (cmd, cwd) => {
51
+ try {
52
+ execSync(cmd, { cwd, stdio: "pipe" });
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ };
58
+
59
+ // src/scaffold.ts
60
+ import fs from "fs-extra";
61
+ import path from "path";
62
+ var TEMPLATE_FILE_RENAMES = {
63
+ _gitignore: ".gitignore"
64
+ };
65
+ var TEXT_EXTS = /* @__PURE__ */ new Set([
66
+ ".cs",
67
+ ".csproj",
68
+ ".xaml",
69
+ ".sln",
70
+ ".json",
71
+ ".xml",
72
+ ".ts",
73
+ ".tsx",
74
+ ".js",
75
+ ".mjs",
76
+ ".jsx",
77
+ ".css",
78
+ ".html",
79
+ ".md",
80
+ ".plist",
81
+ ".targets",
82
+ ".props",
83
+ ".txt",
84
+ ".Config",
85
+ ""
86
+ ]);
87
+ var scaffoldDir = async (srcDir, destDir, replacements) => {
88
+ const entries = await fs.readdir(srcDir, { withFileTypes: true });
89
+ for (const entry of entries) {
90
+ const srcPath = path.join(srcDir, entry.name);
91
+ const destName = TEMPLATE_FILE_RENAMES[entry.name] ?? applyReplacements(entry.name, replacements);
92
+ const destPath = path.join(destDir, destName);
93
+ if (entry.isDirectory()) {
94
+ await fs.ensureDir(destPath);
95
+ await scaffoldDir(srcPath, destPath, replacements);
96
+ } else {
97
+ const ext = path.extname(entry.name);
98
+ if (TEXT_EXTS.has(ext)) {
99
+ let content = await fs.readFile(srcPath, "utf8");
100
+ content = applyReplacements(content, replacements);
101
+ await fs.outputFile(destPath, content);
102
+ } else {
103
+ await fs.copy(srcPath, destPath);
104
+ }
105
+ }
106
+ }
107
+ };
108
+ var applyReplacements = (str, replacements) => {
109
+ for (const [key, val] of Object.entries(replacements)) {
110
+ str = str.replaceAll(key, val);
111
+ }
112
+ return str;
113
+ };
114
+
115
+ // src/index.ts
116
+ var __dirname = path2.dirname(fileURLToPath(import.meta.url));
117
+ var CLI_ROOT = path2.resolve(__dirname, "..");
118
+ var TEMPLATES_DIR = path2.join(CLI_ROOT, "templates");
119
+ var VIDRA_REPO_ROOT = path2.resolve(CLI_ROOT, "..", "..", "..");
120
+ var LOCAL_FEED_DIR = path2.join(VIDRA_REPO_ROOT, "dist", "packages");
121
+ var LOCAL_CLI_DIR = CLI_ROOT;
122
+ var LOCAL_SDK_DIR = path2.join(VIDRA_REPO_ROOT, "src", "sdk", "vidra-js");
123
+ var VIDRA_VERSION = "0.1.0";
124
+ var SDK_VERSION = "0.1.0";
125
+ var main = async () => {
126
+ console.log();
127
+ console.log(chalk2.bold(" create-vidra-app"));
128
+ console.log(chalk2.dim(" Scaffold a new Vidra application\n"));
129
+ const args = parseArgs(process.argv);
130
+ let projectDir = args._[0];
131
+ let appId = args["app-id"];
132
+ if (!projectDir) {
133
+ const res = await prompts(
134
+ {
135
+ type: "text",
136
+ name: "projectDir",
137
+ message: "Project name:",
138
+ initial: "my-vidra-app",
139
+ validate: (v) => /^[a-zA-Z][\w-]*$/.test(v) || "Must start with a letter, alphanumeric/hyphens only"
140
+ },
141
+ { onCancel: () => process.exit(1) }
142
+ );
143
+ projectDir = res.projectDir;
144
+ }
145
+ const projectName = toPascalCase(projectDir);
146
+ const projectNameKebab = toKebabCase(projectDir);
147
+ const appTitle = toTitleCase(projectDir);
148
+ const appGuid = randomUUID().toUpperCase();
149
+ if (!appId) {
150
+ const res = await prompts(
151
+ {
152
+ type: "text",
153
+ name: "appId",
154
+ message: "App ID (reverse domain):",
155
+ initial: `com.vidra.${projectNameKebab.replace(/-/g, "")}`,
156
+ validate: (v) => /^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$/.test(v) || "Must be reverse-domain (e.g. com.company.app)"
157
+ },
158
+ { onCancel: () => process.exit(1) }
159
+ );
160
+ appId = res.appId;
161
+ }
162
+ const root = path2.resolve(projectDir);
163
+ if (fs2.existsSync(root) && fs2.readdirSync(root).length > 0) {
164
+ console.error(
165
+ chalk2.red(
166
+ `
167
+ Directory "${projectDir}" already exists and is not empty.
168
+ `
169
+ )
170
+ );
171
+ process.exit(1);
172
+ }
173
+ console.log();
174
+ console.log(` ${chalk2.dim("Project:")} ${chalk2.cyan(projectName)}`);
175
+ console.log(` ${chalk2.dim("Directory:")} ${chalk2.cyan(root)}`);
176
+ console.log(` ${chalk2.dim("App ID:")} ${chalk2.cyan(appId)}`);
177
+ console.log();
178
+ const isMonorepo = fs2.existsSync(path2.join(LOCAL_SDK_DIR, "package.json"));
179
+ const localFeedExists = isMonorepo && fs2.existsSync(LOCAL_FEED_DIR);
180
+ const localFeedPath = localFeedExists ? toTextPath(LOCAL_FEED_DIR) : "";
181
+ const cliRef = isMonorepo ? `file:${toTextPath(LOCAL_CLI_DIR)}` : `^${VIDRA_VERSION}`;
182
+ const sdkRef = isMonorepo ? `file:${toTextPath(LOCAL_SDK_DIR)}` : `^${SDK_VERSION}`;
183
+ const replacements = {
184
+ "{{projectName}}": projectName,
185
+ "{{projectNameKebab}}": projectNameKebab,
186
+ "{{appId}}": appId,
187
+ "{{appGuid}}": appGuid,
188
+ "{{appTitle}}": appTitle,
189
+ "{{cliVersion}}": cliRef,
190
+ "{{vidraVersion}}": VIDRA_VERSION,
191
+ "{{sdkVersion}}": sdkRef,
192
+ "{{localFeedPath}}": localFeedPath
193
+ };
194
+ const templateDir = path2.join(TEMPLATES_DIR, "react-vite");
195
+ await scaffoldDir(templateDir, root, replacements);
196
+ console.log(chalk2.dim(" Creating solution..."));
197
+ exec(`dotnet new sln -n ${projectName} --force`, root);
198
+ const slnFile = fs2.existsSync(path2.join(root, `${projectName}.slnx`)) ? `${projectName}.slnx` : `${projectName}.sln`;
199
+ exec(
200
+ `dotnet sln ${slnFile} add src/${projectName}.Host/${projectName}.Host.csproj`,
201
+ root
202
+ );
203
+ const uiDir = path2.join(root, "ui");
204
+ console.log(chalk2.dim(" Installing npm dependencies..."));
205
+ const npmOk = tryExec("npm install", uiDir);
206
+ console.log();
207
+ console.log(chalk2.green(" Done! ") + "Your Vidra app is ready.\n");
208
+ if (localFeedExists) {
209
+ console.log(
210
+ chalk2.dim(" NuGet:") + " local feed \u2192 " + chalk2.cyan(LOCAL_FEED_DIR)
211
+ );
212
+ } else if (isMonorepo) {
213
+ console.log(
214
+ chalk2.yellow(" Note: ") + "Local NuGet feed not found. Run " + chalk2.cyan("./pack-local.sh") + " in the Vidra repo, then update NuGet.Config."
215
+ );
216
+ }
217
+ if (isMonorepo) {
218
+ console.log(
219
+ chalk2.dim(" npm: ") + " @vidra-dev/sdk \u2192 " + chalk2.cyan(LOCAL_SDK_DIR)
220
+ );
221
+ console.log(
222
+ chalk2.dim(" npm: ") + " create-vidra-app \u2192 " + chalk2.cyan(LOCAL_CLI_DIR)
223
+ );
224
+ }
225
+ console.log();
226
+ if (!npmOk) {
227
+ console.log(
228
+ chalk2.yellow(" Note: ") + "`npm install` had errors. Run " + chalk2.cyan("cd ui && npm install") + " to retry.\n"
229
+ );
230
+ }
231
+ console.log(chalk2.bold(" Next steps:\n"));
232
+ console.log(` cd ${projectDir}`);
233
+ console.log(
234
+ ` npm run dev ${chalk2.dim("# starts Vite + MAUI host together")}`
235
+ );
236
+ console.log();
237
+ };
238
+ main().catch((e) => {
239
+ console.error(chalk2.red(e.message));
240
+ process.exit(1);
241
+ });
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "create-vidra-app",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a new Vidra application (React + .NET MAUI)",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-vidra-app": "./bin/create-vidra-app.mjs",
8
+ "vidra": "./bin/vidra.mjs"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "dist/",
13
+ "templates/"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "vidra",
24
+ "maui",
25
+ "react",
26
+ "cross-platform",
27
+ "scaffold",
28
+ "cli"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/rzamfiriu/vidra.git",
33
+ "directory": "src/cli/create-vidra-app"
34
+ },
35
+ "homepage": "https://github.com/rzamfiriu/vidra#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/rzamfiriu/vidra/issues"
38
+ },
39
+ "license": "MIT",
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "dependencies": {
44
+ "chalk": "^5.4.1",
45
+ "fs-extra": "^11.2.0",
46
+ "prompts": "^2.4.2"
47
+ },
48
+ "devDependencies": {
49
+ "@types/fs-extra": "^11.0.4",
50
+ "@types/node": "^22.9.0",
51
+ "@types/prompts": "^2.4.9",
52
+ "tsup": "^8.5.1",
53
+ "typescript": "^6.0.2",
54
+ "vitest": "^2.1.4"
55
+ }
56
+ }
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <configuration>
3
+ <packageSources>
4
+ <clear />
5
+ <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
6
+ <add key="vidra-local" value="{{localFeedPath}}" />
7
+ </packageSources>
8
+ </configuration>
@@ -0,0 +1,57 @@
1
+ # {{appTitle}}
2
+
3
+ A cross-platform application built with [Vidra](https://github.com/user/vidra) — React UI + .NET MAUI native host.
4
+
5
+ ## Getting Started
6
+
7
+ ### Prerequisites
8
+
9
+ - [.NET 10 SDK](https://dotnet.microsoft.com/download) with MAUI workload
10
+ - [Node.js](https://nodejs.org/) 18+
11
+ - Windows development must be run from a Windows machine with the MAUI Windows workload installed
12
+
13
+ ### Development
14
+
15
+ ```bash
16
+ npm run dev
17
+ ```
18
+
19
+ This starts the Vite dev server and launches the native host for the current OS.
20
+
21
+ To target a specific desktop platform explicitly:
22
+
23
+ ```bash
24
+ vidra dev --target macos
25
+ vidra dev --target windows
26
+ ```
27
+
28
+ If you want to run the pieces separately:
29
+
30
+ ```bash
31
+ npm run dev:ui
32
+ npm run dev:host:macos
33
+ npm run dev:host:windows
34
+ ```
35
+
36
+ ### Production Build
37
+
38
+ ```bash
39
+ npm run build
40
+ ```
41
+
42
+ ## Project Structure
43
+
44
+ ```
45
+ {{projectNameKebab}}/
46
+ ├── src/
47
+ │ └── {{projectName}}.Host/ # .NET MAUI native host
48
+ │ ├── MauiProgram.cs # App configuration + Vidra setup
49
+ │ ├── MainPage.cs # Main page (extends VidraPage)
50
+ │ └── Platforms/ # Platform-specific code
51
+ └── ui/ # React frontend
52
+ ├── src/
53
+ │ ├── App.tsx # Main React component
54
+ │ └── main.tsx # Entry point
55
+ ├── vite.config.ts
56
+ └── package.json
57
+ ```
@@ -0,0 +1,16 @@
1
+ # Build artifacts
2
+ dist/
3
+ src/*/Resources/Raw/wwwroot/
4
+ *.tsbuildinfo
5
+
6
+ # .NET
7
+ **/bin/
8
+ **/obj/
9
+
10
+ # Node
11
+ node_modules/
12
+
13
+ # IDE
14
+ .vs/
15
+ .vscode/
16
+ *.user
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{projectNameKebab}}",
3
+ "private": true,
4
+ "scripts": {
5
+ "build": "vidra build",
6
+ "dev": "vidra dev",
7
+ "dev:ui": "npm run dev --prefix ui",
8
+ "dev:host:macos": "dotnet build -t:Run -f net10.0-maccatalyst src/{{projectName}}.Host/{{projectName}}.Host.csproj",
9
+ "dev:host:windows": "dotnet build -t:Run -f net10.0-windows10.0.19041.0 src/{{projectName}}.Host/{{projectName}}.Host.csproj"
10
+ },
11
+ "devDependencies": {
12
+ "create-vidra-app": "{{cliVersion}}"
13
+ }
14
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4
+ x:Class="{{projectName}}.App">
5
+ </Application>
@@ -0,0 +1,14 @@
1
+ namespace {{projectName}};
2
+
3
+ public partial class App : Application
4
+ {
5
+ public App()
6
+ {
7
+ InitializeComponent();
8
+ }
9
+
10
+ protected override Window CreateWindow(IActivationState? activationState)
11
+ {
12
+ return new Window(new MainPage());
13
+ }
14
+ }
@@ -0,0 +1,28 @@
1
+ using Vidra.Hosting;
2
+
3
+ namespace {{projectName}};
4
+
5
+ public class MainPage : VidraPage
6
+ {
7
+ public MainPage()
8
+ {
9
+ StartCounterTimer();
10
+ }
11
+
12
+ private async void StartCounterTimer()
13
+ {
14
+ using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
15
+ while (await timer.WaitForNextTickAsync())
16
+ {
17
+ try
18
+ {
19
+ var count = await Bridge.CallJsAsync<int>("counter.increment");
20
+ System.Diagnostics.Debug.WriteLine($"[MainPage] Counter is now {count}");
21
+ }
22
+ catch (Exception ex)
23
+ {
24
+ System.Diagnostics.Debug.WriteLine($"[MainPage] Counter increment failed: {ex.Message}");
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,26 @@
1
+ using Microsoft.Extensions.Logging;
2
+ using Vidra.Hosting;
3
+
4
+ namespace {{projectName}};
5
+
6
+ public static class MauiProgram
7
+ {
8
+ public static MauiApp CreateMauiApp()
9
+ {
10
+ var builder = MauiApp.CreateBuilder();
11
+
12
+ builder
13
+ .UseMauiApp<App>()
14
+ .UseVidra()
15
+ .ConfigureFonts(fonts =>
16
+ {
17
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
18
+ });
19
+
20
+ #if DEBUG
21
+ builder.Logging.AddDebug();
22
+ #endif
23
+
24
+ return builder.Build();
25
+ }
26
+ }
@@ -0,0 +1,9 @@
1
+ using Foundation;
2
+
3
+ namespace {{projectName}};
4
+
5
+ [Register("AppDelegate")]
6
+ public class AppDelegate : MauiUIApplicationDelegate
7
+ {
8
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9
+ }
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>NSAppTransportSecurity</key>
6
+ <dict>
7
+ <key>NSAllowsLocalNetworking</key>
8
+ <true/>
9
+ </dict>
10
+ </dict>
11
+ </plist>
@@ -0,0 +1,11 @@
1
+ using UIKit;
2
+
3
+ namespace {{projectName}};
4
+
5
+ public static class Program
6
+ {
7
+ static void Main(string[] args)
8
+ {
9
+ UIApplication.Main(args, null, typeof(AppDelegate));
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ <!-- Needed for WinUI -->
2
+ <maui:MauiWinUIApplication
3
+ x:Class="{{projectName}}.WinUI.App"
4
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6
+ xmlns:maui="using:Microsoft.Maui"
7
+ xmlns:local="using:{{projectName}}.WinUI">
8
+ </maui:MauiWinUIApplication>
@@ -0,0 +1,11 @@
1
+ namespace {{projectName}}.WinUI;
2
+
3
+ public partial class App : MauiWinUIApplication
4
+ {
5
+ public App()
6
+ {
7
+ this.InitializeComponent();
8
+ }
9
+
10
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
11
+ }
@@ -0,0 +1,57 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('osx'))">net10.0-maccatalyst</TargetFrameworks>
5
+ <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">net10.0-windows10.0.19041.0</TargetFrameworks>
6
+ <ValidateXcodeVersion>false</ValidateXcodeVersion>
7
+ <OutputType>Exe</OutputType>
8
+ <RootNamespace>{{projectName}}</RootNamespace>
9
+ <UseMaui>true</UseMaui>
10
+ <SingleProject>true</SingleProject>
11
+ <ImplicitUsings>enable</ImplicitUsings>
12
+ <Nullable>enable</Nullable>
13
+ <LangVersion>latest</LangVersion>
14
+
15
+ <ApplicationTitle>{{appTitle}}</ApplicationTitle>
16
+ <ApplicationIdGuid>{{appGuid}}</ApplicationIdGuid>
17
+ <ApplicationId>{{appId}}</ApplicationId>
18
+ <ApplicationVersion>1</ApplicationVersion>
19
+ <ApplicationDisplayVersion>0.1.0</ApplicationDisplayVersion>
20
+ <EnableCodeSigning Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">true</EnableCodeSigning>
21
+
22
+ <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
23
+ <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
24
+
25
+ <!--
26
+ Build as an unpackaged Win32-style app on Windows. The scaffolded
27
+ template intentionally ships without a Platforms/Windows/Package.appxmanifest;
28
+ MSIX packaging is the consumer's responsibility (e.g. via the `vidra
29
+ build` CLI) and would otherwise fail a plain `dotnet build` with NETSDK
30
+ errors from the WindowsAppSDK MSIX targets.
31
+ -->
32
+ <WindowsPackageType Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">None</WindowsPackageType>
33
+
34
+ <!--
35
+ Skip the MacCatalyst managed linker in Debug. The linker's Setup step
36
+ validates the MacCatalyst SDK headers against the maui workload version,
37
+ and the maui workload tracks the newest Xcode aggressively (MT0180).
38
+ Disabling the linker for Debug builds gives consistent compile semantics
39
+ regardless of the local Xcode, and matches the fast-iteration default
40
+ that Hot Restart-style flows use. Release builds (e.g. `vidra build
41
+ target macos`) keep the default linker so production trimming is
42
+ preserved.
43
+ -->
44
+ <MtouchLink Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst' And '$(Configuration)' == 'Debug'">None</MtouchLink>
45
+ </PropertyGroup>
46
+
47
+ <ItemGroup>
48
+ <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
49
+ </ItemGroup>
50
+
51
+ <ItemGroup>
52
+ <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
53
+ <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
54
+ <PackageReference Include="Vidra.Hosting.Maui" Version="{{vidraVersion}}" />
55
+ </ItemGroup>
56
+
57
+ </Project>
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{appTitle}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "{{projectNameKebab}}",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@vidra-dev/sdk": "{{sdkVersion}}",
13
+ "react": "^19.0.0",
14
+ "react-dom": "^19.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/react": "^19.0.0",
18
+ "@types/react-dom": "^19.0.0",
19
+ "@vitejs/plugin-react": "^4.4.0",
20
+ "typescript": "^5.7.0",
21
+ "vite": "^6.0.0"
22
+ }
23
+ }