create-rue 0.1.4 → 0.7.2

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.
@@ -2,7 +2,7 @@ import { computed, type FC, ref, useEffect, watch } from '@rue-js/rue'
2
2
  import { RouterLink, useRoute } from '@rue-js/router'
3
3
 
4
4
  const themeStorageKey = 'rue.base.theme'
5
- const headerToggleRoutes = ['/', '/report', '/todo']
5
+ const reportHeaderStorageKey = 'rue.base.report.hideHeader'
6
6
 
7
7
  const navItems = [
8
8
  { to: '/', label: '首页' },
@@ -80,21 +80,9 @@ const themeLabels: Record<string, string> = {
80
80
  sunset: '日落',
81
81
  }
82
82
 
83
- const getStoredTheme = () => {
84
- const storedTheme = localStorage.getItem(themeStorageKey)
85
- return storedTheme && themes.includes(storedTheme) ? storedTheme : 'light'
86
- }
87
-
88
- const getHeaderStorageKey = (path: string) => {
89
- if (path === '/') {
90
- return 'rue.base.header.hide.home'
91
- }
92
-
93
- return `rue.base.header.hide.${path.replace(/^\//, '')}`
94
- }
83
+ const getStoredTheme = () => localStorage.getItem(themeStorageKey) || 'light'
95
84
 
96
- const getStoredHeaderHidden = (path: string) =>
97
- localStorage.getItem(getHeaderStorageKey(path)) === '1'
85
+ const getStoredHeaderHidden = () => localStorage.getItem(reportHeaderStorageKey) === '1'
98
86
 
99
87
  const applyTheme = (theme: string) => {
100
88
  document.documentElement.setAttribute('data-theme', theme)
@@ -103,48 +91,20 @@ const applyTheme = (theme: string) => {
103
91
  const ThemePicker: FC = () => {
104
92
  const theme = ref(getStoredTheme())
105
93
 
106
- const applyThemeState = () => {
107
- const currentTheme = theme.value
108
- const themePicker = document.querySelector<HTMLSelectElement>('[data-base-theme-picker="true"]')
109
-
110
- applyTheme(currentTheme)
111
-
112
- if (themePicker && themePicker.value !== currentTheme) {
113
- themePicker.value = currentTheme
114
- }
115
- }
116
-
117
- const syncStoredTheme = () => {
118
- theme.value = getStoredTheme()
119
- requestAnimationFrame(applyThemeState)
120
- }
121
-
122
94
  useEffect(() => {
123
- localStorage.setItem(themeStorageKey, theme.value)
124
- requestAnimationFrame(applyThemeState)
125
-
126
- window.addEventListener('storage', syncStoredTheme)
127
- window.addEventListener('focus', syncStoredTheme)
128
- document.addEventListener('visibilitychange', syncStoredTheme)
129
-
130
- return () => {
131
- window.removeEventListener('storage', syncStoredTheme)
132
- window.removeEventListener('focus', syncStoredTheme)
133
- document.removeEventListener('visibilitychange', syncStoredTheme)
134
- }
135
- }, [])
95
+ applyTheme(theme.value)
96
+ })
136
97
 
137
98
  watch(
138
99
  () => theme.value,
139
100
  () => {
101
+ applyTheme(theme.value)
140
102
  localStorage.setItem(themeStorageKey, theme.value)
141
- requestAnimationFrame(applyThemeState)
142
103
  },
143
104
  )
144
105
 
145
106
  return (
146
107
  <select
147
- data-base-theme-picker="true"
148
108
  aria-label="切换主题"
149
109
  className="select select-bordered select-sm w-40"
150
110
  onChange={(event: Event) => {
@@ -152,7 +112,7 @@ const ThemePicker: FC = () => {
152
112
  }}
153
113
  >
154
114
  {themes.map((name) => (
155
- <option key={name} value={name}>
115
+ <option key={name} value={name} selected={theme.value === name}>
156
116
  {themeLabels[name] || name}
157
117
  </option>
158
118
  ))}
@@ -163,34 +123,11 @@ const ThemePicker: FC = () => {
163
123
  const Header: FC = () => {
164
124
  const route = useRoute()
165
125
  const currentPath = computed(() => route.get()?.path || '/')
166
- const navClass = (path: string) =>
167
- `btn btn-sm ${currentPath.get() === path ? 'btn-primary' : 'btn-ghost'}`.trim()
168
-
169
- const applyHeaderNavState = () => {
170
- const path = currentPath.get()
171
- const navLinks = document.querySelectorAll<HTMLElement>('[data-base-nav-path]')
172
-
173
- navLinks.forEach((link) => {
174
- const targetPath = link.dataset.baseNavPath || ''
175
- link.className = `btn btn-sm ${path === targetPath ? 'btn-primary' : 'btn-ghost'}`.trim()
176
- })
177
- }
178
-
179
- watch(
180
- () => currentPath.get(),
181
- () => {
182
- requestAnimationFrame(applyHeaderNavState)
183
- },
184
- )
185
-
186
- useEffect(() => {
187
- requestAnimationFrame(applyHeaderNavState)
188
- }, [])
189
126
 
190
127
  return (
191
- <header className="sticky top-0 z-40 border-b border-base-300/70 bg-base-100/90 backdrop-blur">
192
- <div className="content-width flex flex-wrap items-center gap-3 py-3">
193
- <div className="min-w-0 flex-1 md:flex-none">
128
+ <header className="sticky top-0 z-40 border-b border-base-300/70 bg-base-100/85 backdrop-blur">
129
+ <div className="content-width navbar min-h-0 gap-4 py-3">
130
+ <div className="navbar-start min-w-0">
194
131
  <RouterLink to="/" className="flex min-w-0 items-center gap-3">
195
132
  <div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-primary text-primary-content shadow-lg shadow-primary/20">
196
133
  R
@@ -200,19 +137,18 @@ const Header: FC = () => {
200
137
  </div>
201
138
  </RouterLink>
202
139
  </div>
203
- <nav className="order-3 flex w-full flex-wrap gap-2 md:order-none md:w-auto md:flex-1 md:justify-center">
140
+ <nav className="navbar-center hidden flex-1 justify-center gap-2 md:flex">
204
141
  {navItems.map((item) => (
205
142
  <RouterLink
206
143
  key={item.to}
207
144
  to={item.to}
208
- data-base-nav-path={item.to}
209
- className={navClass(item.to)}
145
+ className={`btn btn-sm ${currentPath.get() === item.to ? 'btn-primary' : 'btn-ghost'}`}
210
146
  >
211
- <span>{item.label}</span>
147
+ {item.label}
212
148
  </RouterLink>
213
149
  ))}
214
150
  </nav>
215
- <div className="flex items-center gap-2 md:ml-auto">
151
+ <div className="navbar-end flex items-center gap-2">
216
152
  <ThemePicker />
217
153
  </div>
218
154
  </div>
@@ -238,72 +174,26 @@ const Footer: FC = () => (
238
174
  const HeaderToggleButton: FC<{ hidden: boolean; onToggle: () => void }> = (props) => (
239
175
  <button
240
176
  type="button"
241
- className="btn btn-circle btn-sm fixed left-3 top-3 z-50 border-base-300 bg-base-100/95 text-base-content shadow-lg backdrop-blur"
242
- aria-label={props.hidden ? '显示头部和底部' : '隐藏头部和底部'}
243
- title={props.hidden ? '显示头部和底部' : '隐藏头部和底部'}
177
+ className="btn btn-circle btn-sm fixed left-3 top-3 z-50 border-base-300 bg-base-100/90 shadow-lg backdrop-blur"
178
+ aria-label={props.hidden ? '显示顶部导航' : '隐藏顶部导航'}
179
+ title={props.hidden ? '显示顶部导航' : '隐藏顶部导航'}
244
180
  onClick={props.onToggle}
245
181
  >
246
- {props.hidden ? (
247
- <svg
248
- viewBox="0 0 24 24"
249
- width="16"
250
- height="16"
251
- aria-hidden="true"
252
- fill="none"
253
- stroke="currentColor"
254
- stroke-width="2"
255
- stroke-linecap="round"
256
- stroke-linejoin="round"
257
- >
258
- <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" />
259
- <circle cx="12" cy="12" r="3" />
260
- </svg>
261
- ) : (
262
- <svg
263
- viewBox="0 0 24 24"
264
- width="16"
265
- height="16"
266
- aria-hidden="true"
267
- fill="none"
268
- stroke="currentColor"
269
- stroke-width="2"
270
- stroke-linecap="round"
271
- stroke-linejoin="round"
272
- >
273
- <path d="M3 3l18 18" />
274
- <path d="M10.6 10.7a3 3 0 0 0 4.2 4.2" />
275
- <path d="M9.9 5.1A10.9 10.9 0 0 1 12 5c6.5 0 10 7 10 7a21.7 21.7 0 0 1-4 5.2" />
276
- <path d="M6.6 6.7C4.2 8.3 2.7 10.9 2 12c0 0 3.5 7 10 7 1.8 0 3.4-.5 4.8-1.3" />
277
- </svg>
278
- )}
182
+ {props.hidden ? '显' : '隐'}
279
183
  </button>
280
184
  )
281
185
 
282
186
  const SiteLayout: FC = (props) => {
283
187
  const route = useRoute()
188
+ const reportHeaderHidden = ref(getStoredHeaderHidden())
284
189
  const currentPath = computed(() => route.get()?.path || '/')
285
- const allowHeaderToggle = computed(() => headerToggleRoutes.includes(currentPath.get()))
286
- const reportHeaderHidden = ref(getStoredHeaderHidden(currentPath.get()))
287
- const hideHeader = computed(() => allowHeaderToggle.get() && reportHeaderHidden.value)
288
-
289
- watch(
290
- () => currentPath.get(),
291
- (path) => {
292
- reportHeaderHidden.value = allowHeaderToggle.get() ? getStoredHeaderHidden(path) : false
293
- },
294
- )
190
+ const isReportRoute = computed(() => currentPath.get() === '/report')
191
+ const hideHeader = computed(() => isReportRoute.get() && reportHeaderHidden.value)
295
192
 
296
193
  watch(
297
194
  () => reportHeaderHidden.value,
298
195
  () => {
299
- if (!allowHeaderToggle.get()) {
300
- return
301
- }
302
-
303
- localStorage.setItem(
304
- getHeaderStorageKey(currentPath.get()),
305
- reportHeaderHidden.value ? '1' : '0',
306
- )
196
+ localStorage.setItem(reportHeaderStorageKey, reportHeaderHidden.value ? '1' : '0')
307
197
  },
308
198
  )
309
199
 
@@ -311,7 +201,7 @@ const SiteLayout: FC = (props) => {
311
201
  <div className="app-shell">
312
202
  <div className="app-surface">
313
203
  {!hideHeader.get() && <Header />}
314
- {allowHeaderToggle.get() && (
204
+ {isReportRoute.get() && (
315
205
  <HeaderToggleButton
316
206
  hidden={hideHeader.get()}
317
207
  onToggle={() => {
@@ -319,8 +209,12 @@ const SiteLayout: FC = (props) => {
319
209
  }}
320
210
  />
321
211
  )}
322
- <main className="content-width pb-10 pt-6 sm:pt-8">{props.children}</main>
323
- {!hideHeader.get() && <Footer />}
212
+ <main
213
+ className={`content-width pb-10 ${hideHeader.get() ? 'pt-6 sm:pt-8' : 'pt-6 sm:pt-8'}`}
214
+ >
215
+ {props.children}
216
+ </main>
217
+ <Footer />
324
218
  </div>
325
219
  </div>
326
220
  )
@@ -6,35 +6,32 @@
6
6
  "scripts": {
7
7
  "dev": "vite",
8
8
  "build": "vite build",
9
- "preview": "vite preview",
10
- "typecheck": "tsgo --noEmit",
9
+ "typecheck": "tsc --noEmit",
11
10
  "oxlint": "oxlint -c ./.oxlintrc.json .",
12
11
  "oxlint-fix": "oxlint -c ./.oxlintrc.json . --fix",
13
12
  "format": "oxfmt .",
14
13
  "format-check": "oxfmt --check ."
15
14
  },
16
15
  "dependencies": {
17
- "@rue-js/jsx-runtime": "^0.1.1",
18
- "@rue-js/router": "^0.1.1",
19
- "@rue-js/rue": "^0.1.1",
20
- "@rue-js/runtime": "^0.1.1",
21
- "@rue-js/runtime-vapor": "^0.1.1",
22
- "daisyui": "^5.5.19",
23
- "oxfmt": "^0.48.0",
24
- "oxlint": "^1.63.0"
16
+ "@rue-js/design": "^0.7.2",
17
+ "@rue-js/jsx-runtime": "^0.7.2",
18
+ "@rue-js/router": "^0.7.2",
19
+ "@rue-js/rue": "^0.7.2",
20
+ "@rue-js/runtime": "^0.7.2",
21
+ "@rue-js/runtime-vapor": "^0.7.2"
25
22
  },
26
23
  "devDependencies": {
27
- "@rue-js/jsx-dev-runtime": "^0.1.1",
28
- "@rue-js/vite-plugin-rue": "^0.1.1",
29
- "@swc/core": "^1.15.33",
24
+ "@rue-js/jsx-dev-runtime": "^0.7.2",
25
+ "@rue-js/vite-plugin-rue": "^0.7.2",
26
+ "@swc/core": "^1.15.3",
30
27
  "@tailwindcss/typography": "^0.5.19",
31
- "@tailwindcss/vite": "^4.3.0",
32
- "@types/node": "^25.6.2",
33
- "@typescript/native-preview": "beta",
34
- "tailwindcss": "^4.3.0",
35
- "typescript": "^6.0.3",
36
- "vite": "^8.0.11",
37
- "vite-plugin-wasm": "^3.6.0"
28
+ "@tailwindcss/vite": "^4.1.17",
29
+ "@types/node": "^24.10.1",
30
+ "oxfmt": "^0.42.0",
31
+ "oxlint": "^1.57.0",
32
+ "typescript": "^5.9.3",
33
+ "vite": "^8.0.3",
34
+ "vite-plugin-wasm": "^3.5.0"
38
35
  },
39
36
  "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
40
37
  }
@@ -11,11 +11,12 @@
11
11
  "resolveJsonModule": true,
12
12
  "isolatedModules": true,
13
13
  "noEmit": false,
14
- "types": ["vite/client", "@rue-js/rue/jsx", "node"],
14
+ "types": ["vitest/globals"],
15
15
  "jsx": "react-jsx",
16
16
  "jsxImportSource": "@rue-js",
17
17
  "declaration": true,
18
- "outDir": "./dist"
18
+ "outDir": "./dist",
19
+ "baseUrl": "."
19
20
  },
20
21
  "include": ["app/**/*.ts", "app/**/*.tsx", "app/**/*.d.ts", "vite.config.ts"]
21
22
  }
@@ -3,7 +3,7 @@ import VitePluginRue from '@rue-js/vite-plugin-rue'
3
3
  import wasm from 'vite-plugin-wasm'
4
4
  import tailwindcss from '@tailwindcss/vite'
5
5
 
6
- export default defineConfig(({ command }) => ({
6
+ export default defineConfig({
7
7
  resolve: {
8
8
  conditions: ['import', 'module', 'browser', 'default'],
9
9
  },
@@ -11,11 +11,8 @@ export default defineConfig(({ command }) => ({
11
11
  wasm(),
12
12
  tailwindcss() as any,
13
13
  VitePluginRue({
14
- debug: command === 'serve',
14
+ include: ['/app/'],
15
+ debug: true,
15
16
  }),
16
17
  ],
17
- server: {
18
- port: 5173,
19
- strictPort: false,
20
- },
21
- }))
18
+ })