@h-ai/cli 0.1.0-alpha.19 → 0.1.0-alpha.21

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 (219) hide show
  1. package/README.md +10 -7
  2. package/dist/{chunk-GSMXGUYF.js → chunk-5IGMJVV5.js} +56 -38
  3. package/dist/chunk-5IGMJVV5.js.map +1 -0
  4. package/dist/{chunk-BPO34NRL.js → chunk-CFCXOKGV.js} +27 -5
  5. package/dist/chunk-CFCXOKGV.js.map +1 -0
  6. package/dist/cli-config-templates-5J5VKWWD.js +5 -0
  7. package/dist/{cli-config-templates-JCS7T3NU.js.map → cli-config-templates-5J5VKWWD.js.map} +1 -1
  8. package/dist/cli-skill-templates-JMSV2SVW.js +5 -0
  9. package/dist/{cli-skill-templates-IMYZPQRG.js.map → cli-skill-templates-JMSV2SVW.js.map} +1 -1
  10. package/dist/index.js +627 -107
  11. package/dist/index.js.map +1 -1
  12. package/dist/{src-N4NPS7AG.js → src-QNEJJ3BV.js} +261 -103
  13. package/dist/src-QNEJJ3BV.js.map +1 -0
  14. package/package.json +3 -2
  15. package/templates/README.md +40 -29
  16. package/templates/apps/admin/messages/en-US.json +5 -0
  17. package/templates/apps/admin/messages/zh-CN.json +5 -0
  18. package/templates/apps/admin/src/routes/+layout.svelte.hbs +5 -5
  19. package/templates/apps/admin/src/routes/+page.svelte.hbs +32 -12
  20. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +37 -20
  21. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
  22. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
  23. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +16 -0
  24. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +38 -0
  25. package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
  26. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +33 -0
  27. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +3 -0
  28. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +12 -0
  29. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
  30. package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
  31. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
  32. package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
  33. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +17 -0
  34. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +19 -0
  35. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +26 -0
  36. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +55 -0
  37. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +98 -0
  38. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +21 -0
  39. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +184 -0
  40. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +49 -0
  41. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +18 -0
  42. package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +168 -0
  43. package/templates/apps/api/apps/{{projectName}}-service/tsconfig.json.hbs +16 -0
  44. package/templates/apps/api/apps/{{projectName}}-service/tsup.config.ts.hbs +11 -0
  45. package/templates/apps/api/apps/{{projectName}}-service/vitest.config.ts.hbs +9 -0
  46. package/templates/apps/api/e2e/api-service.spec.ts.hbs +18 -0
  47. package/templates/apps/api/eslint.config.js +32 -0
  48. package/templates/apps/api/package.json.hbs +29 -0
  49. package/templates/apps/api/playwright.config.ts.hbs +58 -0
  50. package/templates/apps/api/pnpm-workspace.yaml.hbs +14 -0
  51. package/templates/apps/api/tsconfig.base.json +15 -0
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -7
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +18 -0
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +1 -1
  56. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +16 -0
  57. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +6 -0
  58. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +6 -0
  59. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +51 -29
  60. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +186 -0
  61. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +2 -0
  62. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +0 -1
  63. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +18 -0
  64. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +14 -0
  65. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +1 -5
  66. package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +10 -4
  67. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +4 -3
  68. package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
  69. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +20 -6
  70. package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +1 -1
  71. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +15 -0
  72. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +33 -30
  73. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +152 -0
  74. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +1 -2
  75. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +13 -0
  76. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +22 -0
  77. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
  78. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +12 -0
  79. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
  80. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
  81. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
  82. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
  83. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
  84. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +16 -0
  85. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
  86. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +41 -0
  87. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +2 -8
  88. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +9 -4
  89. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +44 -21
  90. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +15 -6
  91. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +1 -1
  92. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +15 -0
  93. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +25 -27
  94. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +153 -0
  95. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +0 -1
  96. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
  97. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +1 -5
  98. package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +10 -4
  99. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -2
  100. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +2 -1
  101. package/templates/apps/fullstack/eslint.config.js +0 -1
  102. package/templates/apps/fullstack/package.json.hbs +18 -17
  103. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +3 -3
  104. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
  105. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +14 -0
  106. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +13 -0
  107. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +2 -2
  108. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/{server-app.ts.hbs → app-server.ts.hbs} +23 -3
  109. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +4 -1
  110. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/{server-app.test.ts.hbs → app-server.test.ts.hbs} +1 -1
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +9 -8
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +1 -1
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +5 -1
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +1 -1
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +1 -1
  116. package/templates/apps/fullstack/playwright.config.ts.hbs +41 -4
  117. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +15 -0
  118. package/templates/apps/h5/messages/en-US.json +3 -0
  119. package/templates/apps/h5/messages/zh-CN.json +3 -0
  120. package/templates/apps/h5/src/routes/+layout.svelte.hbs +30 -29
  121. package/templates/apps/h5/src/routes/+page.svelte.hbs +17 -6
  122. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
  123. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
  124. package/templates/apps/mobile-app/.env.hbs +3 -0
  125. package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
  126. package/templates/apps/mobile-app/index.html.hbs +13 -0
  127. package/templates/apps/mobile-app/messages/en-US.json +36 -0
  128. package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
  129. package/templates/apps/mobile-app/src/App.svelte.hbs +162 -0
  130. package/templates/apps/mobile-app/src/lib/api.ts.hbs +54 -0
  131. package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
  132. package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
  133. package/templates/apps/website/src/routes/+layout.svelte.hbs +35 -26
  134. package/templates/apps/website/src/routes/+page.svelte.hbs +28 -10
  135. package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
  136. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +37 -34
  137. package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
  138. package/templates/base/.gitignore.hbs +16 -0
  139. package/templates/base/{eslint.config.js → eslint.config.js.hbs} +4 -1
  140. package/templates/base/package.json.hbs +55 -31
  141. package/templates/base/playwright.config.ts +30 -3
  142. package/templates/base/pnpm-workspace.yaml.hbs +14 -0
  143. package/templates/base/src/app.d.ts.hbs +3 -0
  144. package/templates/base/src/app.html.hbs +7 -0
  145. package/templates/base/src/hooks.server.ts.hbs +17 -7
  146. package/templates/base/src/lib/server/init.ts.hbs +20 -12
  147. package/templates/base/static/favicon.png +0 -0
  148. package/templates/base/svelte.config.js.hbs +43 -3
  149. package/templates/base/tests/scaffold.test.ts.hbs +16 -1
  150. package/templates/base/tsconfig.json.hbs +50 -0
  151. package/templates/base/vite.config.ts.hbs +13 -3
  152. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +5 -5
  153. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +34 -32
  154. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +46 -48
  155. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +35 -31
  156. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +41 -38
  157. package/templates/skills/AGENTS.md +60 -17
  158. package/templates/skills/CLAUDE.md +18 -6
  159. package/templates/skills/bridges/admin/AGENTS.md +66 -0
  160. package/templates/skills/bridges/admin/CLAUDE.md +19 -0
  161. package/templates/skills/bridges/admin/copilot-instructions.md +46 -0
  162. package/templates/skills/bridges/api/AGENTS.md +65 -0
  163. package/templates/skills/bridges/api/CLAUDE.md +19 -0
  164. package/templates/skills/bridges/api/copilot-instructions.md +46 -0
  165. package/templates/skills/bridges/fullstack/AGENTS.md +84 -0
  166. package/templates/skills/bridges/fullstack/CLAUDE.md +19 -0
  167. package/templates/skills/bridges/fullstack/copilot-instructions.md +54 -0
  168. package/templates/skills/bridges/h5/AGENTS.md +65 -0
  169. package/templates/skills/bridges/h5/CLAUDE.md +19 -0
  170. package/templates/skills/bridges/h5/copilot-instructions.md +44 -0
  171. package/templates/skills/bridges/mobile-app/AGENTS.md +77 -0
  172. package/templates/skills/bridges/mobile-app/CLAUDE.md +19 -0
  173. package/templates/skills/bridges/mobile-app/copilot-instructions.md +45 -0
  174. package/templates/skills/bridges/website/AGENTS.md +65 -0
  175. package/templates/skills/bridges/website/CLAUDE.md +19 -0
  176. package/templates/skills/bridges/website/copilot-instructions.md +44 -0
  177. package/templates/skills/copilot-instructions.md +29 -50
  178. package/templates/skills/hai-ai/SKILL.md +115 -648
  179. package/templates/skills/hai-ai/reference.md +197 -0
  180. package/templates/skills/hai-api-client/SKILL.md +3 -1
  181. package/templates/skills/hai-app-create/SKILL.md +2 -1
  182. package/templates/skills/hai-app-review/SKILL.md +2 -2
  183. package/templates/skills/hai-app-tests/SKILL.md +28 -3
  184. package/templates/skills/hai-capacitor/SKILL.md +14 -22
  185. package/templates/skills/hai-deploy/SKILL.md +12 -9
  186. package/templates/skills/hai-kit/SKILL.md +5 -3
  187. package/templates/skills/hai-serv/SKILL.md +2 -0
  188. package/templates/skills/hai-ui/SKILL.md +25 -11
  189. package/templates/skills/opencode.json +1 -6
  190. package/dist/chunk-BPO34NRL.js.map +0 -1
  191. package/dist/chunk-GSMXGUYF.js.map +0 -1
  192. package/dist/cli-config-templates-JCS7T3NU.js +0 -5
  193. package/dist/cli-skill-templates-IMYZPQRG.js +0 -5
  194. package/dist/src-N4NPS7AG.js.map +0 -1
  195. package/templates/apps/android-app/.env.hbs +0 -3
  196. package/templates/apps/android-app/messages/en-US.json +0 -24
  197. package/templates/apps/android-app/messages/zh-CN.json +0 -24
  198. package/templates/apps/android-app/src/lib/api.ts.hbs +0 -31
  199. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
  200. package/templates/apps/android-app/src/routes/+layout.ts +0 -7
  201. package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
  202. package/templates/apps/api/src/routes/+layout.svelte +0 -14
  203. package/templates/apps/api/src/routes/+page.svelte +0 -11
  204. package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
  205. package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
  206. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
  207. package/templates/apps/api/tests/health.test.ts.hbs +0 -26
  208. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.html.hbs +0 -14
  209. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+layout.svelte.hbs +0 -18
  210. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+page.svelte.hbs +0 -162
  211. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.html.hbs +0 -14
  212. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+layout.svelte.hbs +0 -18
  213. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+page.svelte.hbs +0 -162
  214. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.html.hbs +0 -14
  215. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+layout.svelte.hbs +0 -18
  216. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+page.svelte.hbs +0 -162
  217. package/templates/apps/fullstack/pnpm-workspace.yaml +0 -7
  218. package/templates/base/tsconfig.json +0 -20
  219. /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/{theme.svelte.ts.hbs → theme-store.svelte.ts.hbs} +0 -0
@@ -0,0 +1,186 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.app}}
3
+ <script lang="ts">
4
+ import type { AppInfo } from '{{fullstack.sharedPackageName}}'
5
+ import * as appM from './lib/paraglide/messages.js'
6
+ import { setLocale } from './lib/paraglide/runtime.js'
7
+ import { Alert, Button, Card, Skeleton } from '@h-ai/ui'
8
+ import { onMount } from 'svelte'
9
+ import {
10
+ fetchAppInfo,
11
+ getLocale as getSharedLocale,
12
+ LanguageSwitcher,
13
+ messages as sharedM,
14
+ ThemeSwitcher,
15
+ } from '{{fullstack.sharedPackageName}}'
16
+ import { initCapacitor } from './lib/capacitor.js'
17
+
18
+ type TabId = 'workspace' | 'status' | 'settings'
19
+
20
+ let info = $state<AppInfo | null>(null)
21
+ let loading = $state(true)
22
+ let errorMessage = $state('')
23
+ let activeTab = $state<TabId>('workspace')
24
+
25
+ const navItems = $derived([
26
+ { id: 'workspace', label: appM.nav_workspace(), iconClass: 'icon-[tabler--layout-dashboard]' },
27
+ { id: 'status', label: appM.nav_status(), iconClass: 'icon-[tabler--activity-heartbeat]' },
28
+ { id: 'settings', label: appM.nav_settings(), iconClass: 'icon-[tabler--settings]' },
29
+ ])
30
+
31
+ function syncLocale(locale: string) {
32
+ setLocale(locale as Parameters<typeof setLocale>[0])
33
+ }
34
+
35
+ function handleTabChange(id: string): void {
36
+ if (id === 'workspace' || id === 'status' || id === 'settings')
37
+ activeTab = id
38
+ }
39
+
40
+ async function refreshStatus() {
41
+ loading = true
42
+ errorMessage = ''
43
+ try {
44
+ info = await fetchAppInfo({ clientName: '{{projectName}}-app' })
45
+ }
46
+ catch {
47
+ info = null
48
+ errorMessage = sharedM.home_error()
49
+ }
50
+ finally {
51
+ loading = false
52
+ }
53
+ }
54
+
55
+ onMount(() => {
56
+ document.title = appM.page_title({ name: sharedM.app_name() })
57
+ syncLocale(getSharedLocale())
58
+ void initCapacitor()
59
+ void refreshStatus()
60
+ })
61
+ </script>
62
+
63
+ <div class="hai-mobile-viewport">
64
+ <div class="hai-mobile-shell max-w-lg">
65
+ <AppBar title={sharedM.app_name()} safeArea fixed={false} class="bg-base-100/95 backdrop-blur border-base-content/8" />
66
+
67
+ <main class="hai-mobile-main">
68
+ {#if activeTab === 'workspace'}
69
+ <section class="space-y-4 p-4 pb-6">
70
+ <div class="bg-primary text-primary-content rounded-2xl p-5 shadow-sm">
71
+ <p class="text-sm opacity-75">{appM.hero_badge()}</p>
72
+ <h1 class="mt-1 text-2xl font-bold">{appM.hero_title({ name: sharedM.app_name() })}</h1>
73
+ <p class="mt-2 text-sm opacity-80">{appM.hero_subtitle()}</p>
74
+ </div>
75
+
76
+ <div class="grid grid-cols-2 gap-3">
77
+ <Card padding="sm" shadow="sm">
78
+ <div class="flex flex-col gap-2">
79
+ <span class="icon-[tabler--components] text-primary text-2xl"></span>
80
+ <span class="text-sm font-medium">{appM.preview_metric_shared()}</span>
81
+ <span class="text-base-content/60 text-xs">{appM.custom_shared_body()}</span>
82
+ </div>
83
+ </Card>
84
+ <Card padding="sm" shadow="sm">
85
+ <div class="flex flex-col gap-2">
86
+ <span class="icon-[tabler--message-language] text-primary text-2xl"></span>
87
+ <span class="text-sm font-medium">{appM.preview_metric_local()}</span>
88
+ <span class="text-base-content/60 text-xs">{appM.custom_local_body()}</span>
89
+ </div>
90
+ </Card>
91
+ </div>
92
+
93
+ <Alert variant="info">
94
+ {appM.flow_frontend()}
95
+ </Alert>
96
+ </section>
97
+ {:else if activeTab === 'status'}
98
+ <section class="space-y-4 p-4 pb-6">
99
+ <Card title={appM.status_title()} shadow="sm">
100
+ <p class="mb-4 text-sm text-base-content/65">{appM.status_description()}</p>
101
+ {#if loading}
102
+ <Skeleton variant="title" class="mb-3" />
103
+ <Skeleton count={3} />
104
+ <p class="sr-only" data-testid="service-status">{sharedM.home_loading()}</p>
105
+ {:else if info}
106
+ <Alert variant="success" title={appM.status_online({ name: info.name })}>
107
+ <p data-testid="service-status">{info.name}</p>
108
+ </Alert>
109
+ <div class="mt-4 grid grid-cols-2 gap-3 text-sm">
110
+ <div>
111
+ <p class="text-base-content/50">{appM.status_version()}</p>
112
+ <p class="font-medium">{info.version}</p>
113
+ </div>
114
+ <div>
115
+ <p class="text-base-content/50">{appM.status_frontends_count({ count: String(info.frontends.length) })}</p>
116
+ <p class="font-medium">{info.frontends.join(' / ')}</p>
117
+ </div>
118
+ </div>
119
+ {:else}
120
+ <Alert variant="error" title={sharedM.home_error()}>
121
+ <p data-testid="service-status">{errorMessage}</p>
122
+ </Alert>
123
+ {/if}
124
+
125
+ {#snippet footer()}
126
+ <Button variant="ghost" size="sm" onclick={refreshStatus}>{sharedM.home_refresh()}</Button>
127
+ {/snippet}
128
+ </Card>
129
+ </section>
130
+ {:else}
131
+ <section class="space-y-4 p-4 pb-6">
132
+ <Card title={sharedM.settings_title()} shadow="sm">
133
+ <p class="mb-4 text-sm text-base-content/65">{appM.settings_description()}</p>
134
+
135
+ <div class="space-y-3">
136
+ <div class="rounded-2xl border border-base-300/70 bg-base-200/60 p-4">
137
+ <div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
138
+ <div class="min-w-0 flex-1">
139
+ <h2 class="text-sm font-semibold">{sharedM.settings_theme()}</h2>
140
+ <p class="mt-1 text-xs leading-5 text-base-content/65">{appM.settings_theme_description()}</p>
141
+ </div>
142
+ <div class="shrink-0">
143
+ <ThemeSwitcher />
144
+ </div>
145
+ </div>
146
+ </div>
147
+
148
+ <div class="rounded-2xl border border-base-300/70 bg-base-200/60 p-4">
149
+ <div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
150
+ <div class="min-w-0 flex-1">
151
+ <h2 class="text-sm font-semibold">{sharedM.settings_language()}</h2>
152
+ <p class="mt-1 text-xs leading-5 text-base-content/65">{appM.settings_language_description()}</p>
153
+ </div>
154
+ <div class="shrink-0">
155
+ <LanguageSwitcher onLocaleChange={syncLocale} />
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </Card>
161
+
162
+ <Card title={appM.flow_title()} shadow="sm">
163
+ <div class="space-y-3 text-sm">
164
+ <div class="bg-base-200 rounded-xl p-3">{appM.flow_contract()}</div>
165
+ <div class="bg-base-200 rounded-xl p-3">{appM.flow_service()}</div>
166
+ <div class="bg-base-200 rounded-xl p-3">{appM.flow_shared()}</div>
167
+ </div>
168
+ </Card>
169
+ </section>
170
+ {/if}
171
+ </main>
172
+
173
+ <BottomNav
174
+ items={navItems}
175
+ active={activeTab}
176
+ onchange={handleTabChange}
177
+ safeArea
178
+ centered
179
+ maxWidth="lg"
180
+ class="border-base-content/8 shadow-[0_-12px_32px_rgba(15,23,42,0.08)]"
181
+ />
182
+ </div>
183
+ </div>
184
+
185
+ <ToastContainer />
186
+ {{/if}}
@@ -3,6 +3,8 @@
3
3
  @import 'tailwindcss';
4
4
  @import '@h-ai/ui/styles/global.css';
5
5
  @import '@h-ai/ui/styles/theme.css';
6
+ @import '@h-ai/ui/styles/design-tokens.css';
7
+ @import '@h-ai/ui/styles/mobile.css';
6
8
  @source "../node_modules/@h-ai/ui/dist/**/*.{svelte,js,ts}";
7
9
  @source "../../../node_modules/@h-ai/ui/dist/**/*.{svelte,js,ts}";
8
10
  @source "../../../packages/{{fullstack.sharedPackageName}}/src/**/*.{svelte,js,ts}";
@@ -1,6 +1,5 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
- /// <reference types="@sveltejs/kit" />
4
3
  /// <reference types="vite/client" />
5
4
 
6
5
  import '@h-ai/ui/auto-import'
@@ -0,0 +1,18 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.app}}
3
+ import { capacitor } from '@h-ai/capacitor'
4
+
5
+ export async function initCapacitor(): Promise<void> {
6
+ const result = await capacitor.init()
7
+ if (!result.success) {
8
+ return
9
+ }
10
+
11
+ if (capacitor.isNative()) {
12
+ await capacitor.statusBar.configure({
13
+ style: 'dark',
14
+ overlay: true,
15
+ })
16
+ }
17
+ }
18
+ {{/if}}
@@ -0,0 +1,14 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.app}}
3
+ import { mount } from 'svelte'
4
+ import App from './App.svelte'
5
+ import './app.css'
6
+
7
+ const target = document.getElementById('app')
8
+ if (!target)
9
+ throw new Error('Root element #app not found')
10
+
11
+ const app = mount(App, { target })
12
+
13
+ export default app
14
+ {{/if}}
@@ -1,19 +1,15 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
3
  import { autoImportHaiUi } from '@h-ai/ui/auto-import'
4
- import adapter from '@sveltejs/adapter-auto'
5
4
  import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
6
5
 
7
- /** @type {import('@sveltejs/kit').Config} */
6
+ /** @type {import('@sveltejs/vite-plugin-svelte').SvelteConfig} */
8
7
  const config = {
9
8
  preprocess: [autoImportHaiUi(), vitePreprocess()],
10
9
  compilerOptions: {
11
10
  // 使用 Svelte 5 Runes,和 @h-ai/ui 组件库保持一致。
12
11
  runes: true,
13
12
  },
14
- kit: {
15
- adapter: adapter(),
16
- },
17
13
  }
18
14
 
19
15
  export default config
@@ -1,17 +1,23 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
3
  {
4
- "extends": "./.svelte-kit/tsconfig.json",
5
4
  "compilerOptions": {
5
+ "target": "ES2022",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "moduleDetection": "force",
8
+ "module": "ESNext",
6
9
  "moduleResolution": "bundler",
7
- "allowImportingTsExtensions": true,
10
+ "resolveJsonModule": true,
11
+ "types": ["svelte", "vite/client", "node", "vitest/globals"],
8
12
  "allowJs": true,
9
13
  "checkJs": false,
10
14
  "strict": true,
15
+ "noEmit": true,
16
+ "isolatedModules": true,
11
17
  "verbatimModuleSyntax": true,
12
18
  "skipLibCheck": true
13
19
  },
14
- "include": ["src/**/*", "src/**/*.svelte", "tests/**/*"],
15
- "exclude": ["node_modules", ".svelte-kit", "build"]
20
+ "include": ["src/**/*.ts", "src/**/*.svelte", "tests/**/*.ts", "vite.config.ts", "svelte.config.js", "capacitor.config.ts"],
21
+ "exclude": ["node_modules", "dist", "build", "android", "ios"]
16
22
  }
17
23
  {{/if}}
@@ -1,23 +1,24 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
3
  import { paraglideVitePlugin } from '@inlang/paraglide-js'
4
- import { sveltekit } from '@sveltejs/kit/vite'
4
+ import { svelte } from '@sveltejs/vite-plugin-svelte'
5
5
  import tailwindcss from '@tailwindcss/vite'
6
6
  import { defineConfig } from 'vitest/config'
7
7
 
8
8
  export default defineConfig({
9
9
  plugins: [
10
+ svelte(),
11
+ tailwindcss(),
10
12
  paraglideVitePlugin({
11
13
  project: './project.inlang',
12
14
  outdir: './src/lib/paraglide',
13
15
  strategy: ['cookie', 'baseLocale'],
14
16
  }),
15
- sveltekit(),
16
- tailwindcss(),
17
17
  ],
18
18
  optimizeDeps: {
19
19
  exclude: ['bits-ui'],
20
20
  },
21
+ envPrefix: ['VITE_', 'PUBLIC_'],
21
22
  ssr: {
22
23
  noExternal: [/@h-ai\//, /{{fullstack.sharedPackageName}}/],
23
24
  },
@@ -0,0 +1,6 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.desktop}}
3
+ # {{projectName}}-desktop — 环境变量
4
+
5
+ PUBLIC_API_BASE=http://localhost:3000
6
+ {{/if}}
@@ -2,7 +2,7 @@
2
2
  {{#if fullstack.frontends.desktop}}
3
3
  # {{projectName}}-desktop
4
4
 
5
- 桌面端前端工程,使用 `@h-ai/api-client` 绑定共享 contract 调用后端 API,并通过 `@h-ai/ui` 组件搭建页面;后续可按需接入 Tauri。
5
+ 桌面端工程,参考 `apps/desktop-app` 使用 Tauri v2 + Svelte 5 + Vite,前端通过共享 contract 调用 `{{fullstack.servPackageName}}`。
6
6
 
7
7
  ## i18n 与 shared 协同
8
8
 
@@ -12,10 +12,24 @@
12
12
 
13
13
  自定义建议:桌面端菜单、状态、快捷操作文案放在本目录 `messages/`;跨端通用能力放到 shared。
14
14
 
15
- ## 开发
15
+ ## 编译、打包与部署
16
16
 
17
- - `pnpm i18n:compile`:在工作区根目录统一编译 shared + 各前端 Paraglide 输出
18
- - `pnpm --filter {{projectName}}-desktop dev`,默认端口 4175
19
- - `pnpm --filter {{projectName}}-desktop paraglide:compile`:只编译本端 messages
20
- - `pnpm --filter {{projectName}}-desktop test`
17
+ ```bash
18
+ # 仅前端开发,默认端口 {{fullstack.ports.desktop}}
19
+ pnpm --filter {{projectName}}-desktop dev
20
+
21
+ # Tauri 桌面开发
22
+ pnpm --filter {{projectName}}-desktop tauri:dev
23
+
24
+ # 编译 WebView 前端产物到 dist/
25
+ pnpm --filter {{projectName}}-desktop build
26
+
27
+ # 打包桌面安装包
28
+ pnpm --filter {{projectName}}-desktop package
29
+
30
+ # 当前模板的 deploy 入口默认执行 package,可按发布平台替换
31
+ pnpm --filter {{projectName}}-desktop deploy
32
+ ```
33
+
34
+ 打包前需要安装 Rust 工具链和 Tauri 平台依赖。生产环境部署时同步调整 `.env` 中的 `PUBLIC_API_BASE` 与 `src-tauri/tauri.conf.json` 的 `connect-src` 白名单。
21
35
  {{/if}}
@@ -4,7 +4,7 @@ import antfu from '@antfu/eslint-config'
4
4
 
5
5
  export default antfu({
6
6
  formatters: true,
7
- ignores: ['**/.svelte-kit/**', '**/build/**', '**/dist/**', '**/*.d.ts', '**/project.inlang/**', '**/src/lib/paraglide/**'],
7
+ ignores: ['**/build/**', '**/dist/**', '**/*.d.ts', '**/project.inlang/**', '**/src/lib/paraglide/**'],
8
8
  svelte: true,
9
9
  }, {
10
10
  files: ['**/*.ts', '**/*.svelte'],
@@ -0,0 +1,15 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.desktop}}
3
+ <!doctype html>
4
+ <html lang="{{fullstack.i18n.baseLocale}}" data-theme="{{fullstack.theme.defaultTheme}}">
5
+ <head>
6
+ <meta charset="UTF-8" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>{{projectName}} Desktop</title>
9
+ </head>
10
+ <body>
11
+ <div id="app"></div>
12
+ <script type="module" src="/src/main.ts"></script>
13
+ </body>
14
+ </html>
15
+ {{/if}}
@@ -9,42 +9,45 @@
9
9
  "paraglide:compile": "paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide",
10
10
  "paraglide:watch": "paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide --watch",
11
11
  "dev": "vite dev --host 127.0.0.1 --port {{fullstack.ports.desktop}}",
12
- "build": "vite build",
12
+ "build": "pnpm paraglide:compile && vite build",
13
+ "package": "pnpm tauri:build",
14
+ "deploy": "pnpm package",
13
15
  "preview": "vite preview --host 127.0.0.1 --port {{fullstack.ports.desktop}} --strictPort",
14
- "typecheck": "pnpm paraglide:compile && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
15
- "lint": "eslint .",
16
- "test": "vitest run",
17
- "test:watch": "vitest",
18
- "clean": "rimraf .svelte-kit build .turbo src/lib/paraglide"
16
+ "typecheck": "pnpm paraglide:compile && svelte-check --tsconfig ./tsconfig.json",
17
+ "lint": "eslint src tests vite.config.ts svelte.config.js",
18
+ "lint:fix": "eslint src tests vite.config.ts svelte.config.js --fix",
19
+ "test": "pnpm paraglide:compile && vitest run",
20
+ "test:watch": "pnpm paraglide:compile && vitest",
21
+ "clean": "rimraf dist .turbo src-tauri/target src/lib/paraglide",
22
+ "tauri": "tauri",
23
+ "tauri:dev": "tauri dev",
24
+ "tauri:build": "tauri build"
19
25
  },
20
26
  "dependencies": {
21
- "@h-ai/api-client": "{{fullstack.versions.apiClient}}",
22
- "@h-ai/ui": "{{fullstack.versions.ui}}",
23
- "{{fullstack.contractPackageName}}": "workspace:*",
27
+ "@h-ai/ui": "{{haiPackageSpecifier}}",
24
28
  "{{fullstack.sharedPackageName}}": "workspace:*"
25
29
  },
26
30
  "devDependencies": {
27
- "@antfu/eslint-config": "{{fullstack.versions.antfuEslintConfig}}",
28
- "@iconify-json/tabler": "{{fullstack.versions.iconifyJsonTabler}}",
29
- "@iconify/tailwind4": "{{fullstack.versions.iconifyTailwind}}",
30
- "@inlang/paraglide-js": "{{fullstack.versions.inlangParaglide}}",
31
- "@inlang/plugin-message-format": "{{fullstack.versions.inlangMessageFormat}}",
32
- "@sveltejs/adapter-auto": "{{fullstack.versions.adapterAuto}}",
33
- "@sveltejs/kit": "{{fullstack.versions.svelteKit}}",
34
- "@sveltejs/vite-plugin-svelte": "{{fullstack.versions.sveltePlugin}}",
35
- "@tailwindcss/vite": "{{fullstack.versions.tailwindcssVite}}",
36
- "bits-ui": "{{fullstack.versions.bitsUi}}",
37
- "daisyui": "{{fullstack.versions.daisyui}}",
38
- "eslint": "{{fullstack.versions.eslint}}",
39
- "eslint-plugin-format": "{{fullstack.versions.eslintPluginFormat}}",
40
- "eslint-plugin-svelte": "{{fullstack.versions.eslintPluginSvelte}}",
41
- "svelte": "{{fullstack.versions.svelte}}",
42
- "svelte-check": "{{fullstack.versions.svelteCheck}}",
43
- "svelte-eslint-parser": "{{fullstack.versions.svelteEslintParser}}",
44
- "tailwindcss": "{{fullstack.versions.tailwindcss}}",
45
- "typescript": "{{fullstack.versions.typescript}}",
46
- "vite": "{{fullstack.versions.vite}}",
47
- "vitest": "{{fullstack.versions.vitest}}"
31
+ "@antfu/eslint-config": "{{fullstack.packageSpecifiers.antfuEslintConfig}}",
32
+ "@iconify-json/tabler": "{{fullstack.packageSpecifiers.iconifyJsonTabler}}",
33
+ "@iconify/tailwind4": "{{fullstack.packageSpecifiers.iconifyTailwind}}",
34
+ "@inlang/paraglide-js": "{{fullstack.packageSpecifiers.inlangParaglide}}",
35
+ "@inlang/plugin-message-format": "{{fullstack.packageSpecifiers.inlangMessageFormat}}",
36
+ "@sveltejs/vite-plugin-svelte": "{{fullstack.packageSpecifiers.sveltePlugin}}",
37
+ "@tailwindcss/vite": "{{fullstack.packageSpecifiers.tailwindcssVite}}",
38
+ "@tauri-apps/cli": "{{fullstack.packageSpecifiers.tauriCli}}",
39
+ "bits-ui": "{{fullstack.packageSpecifiers.bitsUi}}",
40
+ "daisyui": "{{fullstack.packageSpecifiers.daisyui}}",
41
+ "eslint": "{{fullstack.packageSpecifiers.eslint}}",
42
+ "eslint-plugin-format": "{{fullstack.packageSpecifiers.eslintPluginFormat}}",
43
+ "eslint-plugin-svelte": "{{fullstack.packageSpecifiers.eslintPluginSvelte}}",
44
+ "svelte": "{{fullstack.packageSpecifiers.svelte}}",
45
+ "svelte-check": "{{fullstack.packageSpecifiers.svelteCheck}}",
46
+ "svelte-eslint-parser": "{{fullstack.packageSpecifiers.svelteEslintParser}}",
47
+ "tailwindcss": "{{fullstack.packageSpecifiers.tailwindcss}}",
48
+ "typescript": "{{fullstack.packageSpecifiers.typescript}}",
49
+ "vite": "{{fullstack.packageSpecifiers.vite}}",
50
+ "vitest": "{{fullstack.packageSpecifiers.vitest}}"
48
51
  }
49
52
  }
50
53
  {{/if}}
@@ -0,0 +1,152 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.desktop}}
3
+ <script lang="ts">
4
+ import type { AppInfo } from '{{fullstack.sharedPackageName}}'
5
+ import * as appM from './lib/paraglide/messages.js'
6
+ import { setLocale } from './lib/paraglide/runtime.js'
7
+ import { Alert, Button, Card, Skeleton } from '@h-ai/ui'
8
+ import { onMount } from 'svelte'
9
+ import { AppShell, fetchAppInfo, messages as sharedM } from '{{fullstack.sharedPackageName}}'
10
+
11
+ let info = $state<AppInfo | null>(null)
12
+ let loading = $state(true)
13
+ let errorMessage = $state('')
14
+
15
+ function syncLocale(locale: string) {
16
+ setLocale(locale as Parameters<typeof setLocale>[0])
17
+ }
18
+
19
+ async function refreshStatus() {
20
+ loading = true
21
+ errorMessage = ''
22
+ try {
23
+ info = await fetchAppInfo({ clientName: '{{projectName}}-desktop' })
24
+ }
25
+ catch {
26
+ info = null
27
+ errorMessage = sharedM.home_error()
28
+ }
29
+ finally {
30
+ loading = false
31
+ }
32
+ }
33
+
34
+ onMount(() => {
35
+ document.title = appM.page_title({ name: sharedM.app_name() })
36
+ void refreshStatus()
37
+ })
38
+ </script>
39
+
40
+ <AppShell platform="desktop" onLocaleChange={syncLocale}>
41
+ <section class="space-y-8">
42
+ <div class="grid gap-6 lg:grid-cols-[1.2fr_0.8fr]">
43
+ <section class="relative overflow-hidden rounded-[1.5rem] border border-base-300/70 bg-base-100 p-6 shadow-xl lg:p-10">
44
+ <div class="max-w-2xl space-y-6">
45
+ <span class="badge badge-primary badge-lg gap-2">
46
+ <span class="icon-[tabler--device-desktop] size-4"></span>
47
+ {appM.hero_badge()}
48
+ </span>
49
+ <div class="space-y-4">
50
+ <h1 class="text-4xl font-black tracking-tight text-base-content sm:text-5xl">
51
+ {appM.hero_title({ name: sharedM.app_name() })}
52
+ </h1>
53
+ <p class="max-w-xl text-lg leading-8 text-base-content/70">
54
+ {appM.hero_subtitle()}
55
+ </p>
56
+ </div>
57
+ <div class="flex flex-wrap gap-3">
58
+ <a class="btn btn-primary" href="#status">
59
+ <span class="icon-[tabler--activity-heartbeat] size-5"></span>
60
+ {appM.hero_primary_action()}
61
+ </a>
62
+ <a class="btn btn-ghost" href="#customize">
63
+ <span class="icon-[tabler--adjustments] size-5"></span>
64
+ {appM.hero_secondary_action()}
65
+ </a>
66
+ </div>
67
+ </div>
68
+ </section>
69
+
70
+ <aside class="rounded-[1.5rem] border border-base-300/70 bg-base-100 p-5 shadow-lg">
71
+ <div class="mockup-window border border-base-300 bg-base-200/70">
72
+ <div class="space-y-4 bg-base-100 p-5">
73
+ <div class="flex items-center justify-between">
74
+ <div>
75
+ <p class="text-sm font-semibold">{appM.preview_title()}</p>
76
+ <p class="text-xs text-base-content/55">{appM.preview_body()}</p>
77
+ </div>
78
+ <span class="badge badge-success badge-outline">{appM.platform_name()}</span>
79
+ </div>
80
+ <div class="grid gap-3 text-sm">
81
+ <div class="rounded-2xl bg-primary/10 p-4">
82
+ <span class="icon-[tabler--components] mb-2 size-6 text-primary"></span>
83
+ <p class="font-semibold">{appM.preview_metric_shared()}</p>
84
+ </div>
85
+ <div class="grid grid-cols-2 gap-3">
86
+ <div class="rounded-2xl bg-secondary/10 p-4">
87
+ <span class="icon-[tabler--language] mb-2 size-6 text-secondary"></span>
88
+ <p class="font-semibold">{appM.preview_metric_local()}</p>
89
+ </div>
90
+ <div class="rounded-2xl bg-accent/10 p-4">
91
+ <span class="icon-[tabler--palette] mb-2 size-6 text-accent"></span>
92
+ <p class="font-semibold">{appM.preview_metric_theme()}</p>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </aside>
99
+ </div>
100
+
101
+ <div class="grid gap-6 lg:grid-cols-[0.95fr_1.05fr]">
102
+ <div id="status">
103
+ <Card title={appM.status_title()} bordered shadow="md" class="bg-base-100">
104
+ <p class="mb-5 text-sm text-base-content/65">{appM.status_description()}</p>
105
+ {#if loading}
106
+ <Skeleton variant="title" class="mb-3" />
107
+ <Skeleton count={3} />
108
+ <p class="sr-only" data-testid="service-status">{sharedM.home_loading()}</p>
109
+ {:else if info}
110
+ <Alert variant="success" title={appM.status_online({ name: info.name })}>
111
+ <p data-testid="service-status">{info.name}</p>
112
+ </Alert>
113
+ <dl class="mt-5 grid gap-3 text-sm sm:grid-cols-2">
114
+ <div class="rounded-box border border-base-300/70 bg-base-200/60 p-4">
115
+ <dt class="text-xs text-base-content/55">{appM.status_version()}</dt>
116
+ <dd class="mt-1 font-semibold">{info.version}</dd>
117
+ </div>
118
+ <div class="rounded-box border border-base-300/70 bg-base-200/60 p-4">
119
+ <dt class="text-xs text-base-content/55">{appM.status_started({ time: info.startedAt })}</dt>
120
+ <dd class="mt-1 font-semibold">{appM.status_frontends_count({ count: String(info.frontends.length) })}</dd>
121
+ </div>
122
+ </dl>
123
+ {:else}
124
+ <Alert variant="error" title={sharedM.home_error()}>
125
+ <p data-testid="service-status">{errorMessage}</p>
126
+ </Alert>
127
+ {/if}
128
+
129
+ <div class="mt-5">
130
+ <Button type="button" variant="primary" loading={loading} onclick={refreshStatus}>
131
+ {sharedM.home_refresh()}
132
+ </Button>
133
+ </div>
134
+ </Card>
135
+ </div>
136
+
137
+ <section id="customize" class="grid gap-4 sm:grid-cols-2">
138
+ <div class="rounded-[1.5rem] border border-base-300/70 bg-base-100 p-5 shadow-md">
139
+ <span class="icon-[tabler--package] mb-4 size-7 text-primary"></span>
140
+ <h2 class="text-lg font-bold">{appM.custom_shared_title()}</h2>
141
+ <p class="mt-2 text-sm leading-6 text-base-content/65">{appM.custom_shared_body()}</p>
142
+ </div>
143
+ <div class="rounded-[1.5rem] border border-base-300/70 bg-base-100 p-5 shadow-md">
144
+ <span class="icon-[tabler--message-language] mb-4 size-7 text-secondary"></span>
145
+ <h2 class="text-lg font-bold">{appM.custom_local_title()}</h2>
146
+ <p class="mt-2 text-sm leading-6 text-base-content/65">{appM.custom_local_body()}</p>
147
+ </div>
148
+ </section>
149
+ </div>
150
+ </section>
151
+ </AppShell>
152
+ {{/if}}
@@ -1,9 +1,8 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.desktop}}
3
- /// <reference types="@sveltejs/kit" />
4
3
  /// <reference types="vite/client" />
5
4
 
6
5
  import '@h-ai/ui/auto-import'
7
6
 
8
7
  declare module '*.css'
9
- {{/if}}
8
+ {{/if}}
@@ -0,0 +1,13 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.desktop}}
3
+ import { mount } from 'svelte'
4
+ import App from './App.svelte'
5
+ import './app.css'
6
+
7
+ const target = document.getElementById('app')
8
+ if (!target) {
9
+ throw new Error('Root element #app not found')
10
+ }
11
+
12
+ export default mount(App, { target })
13
+ {{/if}}