create-muten 0.0.9 → 0.0.10
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 +13 -5
- package/index.js +54 -8
- package/package.json +2 -2
- package/template-tauri/muten-logo.png +0 -0
- package/template-tauri/src-tauri/Cargo.toml +15 -0
- package/template-tauri/src-tauri/build.rs +3 -0
- package/template-tauri/src-tauri/capabilities/default.json +7 -0
- package/template-tauri/src-tauri/icons/128x128.png +0 -0
- package/template-tauri/src-tauri/icons/128x128@2x.png +0 -0
- package/template-tauri/src-tauri/icons/32x32.png +0 -0
- package/template-tauri/src-tauri/icons/64x64.png +0 -0
- package/template-tauri/src-tauri/icons/Square107x107Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square142x142Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square150x150Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square284x284Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square30x30Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square310x310Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square44x44Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square71x71Logo.png +0 -0
- package/template-tauri/src-tauri/icons/Square89x89Logo.png +0 -0
- package/template-tauri/src-tauri/icons/StoreLogo.png +0 -0
- package/template-tauri/src-tauri/icons/icon.icns +0 -0
- package/template-tauri/src-tauri/icons/icon.ico +0 -0
- package/template-tauri/src-tauri/icons/icon.png +0 -0
- package/template-tauri/src-tauri/src/lib.rs +6 -0
- package/template-tauri/src-tauri/src/main.rs +6 -0
- package/template-tauri/src-tauri/tauri.conf.json +29 -0
package/README.md
CHANGED
|
@@ -61,14 +61,20 @@ In an interactive terminal it prompts for a few things (defaults in parentheses)
|
|
|
61
61
|
|---|---|---|
|
|
62
62
|
| **Project name** | any valid folder name | `muten-app` |
|
|
63
63
|
| **Template** | `muten` / `muten + React` / `muten + Svelte` | `muten` |
|
|
64
|
-
| **Styling** | `
|
|
65
|
-
| **Add
|
|
66
|
-
| **
|
|
64
|
+
| **Styling** | `CSS` / `SCSS` / `Tailwind CSS` / `DaisyUI` (brings Tailwind) | `CSS` |
|
|
65
|
+
| **Add Vercel deploy config?** | `Y` / `n` | `n` |
|
|
66
|
+
| **Desktop app (Tauri)?** | `Y` / `n` | `n` |
|
|
67
67
|
| **Package manager** | `npm` / `pnpm` / `yarn` / `bun` | the one that launched it |
|
|
68
68
|
| **Install deps and start dev now?** | `Y` / `n` | `Y` |
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
**Styling is one explicit choice** — each is opt-in, nothing is bundled by default: `CSS` (plain) or `SCSS`
|
|
71
|
+
ship no framework; `Tailwind CSS` adds `@tailwindcss/vite` + `@import "tailwindcss"`; `DaisyUI` adds its
|
|
72
|
+
component classes on top (and brings Tailwind). You always style via `class("…")`.
|
|
73
|
+
|
|
74
|
+
**Targets are independent opt-ins** — web, desktop, both, or neither, from the same `.muten` source:
|
|
75
|
+
- **Vercel** writes a `vercel.json` so muten's real-path routes don't 404 on a hard refresh (SPA fallback to `index.html`).
|
|
76
|
+
- **Tauri** adds `src-tauri/` (a native desktop app — ships the OS webview, *not* a browser) + a `tauri` script:
|
|
77
|
+
`npm run tauri dev` / `tauri build`. Needs the [Rust toolchain](https://rustup.rs) installed (not auto-installed).
|
|
72
78
|
|
|
73
79
|
## Templates (flavors)
|
|
74
80
|
|
|
@@ -110,6 +116,8 @@ create-muten my-app --css --no-install # just scaffold, decide later
|
|
|
110
116
|
| `--css` / `--scss` | pick the stylesheet (default: `css`) |
|
|
111
117
|
| `--tailwind` | add Tailwind CSS v4 on top of CSS (forces `--css`) |
|
|
112
118
|
| `--daisyui` | add DaisyUI component classes (implies `--tailwind`) |
|
|
119
|
+
| `--vercel` | add `vercel.json` (SPA fallback so real-path routes work on Vercel) |
|
|
120
|
+
| `--tauri` | add `src-tauri/` — a native desktop app (needs the Rust toolchain) |
|
|
113
121
|
| `--pm <npm\|pnpm\|yarn\|bun>` | package manager to use (default: detected) |
|
|
114
122
|
| `--no-install` | scaffold only — don't install or start the dev server |
|
|
115
123
|
| `--help` | print usage and exit |
|
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import color from 'picocolors';
|
|
|
15
15
|
|
|
16
16
|
const SELF = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
const TEMPLATE = join(SELF, 'template');
|
|
18
|
+
const TAURI_TEMPLATE = join(SELF, 'template-tauri'); // src-tauri/ overlay, copied only when --tauri
|
|
18
19
|
const TEMPLATES = ['muten', 'react', 'svelte']; // the "template" IS the flavor: pure muten, or muten + a framework for islands
|
|
19
20
|
const PKG = JSON.parse(readFileSync(join(SELF, 'package.json'), 'utf8'));
|
|
20
21
|
const PMS = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
@@ -113,6 +114,24 @@ DaisyUI adds **component classes** on top of Tailwind — use them in \`class("
|
|
|
113
114
|
\`@plugin "daisyui";\` is already in \`src/styles.css\`. Interactive behavior (toggle a modal/dropdown) you build
|
|
114
115
|
with Muten: \`state\` + \`class(active when isOpen)\` + \`on(click: …)\`.
|
|
115
116
|
`;
|
|
117
|
+
// Tauri = the SAME web build wrapped in a native OS-webview window (no browser bundled). Desktop target.
|
|
118
|
+
const TAURI_NOTE = (pm) => `
|
|
119
|
+
## Desktop app (Tauri)
|
|
120
|
+
This app also ships as a native desktop app via Tauri (\`src-tauri/\`). The SAME \`.muten\` frontend runs in
|
|
121
|
+
an OS-webview window — build the UI exactly like the web app (routing works as-is: the webview runs the SPA,
|
|
122
|
+
no server, no URL bar, no fallback needed).
|
|
123
|
+
- \`${pm} run tauri dev\` — run the desktop app (hot-reloads the frontend).
|
|
124
|
+
- \`${pm} run tauri build\` — native installer in \`src-tauri/target/release/bundle/\`.
|
|
125
|
+
- Needs the **Rust toolchain** on the machine (https://rustup.rs) — Tauri compiles a small native shell. Not auto-installed.
|
|
126
|
+
- Custom icon: \`${pm} run tauri icon path/to/logo.png\` regenerates \`src-tauri/icons/\`.
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
// Deploy on Vercel: muten routes are real paths (History API), so an unmatched path must fall back to
|
|
130
|
+
// index.html (else a hard refresh of /about 404s). Static assets are served first; only routes rewrite.
|
|
131
|
+
const VERCEL_JSON = `{
|
|
132
|
+
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
116
135
|
|
|
117
136
|
// Which PM launched us? npm/pnpm/yarn/bun set npm_config_user_agent — the idiomatic default.
|
|
118
137
|
const detectPM = () => { const ua = process.env.npm_config_user_agent || ''; return PMS.find((p) => ua.startsWith(p + '/')) || 'npm'; };
|
|
@@ -132,13 +151,15 @@ async function main() {
|
|
|
132
151
|
const has = (f) => argv.includes(f);
|
|
133
152
|
const val = (f) => { const i = argv.indexOf(f); return i >= 0 ? argv[i + 1] : undefined; };
|
|
134
153
|
if (has('-v') || has('--version')) { console.log(PKG.version); return; }
|
|
135
|
-
if (has('-h') || has('--help')) { console.log('Usage: create-muten [name] [--template muten|react|svelte] [--css|--scss] [--tailwind] [--daisyui] [--pm npm|pnpm|yarn|bun] [--no-install]'); return; }
|
|
154
|
+
if (has('-h') || has('--help')) { console.log('Usage: create-muten [name] [--template muten|react|svelte] [--css|--scss] [--tailwind] [--daisyui] [--vercel] [--tauri] [--pm npm|pnpm|yarn|bun] [--no-install]'); return; }
|
|
136
155
|
|
|
137
156
|
let name = argv.filter((a, i) => !a.startsWith('-') && argv[i - 1] !== '--pm' && argv[i - 1] !== '--template')[0];
|
|
138
157
|
let template = val('--template') || (has('--react') ? 'react' : has('--svelte') ? 'svelte' : has('--muten') ? 'muten' : undefined); // flavor: muten | react | svelte
|
|
139
158
|
let style = has('--scss') ? 'scss' : has('--css') ? 'css' : undefined; // the base stylesheet
|
|
140
159
|
let tailwind = has('--tailwind') ? true : undefined; // optional add-on (CSS only)
|
|
141
160
|
let daisyui = has('--daisyui') ? true : undefined; // component classes on Tailwind
|
|
161
|
+
let vercel = has('--vercel') ? true : undefined; // a vercel.json with the SPA fallback rewrite
|
|
162
|
+
let tauri = has('--tauri') ? true : undefined; // src-tauri/ → native desktop app
|
|
142
163
|
let pm = val('--pm');
|
|
143
164
|
let install = has('--no-install') ? false : undefined;
|
|
144
165
|
if (name && !validName(name)) { console.error(`Invalid name: "${name}" (letters, digits, . _ -)`); process.exit(1); }
|
|
@@ -156,12 +177,19 @@ async function main() {
|
|
|
156
177
|
{ value: 'react', label: 'muten + React', hint: 'React islands: shadcn, Radix, any React lib' },
|
|
157
178
|
{ value: 'svelte', label: 'muten + Svelte', hint: 'Svelte islands: a lighter runtime' },
|
|
158
179
|
] }));
|
|
159
|
-
if (!style
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
180
|
+
if (!style && tailwind === undefined) { // ONE explicit styling choice — each is opt-in, "CSS" = nothing extra
|
|
181
|
+
const styling = keep(await select({ message: 'Styling', options: [
|
|
182
|
+
{ value: 'css', label: 'CSS', hint: 'plain — no framework, zero deps' },
|
|
183
|
+
{ value: 'scss', label: 'SCSS', hint: 'adds sass' },
|
|
184
|
+
{ value: 'tailwind', label: 'Tailwind CSS', hint: 'utility classes on top of CSS' },
|
|
185
|
+
{ value: 'daisyui', label: 'DaisyUI', hint: 'component classes (btn, card, modal) — brings Tailwind with it' },
|
|
186
|
+
] }));
|
|
187
|
+
style = styling === 'scss' ? 'scss' : 'css';
|
|
188
|
+
tailwind = styling === 'tailwind' || styling === 'daisyui';
|
|
189
|
+
daisyui = styling === 'daisyui';
|
|
190
|
+
}
|
|
191
|
+
if (vercel === undefined) vercel = keep(await confirm({ message: 'Add Vercel deploy config? (vercel.json — fixes real-path routing on Vercel)', initialValue: false }));
|
|
192
|
+
if (tauri === undefined) tauri = keep(await confirm({ message: 'Desktop app? (Tauri — native window, ships the OS webview, needs Rust)', initialValue: false }));
|
|
165
193
|
}
|
|
166
194
|
name = name || 'muten-app';
|
|
167
195
|
template = template || 'muten';
|
|
@@ -169,6 +197,8 @@ async function main() {
|
|
|
169
197
|
if (daisyui) tailwind = true; // DaisyUI is a Tailwind plugin
|
|
170
198
|
if (tailwind === undefined) tailwind = false;
|
|
171
199
|
if (daisyui === undefined) daisyui = false;
|
|
200
|
+
if (vercel === undefined) vercel = false;
|
|
201
|
+
if (tauri === undefined) tauri = false;
|
|
172
202
|
if (tailwind) style = 'css'; // Tailwind v4 is CSS-native (not SCSS)
|
|
173
203
|
const svelte = template === 'svelte'; // the flavor IS the islands choice
|
|
174
204
|
const react = template === 'react';
|
|
@@ -183,6 +213,7 @@ async function main() {
|
|
|
183
213
|
cpSync(TEMPLATE, target, { recursive: true });
|
|
184
214
|
const ignore = join(target, '_gitignore');
|
|
185
215
|
if (existsSync(ignore)) renameSync(ignore, join(target, '.gitignore'));
|
|
216
|
+
if (vercel) writeFileSync(join(target, 'vercel.json'), VERCEL_JSON); // SPA fallback so real-path routes work on Vercel
|
|
186
217
|
|
|
187
218
|
const pkgPath = join(target, 'package.json');
|
|
188
219
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
@@ -204,10 +235,25 @@ async function main() {
|
|
|
204
235
|
if (svelte) { addDep({ svelte: '^5.0.0' }); addDev({ '@sveltejs/vite-plugin-svelte': '^7.0.0' }); }
|
|
205
236
|
if (react) { addDep({ react: '^19.0.0', 'react-dom': '^19.0.0' }); addDev({ '@vitejs/plugin-react': '^6.0.0' }); }
|
|
206
237
|
if (svelte || react) appendAgents(ISLANDS_NOTE({ svelte, react }));
|
|
238
|
+
if (tauri) { // native desktop wrapper around the same web build (dist)
|
|
239
|
+
cpSync(join(TAURI_TEMPLATE, 'src-tauri'), join(target, 'src-tauri'), { recursive: true });
|
|
240
|
+
writeFileSync(join(target, 'src-tauri', '.gitignore'), '/target\n/gen/schemas\n'); // npm strips real .gitignore from packages
|
|
241
|
+
const confPath = join(target, 'src-tauri', 'tauri.conf.json');
|
|
242
|
+
const conf = JSON.parse(readFileSync(confPath, 'utf8'));
|
|
243
|
+
conf.productName = name;
|
|
244
|
+
conf.identifier = `com.muten.${name.replace(/[^a-z0-9]/gi, '').toLowerCase() || 'app'}`; // reverse-DNS, no dashes/underscores
|
|
245
|
+
conf.app.windows[0].title = name;
|
|
246
|
+
conf.build.beforeDevCommand = `${pm} run dev`;
|
|
247
|
+
conf.build.beforeBuildCommand = `${pm} run build`;
|
|
248
|
+
writeFileSync(confPath, JSON.stringify(conf, null, 2) + '\n');
|
|
249
|
+
addDev({ '@tauri-apps/cli': '^2.0.0' });
|
|
250
|
+
pkg.scripts = { ...pkg.scripts, tauri: 'tauri' };
|
|
251
|
+
appendAgents(TAURI_NOTE(pm));
|
|
252
|
+
}
|
|
207
253
|
writeFileSync(join(target, 'vite.config.mjs'), viteConfig({ tailwind, svelte, react })); // composed: muten + chosen plugins
|
|
208
254
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
209
255
|
|
|
210
|
-
const desc = `${template}, ${style}${tailwind ? ' + Tailwind' : ''}${daisyui ? ' + DaisyUI' : ''}`;
|
|
256
|
+
const desc = `${template}, ${style}${tailwind ? ' + Tailwind' : ''}${daisyui ? ' + DaisyUI' : ''}${vercel ? ' + Vercel' : ''}${tauri ? ' + Tauri' : ''}`;
|
|
211
257
|
if (!install) {
|
|
212
258
|
if (process.stdin.isTTY) { note(`cd ${name}\n${pm} install\n${pm} run dev`, 'Next steps'); outro(color.green(`Created ${name} (${desc})`)); }
|
|
213
259
|
else console.log(`\n Created ${name} (${desc}, ${pm})\n cd ${name} && ${pm} install && ${pm} run dev\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-muten",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Scaffold a new Muten app.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
"picocolors": "^1.0.1"
|
|
20
20
|
},
|
|
21
21
|
"keywords": ["muten", "create-muten", "scaffold", "starter", "cli", "frontend"],
|
|
22
|
-
"files": ["index.js", "template", "overlays"]
|
|
22
|
+
"files": ["index.js", "template", "template-tauri", "overlays"]
|
|
23
23
|
}
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "muten-app"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A muten desktop app"
|
|
5
|
+
edition = "2021"
|
|
6
|
+
|
|
7
|
+
[lib]
|
|
8
|
+
name = "app_lib"
|
|
9
|
+
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
10
|
+
|
|
11
|
+
[build-dependencies]
|
|
12
|
+
tauri-build = { version = "2", features = [] }
|
|
13
|
+
|
|
14
|
+
[dependencies]
|
|
15
|
+
tauri = { version = "2", features = [] }
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://schema.tauri.app/config/2",
|
|
3
|
+
"productName": "muten-app",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"identifier": "com.muten.app",
|
|
6
|
+
"build": {
|
|
7
|
+
"frontendDist": "../dist",
|
|
8
|
+
"devUrl": "http://localhost:5173",
|
|
9
|
+
"beforeDevCommand": "npm run dev",
|
|
10
|
+
"beforeBuildCommand": "npm run build"
|
|
11
|
+
},
|
|
12
|
+
"app": {
|
|
13
|
+
"windows": [
|
|
14
|
+
{ "title": "muten-app", "width": 1000, "height": 700 }
|
|
15
|
+
],
|
|
16
|
+
"security": { "csp": null }
|
|
17
|
+
},
|
|
18
|
+
"bundle": {
|
|
19
|
+
"active": true,
|
|
20
|
+
"targets": "all",
|
|
21
|
+
"icon": [
|
|
22
|
+
"icons/32x32.png",
|
|
23
|
+
"icons/128x128.png",
|
|
24
|
+
"icons/128x128@2x.png",
|
|
25
|
+
"icons/icon.icns",
|
|
26
|
+
"icons/icon.ico"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|