@h-ai/cli 0.1.0-alpha.57 → 0.1.0-alpha.58

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 (175) hide show
  1. package/README.en.md +37 -3
  2. package/README.ja.md +37 -3
  3. package/README.md +37 -3
  4. package/dist/chunk-2H7UOFLK.js +0 -0
  5. package/dist/{chunk-MJGUBOAV.js → chunk-F4IBPVZ5.js} +6 -42
  6. package/dist/chunk-F4IBPVZ5.js.map +1 -0
  7. package/dist/{chunk-O3ANVICQ.js → chunk-WUSFJFW5.js} +13 -7
  8. package/dist/chunk-WUSFJFW5.js.map +1 -0
  9. package/dist/cli-config-templates-6XA7V6CC.js +5 -0
  10. package/dist/{cli-config-templates-OWBEGBZQ.js.map → cli-config-templates-6XA7V6CC.js.map} +1 -1
  11. package/dist/{cli-skill-templates-ZYXES3NT.js → cli-skill-templates-45CK4RQ2.js} +3 -3
  12. package/dist/{cli-skill-templates-ZYXES3NT.js.map → cli-skill-templates-45CK4RQ2.js.map} +1 -1
  13. package/dist/index.js +107 -61
  14. package/dist/index.js.map +1 -1
  15. package/dist/{src-TBIET7YU.js → src-DUGLX4YB.js} +7270 -2119
  16. package/dist/src-DUGLX4YB.js.map +1 -0
  17. package/package.json +17 -17
  18. package/templates/README.md +4 -4
  19. package/templates/apps/admin/src/routes/+layout.svelte.hbs +7 -0
  20. package/templates/apps/admin/src/routes/+page.svelte.hbs +5 -4
  21. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +2 -2
  22. package/templates/apps/api/.npmrc.hbs +1 -2
  23. package/templates/apps/api/Dockerfile.hbs +6 -5
  24. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -1
  25. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +2 -0
  26. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +1 -1
  27. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +8 -0
  28. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -1
  29. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +7 -8
  30. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +4 -3
  31. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +0 -1
  32. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +4 -0
  33. package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +3 -1
  34. package/templates/apps/api/e2e/api-service.spec.ts.hbs +3 -1
  35. package/templates/apps/api/package.json.hbs +2 -2
  36. package/templates/apps/api/pnpm-workspace.yaml.hbs +26 -0
  37. package/templates/apps/api/tsconfig.base.json +0 -1
  38. package/templates/apps/fullstack/.dockerignore.hbs +3 -0
  39. package/templates/apps/fullstack/.npmrc.hbs +1 -2
  40. package/templates/apps/fullstack/Dockerfile.hbs +6 -5
  41. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +2 -2
  42. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -1
  43. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +7 -2
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +1 -34
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +1 -34
  46. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +2 -2
  47. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +30 -170
  48. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +1 -1
  49. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +6 -5
  50. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +1 -1
  51. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +48 -22
  52. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +3 -1
  53. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +6 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/en-US.json.hbs +1 -28
  55. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/zh-CN.json.hbs +1 -28
  56. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +5 -4
  57. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +10 -141
  58. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +5 -5
  59. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +3 -3
  60. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +21 -2
  61. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +2 -2
  62. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +6 -1
  63. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/en-US.json.hbs +1 -28
  64. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/zh-CN.json.hbs +1 -28
  65. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +2 -2
  66. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +5 -143
  67. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +1 -1
  68. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +1 -1
  69. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +48 -22
  70. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +206 -16
  71. package/templates/apps/fullstack/package.json.hbs +2 -2
  72. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +2 -0
  73. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -1
  74. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +66 -0
  75. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +96 -0
  76. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-schemas.ts.hbs +3 -0
  77. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +3 -1
  78. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +5 -0
  79. package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +3 -0
  80. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +1 -1
  81. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +13 -3
  82. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +3 -0
  83. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +2 -0
  84. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +10 -3
  85. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +3 -0
  86. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +21 -0
  87. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +10 -3
  88. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +3 -0
  89. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -3
  90. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +63 -28
  91. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +26 -11
  92. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +276 -0
  93. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +29 -0
  94. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +94 -0
  95. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -0
  96. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +5 -1
  97. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +98 -0
  98. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +446 -33
  99. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +48 -0
  100. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +1 -1
  101. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +19 -26
  102. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +78 -15
  103. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +78 -15
  104. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +4 -1
  105. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +3 -4
  106. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +53 -27
  107. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +73 -0
  108. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +55 -45
  109. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AuditManagement.svelte.hbs +61 -0
  110. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ProfileSettings.svelte.hbs +57 -0
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/RoleManagement.svelte.hbs +49 -0
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/UserManagement.svelte.hbs +174 -0
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +399 -0
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +7 -39
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/svelte.config.js.hbs +1 -0
  116. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +151 -0
  117. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +101 -0
  118. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -0
  119. package/templates/apps/fullstack/playwright.config.ts.hbs +6 -0
  120. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +24 -0
  121. package/templates/apps/fullstack/tsconfig.base.json +0 -1
  122. package/templates/apps/h5/src/routes/+layout.svelte.hbs +4 -0
  123. package/templates/apps/h5/src/routes/+page.svelte.hbs +3 -2
  124. package/templates/apps/mobile-app/index.html.hbs +2 -2
  125. package/templates/apps/mobile-app/src/App.svelte.hbs +6 -2
  126. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +13 -0
  127. package/templates/apps/website/src/routes/+layout.svelte.hbs +8 -2
  128. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -3
  129. package/templates/base/.npmrc.hbs +1 -15
  130. package/templates/base/Dockerfile.hbs +9 -8
  131. package/templates/base/e2e/app.spec.ts.hbs +1 -0
  132. package/templates/base/package.json.hbs +39 -42
  133. package/templates/base/pnpm-workspace.yaml.hbs +26 -0
  134. package/templates/base/src/app.d.ts.hbs +4 -0
  135. package/templates/base/src/hooks.server.ts.hbs +0 -8
  136. package/templates/base/src/lib/server/init.ts.hbs +26 -5
  137. package/templates/base/src/routes/+layout.server.ts.hbs +18 -0
  138. package/templates/base/tests/scaffold.test.ts.hbs +21 -1
  139. package/templates/base/vite.config.ts.hbs +21 -0
  140. package/templates/features/ai/routes/api/chat/+server.ts +6 -7
  141. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +1 -1
  142. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +1 -1
  143. package/templates/features/iam/routes-shared/api/auth/login/+server.ts +14 -10
  144. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +6 -4
  145. package/templates/features/storage/routes/api/upload/+server.ts +10 -8
  146. package/templates/skills/CLAUDE.md +0 -14
  147. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +13 -2
  148. package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +3 -0
  149. package/templates/skills/hai-api-client/SKILL.md +1 -1
  150. package/templates/skills/hai-app-create/development.md +6 -0
  151. package/templates/skills/hai-build/reference.md +4 -2
  152. package/templates/skills/hai-core/SKILL.md +2 -0
  153. package/templates/skills/hai-core/reference.md +6 -4
  154. package/templates/skills/hai-framework-sync/SKILL.md +1 -2
  155. package/templates/skills/hai-serv/SKILL.md +1 -0
  156. package/templates/skills/hai-serv/reference.md +3 -1
  157. package/templates/skills/hai-ui/SKILL.md +6 -0
  158. package/templates/skills/hai-ui/reference.md +7 -4
  159. package/templates/skills/hai-vecdb/reference.md +53 -49
  160. package/dist/chunk-MJGUBOAV.js.map +0 -1
  161. package/dist/chunk-O3ANVICQ.js.map +0 -1
  162. package/dist/cli-config-templates-OWBEGBZQ.js +0 -5
  163. package/dist/src-TBIET7YU.js.map +0 -1
  164. package/templates/apps/fullstack/apps/{{projectName}}-app/tests/api.test.ts.hbs +0 -28
  165. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tests/api.test.ts.hbs +0 -28
  166. package/templates/apps/fullstack/apps/{{projectName}}-web/tests/api.test.ts.hbs +0 -55
  167. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/LanguageSwitcher.svelte.hbs +0 -27
  168. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +0 -18
  169. package/templates/skills/bridges/admin/CLAUDE.md +0 -15
  170. package/templates/skills/bridges/api/CLAUDE.md +0 -15
  171. package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +0 -9
  172. package/templates/skills/bridges/h5/CLAUDE.md +0 -15
  173. package/templates/skills/bridges/mobile-app/CLAUDE.md +0 -15
  174. package/templates/skills/bridges/website/CLAUDE.md +0 -15
  175. package/templates/skills/claude-skill.md.hbs +0 -7
@@ -1,186 +1,46 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
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'
4
+ import type { ManagementApiOptions } from '{{fullstack.sharedPackageName}}'
5
+ import { capacitor, createCapacitorTokenStorage } from '@h-ai/capacitor'
6
+ import { Alert } from '@h-ai/ui/components/compounds/index'
7
+ import { Button, Spinner } from '@h-ai/ui/components/primitives/index'
8
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}}'
9
+ import { AdminWorkspace, messages } from '{{fullstack.sharedPackageName}}'
16
10
  import { initCapacitor } from './lib/capacitor.js'
11
+ import { setLocale } from './lib/paraglide/runtime.js'
17
12
 
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
-
13
+ let apiOptions = $state<ManagementApiOptions | null>(null)
14
+ let startupFailed = $state(false)
31
15
  function syncLocale(locale: string) {
32
- setLocale(locale as Parameters<typeof setLocale>[0])
16
+ setLocale(locale as Parameters<typeof setLocale>[0], { reload: false })
33
17
  }
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' })
18
+ async function initialize() {
19
+ startupFailed = false
20
+ const result = await initCapacitor()
21
+ if (!result.success) {
22
+ startupFailed = true
23
+ return
45
24
  }
46
- catch {
47
- info = null
48
- errorMessage = sharedM.home_error()
49
- }
50
- finally {
51
- loading = false
25
+ apiOptions = {
26
+ clientName: '{{projectName}}-app',
27
+ ...(capacitor.isNative() ? { requireApiBase: true, tokenStorage: createCapacitorTokenStorage(), headers: { 'X-HAI-Token-Transport': 'body' } } : {}),
52
28
  }
53
29
  }
54
-
55
30
  onMount(() => {
56
- document.title = appM.page_title({ name: sharedM.app_name() })
57
- syncLocale(getSharedLocale())
58
- void initCapacitor()
59
- void refreshStatus()
31
+ void initialize()
32
+ return () => void capacitor.close()
60
33
  })
61
34
  </script>
62
35
 
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
- />
36
+ {#if startupFailed}
37
+ <div class="mx-auto flex min-h-dvh max-w-md flex-col justify-center gap-4 p-6">
38
+ <Alert variant="error">{messages.operation_failed()}</Alert>
39
+ <Button onclick={() => void initialize()}>{messages.home_refresh()}</Button>
182
40
  </div>
183
- </div>
184
-
185
- <ToastContainer />
186
- {{/if}}
41
+ {:else if apiOptions}
42
+ <AdminWorkspace platform="app" {apiOptions} onLocaleChange={syncLocale} />
43
+ {:else}
44
+ <div class="flex min-h-dvh items-center justify-center"><Spinner /></div>
45
+ {/if}
46
+ {{/if}}
@@ -5,4 +5,4 @@
5
5
  import '@h-ai/ui/auto-import'
6
6
 
7
7
  declare module '*.css'
8
- {{/if}}
8
+ {{/if}}
@@ -1,18 +1,19 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
+ import type { HaiResult } from '@h-ai/core'
3
4
  import { capacitor } from '@h-ai/capacitor'
4
5
 
5
- export async function initCapacitor(): Promise<void> {
6
+ export async function initCapacitor(): Promise<HaiResult<void>> {
6
7
  const result = await capacitor.init()
7
- if (!result.success) {
8
- return
9
- }
8
+ if (!result.success)
9
+ return result
10
10
 
11
11
  if (capacitor.isNative()) {
12
- await capacitor.statusBar.configure({
12
+ return capacitor.statusBar.configure({
13
13
  style: 'dark',
14
14
  overlay: true,
15
15
  })
16
16
  }
17
+ return result
17
18
  }
18
19
  {{/if}}
@@ -11,4 +11,4 @@ if (!target)
11
11
  const app = mount(App, { target })
12
12
 
13
13
  export default app
14
- {{/if}}
14
+ {{/if}}
@@ -1,32 +1,58 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.app}}
3
+ import process from 'node:process'
4
+ import { fileURLToPath } from 'node:url'
3
5
  import { haiOptimizeExclude, haiPrebundledDeps } from '@h-ai/ui/vite'
4
6
  import { paraglideVitePlugin } from '@inlang/paraglide-js'
5
7
  import { svelte } from '@sveltejs/vite-plugin-svelte'
6
8
  import tailwindcss from '@tailwindcss/vite'
9
+ import { loadEnv } from 'vite'
7
10
  import { defineConfig } from 'vitest/config'
8
11
 
9
- export default defineConfig({
10
- plugins: [
11
- svelte(),
12
- tailwindcss(),
13
- paraglideVitePlugin({
14
- project: './project.inlang',
15
- outdir: './src/lib/paraglide',
16
- strategy: ['cookie', 'baseLocale'],
17
- }),
18
- ],
19
- optimizeDeps: {
20
- exclude: [...haiOptimizeExclude],
21
- include: [...haiPrebundledDeps],
22
- },
23
- envPrefix: ['VITE_', 'PUBLIC_'],
24
- ssr: {
25
- noExternal: [/@h-ai\//, /{{fullstack.sharedPackageName}}/],
26
- },
27
- test: {
28
- environment: 'node',
29
- include: ['tests/**/*.test.ts'],
30
- },
12
+ const envDir = fileURLToPath(new URL('../../', import.meta.url))
13
+
14
+ export default defineConfig(({ mode }) => {
15
+ const env = loadEnv(mode, envDir, 'PUBLIC_')
16
+ const target = (env.PUBLIC_API_BASE || process.env.SERVICE_BASE_URL || 'http://127.0.0.1:3000').replace(/\/api\/v1\/?$/, '')
17
+ const proxy = { '/api': { target, changeOrigin: true } }
18
+ return {
19
+ envDir,
20
+ server: { proxy },
21
+ preview: { proxy },
22
+ build: {
23
+ rolldownOptions: {
24
+ output: {
25
+ codeSplitting: {
26
+ groups: [
27
+ { name: 'vendor-ui', test: /node_modules\/(?:@h-ai\/ui|bits-ui|svelte)\// },
28
+ { name: 'vendor-crypto', test: /node_modules\/(?:@h-ai\/(?:core|crypto)|sm-crypto(?:-v2)?)\// },
29
+ { name: 'vendor-api', test: /node_modules\/(?:@orpc\/|zod\/)/ },
30
+ ],
31
+ },
32
+ },
33
+ },
34
+ },
35
+ plugins: [
36
+ svelte(),
37
+ paraglideVitePlugin({
38
+ project: './project.inlang',
39
+ outdir: './src/lib/paraglide',
40
+ strategy: ['cookie', 'baseLocale'],
41
+ }),
42
+ tailwindcss(),
43
+ ],
44
+ optimizeDeps: {
45
+ exclude: [...haiOptimizeExclude],
46
+ include: [...haiPrebundledDeps],
47
+ },
48
+ envPrefix: ['VITE_', 'PUBLIC_'],
49
+ ssr: {
50
+ noExternal: [/@h-ai\//, /{{fullstack.sharedPackageName}}/],
51
+ },
52
+ test: {
53
+ environment: 'node',
54
+ include: ['tests/**/*.test.ts'],
55
+ },
56
+ }
31
57
  })
32
58
  {{/if}}
@@ -8,7 +8,9 @@
8
8
 
9
9
  - 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
10
10
  - 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
11
- - 语言切换由 shared 的 `LanguageSwitcher` 触发,并同步本端 `$lib/paraglide/runtime.js`
11
+ - 个人设置保存语言与风格偏好到 IAM 元数据,并同步 shared 和本端 Paraglide runtime
12
+
13
+ 原生 Tauri 使用内存 TokenStorage,不将凭证写入磁盘;重启后需要重新登录。浏览器预览使用 httpOnly refresh Cookie。两种方式均经共享加密 API 客户端。`src-tauri/tauri.conf.json` 的资源与网络白名单应与部署的 API 地址同步。
12
14
 
13
15
  自定义建议:桌面端菜单、状态、快捷操作文案放在本目录 `messages/`;跨端通用能力放到 shared。
14
16
 
@@ -5,7 +5,12 @@
5
5
  <head>
6
6
  <meta charset="UTF-8" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
- <title>{{projectName}} Desktop</title>
8
+ <title>{{html fullstack.appName}}</title>
9
+ <link
10
+ rel="icon"
11
+ type="image/svg+xml"
12
+ href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%234438df'/%3E%3Cpath d='M10 8v16M22 8v16M10 16h12' fill='none' stroke='white' stroke-width='3'/%3E%3C/svg%3E"
13
+ />
9
14
  </head>
10
15
  <body>
11
16
  <div id="app"></div>
@@ -2,33 +2,6 @@
2
2
  {{#if fullstack.frontends.desktop}}
3
3
  {
4
4
  "$schema": "https://inlang.com/schema/inlang-message-format",
5
- "page_title": "{name} · Desktop",
6
- "platform_name": "Desktop",
7
- "hero_badge": "Desktop workspace",
8
- "hero_title": "{name} Desktop Command Center",
9
- "hero_subtitle": "A wide-screen desktop starter that can later integrate Tauri while reusing shared shell, theme, locale and API client capabilities.",
10
- "hero_primary_action": "View service status",
11
- "hero_secondary_action": "Customize the starter",
12
- "preview_title": "Desktop productivity UI",
13
- "preview_body": "Ready for command centers, tool workspaces and native-enhanced apps",
14
- "preview_metric_shared": "Shared UI/API capabilities",
15
- "preview_metric_local": "Desktop-owned messages",
16
- "preview_metric_theme": "Configurable themes",
17
- "status_title": "Service connectivity",
18
- "status_description": "The desktop page calls the backend service to verify API contract, service and shared client wiring.",
19
- "status_online": "{name} is connected",
20
- "status_version": "Service version",
21
- "status_started": "Started at: {time}",
22
- "status_frontends": "Service frontends: {list}",
23
- "status_frontends_count": "{count} frontend targets",
24
- "custom_shared_title": "Shared owns common capabilities",
25
- "custom_shared_body": "Common shell, theme, locale switching and API client live in packages/{{projectName}}-shared and are reused by Desktop.",
26
- "custom_local_title": "Desktop owns desktop copy",
27
- "custom_local_body": "Desktop-specific menus, states and feature descriptions live in apps/{{projectName}}-desktop/messages.",
28
- "flow_title": "Collaboration boundary",
29
- "flow_contract": "Contract locks API contracts",
30
- "flow_service": "Service provides runtime capabilities",
31
- "flow_shared": "Shared reuses cross-platform UI/API",
32
- "flow_frontend": "Desktop composes the product experience"
5
+ "page_title": "{name}"
33
6
  }
34
7
  {{/if}}
@@ -2,33 +2,6 @@
2
2
  {{#if fullstack.frontends.desktop}}
3
3
  {
4
4
  "$schema": "https://inlang.com/schema/inlang-message-format",
5
- "page_title": "{name} · Desktop",
6
- "platform_name": "Desktop",
7
- "hero_badge": "桌面端工作台",
8
- "hero_title": "{name} Desktop 控制中心",
9
- "hero_subtitle": "面向桌面端的大屏工作台样板,可后续接入 Tauri,同时复用 shared 的 Shell、主题、语言与 API 客户端。",
10
- "hero_primary_action": "查看服务状态",
11
- "hero_secondary_action": "了解如何自定义",
12
- "preview_title": "桌面端生产力界面",
13
- "preview_body": "适合控制中心、工具台与本地增强应用",
14
- "preview_metric_shared": "共享 UI/API 能力",
15
- "preview_metric_local": "Desktop 自有 messages",
16
- "preview_metric_theme": "主题可配置",
17
- "status_title": "服务联通状态",
18
- "status_description": "桌面端页面会调用后端服务,确认 API contract、service 与 shared client 链路正常。",
19
- "status_online": "{name} 已连接",
20
- "status_version": "服务版本",
21
- "status_started": "启动时间:{time}",
22
- "status_frontends": "服务声明的前端:{list}",
23
- "status_frontends_count": "{count} 个前端目标",
24
- "custom_shared_title": "共享层放通用能力",
25
- "custom_shared_body": "通用 Shell、主题、语言切换和 API client 放在 packages/{{projectName}}-shared,桌面端直接复用。",
26
- "custom_local_title": "Desktop 层放桌面文案",
27
- "custom_local_body": "桌面端特有菜单、状态与功能说明写在 apps/{{projectName}}-desktop/messages。",
28
- "flow_title": "协同边界",
29
- "flow_contract": "Contract 固化接口契约",
30
- "flow_service": "Service 提供运行时能力",
31
- "flow_shared": "Shared 复用跨端基础 UI/API",
32
- "flow_frontend": "Desktop 组合桌面产品体验"
5
+ "page_title": "{name}"
33
6
  }
34
7
  {{/if}}
@@ -1,7 +1,7 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.desktop}}
3
3
  {
4
- "engines": { "node": ">=22.12.0" },
4
+ "engines": { "node": "^22.22.2 || ^24.15.0 || >=26.0.0" },
5
5
  "name": "{{projectName}}-desktop",
6
6
  "type": "module",
7
7
  "version": "0.1.0",
@@ -15,9 +15,9 @@
15
15
  "deploy": "pnpm package",
16
16
  "preview": "vite preview --host 127.0.0.1 --port {{fullstack.ports.desktop}} --strictPort",
17
17
  "typecheck": "pnpm paraglide:compile && svelte-check --tsconfig ./tsconfig.json",
18
- "lint": "eslint src tests vite.config.ts svelte.config.js",
19
- "lint:fix": "eslint src tests vite.config.ts svelte.config.js --fix",
20
- "test": "pnpm paraglide:compile && vitest run",
18
+ "lint": "eslint src vite.config.ts svelte.config.js",
19
+ "lint:fix": "eslint src vite.config.ts svelte.config.js --fix",
20
+ "test": "pnpm paraglide:compile && vitest run --passWithNoTests",
21
21
  "test:watch": "pnpm paraglide:compile && vitest",
22
22
  "clean": "rimraf dist .turbo src-tauri/target src/lib/paraglide",
23
23
  "tauri": "tauri",
@@ -25,6 +25,7 @@
25
25
  "tauri:build": "tauri build"
26
26
  },
27
27
  "dependencies": {
28
+ "@h-ai/api-client": "{{haiPackageSpecifier}}",
28
29
  "@h-ai/ui": "{{haiPackageSpecifier}}",
29
30
  "{{fullstack.sharedPackageName}}": "workspace:*"
30
31
  },
@@ -1,152 +1,21 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if fullstack.frontends.desktop}}
3
3
  <script lang="ts">
4
- import type { AppInfo } from '{{fullstack.sharedPackageName}}'
5
- import * as appM from './lib/paraglide/messages.js'
4
+ import type { ManagementApiOptions } from '{{fullstack.sharedPackageName}}'
5
+ import { apiClient } from '@h-ai/api-client'
6
+ import { AdminWorkspace } from '{{fullstack.sharedPackageName}}'
6
7
  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
8
 
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])
9
+ const native = '__TAURI_INTERNALS__' in globalThis
10
+ const apiOptions: ManagementApiOptions = {
11
+ clientName: '{{projectName}}-desktop',
12
+ ...(native ? { requireApiBase: true, tokenStorage: apiClient.tokenStorage.memory(), headers: { 'X-HAI-Token-Transport': 'body' } } : {}),
17
13
  }
18
14
 
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
- }
15
+ function syncLocale(locale: string) {
16
+ setLocale(locale as Parameters<typeof setLocale>[0], { reload: false })
32
17
  }
33
-
34
- onMount(() => {
35
- document.title = appM.page_title({ name: sharedM.app_name() })
36
- void refreshStatus()
37
- })
38
18
  </script>
39
19
 
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>
20
+ <AdminWorkspace platform="desktop" {apiOptions} onLocaleChange={syncLocale} />
152
21
  {{/if}}
@@ -19,11 +19,11 @@ panic = "abort"
19
19
  strip = true
20
20
 
21
21
  [build-dependencies]
22
- tauri-build = { version = "2", features = [] }
22
+ tauri-build = { version = "2.7.2", features = [] }
23
23
 
24
24
  [dependencies]
25
- tauri = { version = "2", features = [] }
26
- tauri-plugin-shell = "2"
27
- serde = { version = "1", features = ["derive"] }
28
- serde_json = "1"
25
+ tauri = { version = "2.12.3", features = [] }
26
+ tauri-plugin-shell = "2.5.0"
27
+ serde = { version = "1.0.229", features = ["derive"] }
28
+ serde_json = "1.0.152"
29
29
  {{/if}}