create-muten 0.0.12 → 0.0.14
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 +38 -27
- package/package.json +1 -1
- package/template/.claude/skills/muten/SKILL.md +13 -3
- package/template/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
## ALPHA - STILL ON DEVELOPMENT
|
|
2
|
+
Muten is still under active development. We are currently in the alpha stage and are working on training models with Muten. Please keep in mind that improvements are being made gradually, and version 1.0 has not been released yet.
|
|
2
3
|
|
|
3
|
-
The official scaffolder for **[Muten](https://www.npmjs.com/package/@muten/core)
|
|
4
|
+
The official scaffolder for **[Muten](https://www.npmjs.com/package/@muten/core)**: an AI-first
|
|
4
5
|
frontend framework. One command bootstraps a complete, ready-to-run Muten app, so you never copy
|
|
5
6
|
boilerplate by hand.
|
|
6
7
|
|
|
@@ -10,11 +11,11 @@ npm create muten@latest
|
|
|
10
11
|
|
|
11
12
|
## Why this exists
|
|
12
13
|
|
|
13
|
-
Muten ships as **two** packages
|
|
14
|
+
Muten ships as **two** packages - the same split as `vue` ↔ `create-vue`:
|
|
14
15
|
|
|
15
|
-
- **[`@muten/core`](https://www.npmjs.com/package/@muten/core)
|
|
16
|
+
- **[`@muten/core`](https://www.npmjs.com/package/@muten/core)**: the engine (compiler + runtime +
|
|
16
17
|
Vite plugin). Your app installs it as a normal dependency and it stays up to date on its own.
|
|
17
|
-
- **`create-muten`** (this package)
|
|
18
|
+
- **`create-muten`** (this package) - a tiny, **zero-dependency** CLI whose only job is to generate a
|
|
18
19
|
new project already wired to the engine: `index.html`, the Vite config, a `theme.muten`, a first
|
|
19
20
|
page and the right `package.json`.
|
|
20
21
|
|
|
@@ -67,36 +68,36 @@ In an interactive terminal it prompts for a few things (defaults in parentheses)
|
|
|
67
68
|
| **Package manager** | `npm` / `pnpm` / `yarn` / `bun` | the one that launched it |
|
|
68
69
|
| **Install deps and start dev now?** | `Y` / `n` | `Y` |
|
|
69
70
|
|
|
70
|
-
**Styling is one explicit choice
|
|
71
|
+
**Styling is one explicit choice**: each is opt-in, nothing is bundled by default: `CSS` (plain) or `SCSS`
|
|
71
72
|
ship no framework; `Tailwind CSS` adds `@tailwindcss/vite` + `@import "tailwindcss"`; `DaisyUI` adds its
|
|
72
73
|
component classes on top (and brings Tailwind). You always style via `class("…")`.
|
|
73
74
|
|
|
74
|
-
**Targets are independent opt-ins
|
|
75
|
+
**Targets are independent opt-ins**: web, desktop, both, or neither, from the same `.muten` source:
|
|
75
76
|
- **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
|
|
77
|
+
- **Tauri** adds `src-tauri/` (a native desktop app - ships the OS webview, *not* a browser) + a `tauri` script:
|
|
77
78
|
`npm run tauri dev` / `tauri build`. Needs the [Rust toolchain](https://rustup.rs) installed (not auto-installed).
|
|
78
79
|
|
|
79
80
|
## Templates (flavors)
|
|
80
81
|
|
|
81
|
-
Every flavor scaffolds the **same** welcome page and the same `.muten` workflow
|
|
82
|
+
Every flavor scaffolds the **same** welcome page and the same `.muten` workflow, the only difference is
|
|
82
83
|
whether a framework's island plugin is pre-wired:
|
|
83
84
|
|
|
84
85
|
| Template | What you get |
|
|
85
86
|
|---|---|
|
|
86
|
-
| **muten** | pure muten
|
|
87
|
+
| **muten** | pure muten - the AI-first DSL, zero framework runtime |
|
|
87
88
|
| **muten + React** | same, plus `@vitejs/plugin-react` + React, so you can drop in a **React island** (shadcn/Radix, any React lib) |
|
|
88
89
|
| **muten + Svelte** | same, plus `@sveltejs/vite-plugin-svelte` + Svelte, for **Svelte islands** (a lighter runtime) |
|
|
89
90
|
|
|
90
|
-
An *island* is a real framework component used as a node
|
|
91
|
+
An *island* is a real framework component used as a node - `use X from "react:./X.jsx"` →
|
|
91
92
|
`X(value: @s, onChange: act) client:visible` (props ↓ + events ↑, lazy + code-split). Default to `.muten`;
|
|
92
93
|
reach for an island only for a widget muten can't express.
|
|
93
94
|
|
|
94
95
|
When **Tailwind or DaisyUI** is added, `theme.muten` is centralized to **match Tailwind's scale** (so
|
|
95
96
|
`style()` tokens and Tailwind utilities share one scale, e.g. `style(gap.md)` == `gap-4`); plain CSS/SCSS
|
|
96
|
-
keeps the default scale. **DaisyUI** adds component classes (`btn`, `card`, `modal`) usable in `class("…")`
|
|
97
|
+
keeps the default scale. **DaisyUI** adds component classes (`btn`, `card`, `modal`) usable in `class("…")` -
|
|
97
98
|
pure classes, no React; behavior is Muten state + `on()`.
|
|
98
99
|
|
|
99
|
-
If you accept the last prompt it runs `<pm> install` followed by `<pm> run dev
|
|
100
|
+
If you accept the last prompt it runs `<pm> install` followed by `<pm> run dev`, your app is live in a
|
|
100
101
|
single step. Choosing SCSS also adds `sass` and switches the stylesheet to `.scss` automatically.
|
|
101
102
|
|
|
102
103
|
## Non-interactive (CI / scripts)
|
|
@@ -117,9 +118,9 @@ create-muten my-app --css --no-install # just scaffold, decide later
|
|
|
117
118
|
| `--tailwind` | add Tailwind CSS v4 on top of CSS (forces `--css`) |
|
|
118
119
|
| `--daisyui` | add DaisyUI component classes (implies `--tailwind`) |
|
|
119
120
|
| `--vercel` | add `vercel.json` (SPA fallback so real-path routes work on Vercel) |
|
|
120
|
-
| `--tauri` | add `src-tauri/`
|
|
121
|
+
| `--tauri` | add `src-tauri/` - a native desktop app (needs the Rust toolchain) |
|
|
121
122
|
| `--pm <npm\|pnpm\|yarn\|bun>` | package manager to use (default: detected) |
|
|
122
|
-
| `--no-install` | scaffold only
|
|
123
|
+
| `--no-install` | scaffold only - don't install or start the dev server |
|
|
123
124
|
| `--help` | print usage and exit |
|
|
124
125
|
| `--version` | print the version and exit |
|
|
125
126
|
|
|
@@ -129,7 +130,7 @@ A minimal, conventional Muten app:
|
|
|
129
130
|
|
|
130
131
|
```
|
|
131
132
|
my-app/
|
|
132
|
-
├─ index.html # entry
|
|
133
|
+
├─ index.html # entry - loads /src/app.muten through the Vite plugin
|
|
133
134
|
├─ vite.config.mjs # the @muten/core Vite plugin (dev server, HMR, routing)
|
|
134
135
|
├─ theme.muten # your design tokens: spacing, fonts, weights, breakpoints
|
|
135
136
|
├─ package.json # depends on @muten/core + vite
|
|
@@ -137,7 +138,7 @@ my-app/
|
|
|
137
138
|
├─ app.muten # the ROOT: routes (+ an optional persistent shell)
|
|
138
139
|
├─ styles.css # your look (.scss if you chose SCSS)
|
|
139
140
|
└─ pages/
|
|
140
|
-
└─ home/home.muten # a page
|
|
141
|
+
└─ home/home.muten # a page - the folder name is its route
|
|
141
142
|
```
|
|
142
143
|
|
|
143
144
|
There is **no hand-written `main.js`**: the Vite plugin compiles `src/app.muten` into the app's entry,
|
|
@@ -145,16 +146,26 @@ so the whole app is `.muten` from the first line.
|
|
|
145
146
|
|
|
146
147
|
## What you can build
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
**Honest framing first.** muten isn't trying to beat React/Vue/Svelte at being general-purpose, they win there.
|
|
150
|
+
muten wins when an **AI builds and maintains the app**: the whole language fits in context, a compiler (`muten
|
|
151
|
+
check`) catches mistakes in milliseconds without a browser, edits stay tiny, and almost no JS ships. Best fit: the
|
|
152
|
+
declarative 80% - CRUD, dashboards, catalogs, content, internal tools. For the rest, you don't fight it - you
|
|
153
|
+
**couple in other tech** through bounded escapes. Reach for the **lowest tier that works**:
|
|
154
|
+
|
|
155
|
+
- **Pure muten**: CRUD / SaaS / catalog / dashboard / content: pages, routing, `state`/`store` (with page→store
|
|
156
|
+
action composition), `query` over REST, `Form` (text/number/email/bool/enum + validation), `DataTable`,
|
|
157
|
+
`when`/`each`, SSG + SEO, and the bounded **list toolkit**: inline objects, `patch` in-place edit, `each…where`
|
|
158
|
+
filter, aggregates (`sum`/`count`/`avg`/`min`/`max`), `sort`/`sortDesc`. The declarative 80%, zero extra deps.
|
|
159
|
+
- **muten + the platform** *(no framework runtime)* - native HTML (`<input type="date">`, `<dialog>`) + `class()`,
|
|
153
160
|
CSS libs (Tailwind / DaisyUI), **vanilla JS via `Custom`** (charts, maps, date-pickers, rich-text, grids),
|
|
154
161
|
`use fmt from "./lib.ts"` for any JS logic. Almost every "hard widget" lands here, *without React*.
|
|
155
|
-
- **Svelte / React island** (`--svelte` / `--react`)
|
|
162
|
+
- **Svelte / React island** (`--svelte` / `--react`) - only when the component *is* a framework component
|
|
156
163
|
(shadcn/ui, a React-only lib). Ships that runtime, lazy + code-split. The narrow last resort.
|
|
157
164
|
|
|
165
|
+
**Deploy, honestly:** `npm run dev` runs every tier. For production, pure-muten static content can ship via
|
|
166
|
+
`muten build` (zero-JS HTML); the moment you use `use`/islands/shared cross-page state, deploy with a normal
|
|
167
|
+
`vite build` (it bundles them - the static build doesn't). Most real apps use `vite build`.
|
|
168
|
+
|
|
158
169
|
Full reference (every primitive, the three tiers, the roadmap): [`@muten/core`](https://www.npmjs.com/package/@muten/core).
|
|
159
170
|
|
|
160
171
|
> **Status: pre-1.0.** The core (language, compiler, CLI, Vite plugin, extension, islands) is solid; the
|
|
@@ -168,10 +179,10 @@ Full reference (every primitive, the three tiers, the roadmap): [`@muten/core`](
|
|
|
168
179
|
## Cross-platform
|
|
169
180
|
|
|
170
181
|
`create-muten` is a Node CLI (not a shell script), so the **exact same command** works on **Windows,
|
|
171
|
-
macOS and Linux
|
|
182
|
+
macOS and Linux**: npm generates the right launcher on each OS.
|
|
172
183
|
|
|
173
184
|
## Links
|
|
174
185
|
|
|
175
|
-
- Engine
|
|
176
|
-
- Source
|
|
177
|
-
- License
|
|
186
|
+
- Engine - [`@muten/core`](https://www.npmjs.com/package/@muten/core)
|
|
187
|
+
- Source - [github.com/karttofer/create-muten](https://github.com/karttofer/create-muten)
|
|
188
|
+
- License - MIT
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ A page with no reactivity compiles to plain zero-runtime HTML; a reactive one sh
|
|
|
15
15
|
- **`src/app.muten` is the entry.** `index.html` loads it; the plugin boots it. **Never create `main.js`** or a `<script>` bootstrap.
|
|
16
16
|
- Primitives are **PascalCase** (`Stack`, `Text`); keywords/control flow are **lowercase** (`when`, `each`, `state`).
|
|
17
17
|
- `style(...)` = layout/typography **tokens** (Muten builds STRUCTURE). `class("...")` = **look** (your CSS / Tailwind); toggle reactively with `class(active when isOpen)`. Muten ships no skin.
|
|
18
|
-
- `@name` = a state reference. `{expr}` = interpolation inside
|
|
18
|
+
- `@name` = a state reference. `{expr}` = interpolation inside a Text/label/path string: `Text "Hi, {user.name}"`. (NOT inside `class("…")` — for a dynamic class use `class(name when cond)`.)
|
|
19
19
|
- Each page has **one root node**. Reactivity is automatic: reading a state in interpolation / `when` / `each` re-renders just that spot.
|
|
20
20
|
|
|
21
21
|
## 1. What you CAN install / use
|
|
@@ -85,10 +85,11 @@ const TAX = 0.21 # compile-time immutable scalar (inlined, never
|
|
|
85
85
|
|
|
86
86
|
action add mutates users <- item { # mutation; `mutates` lists what it may change (enforced)
|
|
87
87
|
users.push(item) # ops: push | set | reset | remove
|
|
88
|
+
users.push({ name: item.name, role: "admin" }) # inline object literal — build a record inline
|
|
88
89
|
if item.vip { rating.set(5) } else { rating.set(1) } # if/else = the only branching in actions
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
mock { listUsers: [ { name: "Ana", role: admin } ] }
|
|
92
|
+
mock { listUsers: [ { name: "Ana", role: "admin" } ] } # mock data (quote text/enum values, like everywhere)
|
|
92
93
|
sources { listUsers: { url: "https://api…", at: "results" } } # real data source for a query
|
|
93
94
|
```
|
|
94
95
|
|
|
@@ -233,10 +234,17 @@ Responsive: prefix any token with a breakpoint → `md:cols.2`, `lg:cols.4` (`sm
|
|
|
233
234
|
- `query` state is async → render with `when @x.loading { … }`, then use `@x.data`.
|
|
234
235
|
- Mutate **only** through `action`s, and only the state in `mutates` (the linter enforces it):
|
|
235
236
|
- `list.push(x)` (append; auto-fills uuid fields) · `s.set(v)` · `s.reset()` · `list.remove(x => x.id == id)`
|
|
237
|
+
- **Inline object literal** (build a record without leaving Muten): `posts.push({ title: draft.title, body: draft.body })`, `draft.set({ name: c.name })`. Keys must be real fields of the entity.
|
|
238
|
+
- **Edit / move / toggle an item in place**: `list.patch(x => x.id == c.id, { done: not x.done })` — position-preserving, list ONLY the changed fields. This is the right tool for toggle/update/move (NOT remove+push, which reorders the item to the end).
|
|
236
239
|
- There is no `toggle`: `flag.set(not flag)`.
|
|
237
|
-
- Control flow in the tree: `when <expr> { … }` (mount/unmount), `each <list> as item { … }` (item is a scope var).
|
|
240
|
+
- Control flow in the tree: `when <expr> { … }` (mount/unmount), `each <list> as item { … }` (item is a scope var). Filter a list with `where`: `each posts as p where p.published { … }` renders only matching items.
|
|
238
241
|
- Expressions: `== != < > <= >=`, `and or not`, `contains` (case-insensitive substring / list membership),
|
|
239
242
|
`+ - * /`, ternary `c ? a : b`, parentheses, refs (`user.name`, `cart.total`, `$item.x`).
|
|
243
|
+
- **List aggregates** (method + lambda, like `remove`) — for a cart total / KPI count / "N active", NO JS needed:
|
|
244
|
+
- `lines.sum(l => l.price * l.qty)` · `todos.count(t => not t.done)` · `reviews.avg(r => r.score)` · `min/max(x => …)`.
|
|
245
|
+
- `.length` is the count-all; `count(x => cond)` is the filtered count. Works in interpolation, `when`, and store `get`.
|
|
246
|
+
- **Sort a list** (same method+lambda shape; returns a sorted COPY): `each contacts.sort(c => c.name) as c { … }` (ascending) ·
|
|
247
|
+
`each scores.sortDesc(s => s.points) as s { … }` (descending). Use in `each` or a store `get`.
|
|
240
248
|
|
|
241
249
|
## 9. Stores — app-global state
|
|
242
250
|
A `.store` file = state shared across pages, **no prop drilling**. The file name is the domain.
|
|
@@ -249,6 +257,8 @@ effect { /* runs whenever the store state it reads changes */ }
|
|
|
249
257
|
```
|
|
250
258
|
Use it from any page/shell by name: `when ui.menuOpen { … }`, `Button "☰" -> ui.toggleMenu`. The Vite
|
|
251
259
|
plugin auto-detects every `.store` file. `get` = memoized; `effect` = reactive side-effect (Angular-style).
|
|
260
|
+
**A page action can CALL a store action** (composition) — `action add <- d { cart.add(d) draft.reset() }` does
|
|
261
|
+
store work AND local work in one handler (e.g. add to the store, then clear the form). Wire it with `Form submit add`.
|
|
252
262
|
|
|
253
263
|
## 10. Routing — how it works
|
|
254
264
|
`src/app.muten` maps URLs to pages. It uses **real paths** (`/about`, History API — client-side nav, no
|