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.
- package/README.md +39 -0
- package/bin/create-vidra-app.mjs +2 -0
- package/bin/vidra.mjs +2 -0
- package/dist/cli.js +777 -0
- package/dist/index.js +241 -0
- package/package.json +56 -0
- package/templates/react-vite/NuGet.Config +8 -0
- package/templates/react-vite/README.md +57 -0
- package/templates/react-vite/_gitignore +16 -0
- package/templates/react-vite/package.json +14 -0
- package/templates/react-vite/src/{{projectName}}.Host/App.xaml +5 -0
- package/templates/react-vite/src/{{projectName}}.Host/App.xaml.cs +14 -0
- package/templates/react-vite/src/{{projectName}}.Host/MainPage.cs +28 -0
- package/templates/react-vite/src/{{projectName}}.Host/MauiProgram.cs +26 -0
- package/templates/react-vite/src/{{projectName}}.Host/Platforms/MacCatalyst/AppDelegate.cs +9 -0
- package/templates/react-vite/src/{{projectName}}.Host/Platforms/MacCatalyst/Info.plist +11 -0
- package/templates/react-vite/src/{{projectName}}.Host/Platforms/MacCatalyst/Program.cs +11 -0
- package/templates/react-vite/src/{{projectName}}.Host/Platforms/Windows/App.xaml +8 -0
- package/templates/react-vite/src/{{projectName}}.Host/Platforms/Windows/App.xaml.cs +11 -0
- package/templates/react-vite/src/{{projectName}}.Host/{{projectName}}.Host.csproj +57 -0
- package/templates/react-vite/ui/index.html +12 -0
- package/templates/react-vite/ui/package.json +23 -0
- package/templates/react-vite/ui/src/App.tsx +240 -0
- package/templates/react-vite/ui/src/index.css +109 -0
- package/templates/react-vite/ui/src/main.tsx +10 -0
- package/templates/react-vite/ui/src/vite-env.d.ts +1 -0
- package/templates/react-vite/ui/tsconfig.json +21 -0
- 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,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,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,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,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,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,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
|
+
}
|