create-tauri-ui 1.0.5 → 1.0.6

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 (3) hide show
  1. package/README.md +92 -108
  2. package/dist/index.mjs +80 -65
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,141 +1,142 @@
1
1
  # create-tauri-ui
2
2
 
3
- Create modern Tauri desktop apps with `shadcn/ui` in a few steps.
3
+ CLI to scaffold Tauri desktop apps with `shadcn/ui`. Drives the upstream `shadcn` and `create-tauri-app` CLIs — no local template tree to maintain.
4
4
 
5
- This package scaffolds a frontend with the upstream `shadcn` CLI, generates a native shell with `create-tauri-app`, and merges the result into a Tauri-ready project. It is designed to avoid maintaining a large local template directory.
6
-
7
- ## Quick Start
8
-
9
- Primary commands:
10
-
11
- ```bash
12
- npm create tauri-ui@latest my-app
13
- ```
5
+ ## Install & run
14
6
 
15
7
  ```bash
8
+ # bun
16
9
  bun create tauri-ui my-app
17
- ```
18
-
19
- Equivalent direct binary commands:
10
+ bunx create-tauri-ui@latest my-app
20
11
 
21
- ```bash
12
+ # npm
13
+ npm create tauri-ui@latest my-app
22
14
  npx create-tauri-ui@latest my-app
23
15
  ```
24
16
 
17
+ ## After generation
18
+
25
19
  ```bash
26
- bunx create-tauri-ui@latest my-app
20
+ cd my-app
21
+ bun install
22
+ bun run tauri dev
27
23
  ```
28
24
 
29
- ## Supported Templates
30
-
31
- - `vite`
32
- - `next`
33
- - `start`
34
- - `react-router`
35
- - `astro`
36
-
37
- ## Batteries Included
38
-
39
- Every generated project gets:
40
-
41
- - a Tauri `src-tauri` native layer
42
- - a frontend scaffold generated by the upstream `shadcn` CLI
43
- - template adapters for `vite`, `next`, `start`, `react-router`, and `astro`
44
- - Tauri config patches for desktop dev/build output
45
- - centered desktop window defaults with a `1400x918` main window
46
- - startup flash prevention by hiding the main window until the first page load finishes
47
- - external link guarding so internal links stay in-app and external links open in the system browser
48
- - a dev-only debug panel with Tauri runtime info, tracked invokes, runtime events, and log stream wiring
49
- - desktop scroll-container defaults that disable overscroll and rubber-band scrolling
50
- - desktop selection defaults with global `select-none`, an intrinsic selectable allowlist, and a `.ui-selectable` utility
51
- - a branded `app-icon.png` source asset
25
+ To regenerate the Tauri icon set from the included source image:
52
26
 
53
- Optional batteries:
27
+ ```bash
28
+ bunx tauri icon app-icon.png
29
+ ```
54
30
 
55
- - a starter dashboard based on `dashboard-01`
56
- - a small Rust invoke example
57
- - a GitHub release workflow
31
+ > Bun is required in generated projects.
58
32
 
59
- ## Example Commands
33
+ ## CLI reference
60
34
 
61
- Generate a Vite app with defaults:
35
+ ```
36
+ Usage: create-tauri-ui [target-dir] [options]
62
37
 
38
+ Options:
39
+ -t, --template <name> vite | next | start | react-router | astro
40
+ --identifier <value> Tauri app identifier (e.g. com.example.myapp)
41
+ --preset <value> shadcn preset (default: b0)
42
+ --size-optimize optimize release binaries for size
43
+ --no-size-optimize skip size optimization
44
+ --starter include the starter dashboard
45
+ --no-starter skip the starter dashboard
46
+ --invoke-example include the Rust invoke example
47
+ --no-invoke-example skip the Rust invoke example
48
+ --workflow include the GitHub release workflow
49
+ --no-workflow skip the GitHub release workflow
50
+ -f, --force overwrite an existing target directory
51
+ -y, --yes accept all defaults
52
+ -v, --version display version
53
+ -h, --help display help
54
+ ```
55
+
56
+ ## Examples
57
+
58
+ **Vite app with all defaults:**
63
59
  ```bash
64
60
  npm create tauri-ui@latest my-app -- --template vite --yes
65
61
  ```
66
62
 
67
- Generate a Next.js app without the starter dashboard or workflow:
68
-
63
+ **Next.js app, no dashboard, no workflow:**
69
64
  ```bash
70
65
  npm create tauri-ui@latest my-app -- --template next --yes --no-starter --no-workflow
71
66
  ```
72
67
 
73
- Generate an Astro app with a custom identifier:
74
-
68
+ **Astro app with a custom bundle identifier:**
75
69
  ```bash
76
70
  bun create tauri-ui my-app --template astro --identifier com.example.astroapp --yes
77
71
  ```
78
72
 
79
- ## CLI Options
73
+ **Vite app with size-optimized release binary:**
74
+ ```bash
75
+ bun create tauri-ui my-app --template vite --size-optimize --yes
76
+ ```
80
77
 
81
- ```text
82
- Usage: create-tauri-ui [target-dir] [options]
78
+ ## Batteries included
83
79
 
84
- Options:
85
- -t, --template <name> vite | next | start | react-router | astro
86
- --identifier <value> set the Tauri app identifier
87
- --preset <value> set the shadcn preset (default: b0)
88
- --starter include the starter dashboard
89
- --no-starter skip the starter dashboard
90
- --invoke-example include the Rust invoke example
91
- --no-invoke-example skip the Rust invoke example
92
- --workflow include the GitHub release workflow
93
- --no-workflow skip the GitHub release workflow
94
- -f, --force overwrite an existing target directory
95
- -y, --yes accept defaults
96
- -h, --help display help
97
- ```
80
+ Every generated project gets:
98
81
 
99
- ## After Generation
82
+ - `src-tauri` native layer (from `create-tauri-app`)
83
+ - shadcn frontend scaffold (from the upstream `shadcn` CLI)
84
+ - framework adapters for `vite`, `next`, `start`, `react-router`, `astro`
85
+ - Tauri config patches for desktop dev and build output
86
+ - centered `1400×918` main window
87
+ - startup flash prevention (window hidden until first paint)
88
+ - external link guard — internal links stay in-app, external links open in the system browser
89
+ - overscroll / rubber-band scroll disabled
90
+ - desktop selection defaults — global `select-none`, intrinsic selectable allowlist, `.ui-selectable` utility class
91
+ - dev-only debug panel with runtime info, tracked invokes, runtime events, and log stream
92
+ - `app-icon.png` source asset
100
93
 
101
- In the generated project:
94
+ **Optional** (prompted during scaffolding, or passed as flags):
102
95
 
103
- ```bash
104
- cd my-app
105
- bun install
106
- bun run build
107
- bun run tauri dev
108
- ```
96
+ | Battery | Flag | Notes |
97
+ |---|---|---|
98
+ | Starter dashboard | `--starter` / `--no-starter` | based on `dashboard-01` |
99
+ | Rust invoke example | `--invoke-example` / `--no-invoke-example` | |
100
+ | Size optimization | `--size-optimize` / `--no-size-optimize` | ~65% smaller release binary (9 MB → 3.1 MB) |
101
+ | GitHub release workflow | `--workflow` / `--no-workflow` | |
109
102
 
110
- To generate the Tauri icon set from the included source image:
103
+ ## Template status
111
104
 
112
- ```bash
113
- bunx tauri icon app-icon.png
114
- ```
105
+ | Template | Status |
106
+ |---|---|
107
+ | `vite` | smoke-tested end to end |
108
+ | `next` | stable |
109
+ | `react-router` | stable |
110
+ | `astro` | stable |
111
+ | `start` (TanStack) | experimental — validate carefully after changes |
112
+
113
+ Scaffolding into `.` (current directory) is not supported yet.
115
114
 
116
- ## How It Works
115
+ ## How it works
117
116
 
118
- ```text
119
- prompts -> shadcn init -> create-tauri-app (temp) -> merge src-tauri -> apply framework patches -> add batteries
117
+ ```
118
+ prompts shadcn init create-tauri-app (temp dir) merge src-tauri apply framework patches add batteries
120
119
  ```
121
120
 
122
- The package keeps only a small local asset surface:
121
+ Local asset surface is intentionally small:
123
122
 
124
- - `assets/app-icon.png`
125
- - `assets/release.yml.tmpl`
123
+ ```
124
+ assets/app-icon.png
125
+ assets/release.yml.tmpl
126
+ ```
126
127
 
127
- It does not ship full frontend templates.
128
+ No full frontend templates are shipped. The upstream CLIs do the heavy lifting; this package handles the merge, patching, and battery injection.
128
129
 
129
130
  ## Development
130
131
 
131
- Run these from the monorepo root:
132
+ Run from the monorepo root:
132
133
 
133
134
  ```bash
134
135
  bun run --cwd packages/create-tauri-ui check-types
135
136
  bun run --cwd packages/create-tauri-ui build
136
137
  ```
137
138
 
138
- Run the local CLI build:
139
+ Run the local build directly:
139
140
 
140
141
  ```bash
141
142
  bun run --cwd packages/create-tauri-ui start -- --help
@@ -143,46 +144,29 @@ bun run --cwd packages/create-tauri-ui start -- --help
143
144
 
144
145
  ## Testing
145
146
 
146
- Quick validation:
147
-
147
+ **Type check + build:**
148
148
  ```bash
149
149
  bun run --cwd packages/create-tauri-ui check-types
150
150
  bun run --cwd packages/create-tauri-ui build
151
151
  ```
152
152
 
153
- End-to-end smoke test from the monorepo root:
154
-
153
+ **Single template smoke test:**
155
154
  ```bash
156
155
  rm -rf /tmp/ctui-vite
157
156
  node packages/create-tauri-ui/index.js /tmp/ctui-vite --template vite --yes --no-workflow
158
- cd /tmp/ctui-vite
159
- bun install
160
- bun run build
157
+ cd /tmp/ctui-vite && bun install && bun run build
161
158
  ```
162
159
 
163
- Smoke test all templates:
164
-
160
+ **All templates:**
165
161
  ```bash
166
162
  for template in vite next start react-router astro; do
167
163
  dir="/tmp/create-tauri-ui-$template"
168
164
  rm -rf "$dir"
169
165
  node packages/create-tauri-ui/index.js "$dir" --template "$template" --yes --no-workflow
170
- (
171
- cd "$dir" &&
172
- bun install &&
173
- bun run build
174
- ) || exit 1
166
+ (cd "$dir" && bun install && bun run build) || exit 1
175
167
  done
176
168
  ```
177
169
 
178
- ## Current Notes
179
-
180
- - Bun is required in generated projects.
181
- - Scaffolding into `.` is not supported yet.
182
- - Vite has already been smoke-tested end to end.
183
- - The other adapters are implemented, but they still need broader end-to-end verification coverage.
184
- - `TanStack Start` is the highest-risk adapter and should be validated carefully after changes.
185
-
186
170
  ## License
187
171
 
188
172
  MIT
package/dist/index.mjs CHANGED
@@ -1,33 +1,33 @@
1
- import s from"node:fs";import i from"node:path";import _ from"node:process";import{text as P,confirm as D,select as xe,multiselect as be,isCancel as ye,cancel as K,log as w,intro as ke,spinner as $e,outro as Ee,note as z}from"@clack/prompts";import A from"picocolors";import Y from"node:os";import{execFile as je}from"node:child_process";import{fileURLToPath as R}from"node:url";const L=new Set;let X=!1,q=!1;class C extends Error{constructor(t,r,n,a){super(t),this.stdout=r,this.stderr=n,this.code=a}}class v extends Error{constructor(t,r,n){super(r),this.tool=t,this.stderr=n}}class u extends Error{constructor(t,r){super(r),this.file=t}}function Se(e){return e?.trim().replace(/\/+$/g,"")}function J(e,t){s.statSync(e).isDirectory()?Q(e,t):(s.mkdirSync(i.dirname(t),{recursive:!0}),s.copyFileSync(e,t))}function O(e){return/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e)}function Z(e){return e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-")}function Q(e,t){s.mkdirSync(t,{recursive:!0});for(const r of s.readdirSync(e)){const n=i.resolve(e,r),a=i.resolve(t,r);J(n,a)}}function _e(e){const t=s.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"}function c(e,t){const r=s.readFileSync(e,"utf-8").replace(/\r\n/g,`
1
+ import s from"node:fs";import a from"node:path";import P from"node:process";import{text as D,confirm as w,select as be,multiselect as ye,isCancel as ke,cancel as W,log as v,intro as $e,spinner as Ee,outro as je,note as K}from"@clack/prompts";import A from"picocolors";import Y from"node:os";import{execFile as Se}from"node:child_process";import{fileURLToPath as R}from"node:url";const L=new Set;let J=!1,X=!1;class C extends Error{constructor(t,r,n,i){super(t),this.stdout=r,this.stderr=n,this.code=i}}class x extends Error{constructor(t,r,n){super(r),this.tool=t,this.stderr=n}}class u extends Error{constructor(t,r){super(r),this.file=t}}function _e(e){return e?.trim().replace(/\/+$/g,"")}function q(e,t){s.statSync(e).isDirectory()?Q(e,t):(s.mkdirSync(a.dirname(t),{recursive:!0}),s.copyFileSync(e,t))}function O(e){return/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e)}function Z(e){return e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-")}function Q(e,t){s.mkdirSync(t,{recursive:!0});for(const r of s.readdirSync(e)){const n=a.resolve(e,r),i=a.resolve(t,r);q(n,i)}}function Pe(e){const t=s.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"}function c(e,t){const r=s.readFileSync(e,"utf-8").replace(/\r\n/g,`
2
2
  `);s.writeFileSync(e,t(r),"utf-8")}function f(e,t){const r=JSON.parse(s.readFileSync(e,"utf-8")),n=t(r);s.writeFileSync(e,JSON.stringify(n,null,2)+`
3
- `)}async function T(e,t,r){return await new Promise((n,a)=>{je(e,t,{cwd:r?.cwd,env:{...process.env,...r?.env},maxBuffer:1024*1024*10},(o,l,I)=>{if(o){a(new C(`Command failed: ${e} ${t.join(" ")}`,l,I,typeof o.code=="number"?o.code:null));return}n(l.trim())})})}function ee(e){return s.mkdtempSync(i.join(Y.tmpdir(),e))}function F(e){L.add(e)}function g(e){L.delete(e)}function h(e){s.existsSync(e)&&s.rmSync(e,{recursive:!0,force:!0})}function Pe(){const e=[...L].sort((t,r)=>r.length-t.length);for(const t of e)try{h(t)}catch{}finally{L.delete(t)}}function De(){if(X)return;X=!0;const e=(t,r)=>{q&&process.exit(r),q=!0,Pe(),process.exit(r)};process.once("SIGINT",()=>e("SIGINT",130)),process.once("SIGTERM",()=>e("SIGTERM",143))}function Le(e){return e.replace(/^@[^/]+\//,"").replace(/[^a-zA-Z0-9]+/g,"_").replace(/^_+|_+$/g,"")}const Ce={name:"astro",async apply(e,t){c(i.join(e,"astro.config.mjs"),r=>{if(r.includes("server: {"))return r;const n=r.lastIndexOf(`
3
+ `)}async function T(e,t,r){return await new Promise((n,i)=>{Se(e,t,{cwd:r?.cwd,env:{...process.env,...r?.env},maxBuffer:1024*1024*10},(o,l,I)=>{if(o){i(new C(`Command failed: ${e} ${t.join(" ")}`,l,I,typeof o.code=="number"?o.code:null));return}n(l.trim())})})}function ee(e){return s.mkdtempSync(a.join(Y.tmpdir(),e))}function z(e){L.add(e)}function g(e){L.delete(e)}function h(e){s.existsSync(e)&&s.rmSync(e,{recursive:!0,force:!0})}function De(){const e=[...L].sort((t,r)=>r.length-t.length);for(const t of e)try{h(t)}catch{}finally{L.delete(t)}}function Le(){if(J)return;J=!0;const e=(t,r)=>{X&&process.exit(r),X=!0,De(),process.exit(r)};process.once("SIGINT",()=>e("SIGINT",130)),process.once("SIGTERM",()=>e("SIGTERM",143))}function Ce(e){return e.replace(/^@[^/]+\//,"").replace(/[^a-zA-Z0-9]+/g,"_").replace(/^_+|_+$/g,"")}const Te={name:"astro",async apply(e,t){c(a.join(e,"astro.config.mjs"),r=>{if(r.includes("server: {"))return r;const n=r.lastIndexOf(`
4
4
  })`);if(n===-1)throw new u("astro.config.mjs","Could not find the Astro config closing brace.");return`${r.slice(0,n)}
5
5
  server: {
6
6
  port: 1420,
7
- },${r.slice(n)}`})},tauriConfig(){return{frontendDist:"../dist",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}},Te={name:"next",async apply(e,t){c(i.join(e,"next.config.mjs"),r=>{if(r.includes('output: "export"'))return r;if(!r.includes("const nextConfig = {}"))throw new u("next.config.mjs","Could not find the default Next.js config shape.");return r.replace("const nextConfig = {}",`const nextConfig = {
7
+ },${r.slice(n)}`})},tauriConfig(){return{frontendDist:"../dist",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}},Ge={name:"next",async apply(e,t){c(a.join(e,"next.config.mjs"),r=>{if(r.includes('output: "export"'))return r;if(!r.includes("const nextConfig = {}"))throw new u("next.config.mjs","Could not find the default Next.js config shape.");return r.replace("const nextConfig = {}",`const nextConfig = {
8
8
  output: "export",
9
9
  images: {
10
10
  unoptimized: true,
11
11
  },
12
- }`)}),f(i.join(e,"package.json"),r=>(r.scripts?.dev&&(r.scripts.dev=r.scripts.dev.replace("next dev --turbopack","next dev --turbopack -p 1420")),r))},tauriConfig(){return{frontendDist:"../out",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}};function Ge(e){if(e.includes("strictPort: true"))return e;const t=e.lastIndexOf(`
12
+ }`)}),f(a.join(e,"package.json"),r=>(r.scripts?.dev&&(r.scripts.dev=r.scripts.dev.replace("next dev --turbopack","next dev --turbopack -p 1420")),r))},tauriConfig(){return{frontendDist:"../out",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}};function Ne(e){if(e.includes("strictPort: true"))return e;const t=e.lastIndexOf(`
13
13
  })`);if(t===-1)throw new u("vite.config.ts","Could not find the React Router Vite config closing brace.");return`${e.slice(0,t)}
14
14
  server: {
15
15
  port: 1420,
16
16
  strictPort: true,
17
- },${e.slice(t)}`}const Ne={name:"react-router",async apply(e,t){c(i.join(e,"react-router.config.ts"),r=>{if(r.includes("ssr: false"))return r;if(!r.includes("ssr: true"))throw new u("react-router.config.ts","Could not find the SSR flag in the generated React Router config.");return r.replace("ssr: true","ssr: false")}),c(i.join(e,"vite.config.ts"),Ge)},tauriConfig(){return{frontendDist:"../build/client",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}};function Ie(e){if(e.includes("strictPort: true"))return e;const t=e.lastIndexOf(`
17
+ },${e.slice(t)}`}const Ie={name:"react-router",async apply(e,t){c(a.join(e,"react-router.config.ts"),r=>{if(r.includes("ssr: false"))return r;if(!r.includes("ssr: true"))throw new u("react-router.config.ts","Could not find the SSR flag in the generated React Router config.");return r.replace("ssr: true","ssr: false")}),c(a.join(e,"vite.config.ts"),Ne)},tauriConfig(){return{frontendDist:"../build/client",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}};function Ae(e){if(e.includes("strictPort: true"))return e;const t=e.lastIndexOf(`
18
18
  })`);if(t===-1)throw new u("vite.config.ts","Could not find the TanStack Start config closing brace.");return`${e.slice(0,t)}
19
19
  server: {
20
20
  port: 1420,
21
21
  strictPort: true,
22
- },${e.slice(t)}`}const Ae={name:"start",async apply(e,t){c(i.join(e,"vite.config.ts"),r=>{let n=Ie(r);if(!n.includes("tanstackStart({ spa: { enabled: true } })")){if(!n.includes("tanstackStart(),"))throw new u("vite.config.ts","Could not find tanstackStart() in the generated Vite config.");n=n.replace("tanstackStart(),","tanstackStart({ spa: { enabled: true } }),")}return n}),f(i.join(e,"package.json"),r=>(r.scripts?.dev&&(r.scripts.dev=r.scripts.dev.replace("--port 3000","--port 1420")),r))},tauriConfig(){return{frontendDist:"../.output/public",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}};function Re(e){if(e.includes("strictPort: true"))return e;const t=e.lastIndexOf(`
22
+ },${e.slice(t)}`}const Re={name:"start",async apply(e,t){c(a.join(e,"vite.config.ts"),r=>{let n=Ae(r);if(!n.includes("tanstackStart({ spa: { enabled: true } })")){if(!n.includes("tanstackStart(),"))throw new u("vite.config.ts","Could not find tanstackStart() in the generated Vite config.");n=n.replace("tanstackStart(),","tanstackStart({ spa: { enabled: true } }),")}return n}),f(a.join(e,"package.json"),r=>(r.scripts?.dev&&(r.scripts.dev=r.scripts.dev.replace("--port 3000","--port 1420")),r))},tauriConfig(){return{frontendDist:"../.output/public",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}};function Oe(e){if(e.includes("strictPort: true"))return e;const t=e.lastIndexOf(`
23
23
  })`);if(t===-1)throw new u("vite.config.ts","Could not find the Vite config closing brace.");return`${e.slice(0,t)}
24
24
  server: {
25
25
  port: 1420,
26
26
  strictPort: true,
27
- },${e.slice(t)}`}const Oe={name:"vite",async apply(e,t){c(i.join(e,"vite.config.ts"),Re)},tauriConfig(){return{frontendDist:"../dist",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}},Fe={vite:Oe,next:Te,start:Ae,"react-router":Ne,astro:Ce};function Ue(e){return Fe[e]}async function te(e,t){try{await T("bunx",["--bun","shadcn@latest",...t],{cwd:e})}catch(r){throw r instanceof C?new v("shadcn","shadcn CLI failed while updating the frontend scaffold.",r.stderr||r.stdout):r}}const Ve="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E%3Crect width='96' height='96' rx='24' fill='%23181f2a'/%3E%3Ccircle cx='48' cy='34' r='18' fill='%23f8fafc'/%3E%3Cpath d='M18 82c6-14 18-22 30-22s24 8 30 22' fill='%23f8fafc'/%3E%3C/svg%3E";function Be(e){const t=i.join(e,"app/layout.tsx");s.existsSync(t)&&c(t,r=>{let n=r;if(n.includes('import { TooltipProvider } from "@/components/ui/tooltip"')||(n=n.replace(`import { ThemeProvider } from "@/components/theme-provider"
27
+ },${e.slice(t)}`}const ze={name:"vite",async apply(e,t){c(a.join(e,"vite.config.ts"),Oe)},tauriConfig(){return{frontendDist:"../dist",devUrl:"http://localhost:1420",beforeDevCommand:"bun run dev",beforeBuildCommand:"bun run build"}}},Fe={vite:ze,next:Ge,start:Re,"react-router":Ie,astro:Te};function Ue(e){return Fe[e]}async function te(e,t){try{await T("bunx",["--bun","shadcn@latest",...t],{cwd:e})}catch(r){throw r instanceof C?new x("shadcn","shadcn CLI failed while updating the frontend scaffold.",r.stderr||r.stdout):r}}const Ve="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E%3Crect width='96' height='96' rx='24' fill='%23181f2a'/%3E%3Ccircle cx='48' cy='34' r='18' fill='%23f8fafc'/%3E%3Cpath d='M18 82c6-14 18-22 30-22s24 8 30 22' fill='%23f8fafc'/%3E%3C/svg%3E";function Be(e){const t=a.join(e,"app/layout.tsx");s.existsSync(t)&&c(t,r=>{let n=r;if(n.includes('import { TooltipProvider } from "@/components/ui/tooltip"')||(n=n.replace(`import { ThemeProvider } from "@/components/theme-provider"
28
28
  `,`import { ThemeProvider } from "@/components/theme-provider"
29
29
  import { TooltipProvider } from "@/components/ui/tooltip"
30
- `)),n.includes("<TooltipProvider>"))return n;const a=n.replace(/<ThemeProvider>([\s\S]*?)<\/ThemeProvider>/,"<ThemeProvider><TooltipProvider>$1</TooltipProvider></ThemeProvider>");if(a===n)throw new u(t,"Could not wrap the Next.js layout with TooltipProvider.");return a})}function Me(e,t){const r=t.template==="next"?i.join(e,"components/app-sidebar.tsx"):t.template==="react-router"?i.join(e,"app/components/app-sidebar.tsx"):i.join(e,"src/components/app-sidebar.tsx");s.existsSync(r)&&c(r,n=>n.replace('avatar: "/avatars/shadcn.jpg"',`avatar: "${Ve}"`))}function x(e,t,r,n){return`import type { CSSProperties } from "react"
30
+ `)),n.includes("<TooltipProvider>"))return n;const i=n.replace(/<ThemeProvider>([\s\S]*?)<\/ThemeProvider>/,"<ThemeProvider><TooltipProvider>$1</TooltipProvider></ThemeProvider>");if(i===n)throw new u(t,"Could not wrap the Next.js layout with TooltipProvider.");return i})}function Me(e,t){const r=t.template==="next"?a.join(e,"components/app-sidebar.tsx"):t.template==="react-router"?a.join(e,"app/components/app-sidebar.tsx"):a.join(e,"src/components/app-sidebar.tsx");s.existsSync(r)&&c(r,n=>n.replace('avatar: "/avatars/shadcn.jpg"',`avatar: "${Ve}"`))}function b(e,t,r,n){return`import type { CSSProperties } from "react"
31
31
  import { AppSidebar } from "${t}components/app-sidebar"
32
32
  import { ChartAreaInteractive } from "${t}components/chart-area-interactive"
33
33
  import { DataTable } from "${t}components/data-table"
@@ -75,10 +75,10 @@ ${n?` <div className="px-4 lg:px-6">
75
75
  )
76
76
  }
77
77
  `}function He(e){return`import { createFileRoute } from "@tanstack/react-router"
78
- ${x("DashboardPage","@/","@/app/dashboard/data.json",e)}export const Route = createFileRoute("/")({
78
+ ${b("DashboardPage","@/","@/app/dashboard/data.json",e)}export const Route = createFileRoute("/")({
79
79
  component: DashboardPage,
80
80
  })
81
- `}function We(e){return x("DashboardShell","@/","@/app/dashboard/data.json",e).replace("export default function DashboardShell()","export function DashboardShell()")}function Ke(e,t){try{s.renameSync(e,t);return}catch(r){if(!(r instanceof Error)||!("code"in r)||r.code!=="EXDEV")throw r}s.cpSync(e,t,{recursive:!0,force:!0}),s.rmSync(e,{recursive:!0,force:!0})}async function ze(e){s.mkdirSync(i.dirname(e.targetDir),{recursive:!0});const t=ee("create-tauri-ui-frontend-"),r=i.join(t,e.projectName);F(t);try{if(await T("bunx",["--bun","shadcn@latest","init","--name",e.projectName,"--template",e.template,"--preset",e.preset,"--yes"],{cwd:t}),!s.existsSync(r))throw new Error("shadcn CLI did not produce the expected project directory.");s.rmSync(i.join(r,"node_modules"),{recursive:!0,force:!0}),Ke(r,e.targetDir)}catch(n){if(n instanceof C){g(t);try{h(t)}catch{}throw new v("shadcn","shadcn CLI failed while creating the frontend scaffold.",n.stderr||n.stdout)}g(t);try{h(t)}catch{}throw n}finally{g(t);try{h(t)}catch{}}return e.targetDir}async function Ye(e){const t=ee("create-tauri-ui-"),r="tauri-native";F(t);try{await T("bunx",["create-tauri-app",r,"--template","vanilla-ts","--manager","bun","--identifier",e.identifier,"--yes"],{cwd:t})}catch(n){if(n instanceof C){g(t);try{h(t)}catch{}throw new v("cta","create-tauri-app failed while creating the native scaffold.",n.stderr||n.stdout)}g(t);try{h(t)}catch{}throw n}return{tempDir:t,projectDir:i.join(t,r)}}async function Xe(e,t){await te(e,["add","dashboard-01","--yes"]),Me(e,t);const r=t.template==="next"?i.join(e,"components/site-header.tsx"):t.template==="react-router"?i.join(e,"app/components/site-header.tsx"):i.join(e,"src/components/site-header.tsx");switch(s.existsSync(r)&&c(r,n=>n.includes("<Button")?n:n.replace(/import \{ Button \} from ["'][@~]\/components\/ui\/button["']\n/,"")),t.template){case"next":Be(e),s.writeFileSync(i.join(e,"app/page.tsx"),x("Page","@/","@/app/dashboard/data.json",t.includeInvokeExample));return;case"vite":s.writeFileSync(i.join(e,"src/App.tsx"),x("App","@/","@/app/dashboard/data.json",t.includeInvokeExample));return;case"start":s.writeFileSync(i.join(e,"src/routes/index.tsx"),He(t.includeInvokeExample));return;case"react-router":s.writeFileSync(i.join(e,"app/routes/home.tsx"),x("Home","~/","~/dashboard/data.json",t.includeInvokeExample));return;case"astro":s.writeFileSync(i.join(e,"src/components/dashboard-shell.tsx"),We(t.includeInvokeExample)),s.writeFileSync(i.join(e,"src/pages/index.astro"),`---
81
+ `}function We(e){return b("DashboardShell","@/","@/app/dashboard/data.json",e).replace("export default function DashboardShell()","export function DashboardShell()")}function Ke(e,t){try{s.renameSync(e,t);return}catch(r){if(!(r instanceof Error)||!("code"in r)||r.code!=="EXDEV")throw r}s.cpSync(e,t,{recursive:!0,force:!0}),s.rmSync(e,{recursive:!0,force:!0})}async function Ye(e){s.mkdirSync(a.dirname(e.targetDir),{recursive:!0});const t=ee("create-tauri-ui-frontend-"),r=a.join(t,e.projectName);z(t);try{if(await T("bunx",["--bun","shadcn@latest","init","--name",e.projectName,"--template",e.template,"--preset",e.preset,"--yes"],{cwd:t}),!s.existsSync(r))throw new Error("shadcn CLI did not produce the expected project directory.");s.rmSync(a.join(r,"node_modules"),{recursive:!0,force:!0}),Ke(r,e.targetDir)}catch(n){if(n instanceof C){g(t);try{h(t)}catch{}throw new x("shadcn","shadcn CLI failed while creating the frontend scaffold.",n.stderr||n.stdout)}g(t);try{h(t)}catch{}throw n}finally{g(t);try{h(t)}catch{}}return e.targetDir}async function Je(e){const t=ee("create-tauri-ui-"),r="tauri-native";z(t);try{await T("bunx",["create-tauri-app",r,"--template","vanilla-ts","--manager","bun","--identifier",e.identifier,"--yes"],{cwd:t})}catch(n){if(n instanceof C){g(t);try{h(t)}catch{}throw new x("cta","create-tauri-app failed while creating the native scaffold.",n.stderr||n.stdout)}g(t);try{h(t)}catch{}throw n}return{tempDir:t,projectDir:a.join(t,r)}}async function Xe(e,t){await te(e,["add","dashboard-01","--yes"]),Me(e,t);const r=t.template==="next"?a.join(e,"components/site-header.tsx"):t.template==="react-router"?a.join(e,"app/components/site-header.tsx"):a.join(e,"src/components/site-header.tsx");switch(s.existsSync(r)&&c(r,n=>n.includes("<Button")?n:n.replace(/import \{ Button \} from ["'][@~]\/components\/ui\/button["']\n/,"")),t.template){case"next":Be(e),s.writeFileSync(a.join(e,"app/page.tsx"),b("Page","@/","@/app/dashboard/data.json",t.includeInvokeExample));return;case"vite":s.writeFileSync(a.join(e,"src/App.tsx"),b("App","@/","@/app/dashboard/data.json",t.includeInvokeExample));return;case"start":s.writeFileSync(a.join(e,"src/routes/index.tsx"),He(t.includeInvokeExample));return;case"react-router":s.writeFileSync(a.join(e,"app/routes/home.tsx"),b("Home","~/","~/dashboard/data.json",t.includeInvokeExample));return;case"astro":s.writeFileSync(a.join(e,"src/components/dashboard-shell.tsx"),We(t.includeInvokeExample)),s.writeFileSync(a.join(e,"src/pages/index.astro"),`---
82
82
  import Layout from "@/layouts/main.astro"
83
83
  import { DashboardShell } from "@/components/dashboard-shell"
84
84
  ---
@@ -87,8 +87,8 @@ import { DashboardShell } from "@/components/dashboard-shell"
87
87
  <DashboardShell client:load />
88
88
  </Layout>
89
89
  `);return;default:throw new u(e,`No starter UI implementation exists for template "${t.template}".`)}}async function re(e,t){await te(e,["add",t,"--yes"])}const G="use tauri_plugin_log::{Target, TargetKind};",ne=` log::info!("greet command executed for {}", name);
90
- `,ae=` log::info!("opening external link in system browser: {}", url);
91
- `,ie=` log::info!("main webview finished loading");
90
+ `,ie=` log::info!("opening external link in system browser: {}", url);
91
+ `,ae=` log::info!("main webview finished loading");
92
92
  `,qe=` .plugin(
93
93
  tauri_plugin_log::Builder::new()
94
94
  .targets([
@@ -98,33 +98,33 @@ import { DashboardShell } from "@/components/dashboard-shell"
98
98
  ])
99
99
  .build(),
100
100
  )
101
- `;function Je(){const e=i.dirname(R(import.meta.url)),t=[i.resolve(e,"../assets/debug-panel"),i.resolve(e,"../../assets/debug-panel")];return t.find(r=>s.existsSync(r))??t[0]}const Ze=Je();function Qe(e){return e==="react-router"?"~/":"@/"}function oe(e,t){switch(t.template){case"next":return i.join(e,"components");case"vite":case"start":case"astro":return i.join(e,"src/components");case"react-router":return i.join(e,"app/components")}}function et(e,t){switch(t.template){case"next":return i.join(e,"lib");case"vite":case"start":case"astro":return i.join(e,"src/lib");case"react-router":return i.join(e,"app/lib")}}function tt(e,t){return i.join(oe(e,t),"ui")}function b(e){switch(e.template){case"next":return"@/components/debug-panel";case"vite":return"./components/debug-panel.tsx";case"start":return"../components/debug-panel";case"react-router":return"./components/debug-panel";case"astro":return"@/components/debug-panel"}}async function rt(e,t){for(const r of["button","badge","dropdown-menu","tabs"]){const n=i.join(tt(e,t),`${r}.tsx`);s.existsSync(n)||await re(e,r)}}function U(e){return s.readFileSync(i.join(Ze,e),"utf-8")}function nt(e,t){const r=i.join(oe(e,t),"debug-panel.tsx"),n=et(e,t);s.mkdirSync(i.dirname(r),{recursive:!0}),s.mkdirSync(n,{recursive:!0}),s.writeFileSync(r,U("debug-panel.tsx.tmpl").split("__ALIAS_PREFIX__").join(Qe(t.template)),"utf-8"),s.writeFileSync(i.join(n,"debug-events.ts"),U("debug-events.ts.tmpl"),"utf-8"),s.writeFileSync(i.join(n,"tauri.ts"),U("tauri.ts.tmpl"),"utf-8")}function at(e){f(i.join(e,"package.json"),t=>(t.dependencies=t.dependencies||{},t.dependencies["@tauri-apps/plugin-log"]||(t.dependencies["@tauri-apps/plugin-log"]="^2"),t))}function se(e,t){if(e.includes(t))return e;const r=/\[dependencies\]\r?\n/;if(!r.test(e))throw new Error(`Could not find [dependencies] while inserting ${t}.`);return e.replace(r,`[dependencies]
101
+ `;function Ze(){const e=a.dirname(R(import.meta.url)),t=[a.resolve(e,"../assets/debug-panel"),a.resolve(e,"../../assets/debug-panel")];return t.find(r=>s.existsSync(r))??t[0]}const Qe=Ze();function et(e){return e==="react-router"?"~/":"@/"}function oe(e,t){switch(t.template){case"next":return a.join(e,"components");case"vite":case"start":case"astro":return a.join(e,"src/components");case"react-router":return a.join(e,"app/components")}}function tt(e,t){switch(t.template){case"next":return a.join(e,"lib");case"vite":case"start":case"astro":return a.join(e,"src/lib");case"react-router":return a.join(e,"app/lib")}}function rt(e,t){return a.join(oe(e,t),"ui")}function y(e){switch(e.template){case"next":return"@/components/debug-panel";case"vite":return"./components/debug-panel.tsx";case"start":return"../components/debug-panel";case"react-router":return"./components/debug-panel";case"astro":return"@/components/debug-panel"}}async function nt(e,t){for(const r of["button","badge","dropdown-menu","tabs"]){const n=a.join(rt(e,t),`${r}.tsx`);s.existsSync(n)||await re(e,r)}}function F(e){return s.readFileSync(a.join(Qe,e),"utf-8")}function it(e,t){const r=a.join(oe(e,t),"debug-panel.tsx"),n=tt(e,t);s.mkdirSync(a.dirname(r),{recursive:!0}),s.mkdirSync(n,{recursive:!0}),s.writeFileSync(r,F("debug-panel.tsx.tmpl").split("__ALIAS_PREFIX__").join(et(t.template)),"utf-8"),s.writeFileSync(a.join(n,"debug-events.ts"),F("debug-events.ts.tmpl"),"utf-8"),s.writeFileSync(a.join(n,"tauri.ts"),F("tauri.ts.tmpl"),"utf-8")}function at(e){f(a.join(e,"package.json"),t=>(t.dependencies=t.dependencies||{},t.dependencies["@tauri-apps/plugin-log"]||(t.dependencies["@tauri-apps/plugin-log"]="^2"),t))}function se(e,t){if(e.includes(t))return e;const r=/\[dependencies\]\r?\n/;if(!r.test(e))throw new Error(`Could not find [dependencies] while inserting ${t}.`);return e.replace(r,`[dependencies]
102
102
  ${t}
103
- `)}function it(e){const t=i.join(e,"src-tauri/Cargo.toml"),r=i.join(e,"src-tauri/src/lib.rs");c(t,n=>{let a=n;return a=se(a,'tauri-plugin-log = "2"'),a=se(a,'log = "0.4"'),a}),f(i.join(e,"src-tauri/capabilities/default.json"),n=>(n.permissions=Array.isArray(n.permissions)?n.permissions:[],n.permissions.includes("log:default")||n.permissions.push("log:default"),n)),c(r,n=>{let a=n;if(a.includes(G)||(a.includes(`use tauri_plugin_opener::OpenerExt;
104
- `)?a=a.replace(`use tauri_plugin_opener::OpenerExt;
103
+ `)}function ot(e){const t=a.join(e,"src-tauri/Cargo.toml"),r=a.join(e,"src-tauri/src/lib.rs");c(t,n=>{let i=n;return i=se(i,'tauri-plugin-log = "2"'),i=se(i,'log = "0.4"'),i}),f(a.join(e,"src-tauri/capabilities/default.json"),n=>(n.permissions=Array.isArray(n.permissions)?n.permissions:[],n.permissions.includes("log:default")||n.permissions.push("log:default"),n)),c(r,n=>{let i=n;if(i.includes(G)||(i.includes(`use tauri_plugin_opener::OpenerExt;
104
+ `)?i=i.replace(`use tauri_plugin_opener::OpenerExt;
105
105
  `,`use tauri_plugin_opener::OpenerExt;
106
106
  ${G}
107
- `):a.includes(`use tauri::webview::PageLoadEvent;
108
- `)?a=a.replace(`use tauri::webview::PageLoadEvent;
107
+ `):i.includes(`use tauri::webview::PageLoadEvent;
108
+ `)?i=i.replace(`use tauri::webview::PageLoadEvent;
109
109
  `,`use tauri::webview::PageLoadEvent;
110
110
  ${G}
111
- `):a=`${G}
112
- ${a}`),a.includes("tauri_plugin_log::Builder::new()"))return a;const o=a.replace(` tauri::Builder::default()
111
+ `):i=`${G}
112
+ ${i}`),i.includes("tauri_plugin_log::Builder::new()"))return i;const o=i.replace(` tauri::Builder::default()
113
113
  `,` tauri::Builder::default()
114
- ${qe}`);if(o===a)throw new u(r,"Could not register the Tauri log plugin.");return a=o,!a.includes(ne.trim())&&a.includes(` let message = format!("Hello, {}! You've been greeted from Rust!", name);
115
- `)&&(a=a.replace(` let message = format!("Hello, {}! You've been greeted from Rust!", name);
114
+ ${qe}`);if(o===i)throw new u(r,"Could not register the Tauri log plugin.");return i=o,!i.includes(ne.trim())&&i.includes(` let message = format!("Hello, {}! You've been greeted from Rust!", name);
115
+ `)&&(i=i.replace(` let message = format!("Hello, {}! You've been greeted from Rust!", name);
116
116
  `,` let message = format!("Hello, {}! You've been greeted from Rust!", name);
117
- ${ne}`)),!a.includes(ae.trim())&&a.includes(` let _ = webview.opener().open_url(url.as_str(), None::<&str>);
118
- `)&&(a=a.replace(` let _ = webview.opener().open_url(url.as_str(), None::<&str>);
119
- `,`${ae} let _ = webview.opener().open_url(url.as_str(), None::<&str>);
120
- `)),!a.includes(ie.trim())&&a.includes(` let _ = webview.window().show();
121
- `)&&(a=a.replace(` let _ = webview.window().show();
122
- `,`${ie} let _ = webview.window().show();
123
- `)),a})}function ot(e,t){const r=i.join(e,"src/main.tsx"),n=b(t);c(r,a=>{let o=a;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(/import { ExternalLinkGuard } from "\.\/components\/external-link-guard\.tsx"\r?\n/,`import { ExternalLinkGuard } from "./components/external-link-guard.tsx"
117
+ ${ne}`)),!i.includes(ie.trim())&&i.includes(` let _ = webview.opener().open_url(url.as_str(), None::<&str>);
118
+ `)&&(i=i.replace(` let _ = webview.opener().open_url(url.as_str(), None::<&str>);
119
+ `,`${ie} let _ = webview.opener().open_url(url.as_str(), None::<&str>);
120
+ `)),!i.includes(ae.trim())&&i.includes(` let _ = webview.window().show();
121
+ `)&&(i=i.replace(` let _ = webview.window().show();
122
+ `,`${ae} let _ = webview.window().show();
123
+ `)),i})}function st(e,t){const r=a.join(e,"src/main.tsx"),n=y(t);c(r,i=>{let o=i;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(/import { ExternalLinkGuard } from "\.\/components\/external-link-guard\.tsx"\r?\n/,`import { ExternalLinkGuard } from "./components/external-link-guard.tsx"
124
124
  import { DebugPanel } from "${n}"
125
125
  `)),o.includes("<DebugPanel />"))return o;const l=o.replace(/<ExternalLinkGuard \/>\r?\n(\s*)<main(?:\s+data-ui-scroll-container)?><App \/><\/main>/,`<ExternalLinkGuard />
126
126
  $1{import.meta.env.DEV ? <DebugPanel /> : null}
127
- $1<main data-ui-scroll-container><App /></main>`);if(l===o)throw new u(r,"Could not mount DebugPanel in the Vite entrypoint.");return l})}function st(e,t){const r=i.join(e,"app/layout.tsx"),n=b(t);c(r,a=>{let o=a;if(o.includes(`import { DebugPanel } from "${n}"`)||(o.includes(`import { ExternalLinkGuard } from "@/components/external-link-guard"
127
+ $1<main data-ui-scroll-container><App /></main>`);if(l===o)throw new u(r,"Could not mount DebugPanel in the Vite entrypoint.");return l})}function lt(e,t){const r=a.join(e,"app/layout.tsx"),n=y(t);c(r,i=>{let o=i;if(o.includes(`import { DebugPanel } from "${n}"`)||(o.includes(`import { ExternalLinkGuard } from "@/components/external-link-guard"
128
128
  `)?o=o.replace(`import { ExternalLinkGuard } from "@/components/external-link-guard"
129
129
  `,`import { ExternalLinkGuard } from "@/components/external-link-guard"
130
130
  import { DebugPanel } from "${n}"
@@ -132,19 +132,19 @@ import { DebugPanel } from "${n}"
132
132
  `)&&(o=o.replace(`import { ThemeProvider } from "@/components/theme-provider"
133
133
  `,`import { ThemeProvider } from "@/components/theme-provider"
134
134
  import { DebugPanel } from "${n}"
135
- `))),o.includes("<DebugPanel />"))return o;const l=o.replace(/<ExternalLinkGuard \/>\{children\}/,'<ExternalLinkGuard />{process.env.NODE_ENV === "development" ? <DebugPanel /> : null}{children}');if(l===o)throw new u(r,"Could not mount DebugPanel in the Next.js layout.");return l})}function lt(e,t){const r=i.join(e,"src/routes/__root.tsx"),n=b(t);c(r,a=>{let o=a;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(`import { ExternalLinkGuard } from "../components/external-link-guard"
135
+ `))),o.includes("<DebugPanel />"))return o;const l=o.replace(/<ExternalLinkGuard \/>\{children\}/,'<ExternalLinkGuard />{process.env.NODE_ENV === "development" ? <DebugPanel /> : null}{children}');if(l===o)throw new u(r,"Could not mount DebugPanel in the Next.js layout.");return l})}function ct(e,t){const r=a.join(e,"src/routes/__root.tsx"),n=y(t);c(r,i=>{let o=i;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(`import { ExternalLinkGuard } from "../components/external-link-guard"
136
136
  `,`import { ExternalLinkGuard } from "../components/external-link-guard"
137
137
  import { DebugPanel } from "${n}"
138
- `)),o.includes("<DebugPanel />"))return o;const l=o.replace(/<main(?:\s+data-ui-scroll-container)?><ExternalLinkGuard \/>\{children\}<\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard />{import.meta.env.DEV ? <DebugPanel /> : null}{children}</main>");if(l===o)throw new u(r,"Could not mount DebugPanel in the TanStack Start root route.");return l})}function ct(e,t){const r=i.join(e,"app/root.tsx"),n=b(t);c(r,a=>{let o=a;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(`import { ExternalLinkGuard } from "./components/external-link-guard"
138
+ `)),o.includes("<DebugPanel />"))return o;const l=o.replace(/<main(?:\s+data-ui-scroll-container)?><ExternalLinkGuard \/>\{children\}<\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard />{import.meta.env.DEV ? <DebugPanel /> : null}{children}</main>");if(l===o)throw new u(r,"Could not mount DebugPanel in the TanStack Start root route.");return l})}function ut(e,t){const r=a.join(e,"app/root.tsx"),n=y(t);c(r,i=>{let o=i;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(`import { ExternalLinkGuard } from "./components/external-link-guard"
139
139
  `,`import { ExternalLinkGuard } from "./components/external-link-guard"
140
140
  import { DebugPanel } from "${n}"
141
141
  `)),o.includes("<DebugPanel />"))return o;const l=o.replace(`<ExternalLinkGuard />
142
142
  {children}`,`<ExternalLinkGuard />
143
143
  {import.meta.env.DEV ? <DebugPanel /> : null}
144
- {children}`);if(l===o)throw new u(r,"Could not mount DebugPanel in the React Router root layout.");return l})}function ut(e,t){const r=i.join(e,"src/layouts/main.astro"),n=b(t);c(r,a=>{let o=a;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(`import { ExternalLinkGuard } from "@/components/external-link-guard"
144
+ {children}`);if(l===o)throw new u(r,"Could not mount DebugPanel in the React Router root layout.");return l})}function pt(e,t){const r=a.join(e,"src/layouts/main.astro"),n=y(t);c(r,i=>{let o=i;if(o.includes(`import { DebugPanel } from "${n}"`)||(o=o.replace(`import { ExternalLinkGuard } from "@/components/external-link-guard"
145
145
  `,`import { ExternalLinkGuard } from "@/components/external-link-guard"
146
146
  import { DebugPanel } from "${n}"
147
- `)),o.includes("<DebugPanel client:load />"))return o;const l=o.replace(/<main(?:\s+data-ui-scroll-container)?><ExternalLinkGuard client:load \/><slot \/><\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard client:load />{import.meta.env.DEV ? <DebugPanel client:load /> : null}<slot /></main>");if(l===o)throw new u(r,"Could not mount DebugPanel in the Astro layout.");return l})}async function pt(e,t){switch(await rt(e,t),nt(e,t),at(e),it(e),t.template){case"vite":ot(e,t);return;case"next":st(e,t);return;case"start":lt(e,t);return;case"react-router":ct(e,t);return;case"astro":ut(e,t);return}}const V="use tauri_plugin_opener::OpenerExt;",dt=`fn external_navigation_plugin<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
147
+ `)),o.includes("<DebugPanel client:load />"))return o;const l=o.replace(/<main(?:\s+data-ui-scroll-container)?><ExternalLinkGuard client:load \/><slot \/><\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard client:load />{import.meta.env.DEV ? <DebugPanel client:load /> : null}<slot /></main>");if(l===o)throw new u(r,"Could not mount DebugPanel in the Astro layout.");return l})}async function dt(e,t){switch(await nt(e,t),it(e,t),at(e),ot(e),t.template){case"vite":st(e,t);return;case"next":lt(e,t);return;case"start":ct(e,t);return;case"react-router":ut(e,t);return;case"astro":pt(e,t);return}}const U="use tauri_plugin_opener::OpenerExt;",mt=`fn external_navigation_plugin<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
148
148
  tauri::plugin::Builder::<R>::new("external-navigation")
149
149
  .on_navigation(|webview, url| {
150
150
  let is_internal_host = matches!(
@@ -170,7 +170,7 @@ import { DebugPanel } from "${n}"
170
170
  .build()
171
171
  }
172
172
 
173
- `,mt=`"use client"
173
+ `,ft=`"use client"
174
174
 
175
175
  import { useEffect } from "react"
176
176
 
@@ -271,49 +271,49 @@ export function ExternalLinkGuard() {
271
271
 
272
272
  return null
273
273
  }
274
- `;function y(e){s.mkdirSync(i.dirname(e),{recursive:!0}),s.writeFileSync(e,mt,"utf-8")}function ft(e){const t=i.join(e,"src-tauri/src/lib.rs");c(t,r=>{let n=r;if(n.includes(V)||(n.includes(`use tauri::webview::PageLoadEvent;
274
+ `;function k(e){s.mkdirSync(a.dirname(e),{recursive:!0}),s.writeFileSync(e,ft,"utf-8")}function gt(e){const t=a.join(e,"src-tauri/src/lib.rs");c(t,r=>{let n=r;if(n.includes(U)||(n.includes(`use tauri::webview::PageLoadEvent;
275
275
  `)?n=n.replace(`use tauri::webview::PageLoadEvent;
276
276
  `,`use tauri::webview::PageLoadEvent;
277
- ${V}
278
- `):n=`${V}
279
- ${n}`),!n.includes("fn external_navigation_plugin<R: tauri::Runtime>()")){if(!n.includes("#[cfg_attr(mobile, tauri::mobile_entry_point)]"))throw new u(t,"Could not find the Tauri entry point while inserting the external navigation plugin.");n=n.replace("#[cfg_attr(mobile, tauri::mobile_entry_point)]",`${dt}#[cfg_attr(mobile, tauri::mobile_entry_point)]`)}if(!n.includes(".plugin(external_navigation_plugin())")){if(!n.includes(".plugin(tauri_plugin_opener::init())"))throw new u(t,"Could not find the opener plugin while inserting the external navigation guard.");n=n.replace(` .plugin(tauri_plugin_opener::init())
277
+ ${U}
278
+ `):n=`${U}
279
+ ${n}`),!n.includes("fn external_navigation_plugin<R: tauri::Runtime>()")){if(!n.includes("#[cfg_attr(mobile, tauri::mobile_entry_point)]"))throw new u(t,"Could not find the Tauri entry point while inserting the external navigation plugin.");n=n.replace("#[cfg_attr(mobile, tauri::mobile_entry_point)]",`${mt}#[cfg_attr(mobile, tauri::mobile_entry_point)]`)}if(!n.includes(".plugin(external_navigation_plugin())")){if(!n.includes(".plugin(tauri_plugin_opener::init())"))throw new u(t,"Could not find the opener plugin while inserting the external navigation guard.");n=n.replace(` .plugin(tauri_plugin_opener::init())
280
280
  `,` .plugin(tauri_plugin_opener::init())
281
281
  .plugin(external_navigation_plugin())
282
- `)}return n})}function gt(e){const t=i.join(e,"src/components/external-link-guard.tsx"),r=i.join(e,"src/main.tsx");y(t),c(r,n=>{let a=n;if(a.includes('import { ExternalLinkGuard } from "./components/external-link-guard.tsx"')||(a=a.replace(/import { ThemeProvider } from "@\/components\/theme-provider\.tsx"\r?\n/,`import { ThemeProvider } from "@/components/theme-provider.tsx"
282
+ `)}return n})}function ht(e){const t=a.join(e,"src/components/external-link-guard.tsx"),r=a.join(e,"src/main.tsx");k(t),c(r,n=>{let i=n;if(i.includes('import { ExternalLinkGuard } from "./components/external-link-guard.tsx"')||(i=i.replace(/import { ThemeProvider } from "@\/components\/theme-provider\.tsx"\r?\n/,`import { ThemeProvider } from "@/components/theme-provider.tsx"
283
283
  import { ExternalLinkGuard } from "./components/external-link-guard.tsx"
284
- `)),a.includes("<ExternalLinkGuard />"))return a;const o=a.replace(/<ThemeProvider>\r?\n(\s*)<main(?:\s+data-ui-scroll-container)?><App \/><\/main>/,`<ThemeProvider>
284
+ `)),i.includes("<ExternalLinkGuard />"))return i;const o=i.replace(/<ThemeProvider>\r?\n(\s*)<main(?:\s+data-ui-scroll-container)?><App \/><\/main>/,`<ThemeProvider>
285
285
  $1<ExternalLinkGuard />
286
- $1<main data-ui-scroll-container><App /></main>`);if(o===a)throw new u(r,"Could not mount ExternalLinkGuard in the Vite entrypoint.");return o})}function ht(e){const t=i.join(e,"components/external-link-guard.tsx"),r=i.join(e,"app/layout.tsx");y(t),c(r,n=>{let a=n;if(a.includes('import { ExternalLinkGuard } from "@/components/external-link-guard"')||(a.includes(`import { TooltipProvider } from "@/components/ui/tooltip"
287
- `)?a=a.replace(`import { TooltipProvider } from "@/components/ui/tooltip"
286
+ $1<main data-ui-scroll-container><App /></main>`);if(o===i)throw new u(r,"Could not mount ExternalLinkGuard in the Vite entrypoint.");return o})}function wt(e){const t=a.join(e,"components/external-link-guard.tsx"),r=a.join(e,"app/layout.tsx");k(t),c(r,n=>{let i=n;if(i.includes('import { ExternalLinkGuard } from "@/components/external-link-guard"')||(i.includes(`import { TooltipProvider } from "@/components/ui/tooltip"
287
+ `)?i=i.replace(`import { TooltipProvider } from "@/components/ui/tooltip"
288
288
  `,`import { TooltipProvider } from "@/components/ui/tooltip"
289
289
  import { ExternalLinkGuard } from "@/components/external-link-guard"
290
- `):a=a.replace(`import { ThemeProvider } from "@/components/theme-provider"
290
+ `):i=i.replace(`import { ThemeProvider } from "@/components/theme-provider"
291
291
  `,`import { ThemeProvider } from "@/components/theme-provider"
292
292
  import { ExternalLinkGuard } from "@/components/external-link-guard"
293
- `)),a.includes("<ExternalLinkGuard />"))return a;const o=a.replace(/<main(?:\s+data-ui-scroll-container)?>\{children\}<\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard />{children}</main>");if(o===a)throw new u(r,"Could not mount ExternalLinkGuard in the Next.js layout.");return o})}function wt(e){const t=i.join(e,"src/components/external-link-guard.tsx"),r=i.join(e,"src/routes/__root.tsx");y(t),c(r,n=>{let a=n;if(a.includes('import { ExternalLinkGuard } from "../components/external-link-guard"')||(a=a.replace(`import appCss from "../styles.css?url"
293
+ `)),i.includes("<ExternalLinkGuard />"))return i;const o=i.replace(/<main(?:\s+data-ui-scroll-container)?>\{children\}<\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard />{children}</main>");if(o===i)throw new u(r,"Could not mount ExternalLinkGuard in the Next.js layout.");return o})}function vt(e){const t=a.join(e,"src/components/external-link-guard.tsx"),r=a.join(e,"src/routes/__root.tsx");k(t),c(r,n=>{let i=n;if(i.includes('import { ExternalLinkGuard } from "../components/external-link-guard"')||(i=i.replace(`import appCss from "../styles.css?url"
294
294
  `,`import appCss from "../styles.css?url"
295
295
  import { ExternalLinkGuard } from "../components/external-link-guard"
296
- `)),a.includes("<ExternalLinkGuard />"))return a;const o=a.replace(/<main(?:\s+data-ui-scroll-container)?>\{children\}<\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard />{children}</main>");if(o===a)throw new u(r,"Could not mount ExternalLinkGuard in the TanStack Start root route.");return o})}function vt(e){const t=i.join(e,"app/components/external-link-guard.tsx"),r=i.join(e,"app/root.tsx");y(t),c(r,n=>{let a=n;if(a.includes('import { ExternalLinkGuard } from "./components/external-link-guard"')||(a=a.replace(`import "./app.css"
296
+ `)),i.includes("<ExternalLinkGuard />"))return i;const o=i.replace(/<main(?:\s+data-ui-scroll-container)?>\{children\}<\/main>/,"<main data-ui-scroll-container><ExternalLinkGuard />{children}</main>");if(o===i)throw new u(r,"Could not mount ExternalLinkGuard in the TanStack Start root route.");return o})}function xt(e){const t=a.join(e,"app/components/external-link-guard.tsx"),r=a.join(e,"app/root.tsx");k(t),c(r,n=>{let i=n;if(i.includes('import { ExternalLinkGuard } from "./components/external-link-guard"')||(i=i.replace(`import "./app.css"
297
297
  `,`import "./app.css"
298
298
  import { ExternalLinkGuard } from "./components/external-link-guard"
299
- `)),a.includes("<ExternalLinkGuard />"))return a;const o=a.replace(`<body>
299
+ `)),i.includes("<ExternalLinkGuard />"))return i;const o=i.replace(`<body>
300
300
  {children}`,`<body>
301
301
  <ExternalLinkGuard />
302
- {children}`);if(o===a)throw new u(r,"Could not mount ExternalLinkGuard in the React Router root layout.");return o})}function xt(e){const t=i.join(e,"src/components/external-link-guard.tsx"),r=i.join(e,"src/layouts/main.astro");y(t),c(r,n=>{let a=n;if(a.includes('import { ExternalLinkGuard } from "@/components/external-link-guard"')||(a=a.replace(`import "@/styles/global.css"
302
+ {children}`);if(o===i)throw new u(r,"Could not mount ExternalLinkGuard in the React Router root layout.");return o})}function bt(e){const t=a.join(e,"src/components/external-link-guard.tsx"),r=a.join(e,"src/layouts/main.astro");k(t),c(r,n=>{let i=n;if(i.includes('import { ExternalLinkGuard } from "@/components/external-link-guard"')||(i=i.replace(`import "@/styles/global.css"
303
303
  `,`import "@/styles/global.css"
304
304
  import { ExternalLinkGuard } from "@/components/external-link-guard"
305
- `)),a.includes("<ExternalLinkGuard client:load />"))return a;const o=a.replace(/ <body>\n <main(?:\s+data-ui-scroll-container)?><slot \/><\/main>\n <\/body>/,` <body>
305
+ `)),i.includes("<ExternalLinkGuard client:load />"))return i;const o=i.replace(/ <body>\n <main(?:\s+data-ui-scroll-container)?><slot \/><\/main>\n <\/body>/,` <body>
306
306
  <main data-ui-scroll-container><ExternalLinkGuard client:load /><slot /></main>
307
- </body>`);if(o===a)throw new u(r,"Could not mount ExternalLinkGuard in the Astro layout.");return o})}async function bt(e,t){switch(ft(e),t.template){case"vite":gt(e);return;case"next":ht(e);return;case"start":wt(e);return;case"react-router":vt(e);return;case"astro":xt(e);return}}const B="use tauri::webview::PageLoadEvent;",yt=` .on_page_load(|webview, payload| {
307
+ </body>`);if(o===i)throw new u(r,"Could not mount ExternalLinkGuard in the Astro layout.");return o})}async function yt(e,t){switch(gt(e),t.template){case"vite":ht(e);return;case"next":wt(e);return;case"start":vt(e);return;case"react-router":xt(e);return;case"astro":bt(e);return}}const V="use tauri::webview::PageLoadEvent;",kt=` .on_page_load(|webview, payload| {
308
308
  if webview.label() == "main" && matches!(payload.event(), PageLoadEvent::Finished) {
309
309
  let _ = webview.window().show();
310
310
  }
311
311
  })
312
- `;async function kt(e){f(i.join(e,"src-tauri/tauri.conf.json"),r=>{r.app=r.app||{},r.app.windows=Array.isArray(r.app.windows)?r.app.windows:[{}];const n=r.app.windows[0]||{};return r.app.windows[0]={...n,visible:!1},r});const t=i.join(e,"src-tauri/src/lib.rs");c(t,r=>{let n=r;if(n.includes(B)||(n.includes(`use tauri::Manager;
312
+ `;async function $t(e){f(a.join(e,"src-tauri/tauri.conf.json"),r=>{r.app=r.app||{},r.app.windows=Array.isArray(r.app.windows)?r.app.windows:[{}];const n=r.app.windows[0]||{};return r.app.windows[0]={...n,visible:!1},r});const t=a.join(e,"src-tauri/src/lib.rs");c(t,r=>{let n=r;if(n.includes(V)||(n.includes(`use tauri::Manager;
313
313
  `)?n=n.replace(`use tauri::Manager;
314
- `,`${B}
315
- `):n=`${B}
316
- ${n}`),n.includes(".on_page_load("))return n;const a=n.replace(" .run(tauri::generate_context!())",`${yt} .run(tauri::generate_context!())`);if(a===n)throw new u(t,"Could not insert the startup flash-prevention page-load hook.");return a})}function $t(){const e=i.dirname(R(import.meta.url)),t=[i.resolve(e,"../assets"),i.resolve(e,"../../assets")];return t.find(r=>s.existsSync(r))??t[0]}const Et=$t();async function jt(e){J(i.join(Et,"app-icon.png"),i.join(e,"app-icon.png"))}function St(e){return`"use client"
314
+ `,`${V}
315
+ `):n=`${V}
316
+ ${n}`),n.includes(".on_page_load("))return n;const i=n.replace(" .run(tauri::generate_context!())",`${kt} .run(tauri::generate_context!())`);if(i===n)throw new u(t,"Could not insert the startup flash-prevention page-load hook.");return i})}function Et(){const e=a.dirname(R(import.meta.url)),t=[a.resolve(e,"../assets"),a.resolve(e,"../../assets")];return t.find(r=>s.existsSync(r))??t[0]}const jt=Et();async function St(e){q(a.join(jt,"app-icon.png"),a.join(e,"app-icon.png"))}function _t(e){return`"use client"
317
317
 
318
318
  import { useState } from "react"
319
319
  import { Button } from "${e}components/ui/button"
@@ -356,7 +356,7 @@ export function Greet() {
356
356
  </div>
357
357
  )
358
358
  }
359
- `}function _t(e,t){return`import { Greet } from "${t}components/greet"
359
+ `}function Pt(e,t){return`import { Greet } from "${t}components/greet"
360
360
 
361
361
  export default function ${e}() {
362
362
  return (
@@ -373,7 +373,7 @@ export default function ${e}() {
373
373
  </div>
374
374
  )
375
375
  }
376
- `}function Pt(){return`import { createFileRoute } from "@tanstack/react-router"
376
+ `}function Dt(){return`import { createFileRoute } from "@tanstack/react-router"
377
377
  import { Greet } from "@/components/greet"
378
378
 
379
379
  export const Route = createFileRoute("/")({
@@ -395,7 +395,7 @@ function Home() {
395
395
  </div>
396
396
  )
397
397
  }
398
- `}function le(e,t){switch(e){case"next":return i.join(t,"app/page.tsx");case"vite":return i.join(t,"src/App.tsx");case"start":return i.join(t,"src/routes/index.tsx");case"react-router":return i.join(t,"app/routes/home.tsx");case"astro":return i.join(t,"src/pages/index.astro")}}function Dt(e,t){switch(e){case"next":return i.join(t,"components/greet.tsx");case"vite":case"start":case"astro":return i.join(t,"src/components/greet.tsx");case"react-router":return i.join(t,"app/components/greet.tsx")}}async function Lt(e,t){t.includeStarterUI||await re(e,"input");const r=t.template==="react-router"?"~/":"@/";if(s.writeFileSync(Dt(t.template,e),St(r)),t.includeStarterUI)return;if(t.template==="astro"){s.writeFileSync(le(t.template,e),`---
398
+ `}function le(e,t){switch(e){case"next":return a.join(t,"app/page.tsx");case"vite":return a.join(t,"src/App.tsx");case"start":return a.join(t,"src/routes/index.tsx");case"react-router":return a.join(t,"app/routes/home.tsx");case"astro":return a.join(t,"src/pages/index.astro")}}function Lt(e,t){switch(e){case"next":return a.join(t,"components/greet.tsx");case"vite":case"start":case"astro":return a.join(t,"src/components/greet.tsx");case"react-router":return a.join(t,"app/components/greet.tsx")}}async function Ct(e,t){t.includeStarterUI||await re(e,"input");const r=t.template==="react-router"?"~/":"@/";if(s.writeFileSync(Lt(t.template,e),_t(r)),t.includeStarterUI)return;if(t.template==="astro"){s.writeFileSync(le(t.template,e),`---
399
399
  import Layout from "@/layouts/main.astro"
400
400
  import { Greet } from "@/components/greet"
401
401
  ---
@@ -413,7 +413,7 @@ import { Greet } from "@/components/greet"
413
413
  </div>
414
414
  </div>
415
415
  </Layout>
416
- `);return}const n=t.template==="start"?Pt():_t(t.template==="vite"?"App":t.template==="react-router"?"Home":"Page",r);s.writeFileSync(le(t.template,e),n)}const m="data-ui-scroll-container",k=`
416
+ `);return}const n=t.template==="start"?Dt():Pt(t.template==="vite"?"App":t.template==="react-router"?"Home":"Page",r);s.writeFileSync(le(t.template,e),n)}const m="data-ui-scroll-container",$=`
417
417
  /* Disable page-level overscroll and rubber-band scrolling so the UI feels more desktop-native. */
418
418
  html,
419
419
  body {
@@ -432,13 +432,13 @@ body {
432
432
  overflow-y: auto;
433
433
  overscroll-behavior-y: none;
434
434
  }
435
- `,Ct=`
435
+ `,Tt=`
436
436
  /* Vite mounts into #root, so it needs to inherit the full-height desktop shell. */
437
437
  #root {
438
438
  height: 100%;
439
439
  }
440
- `;function $(e,t){c(e,r=>r.includes("Disable page-level overscroll and rubber-band scrolling")?r:`${r.trimEnd()}
441
- ${t}`)}function E(e,t,r){c(e,n=>{if(n.includes(`<main ${m}>`))return n;const a=n.replace(new RegExp(`<main(?![^>]*${m})([^>]*)>`),`<main ${m}$1>`);if(a!==n)return a;const o=n.replace(t,r);if(o===n)throw new u(e,"Could not insert the scroll container <main> wrapper.");return o})}async function Tt(e,t){switch(t.template){case"next":$(i.join(e,"app/globals.css"),k),E(i.join(e,"app/layout.tsx"),/\{children\}/,`<main ${m}>{children}</main>`);return;case"vite":$(i.join(e,"src/index.css"),`${k}${Ct}`),E(i.join(e,"src/main.tsx"),/<App \/>/,`<main ${m}><App /></main>`);return;case"start":$(i.join(e,"src/styles.css"),k),E(i.join(e,"src/routes/__root.tsx"),/\{children\}/,`<main ${m}>{children}</main>`);return;case"react-router":$(i.join(e,"app/app.css"),k),E(i.join(e,"app/root.tsx"),/return <Outlet \/>/,`return <main ${m}><Outlet /></main>`);return;case"astro":$(i.join(e,"src/styles/global.css"),k),E(i.join(e,"src/layouts/main.astro"),/<slot \/>/,`<main ${m}><slot /></main>`);return;default:throw new u(e,`No scroll container battery implementation exists for template "${t.template}".`)}}const j=`
440
+ `;function E(e,t){c(e,r=>r.includes("Disable page-level overscroll and rubber-band scrolling")?r:`${r.trimEnd()}
441
+ ${t}`)}function j(e,t,r){c(e,n=>{if(n.includes(`<main ${m}>`))return n;const i=n.replace(new RegExp(`<main(?![^>]*${m})([^>]*)>`),`<main ${m}$1>`);if(i!==n)return i;const o=n.replace(t,r);if(o===n)throw new u(e,"Could not insert the scroll container <main> wrapper.");return o})}async function Gt(e,t){switch(t.template){case"next":E(a.join(e,"app/globals.css"),$),j(a.join(e,"app/layout.tsx"),/\{children\}/,`<main ${m}>{children}</main>`);return;case"vite":E(a.join(e,"src/index.css"),`${$}${Tt}`),j(a.join(e,"src/main.tsx"),/<App \/>/,`<main ${m}><App /></main>`);return;case"start":E(a.join(e,"src/styles.css"),$),j(a.join(e,"src/routes/__root.tsx"),/\{children\}/,`<main ${m}>{children}</main>`);return;case"react-router":E(a.join(e,"app/app.css"),$),j(a.join(e,"app/root.tsx"),/return <Outlet \/>/,`return <main ${m}><Outlet /></main>`);return;case"astro":E(a.join(e,"src/styles/global.css"),$),j(a.join(e,"src/layouts/main.astro"),/<slot \/>/,`<main ${m}><slot /></main>`);return;default:throw new u(e,`No scroll container battery implementation exists for template "${t.template}".`)}}const S=`
442
442
  @layer base {
443
443
  /* Desktop UIs often feel cleaner with accidental text selection disabled by default. */
444
444
  body {
@@ -461,16 +461,30 @@ ${t}`)}function E(e,t,r){c(e,n=>{if(n.includes(`<main ${m}>`))return n;const a=n
461
461
  @apply select-text;
462
462
  }
463
463
  }
464
- `;function S(e,t){c(e,r=>r.includes(".ui-selectable")?r:`${r.trimEnd()}
465
- ${t}`)}async function Gt(e,t){switch(t.template){case"next":S(i.join(e,"app/globals.css"),j);return;case"vite":S(i.join(e,"src/index.css"),j);return;case"start":S(i.join(e,"src/styles.css"),j);return;case"react-router":S(i.join(e,"app/app.css"),j);return;case"astro":S(i.join(e,"src/styles/global.css"),j);return;default:throw new u(e,`No selection behavior battery implementation exists for template "${t.template}".`)}}function Nt(){const e=i.dirname(R(import.meta.url)),t=[i.resolve(e,"../assets"),i.resolve(e,"../../assets")];return t.find(r=>s.existsSync(r))??t[0]}const It=Nt();function At(e){const t=[];for(const r of e){if(r==="macos-latest"){t.push({name:"macOS Apple Silicon",platform:"macos-latest",args:"--target aarch64-apple-darwin"},{name:"macOS Intel",platform:"macos-latest",args:"--target x86_64-apple-darwin"});continue}if(r==="ubuntu-latest"){t.push({name:"Linux",platform:"ubuntu-22.04",args:""});continue}t.push({name:"Windows",platform:r,args:""})}return t.map(r=>` - name: "${r.name}"
464
+ `;function _(e,t){c(e,r=>r.includes(".ui-selectable")?r:`${r.trimEnd()}
465
+ ${t}`)}async function Nt(e,t){switch(t.template){case"next":_(a.join(e,"app/globals.css"),S);return;case"vite":_(a.join(e,"src/index.css"),S);return;case"start":_(a.join(e,"src/styles.css"),S);return;case"react-router":_(a.join(e,"app/app.css"),S);return;case"astro":_(a.join(e,"src/styles/global.css"),S);return;default:throw new u(e,`No selection behavior battery implementation exists for template "${t.template}".`)}}const It=`
466
+ [profile.dev]
467
+ incremental = true # Compile your binary in smaller steps.
468
+
469
+ [profile.release]
470
+ codegen-units = 1 # Allows LLVM to perform better optimization.
471
+ lto = true # Enables link-time-optimizations.
472
+ opt-level = "s" # Prioritizes small binary size. Use \`3\` if you prefer speed.
473
+ panic = "abort" # Higher performance by disabling panic handlers.
474
+ strip = true # Ensures debug symbols are removed.
475
+ `;function At(e){const t=a.join(e,"src-tauri/Cargo.toml");c(t,r=>r.includes("[profile.release]")?r:`${r.trimEnd()}
476
+
477
+ ${It}`)}function Rt(e){f(a.join(e,"src-tauri/tauri.conf.json"),t=>(t.build=t.build||{},t.build.removeUnusedCommands=!0,t))}async function Ot(e,t){At(e),Rt(e)}function zt(){const e=a.dirname(R(import.meta.url)),t=[a.resolve(e,"../assets"),a.resolve(e,"../../assets")];return t.find(r=>s.existsSync(r))??t[0]}const Ft=zt();function Ut(e){const t=[];for(const r of e){if(r==="macos-latest"){t.push({name:"macOS Apple Silicon",platform:"macos-latest",args:"--target aarch64-apple-darwin"},{name:"macOS Intel",platform:"macos-latest",args:"--target x86_64-apple-darwin"});continue}if(r==="ubuntu-latest"){t.push({name:"Linux",platform:"ubuntu-22.04",args:""});continue}t.push({name:"Windows",platform:r,args:""})}return t.map(r=>` - name: "${r.name}"
466
478
  platform: "${r.platform}"
467
479
  args: "${r.args}"`).join(`
468
- `)}async function Rt(e,t){const r=s.readFileSync(i.join(It,"release.yml.tmpl"),"utf-8").replace("{{PLATFORMS}}",At(t.targetOS)),n=i.join(e,".github/workflows");s.mkdirSync(n,{recursive:!0}),s.writeFileSync(i.join(n,"release.yml"),r)}const Ot=1400,Ft=918;async function Ut(e,t,r){Q(i.join(t,"src-tauri"),i.join(e,"src-tauri")),f(i.join(e,"package.json"),o=>(o.name=r.packageName,o.dependencies=o.dependencies||{},o.dependencies["@tauri-apps/api"]="^2",o.dependencies["@tauri-apps/plugin-opener"]="^2",o.devDependencies=o.devDependencies||{},o.devDependencies["@tauri-apps/cli"]="^2",o.scripts=o.scripts||{},o.scripts.tauri="tauri",o));const n=Le(r.packageName),a=`${n}_lib`;c(i.join(e,"src-tauri/Cargo.toml"),o=>o.replace(/^name = "tauri-app"$/m,`name = "${n}"`).replace(/^name = "tauri_app_lib"$/m,`name = "${a}"`)),c(i.join(e,"src-tauri/src/main.rs"),o=>o.replace("tauri_app_lib::run()",`${a}::run()`))}async function Vt(e,t,r){f(i.join(e,"src-tauri/tauri.conf.json"),n=>{n.productName=t.projectName,n.identifier=t.identifier,n.build={...n.build,...r},n.app=n.app||{},n.app.windows=Array.isArray(n.app.windows)?n.app.windows:[{}];const a=n.app.windows[0]||{};return n.app.windows[0]={...a,title:t.projectName,center:!0,width:Ot,height:Ft},n})}const M=["vite","next","start","react-router","astro"],H=["windows-latest","macos-latest","ubuntu-latest"],ce="tauri-ui",ue="b0",Bt={vite:"Vite",next:"Next.js",start:"TanStack Start","react-router":"React Router",astro:"Astro"},Mt={"windows-latest":"Windows","macos-latest":"macOS (Apple Silicon, Intel)","ubuntu-22.04":"Linux","ubuntu-latest":"Linux"};function p(e,t="Operation cancelled"){if(ye(e))throw new Error(t);return e}function Ht(e){if(M.includes(e))return e;throw new Error(`Unsupported template "${e}". Expected one of: ${M.join(", ")}`)}function pe(e){try{return`com.${Z(Y.userInfo().username)||"example"}.${e}`}catch{return`com.example.${e}`}}function N(e){return{placeholder:e,defaultValue:e}}function de(e){const t=e.trim().replace(/^['"]|['"]$/g,"");return(t.match(/^(?:--preset(?:=|\s+))?(.+)$/)?.[1]??t).trim()}async function Wt(e,t=process.cwd()){let r=Se(e.targetDir);if(r||(r=p(await P({message:"Project name",...N(ce)}))),r||(r=ce),r===".")throw new Error("Scaffolding into the current directory is not supported yet.");const n=i.resolve(t,r),a=i.basename(n),o=O(a)?a:Z(a);if(s.existsSync(n)&&!_e(n))if(e.force)s.rmSync(n,{recursive:!0,force:!0});else{if(e.yes)throw new Error(`Target directory "${r}" is not empty. Re-run with --force to overwrite it.`);if(!p(await D({message:`Target directory "${r}" is not empty. Remove it and continue?`,initialValue:!1})))throw new Error("Operation cancelled");s.rmSync(n,{recursive:!0,force:!0})}let l=o;O(a)||(e.yes?l=o:l=p(await P({message:"Package name",...N(o),validate(d){if(!d||!O(d))return"Enter a valid package.json name"}})));const I=e.template?Ht(e.template):e.yes?"vite":p(await xe({message:"Frontend template",initialValue:"vite",options:M.map(d=>({value:d,label:Bt[d]}))})),fe=e.identifier?e.identifier:e.yes?pe(l):p(await P({message:"App identifier",...N(pe(l))})),ge=e.preset?de(e.preset):e.yes?ue:de(p(await P({message:"shadcn preset",...N(ue)}))),he=e.includeStarterUI??(e.yes?!0:p(await D({message:"Include starter UI?",initialValue:!0}))),we=e.includeInvokeExample??(e.yes?!0:p(await D({message:"Include Rust invoke example?",initialValue:!0}))),W=e.includeWorkflow??(e.yes?!0:p(await D({message:"Include GitHub release workflow?",initialValue:!0}))),ve=W?e.yes?[...H]:p(await be({message:"GitHub workflow target operating systems",initialValues:[...H],required:!0,options:H.map(d=>({value:d,label:Mt[d]??d}))})):[];return{projectName:a,packageName:l,template:I,identifier:fe,preset:ge,includeStarterUI:he,includeInvokeExample:we,includeWorkflow:W,targetOS:ve,targetDir:n}}function Kt(){console.log(`Usage: create-tauri-ui [target-dir] [options]
480
+ `)}async function Vt(e,t){const r=s.readFileSync(a.join(Ft,"release.yml.tmpl"),"utf-8").replace("{{PLATFORMS}}",Ut(t.targetOS)),n=a.join(e,".github/workflows");s.mkdirSync(n,{recursive:!0}),s.writeFileSync(a.join(n,"release.yml"),r)}const Bt=1400,Mt=918;async function Ht(e,t,r){Q(a.join(t,"src-tauri"),a.join(e,"src-tauri")),f(a.join(e,"package.json"),o=>(o.name=r.packageName,o.dependencies=o.dependencies||{},o.dependencies["@tauri-apps/api"]="^2",o.dependencies["@tauri-apps/plugin-opener"]="^2",o.devDependencies=o.devDependencies||{},o.devDependencies["@tauri-apps/cli"]="^2",o.scripts=o.scripts||{},o.scripts.tauri="tauri",o));const n=Ce(r.packageName),i=`${n}_lib`;c(a.join(e,"src-tauri/Cargo.toml"),o=>o.replace(/^name = "tauri-app"$/m,`name = "${n}"`).replace(/^name = "tauri_app_lib"$/m,`name = "${i}"`)),c(a.join(e,"src-tauri/src/main.rs"),o=>o.replace("tauri_app_lib::run()",`${i}::run()`))}async function Wt(e,t,r){f(a.join(e,"src-tauri/tauri.conf.json"),n=>{n.productName=t.projectName,n.identifier=t.identifier,n.build={...n.build,...r},n.app=n.app||{},n.app.windows=Array.isArray(n.app.windows)?n.app.windows:[{}];const i=n.app.windows[0]||{};return n.app.windows[0]={...i,title:t.projectName,center:!0,width:Bt,height:Mt},n})}const B=["vite","next","start","react-router","astro"],M=["windows-latest","macos-latest","ubuntu-latest"],ce="tauri-ui",ue="b0",Kt={vite:"Vite",next:"Next.js",start:"TanStack Start","react-router":"React Router",astro:"Astro"},Yt={"windows-latest":"Windows","macos-latest":"macOS (Apple Silicon, Intel)","ubuntu-22.04":"Linux","ubuntu-latest":"Linux"};function p(e,t="Operation cancelled"){if(ke(e))throw new Error(t);return e}function Jt(e){if(B.includes(e))return e;throw new Error(`Unsupported template "${e}". Expected one of: ${B.join(", ")}`)}function pe(e){try{return`com.${Z(Y.userInfo().username)||"example"}.${e}`}catch{return`com.example.${e}`}}function N(e){return{placeholder:e,defaultValue:e}}function de(e){const t=e.trim().replace(/^['"]|['"]$/g,"");return(t.match(/^(?:--preset(?:=|\s+))?(.+)$/)?.[1]??t).trim()}async function Xt(e,t=process.cwd()){let r=_e(e.targetDir);if(r||(r=p(await D({message:"Project name",...N(ce)}))),r||(r=ce),r===".")throw new Error("Scaffolding into the current directory is not supported yet.");const n=a.resolve(t,r),i=a.basename(n),o=O(i)?i:Z(i);if(s.existsSync(n)&&!Pe(n))if(e.force)s.rmSync(n,{recursive:!0,force:!0});else{if(e.yes)throw new Error(`Target directory "${r}" is not empty. Re-run with --force to overwrite it.`);if(!p(await w({message:`Target directory "${r}" is not empty. Remove it and continue?`,initialValue:!1})))throw new Error("Operation cancelled");s.rmSync(n,{recursive:!0,force:!0})}let l=o;O(i)||(e.yes?l=o:l=p(await D({message:"Package name",...N(o),validate(d){if(!d||!O(d))return"Enter a valid package.json name"}})));const I=e.template?Jt(e.template):e.yes?"vite":p(await be({message:"Frontend template",initialValue:"vite",options:B.map(d=>({value:d,label:Kt[d]}))})),fe=e.identifier?e.identifier:e.yes?pe(l):p(await D({message:"App identifier",...N(pe(l))})),ge=e.preset?de(e.preset):e.yes?ue:de(p(await D({message:"shadcn preset",...N(ue)}))),he=e.includeStarterUI??(e.yes?!0:p(await w({message:"Include starter UI?",initialValue:!0}))),we=e.includeSizeOptimization??(e.yes?!1:p(await w({message:"Optimize app size?",initialValue:!0}))),ve=e.includeInvokeExample??(e.yes?!0:p(await w({message:"Include Rust invoke example?",initialValue:!0}))),H=e.includeWorkflow??(e.yes?!0:p(await w({message:"Include GitHub release workflow?",initialValue:!0}))),xe=H?e.yes?[...M]:p(await ye({message:"GitHub workflow target operating systems",initialValues:[...M],required:!0,options:M.map(d=>({value:d,label:Yt[d]??d}))})):[];return{projectName:i,packageName:l,template:I,identifier:fe,preset:ge,includeSizeOptimization:we,includeStarterUI:he,includeInvokeExample:ve,includeWorkflow:H,targetOS:xe,targetDir:n}}function qt(){const e=new URL("../package.json",import.meta.url);return JSON.parse(s.readFileSync(e,"utf-8")).version??"0.0.0"}function Zt(){console.log(`Usage: create-tauri-ui [target-dir] [options]
469
481
 
470
482
  Options:
471
483
  -t, --template <name> vite | next | start | react-router | astro
472
484
  --identifier <value> set the Tauri app identifier
473
485
  --preset <value> set the shadcn preset (default: b0)
486
+ --size-optimize optimize the Tauri app for smaller release binaries
487
+ --no-size-optimize skip size optimization
474
488
  --starter include the starter dashboard
475
489
  --no-starter skip the starter dashboard
476
490
  --invoke-example include the Rust invoke example
@@ -479,5 +493,6 @@ Options:
479
493
  --no-workflow skip the GitHub release workflow
480
494
  -f, --force overwrite an existing target directory
481
495
  -y, --yes accept defaults
482
- -h, --help display help`)}function zt(e){const t={},r=[],n=(a,o)=>{const l=e[a+1];if(!l||l.startsWith("-"))throw new Error(`Missing value for ${o}`);return l};for(let a=0;a<e.length;a+=1){const o=e[a];switch(o){case"-h":case"--help":t.help=!0;break;case"-t":case"--template":t.template=n(a,o),a+=1;break;case"--identifier":t.identifier=n(a,o),a+=1;break;case"--preset":t.preset=n(a,o),a+=1;break;case"--starter":t.includeStarterUI=!0;break;case"--no-starter":t.includeStarterUI=!1;break;case"--invoke-example":case"--example":t.includeInvokeExample=!0;break;case"--no-invoke-example":case"--no-example":t.includeInvokeExample=!1;break;case"--workflow":t.includeWorkflow=!0;break;case"--no-workflow":t.includeWorkflow=!1;break;case"-f":case"--force":t.force=!0;break;case"-y":case"--yes":t.yes=!0;break;default:if(o.startsWith("-"))throw new Error(`Unknown flag: ${o}`);r.push(o)}}if(r.length>1)throw new Error("Only one target directory may be provided.");return r[0]&&(t.targetDir=r[0]),t}function Yt(e){const t=i.relative(_.cwd(),e)||".";return t.includes(" ")?`"${t}"`:t}function Xt(e){s.rmSync(i.join(e,"node_modules"),{recursive:!0,force:!0})}function qt(e,t){const r=[`cd ${Yt(e)}`,"bun install","bun run tauri dev","bunx tauri icon app-icon.png"];z(r.join(`
483
- `),"Next steps"),t&&z("Configure the GitHub release workflow secrets before publishing builds.","Release workflow")}async function Jt(){try{await T("bun",["--version"])}catch{throw new Error("bun is required. Install it from https://bun.sh.")}}function me(e){return e instanceof v?{message:e.message,detail:e.stderr.trim()}:e instanceof Error?{message:e.message,detail:""}:{message:"An unknown error occurred.",detail:""}}async function Zt(){const e=zt(_.argv.slice(2));if(e.help){Kt();return}ke(A.bold("create-tauri-ui")),await Jt();const t=await Wt(e);De(),F(t.targetDir);const r=$e();let n;try{r.start("Creating the shadcn frontend scaffold"),await ze(t),r.message("Creating the Tauri native scaffold");const a=await Ye(t);n=a.tempDir,r.message("Merging the native layer into the frontend project"),await Ut(t.targetDir,a.projectDir,t);const o=Ue(t.template);r.message(`Patching the ${t.template} project for Tauri`);try{await o.apply(t.targetDir,t)}catch(l){if(l instanceof u)w.warn(`${l.message} (${l.file})`);else throw l}if(await Vt(t.targetDir,t,o.tauriConfig()),r.message("Applying the startup flash-prevention battery"),await kt(t.targetDir),r.message("Applying the desktop scroll container battery"),await Tt(t.targetDir,t),r.message("Applying the external link guard battery"),await bt(t.targetDir,t),t.includeStarterUI){r.message("Installing the starter dashboard");try{await Xe(t.targetDir,t)}catch(l){if(l instanceof v)w.warn(l.message),l.stderr.trim()&&w.message(l.stderr.trim());else throw l}}r.message("Applying the development debug panel battery"),await pt(t.targetDir,t),r.message("Applying the desktop selection-behavior battery"),await Gt(t.targetDir,t),t.includeInvokeExample&&(r.message("Adding the Rust invoke example"),await Lt(t.targetDir,t)),t.includeWorkflow&&(r.message("Writing the GitHub release workflow"),await Rt(t.targetDir,t)),r.message("Copying the app icon source"),await jt(t.targetDir),Xt(t.targetDir),g(t.targetDir),r.stop("Project ready"),qt(t.targetDir,t.includeWorkflow),Ee(`Scaffolded ${A.cyan(t.projectName)} in ${A.dim(t.targetDir)}`)}catch(a){const o=me(a);r.error("Scaffolding failed"),K(o.message),o.detail&&w.message(o.detail),_.exitCode=1}finally{if(n){g(n);try{h(n)}catch{}}}}Zt().catch(e=>{const t=me(e);K(t.message),t.detail&&w.message(t.detail),_.exit(1)});
496
+ -v, --version display version
497
+ -h, --help display help`)}function Qt(e){const t={},r=[],n=(i,o)=>{const l=e[i+1];if(!l||l.startsWith("-"))throw new Error(`Missing value for ${o}`);return l};for(let i=0;i<e.length;i+=1){const o=e[i];switch(o){case"-h":case"--help":t.help=!0;break;case"-v":case"--version":t.version=!0;break;case"-t":case"--template":t.template=n(i,o),i+=1;break;case"--identifier":t.identifier=n(i,o),i+=1;break;case"--preset":t.preset=n(i,o),i+=1;break;case"--size-optimize":t.includeSizeOptimization=!0;break;case"--no-size-optimize":t.includeSizeOptimization=!1;break;case"--starter":t.includeStarterUI=!0;break;case"--no-starter":t.includeStarterUI=!1;break;case"--invoke-example":case"--example":t.includeInvokeExample=!0;break;case"--no-invoke-example":case"--no-example":t.includeInvokeExample=!1;break;case"--workflow":t.includeWorkflow=!0;break;case"--no-workflow":t.includeWorkflow=!1;break;case"-f":case"--force":t.force=!0;break;case"-y":case"--yes":t.yes=!0;break;default:if(o.startsWith("-"))throw new Error(`Unknown flag: ${o}`);r.push(o)}}if(r.length>1)throw new Error("Only one target directory may be provided.");return r[0]&&(t.targetDir=r[0]),t}function er(e){const t=a.relative(P.cwd(),e)||".";return t.includes(" ")?`"${t}"`:t}function tr(e){s.rmSync(a.join(e,"node_modules"),{recursive:!0,force:!0})}function rr(e,t){const r=[`cd ${er(e)}`,"bun install","bun run tauri dev","bunx tauri icon app-icon.png"];K(r.join(`
498
+ `),"Next steps"),t&&K("Configure the GitHub release workflow secrets before publishing builds.","Release workflow")}async function nr(){try{await T("bun",["--version"])}catch{throw new Error("bun is required. Install it from https://bun.sh.")}}function me(e){return e instanceof x?{message:e.message,detail:e.stderr.trim()}:e instanceof Error?{message:e.message,detail:""}:{message:"An unknown error occurred.",detail:""}}async function ir(){const e=Qt(P.argv.slice(2));if(e.help){Zt();return}if(e.version){console.log(qt());return}$e(A.bold("create-tauri-ui")),await nr();const t=await Xt(e);Le(),z(t.targetDir);const r=Ee();let n;try{r.start("Creating the shadcn frontend scaffold"),await Ye(t),r.message("Creating the Tauri native scaffold");const i=await Je(t);n=i.tempDir,r.message("Merging the native layer into the frontend project"),await Ht(t.targetDir,i.projectDir,t);const o=Ue(t.template);r.message(`Patching the ${t.template} project for Tauri`);try{await o.apply(t.targetDir,t)}catch(l){if(l instanceof u)v.warn(`${l.message} (${l.file})`);else throw l}if(await Wt(t.targetDir,t,o.tauriConfig()),t.includeSizeOptimization&&(r.message("Applying the app size optimization battery"),await Ot(t.targetDir,t)),r.message("Applying the startup flash-prevention battery"),await $t(t.targetDir),r.message("Applying the desktop scroll container battery"),await Gt(t.targetDir,t),r.message("Applying the external link guard battery"),await yt(t.targetDir,t),t.includeStarterUI){r.message("Installing the starter dashboard");try{await Xe(t.targetDir,t)}catch(l){if(l instanceof x)v.warn(l.message),l.stderr.trim()&&v.message(l.stderr.trim());else throw l}}r.message("Applying the development debug panel battery"),await dt(t.targetDir,t),r.message("Applying the desktop selection-behavior battery"),await Nt(t.targetDir,t),t.includeInvokeExample&&(r.message("Adding the Rust invoke example"),await Ct(t.targetDir,t)),t.includeWorkflow&&(r.message("Writing the GitHub release workflow"),await Vt(t.targetDir,t)),r.message("Copying the app icon source"),await St(t.targetDir),tr(t.targetDir),g(t.targetDir),r.stop("Project ready"),rr(t.targetDir,t.includeWorkflow),je(`Scaffolded ${A.cyan(t.projectName)} in ${A.dim(t.targetDir)}`)}catch(i){const o=me(i);r.error("Scaffolding failed"),W(o.message),o.detail&&v.message(o.detail),P.exitCode=1}finally{if(n){g(n);try{h(n)}catch{}}}}ir().catch(e=>{const t=me(e);W(t.message),t.detail&&v.message(t.detail),P.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tauri-ui",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "🦀 Create modern Tauri desktop apps in just a few simple steps.",
5
5
  "homepage": "https://github.com/agmmnn/tauri-ui",
6
6
  "bugs": {