@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.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 (254) hide show
  1. package/README.en.md +74 -154
  2. package/README.ja.md +74 -154
  3. package/README.md +74 -154
  4. package/dist/{chunk-AKV2CQZV.js → chunk-2ZL5ORQ2.js} +316 -97
  5. package/dist/chunk-2ZL5ORQ2.js.map +1 -0
  6. package/dist/{chunk-EF7WOFWZ.js → chunk-L53P43B2.js} +1312 -163
  7. package/dist/chunk-L53P43B2.js.map +1 -0
  8. package/dist/cli-config-templates-LWMD2I6V.js +5 -0
  9. package/dist/{cli-config-templates-HHFHNXBN.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
  10. package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
  11. package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +30118 -3253
  14. package/dist/index.js.map +1 -1
  15. package/dist/metafile-esm.json +1 -1
  16. package/package.json +22 -18
  17. package/templates/README.md +45 -30
  18. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
  19. package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
  20. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
  21. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
  22. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
  23. package/templates/apps/api/.dockerignore.hbs +10 -0
  24. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +8 -0
  25. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
  26. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
  27. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
  28. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  29. package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +6 -2
  30. package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +24 -2
  31. package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +4 -4
  32. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +74 -24
  33. package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  34. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +153 -37
  35. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
  36. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +106 -33
  37. package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
  38. package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  39. package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
  40. package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
  41. package/templates/apps/api/eslint.config.js.hbs +3 -1
  42. package/templates/apps/api/images/serv.Dockerfile.hbs +15 -10
  43. package/templates/apps/api/playwright.config.ts.hbs +23 -19
  44. package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
  45. package/templates/apps/fullstack/.dockerignore.hbs +10 -0
  46. package/templates/apps/fullstack/.gitignore.hbs +7 -2
  47. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +14 -12
  48. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +5 -3
  49. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
  50. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -1
  51. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
  56. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +10 -8
  57. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +3 -3
  58. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
  59. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
  60. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +0 -2
  61. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
  62. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
  63. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +2 -0
  64. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +9 -9
  65. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
  66. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +1 -0
  67. package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
  68. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
  69. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
  70. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
  71. package/templates/apps/fullstack/docs/security-configuration.md.hbs +4 -1
  72. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +140 -28
  73. package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
  74. package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
  75. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +19 -11
  76. package/templates/apps/fullstack/images/web.Dockerfile.hbs +17 -8
  77. package/templates/apps/fullstack/package.json.hbs +2 -1
  78. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +7 -5
  79. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
  80. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
  81. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  82. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +17 -7
  83. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +13 -7
  84. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +17 -7
  85. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -4
  86. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
  87. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  88. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -2
  89. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
  90. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
  91. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
  92. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
  93. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
  94. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +147 -33
  95. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
  96. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +146 -30
  97. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
  98. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +142 -26
  99. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
  100. package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  101. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +48 -66
  102. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +1 -0
  103. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +1 -0
  104. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +1 -0
  105. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
  106. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
  107. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
  108. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
  109. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
  110. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
  116. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
  117. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
  118. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
  119. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
  120. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
  121. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
  122. package/templates/apps/fullstack/playwright.config.ts.hbs +25 -3
  123. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -14
  124. package/templates/apps/h5/src/routes/+layout.svelte.hbs +74 -30
  125. package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
  126. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
  127. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
  128. package/templates/apps/mobile-app/capacitor.config.ts.hbs +5 -2
  129. package/templates/apps/mobile-app/messages/en-US.json +2 -2
  130. package/templates/apps/mobile-app/messages/ja-JP.json +2 -2
  131. package/templates/apps/mobile-app/messages/zh-CN.json +2 -2
  132. package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
  133. package/templates/apps/mobile-app/src/lib/api.ts.hbs +20 -6
  134. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
  135. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
  136. package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -1
  137. package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
  138. package/templates/apps/website/scripts/static-website.mjs.hbs +19 -6
  139. package/templates/apps/website/src/routes/+layout.svelte.hbs +56 -29
  140. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
  141. package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
  142. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
  143. package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
  144. package/templates/base/.dockerignore.hbs +10 -0
  145. package/templates/base/.gitignore.hbs +7 -2
  146. package/templates/base/e2e/app.spec.ts.hbs +130 -36
  147. package/templates/base/eslint-rules.js +44 -25
  148. package/templates/base/eslint.config.js.hbs +3 -1
  149. package/templates/base/images/web.Dockerfile.hbs +25 -14
  150. package/templates/base/package.json.hbs +13 -7
  151. package/templates/base/patches/daisyui-5.7.50.patch +8 -0
  152. package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
  153. package/templates/base/playwright.config.ts +3 -1
  154. package/templates/base/pnpm-workspace.yaml.hbs +33 -16
  155. package/templates/base/src/app.d.ts.hbs +1 -1
  156. package/templates/base/src/app.html.hbs +1 -0
  157. package/templates/base/src/hooks.server.ts.hbs +44 -33
  158. package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
  159. package/templates/base/src/lib/server/init.ts.hbs +146 -35
  160. package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
  161. package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
  162. package/templates/base/svelte.config.js.hbs +10 -100
  163. package/templates/base/tests/scaffold.test.ts.hbs +12 -3
  164. package/templates/base/tsconfig.json.hbs +7 -4
  165. package/templates/base/vite.config.ts.hbs +108 -14
  166. package/templates/base/vitest.config.ts.hbs +6 -2
  167. package/templates/features/ai/routes/api/chat/+server.ts +3 -1
  168. package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
  169. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
  170. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
  171. package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
  172. package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
  173. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
  174. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
  175. package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
  176. package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
  177. package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
  178. package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
  179. package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
  180. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +14 -3
  181. package/templates/shared/browser-check/startup.js +115 -0
  182. package/templates/shared/browser-check/update.css +37 -0
  183. package/templates/shared/browser-check/update.html +19 -0
  184. package/templates/shared/browser-startup.spec.ts +23 -0
  185. package/templates/shared/build-metadata.ts +18 -4
  186. package/templates/shared/e2e-env.ts +22 -6
  187. package/templates/shared/native-name.d.mts +5 -0
  188. package/templates/shared/native-name.mjs +94 -0
  189. package/templates/shared/public-base-path.mjs +17 -4
  190. package/templates/shared/run-e2e.mjs +46 -12
  191. package/templates/shared/run-node.mjs +56 -0
  192. package/templates/shared/run-tauri.mjs +74 -0
  193. package/templates/shared/vite-runtime-config.ts +9 -3
  194. package/templates/skills/AGENTS.md +10 -73
  195. package/templates/skills/bridges/admin/AGENTS.md +9 -65
  196. package/templates/skills/bridges/api/AGENTS.md +8 -59
  197. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
  198. package/templates/skills/bridges/h5/AGENTS.md +8 -63
  199. package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
  200. package/templates/skills/bridges/website/AGENTS.md +8 -63
  201. package/templates/skills/hai-ai/SKILL.md +6 -2
  202. package/templates/skills/hai-ai/acp.md +153 -0
  203. package/templates/skills/hai-ai/guide.md +27 -12
  204. package/templates/skills/hai-ai/reference.md +24 -15
  205. package/templates/skills/hai-ai/resources.md +69 -0
  206. package/templates/skills/hai-api-client/SKILL.md +1 -1
  207. package/templates/skills/hai-api-client/reference.md +3 -1
  208. package/templates/skills/hai-app-create/collaboration.md +7 -0
  209. package/templates/skills/hai-app-create/development.md +19 -10
  210. package/templates/skills/hai-app-create/project.md +33 -0
  211. package/templates/skills/hai-app-create/reference.md +10 -10
  212. package/templates/skills/hai-app-tests/reference.md +1 -1
  213. package/templates/skills/hai-app-tests/validation.md +1 -0
  214. package/templates/skills/hai-build/SKILL.md +19 -9
  215. package/templates/skills/hai-build/paths.md +4 -2
  216. package/templates/skills/hai-build/reference.md +26 -8
  217. package/templates/skills/hai-cache/reference.md +6 -2
  218. package/templates/skills/hai-capacitor/SKILL.md +1 -0
  219. package/templates/skills/hai-capacitor/reference.md +24 -0
  220. package/templates/skills/hai-ci/SKILL.md +3 -0
  221. package/templates/skills/hai-core/SKILL.md +9 -4
  222. package/templates/skills/hai-core/reference.md +40 -10
  223. package/templates/skills/hai-core/request-access.md +2 -2
  224. package/templates/skills/hai-crypto/SKILL.md +2 -2
  225. package/templates/skills/hai-crypto/reference.md +7 -3
  226. package/templates/skills/hai-deploy/SKILL.md +41 -5
  227. package/templates/skills/hai-deploy/middleware.md +26 -14
  228. package/templates/skills/hai-framework-sync/SKILL.md +13 -9
  229. package/templates/skills/hai-framework-sync/sync.md +2 -0
  230. package/templates/skills/hai-iam/reference.md +2 -2
  231. package/templates/skills/hai-kit/SKILL.md +1 -1
  232. package/templates/skills/hai-kit/reference.md +19 -5
  233. package/templates/skills/hai-payment/reference.md +41 -17
  234. package/templates/skills/hai-reach/SKILL.md +3 -1
  235. package/templates/skills/hai-reach/reference.md +29 -9
  236. package/templates/skills/hai-reldb/SKILL.md +1 -1
  237. package/templates/skills/hai-reldb/reference.md +9 -2
  238. package/templates/skills/hai-scheduler/reference.md +1 -1
  239. package/templates/skills/hai-serv/SKILL.md +2 -1
  240. package/templates/skills/hai-serv/reference.md +26 -15
  241. package/templates/skills/hai-storage/reference.md +23 -6
  242. package/templates/skills/hai-ui/SKILL.md +11 -6
  243. package/templates/skills/hai-ui/charts.md +51 -0
  244. package/templates/skills/hai-ui/guide.md +1 -1
  245. package/templates/skills/hai-ui/reference.md +58 -38
  246. package/templates/skills/hai-vecdb/reference.md +8 -3
  247. package/dist/chunk-AKV2CQZV.js.map +0 -1
  248. package/dist/chunk-EF7WOFWZ.js.map +0 -1
  249. package/dist/chunk-MUHRBJSW.js +0 -29272
  250. package/dist/chunk-MUHRBJSW.js.map +0 -1
  251. package/dist/cli-config-templates-HHFHNXBN.js +0 -5
  252. package/dist/src-TEBHR5I5.js +0 -5
  253. package/dist/src-TEBHR5I5.js.map +0 -1
  254. package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +0 -18
@@ -2,6 +2,11 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
2
2
  import { defineConfig } from 'vitest/config'
3
3
 
4
4
  export default defineConfig({
5
- plugins: [svelte()],
6
- test: { environment: 'node', include: ['tests/**/*.test.ts'] },
5
+ plugins: [
6
+ svelte(),
7
+ ],
8
+ test: {
9
+ environment: 'node',
10
+ include: ['tests/**/*.test.ts'],
11
+ },
7
12
  })
@@ -2,6 +2,9 @@ import process from 'node:process'
2
2
  import { defineConfig } from '@playwright/test'
3
3
  import { createE2eNotificationEnv, createE2eServerEnv, createE2eTransportRing } from './scripts/e2e-env.js'
4
4
 
5
+ {{#if fullstack.e2eFrontend}}
6
+ const sessionClose = process.env.HAI_E2E_GROUP === 'session-close'
7
+ {{/if}}
5
8
  const defaultBaseURL = '{{#if fullstack.e2eFrontend}}http://127.0.0.1:{{fullstack.e2eFrontend.port}}{{else}}http://127.0.0.1:3000{{/if}}'
6
9
  const defaultServiceBaseURL = 'http://127.0.0.1:3000'
7
10
  const baseURL = process.env.BASE_URL || defaultBaseURL
@@ -41,7 +44,9 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
41
44
  return url.port
42
45
  }
43
46
 
44
- return url.protocol === 'https:' ? '443' : '80'
47
+ return url.protocol === 'https:'
48
+ ? '443'
49
+ : '80'
45
50
  }
46
51
  catch {
47
52
  return fallbackPort
@@ -50,7 +55,11 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
50
55
 
51
56
  export default defineConfig({
52
57
  testDir: './e2e',
53
- testIgnore: '**/session-close.spec.ts',
58
+ {{#if fullstack.e2eFrontend}}
59
+ ...(sessionClose
60
+ ? { testMatch: '**/session-close.spec.ts' }
61
+ : { testIgnore: '**/session-close.spec.ts' }),
62
+ {{/if}}
54
63
  // 多个界面验收共用管理员资料与引导状态,保留串行以免互相覆盖。
55
64
  fullyParallel: false,
56
65
  forbidOnly: !!process.env.CI,
@@ -87,6 +96,17 @@ export default defineConfig({
87
96
  HAI_IAM_CAPTCHA_COST: '100',
88
97
  HAI_IAM_CAPTCHA_MAXCOUNTER: '2',
89
98
  HAI_IAM_INITIALCREDENTIALS_ENABLED: 'true',
99
+ {{#if fullstack.e2eFrontend}}
100
+ // 会话回归单独启动服务,短超时和首次改密仅应用于这一轮。
101
+ ...(sessionClose
102
+ ? {
103
+ HAI_IAM_SESSION_ENDONPAGEHIDE: 'true',
104
+ HAI_IAM_SESSION_IDLETIMEOUT: '5',
105
+ HAI_IAM_SESSION_ABSOLUTETIMEOUT: '60',
106
+ HAI_IAM_PASSWORD_REQUIREINITIALCHANGE: 'true',
107
+ }
108
+ : {}),
109
+ {{/if}}
90
110
  HAI_IAM_REAUTHENTICATION_ENABLED: 'true',
91
111
  HAI_IAM_REAUTHENTICATION_OPERATIONS: JSON.stringify(['iam.users.invite', 'audit.backup']),
92
112
  HAI_AUDIT_BACKUP_ENABLED: 'true',
@@ -100,7 +120,9 @@ export default defineConfig({
100
120
  timeout: 120_000,
101
121
  },
102
122
  ...frontends.map((frontend) => {
103
- const url = frontend.target === primaryFrontend ? baseURL : frontend.url
123
+ const url = frontend.target === primaryFrontend
124
+ ? baseURL
125
+ : frontend.url
104
126
  return {
105
127
  name: frontend.name,
106
128
  command: `pnpm --filter ${frontend.name} exec vite preview --host ${resolveHostFromBaseUrl(url, '127.0.0.1')} --port ${resolvePortFromBaseUrl(url, '3000')} --strictPort`,
@@ -1,5 +1,5 @@
1
1
  {{! @skipIfEmpty }}
2
- shellEmulator: true
2
+ minimumReleaseAgeExcludePrune: true
3
3
 
4
4
  trustPolicy: no-downgrade
5
5
  # 上游构建工具仍要求 4.x;4.0.3 tarball 已核对原 lockfile 的 SHA-512。
@@ -8,31 +8,47 @@ trustPolicyExclude:
8
8
 
9
9
  # pnpm 12 配置;原生依赖仅运行明确列出的安装脚本。
10
10
  nodeLinker: hoisted
11
- allowBuilds:
12
- protobufjs: true
13
- vue-demi: true
14
- better-sqlite3: true
15
- esbuild: true
16
- core-js: false
11
+ shellEmulator: true
12
+
13
+ packages:
14
+ - packages/*
15
+ - apps/*
17
16
 
17
+ # 构建与浏览器依赖的安全修复;版本与框架工作区同步维护。
18
18
  overrides:
19
- "@ai-sdk/provider-utils@>=4.0.0 <4.0.33": 4.0.57
19
+ '@ai-sdk/provider-utils@>=4.0.0 <4.0.33': 4.0.57
20
+ '@types/node': {{versions.nodeTypes}}
21
+ body-parser@>=2 <2.4.0: 2.4.0
20
22
  cookie@<0.7.0: 0.7.2
23
+ dompurify@>=3 <3.4.17: 3.4.17
21
24
  esbuild@<0.28.1: 0.28.2
25
+ fast-uri@>=3 <3.1.9: 3.1.9
26
+ follow-redirects@>=1.16.0 <1.16.2: 1.16.2
27
+ glob@>=9 <=14.0.1: 14.0.4
28
+ ip-address@<=10.7.3: 10.7.6
22
29
  katex@<0.18.2: 0.18.10
30
+ minimatch@>=7 <11.0.1: 11.0.5
31
+ parse5@>=8 <8.1.0: 8.1.0
32
+ qs@>=6.7 <6.16.1: 6.17.1
33
+ runed@<0.38.0: 0.38.0
23
34
  sharp@<0.35.5: 0.35.5
24
35
  tinypool@<2.1.2: 2.2.0
25
- '@qdrant/js-client-rest>undici': 7.30.0
26
36
  xcode>uuid: 11.1.1
27
- "@types/node": "{{versions.nodeTypes}}"
28
37
 
29
- packages:
30
- - packages/*
31
- - apps/*
38
+ patchedDependencies:
39
+ daisyui@5.7.50: patches/daisyui-5.7.50.patch
40
+ deepmerge@4.3.1: patches/deepmerge-4.3.1.patch
41
+
42
+ allowBuilds:
43
+ core-js: false
44
+ esbuild: true
45
+ onnxruntime-node: true
46
+ protobufjs: true
47
+ vue-demi: true
32
48
 
33
49
  {{#if useCatalogProtocol}}
34
50
  catalog:
35
51
  {{#each haiCatalogPackages}}
36
- '{{@key}}': {{this}}
52
+ {{{yaml @key this}}}
37
53
  {{/each}}
38
54
  {{/if}}
@@ -5,20 +5,10 @@
5
5
  */
6
6
  import type { BottomNavItem } from '@h-ai/ui'
7
7
  import type { Snippet } from 'svelte'
8
- {{#if hasI18n}}
9
- import { browser } from '$app/environment'
10
- {{else}}
11
- {{#if features.crypto}}
12
- import { browser } from '$app/environment'
13
- {{/if}}
14
- {{/if}}
8
+ import { browser } from '$app/env'
15
9
  import { goto } from '$app/navigation'
16
- import { base, resolve } from '$app/paths'
10
+ import { resolve } from '$app/paths'
17
11
  import { page } from '$app/state'
18
- {{#if hasI18n}}
19
- import * as m from '$lib/paraglide/messages'
20
- import { getLocale, setLocale } from '$lib/paraglide/runtime.js'
21
- {{/if}}
22
12
  {{#if features.crypto}}
23
13
  import { crypto } from '@h-ai/crypto'
24
14
  import { kit } from '@h-ai/kit'
@@ -28,12 +18,17 @@
28
18
  {{/if}}
29
19
  {{/if}}
30
20
  {{#if hasI18n}}
31
- import { AppBar, BottomNav, LanguageSwitch, setGlobalLocale } from '@h-ai/ui'
21
+ import { AppBar, BottomNav, LanguageSwitch, setGlobalLocale, ThemeSelector } from '@h-ai/ui'
32
22
  {{else}}
33
- import { AppBar, BottomNav } from '@h-ai/ui'
23
+ import { AppBar, BottomNav, ThemeSelector } from '@h-ai/ui'
34
24
  {{/if}}
25
+ import { applyTheme, getSavedTheme } from '@h-ai/ui/theme'
35
26
  {{#if features.crypto}}
36
27
  import { untrack } from 'svelte'
28
+ {{/if}}
29
+ {{#if hasI18n}}
30
+ import * as m from '#lib/paraglide/messages.js'
31
+ import { getLocale, setLocale } from '#lib/paraglide/runtime.js'
37
32
  {{/if}}
38
33
  import '../app.css'
39
34
 
@@ -42,36 +37,82 @@
42
37
  }
43
38
 
44
39
  const { children }: Props = $props()
45
- {{#if features.crypto}}
40
+ const initialTheme = browser
41
+ ? getSavedTheme()
42
+ : 'light'
43
+ let currentTheme = $state(initialTheme)
46
44
  if (browser)
47
- kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
45
+ applyTheme(initialTheme, false)
46
+
47
+ function handleThemeChange(theme: string) {
48
+ currentTheme = theme
49
+ applyTheme(theme)
50
+ }
51
+ {{#if features.crypto}}
52
+ if (browser) {
53
+ kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), {
54
+ crypto,
55
+ basePath: resolve('/').replace(/\/$/, ''),
56
+ })
57
+ }
48
58
  {{/if}}
49
59
  {{#if features.iam}}
50
- const authActions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
60
+ const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
51
61
  $effect(() => authActions.bindPageExit({ enabled: page.data.iamPublicConfig?.session.endOnPageHide }))
52
62
  {{/if}}
53
63
 
54
- const navItems: (BottomNavItem & { path: '/' | '/discover' | '/profile' })[] = [
64
+ const navItems: (BottomNavItem & { path: ReturnType<typeof resolve> })[] = [
55
65
  {{#if hasI18n}}
56
- { id: 'home', path: '/', label: m.nav_home(), iconClass: 'icon-[tabler--home]' },
57
- { id: 'discover', path: '/discover', label: m.nav_discover(), iconClass: 'icon-[tabler--compass]' },
58
- { id: 'profile', path: '/profile', label: m.nav_profile(), iconClass: 'icon-[tabler--user]' },
66
+ {
67
+ id: 'home',
68
+ path: resolve('/'),
69
+ label: m.nav_home(),
70
+ iconClass: 'icon-[tabler--home]',
71
+ },
72
+ {
73
+ id: 'discover',
74
+ path: resolve('discover'),
75
+ label: m.nav_discover(),
76
+ iconClass: 'icon-[tabler--compass]',
77
+ },
78
+ {
79
+ id: 'profile',
80
+ path: resolve('profile'),
81
+ label: m.nav_profile(),
82
+ iconClass: 'icon-[tabler--user]',
83
+ },
59
84
  {{else}}
60
- { id: 'home', path: '/', label: '首页', iconClass: 'icon-[tabler--home]' },
61
- { id: 'discover', path: '/discover', label: '发现', iconClass: 'icon-[tabler--compass]' },
62
- { id: 'profile', path: '/profile', label: '我的', iconClass: 'icon-[tabler--user]' },
85
+ {
86
+ id: 'home',
87
+ path: resolve('/'),
88
+ label: '首页',
89
+ iconClass: 'icon-[tabler--home]',
90
+ },
91
+ {
92
+ id: 'discover',
93
+ path: resolve('discover'),
94
+ label: '发现',
95
+ iconClass: 'icon-[tabler--compass]',
96
+ },
97
+ {
98
+ id: 'profile',
99
+ path: resolve('profile'),
100
+ label: '我的',
101
+ iconClass: 'icon-[tabler--user]',
102
+ },
63
103
  {{/if}}
64
104
  ]
65
105
 
66
- const localPath = $derived(page.url.pathname.slice(base.length) || '/')
67
- const activeNav = $derived(navItems.find(item => item.path === localPath)?.id)
106
+ const localPath = $derived(page.url.pathname.slice(resolve('/').replace(/\/$/, '').length) || '/')
107
+ const activeNav = $derived(navItems.find(item => item.path === page.url.pathname)?.id)
68
108
  // 所有页面共用页头页脚;仅业务页面显示底部导航。
69
109
  const isAuthPage = $derived(localPath.startsWith('/auth'))
70
110
 
71
111
  function handleNavigation(id: string) {
72
112
  const item = navItems.find(item => item.id === id)
73
113
  if (item)
74
- void goto(resolve(item.path, {}))
114
+ // eslint-disable-next-line svelte/no-navigation-without-resolve -- path 仅来自上方 resolve 构造的导航表,不能二次添加挂载前缀。
115
+ void goto(item.path)
75
116
  }
76
117
  {{#if hasI18n}}
77
118
 
@@ -94,11 +135,14 @@
94
135
  <div class='hai-mobile-viewport'>
95
136
  <div class='hai-mobile-shell min-w-0'>
96
137
  <AppBar title={page.data.appConfig.name} safeArea fixed={false}>
97
- {{#if hasI18n}}
98
138
  {#snippet trailing()}
99
- <LanguageSwitch currentLanguage={getLocale()} onchange={handleLanguageChange} compact />
100
- {/snippet}
139
+ <div class='flex items-center gap-1'>
140
+ {{#if hasI18n}}
141
+ <LanguageSwitch currentLanguage={getLocale()} onchange={handleLanguageChange} compact />
101
142
  {{/if}}
143
+ <ThemeSelector {currentTheme} onchange={handleThemeChange} compact grouped={false} />
144
+ </div>
145
+ {/snippet}
102
146
  </AppBar>
103
147
  <main class='hai-mobile-main'>{@render children()}</main>
104
148
  <footer class='border-t border-base-300 bg-base-100 p-2 text-center text-xs text-base-content/60'>
@@ -4,10 +4,10 @@
4
4
  /**
5
5
  * H5 首页
6
6
  */
7
+ import { Card } from '@h-ai/ui'
7
8
  {{#if hasI18n}}
8
- import * as m from '$lib/paraglide/messages'
9
+ import * as m from '#lib/paraglide/messages.js'
9
10
  {{/if}}
10
- import { Card } from '@h-ai/ui'
11
11
  </script>
12
12
 
13
13
  <svelte:head>
@@ -3,10 +3,10 @@
3
3
  /**
4
4
  * 发现页面
5
5
  */
6
+ import { Card } from '@h-ai/ui'
6
7
  {{#if hasI18n}}
7
- import * as m from '$lib/paraglide/messages'
8
+ import * as m from '#lib/paraglide/messages.js'
8
9
  {{/if}}
9
- import { Card } from '@h-ai/ui'
10
10
  </script>
11
11
 
12
12
  <svelte:head>
@@ -3,10 +3,10 @@
3
3
  /**
4
4
  * 个人中心页面
5
5
  */
6
+ import { Card } from '@h-ai/ui'
6
7
  {{#if hasI18n}}
7
- import * as m from '$lib/paraglide/messages'
8
+ import * as m from '#lib/paraglide/messages.js'
8
9
  {{/if}}
9
- import { Card } from '@h-ai/ui'
10
10
  </script>
11
11
 
12
12
  <svelte:head>
@@ -1,12 +1,15 @@
1
1
  import type { CapacitorConfig } from '@capacitor/cli'
2
+ import { resolve } from 'node:path'
3
+ import process from 'node:process'
4
+ import { resolveNativeName } from './scripts/native-name.mjs'
2
5
 
3
6
  const config: CapacitorConfig = {
4
7
  appId: 'com.hai.{{projectName}}',
5
- appName: '{{projectName}}',
8
+ appName: resolveNativeName(process.cwd(), resolve('config/_core.yml')),
6
9
  webDir: 'dist',
7
10
  server: {
8
11
  androidScheme: 'https',
9
12
  },
10
13
  }
11
14
 
12
- export default config
15
+ export default config
@@ -24,13 +24,13 @@
24
24
  "status_runtime_web": "Browser preview",
25
25
  "status_token_label": "Token storage",
26
26
  "status_token_native": "Secure storage",
27
- "status_token_memory": "Memory storage",
27
+ "status_token_memory": "Memory access token + httpOnly cookie",
28
28
  "tasks_title": "Today's tasks",
29
29
  "tasks_sync": "Wire backend APIs",
30
30
  "tasks_polish": "Polish mobile UX",
31
31
  "tasks_release": "Prepare native packaging",
32
32
  "profile_title": "App settings",
33
33
  "profile_language": "Locale follows Paraglide",
34
- "profile_theme": "Theme follows @h-ai/ui",
34
+ "profile_theme": "Theme",
35
35
  "profile_secure": "Sensitive tokens avoid localStorage"
36
36
  }
@@ -24,13 +24,13 @@
24
24
  "status_runtime_web": "ブラウザープレビュー",
25
25
  "status_token_label": "トークン保存",
26
26
  "status_token_native": "安全なストレージ",
27
- "status_token_memory": "メモリー内保存",
27
+ "status_token_memory": "メモリー内access token + httpOnly Cookie",
28
28
  "tasks_title": "本日のタスク",
29
29
  "tasks_sync": "バックエンドAPIを接続",
30
30
  "tasks_polish": "モバイルUXを改善",
31
31
  "tasks_release": "ネイティブのパッケージ化を準備",
32
32
  "profile_title": "アプリ設定",
33
33
  "profile_language": "言語はParaglideに従います",
34
- "profile_theme": "テーマは@h-ai/uiに従います",
34
+ "profile_theme": "テーマ",
35
35
  "profile_secure": "機密トークンはlocalStorageに保存しません"
36
36
  }
@@ -24,13 +24,13 @@
24
24
  "status_runtime_web": "浏览器预览",
25
25
  "status_token_label": "Token 存储",
26
26
  "status_token_native": "安全存储",
27
- "status_token_memory": "内存存储",
27
+ "status_token_memory": "内存 access token + httpOnly Cookie",
28
28
  "tasks_title": "今日任务",
29
29
  "tasks_sync": "同步后端接口",
30
30
  "tasks_polish": "打磨移动端体验",
31
31
  "tasks_release": "准备原生打包",
32
32
  "profile_title": "应用设置",
33
33
  "profile_language": "语言跟随 Paraglide",
34
- "profile_theme": "主题跟随 @h-ai/ui",
34
+ "profile_theme": "主题",
35
35
  "profile_secure": "敏感 Token 不落 localStorage"
36
36
  }
@@ -48,9 +48,21 @@
48
48
  let nativeTokenStorage = $state(false)
49
49
 
50
50
  const navItems = $derived([
51
- { id: 'home', label: m.nav_home(), iconClass: 'icon-[tabler--layout-dashboard]' },
52
- { id: 'tasks', label: m.nav_tasks(), iconClass: 'icon-[tabler--checkbox]' },
53
- { id: 'profile', label: m.nav_profile(), iconClass: 'icon-[tabler--user-circle]' },
51
+ {
52
+ id: 'home',
53
+ label: m.nav_home(),
54
+ iconClass: 'icon-[tabler--layout-dashboard]',
55
+ },
56
+ {
57
+ id: 'tasks',
58
+ label: m.nav_tasks(),
59
+ iconClass: 'icon-[tabler--checkbox]',
60
+ },
61
+ {
62
+ id: 'profile',
63
+ label: m.nav_profile(),
64
+ iconClass: 'icon-[tabler--user-circle]',
65
+ },
54
66
  ])
55
67
 
56
68
  async function bootstrap(): Promise<void> {
@@ -143,10 +155,10 @@
143
155
  </footer>
144
156
  {{#if features.iam}}
145
157
  {#if authenticated && !booting && !bootError}
146
- {{/if}}
147
158
  <BottomNav items={navItems} active={router.path.slice(1)} onchange={handleTabChange} safeArea centered maxWidth='none' />
148
- {{#if features.iam}}
149
159
  {/if}
160
+ {{else}}
161
+ <BottomNav items={navItems} active={router.path.slice(1)} onchange={handleTabChange} safeArea centered maxWidth='none' />
150
162
  {{/if}}
151
163
  </div>
152
164
  </div>
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * {{projectName}} — API 客户端初始化。
3
- * Web 预览仅使用内存 TokenStore;原生端使用 Capacitor 安全存储。
3
+ * Web 预览使用内存 access token + httpOnly refresh Cookie;原生端使用 Capacitor 安全存储。
4
4
  * 原生存储失败会拒绝 Promise;登录/退出调用方须处理 auth.setTokens/clear 失败并释放界面 loading。
5
5
  */
6
6
  import { apiClient } from '@h-ai/api-client'
@@ -56,7 +56,7 @@ export function resolveApiBase(): string {
56
56
  }
57
57
 
58
58
  /**
59
- * 查询实际 Capacitor 平台,决定安全原生存储或 Web 内存存储。
59
+ * 查询实际 Capacitor 平台,决定安全原生存储或 Web Cookie 刷新通道。
60
60
  * @returns 是否处于 Capacitor 原生平台;浏览器预览返回 false。
61
61
  * @throws Error - 平台模块加载或检测失败时拒绝 Promise,不伪装为 Web 模式。
62
62
  * @example 模块内部上下文
@@ -74,7 +74,7 @@ export async function usesNativeTokenStorage(): Promise<boolean> {
74
74
 
75
75
  /**
76
76
  * 按真实平台创建 TokenStorage;绑定 client 后通过 client.auth 变更身份。
77
- * @returns 原生安全存储或 Web 内存存储;其方法的读写故障继续拒绝 Promise。
77
+ * @returns 原生安全存储或内存 access token + httpOnly refresh Cookie;其方法的读写故障继续拒绝 Promise。
78
78
  * @throws Error - 平台检测或存储工厂失败时拒绝,不改用较弱的持久化方式。
79
79
  * @example 模块内部上下文
80
80
  * ```ts
@@ -86,7 +86,7 @@ async function createTokenStorage() {
86
86
  if (await usesNativeTokenStorage())
87
87
  return createCapacitorTokenStorage()
88
88
 
89
- return apiClient.tokenStorage.memory()
89
+ return apiClient.tokenStorage.httpOnlyCookie()
90
90
  }
91
91
 
92
92
  /**
@@ -137,6 +137,7 @@ export async function initApi(): Promise<void> {
137
137
  * ```
138
138
  */
139
139
  async function initialize(): Promise<void> {
140
+ const native = await usesNativeTokenStorage()
140
141
  const storage = await createTokenStorage()
141
142
  {{#if features.crypto}}
142
143
  const transport = resolveTransport()
@@ -149,8 +150,14 @@ async function initialize(): Promise<void> {
149
150
  }
150
151
  const result = await apiClient.init({
151
152
  baseUrl: resolveApiBase(),
153
+ clientName: 'hai-mobile-app',
154
+ headers: native
155
+ ? { 'x-hai-token-transport': 'body' }
156
+ : {},
152
157
  auth: { storage },
153
- ...(transport ? { transport } : {}),
158
+ ...(transport
159
+ ? { transport }
160
+ : {}),
154
161
  })
155
162
  if (!result.success)
156
163
  throw new Error(result.error.message)
@@ -169,7 +176,14 @@ async function initialize(): Promise<void> {
169
176
  throw error
170
177
  }
171
178
  {{else}}
172
- const result = await apiClient.init({ baseUrl: resolveApiBase(), auth: { storage } })
179
+ const result = await apiClient.init({
180
+ baseUrl: resolveApiBase(),
181
+ clientName: 'hai-mobile-app',
182
+ headers: native
183
+ ? { 'x-hai-token-transport': 'body' }
184
+ : {},
185
+ auth: { storage },
186
+ })
173
187
  if (!result.success)
174
188
  throw new Error(result.error.message)
175
189
  initialized = true
@@ -7,7 +7,14 @@ declare const __APP_CORE_CONFIG__: Pick<CoreConfig, 'name' | 'version' | 'env' |
7
7
 
8
8
  /** 构建包含公开默认值;预览或容器启动时的公开配置覆盖默认值,不重建静态资源。 */
9
9
  const runtime = (globalThis as typeof globalThis & { __HAI_PUBLIC_CONFIG__?: { core?: Partial<typeof __APP_CORE_CONFIG__> } }).__HAI_PUBLIC_CONFIG__?.core
10
- export const appConfig = { ...__APP_CORE_CONFIG__, ...runtime, logging: { ...__APP_CORE_CONFIG__.logging, ...runtime?.logging } }
10
+ export const appConfig = {
11
+ ...__APP_CORE_CONFIG__,
12
+ ...runtime,
13
+ logging: {
14
+ ...__APP_CORE_CONFIG__.logging,
15
+ ...runtime?.logging,
16
+ },
17
+ }
11
18
 
12
19
  export function initClientCore(): void {
13
20
  core.init({ logging: appConfig.logging })
@@ -46,12 +46,16 @@
46
46
  <div>
47
47
  <p class='text-base-content/50'>{m.status_runtime_label()}</p>
48
48
  <Badge variant={nativeRuntime ? 'success' : 'warning'} size='sm'>
49
- {nativeRuntime ? m.status_runtime_native() : m.status_runtime_web()}
49
+ {nativeRuntime
50
+ ? m.status_runtime_native()
51
+ : m.status_runtime_web()}
50
52
  </Badge>
51
53
  </div>
52
54
  <div class='col-span-2'>
53
55
  <p class='text-base-content/50'>{m.status_token_label()}</p>
54
- <p class='font-medium'>{nativeTokenStorage ? m.status_token_native() : m.status_token_memory()}</p>
56
+ <p class='font-medium'>{nativeTokenStorage
57
+ ? m.status_token_native()
58
+ : m.status_token_memory()}</p>
55
59
  </div>
56
60
  </div>
57
61
  </Card>
@@ -1,12 +1,24 @@
1
1
  <script lang='ts'>
2
+ import { ThemeSelector } from '@h-ai/ui'
3
+ import { applyTheme, getSavedTheme } from '@h-ai/ui/theme'
2
4
  import * as m from '../paraglide/messages.js'
5
+
6
+ let currentTheme = $state(getSavedTheme())
7
+
8
+ function handleThemeChange(theme: string) {
9
+ currentTheme = theme
10
+ applyTheme(theme)
11
+ }
3
12
  </script>
4
13
 
5
14
  <section class='space-y-4 p-4 pb-6'>
6
15
  <Card title={m.profile_title()} shadow='sm'>
7
16
  <div class='space-y-3 text-sm'>
8
17
  <div class='bg-base-200 rounded-xl p-3'>{m.profile_language()}</div>
9
- <div class='bg-base-200 rounded-xl p-3'>{m.profile_theme()}</div>
18
+ <div class='flex flex-wrap items-center justify-between gap-3 bg-base-200 rounded-xl p-3' data-testid='profile-theme'>
19
+ <span>{m.profile_theme()}</span>
20
+ <ThemeSelector {currentTheme} onchange={handleThemeChange} compact grouped={false} />
21
+ </div>
10
22
  <div class='bg-base-200 rounded-xl p-3'>{m.profile_secure()}</div>
11
23
  </div>
12
24
  </Card>
@@ -5,7 +5,11 @@
5
5
  <section class='space-y-4 p-4 pb-6'>
6
6
  <Card title={m.tasks_title()} shadow='sm'>
7
7
  <div class='space-y-3'>
8
- {#each [m.tasks_sync(), m.tasks_polish(), m.tasks_release()] as task (task)}
8
+ {#each [
9
+ m.tasks_sync(),
10
+ m.tasks_polish(),
11
+ m.tasks_release(),
12
+ ] as task (task)}
9
13
  <div class='bg-base-200 rounded-xl flex items-center gap-3 p-3'>
10
14
  <span class='icon-[tabler--circle-check] text-success text-xl'></span>
11
15
  <span class='text-sm font-medium'>{task}</span>
@@ -9,28 +9,41 @@ const message = core.i18n.createMessageGetter({
9
9
  'zh-CN': {
10
10
  flag: 'HAI_BUILD_STATIC仅支持0或1;省略或0保持SSR,1启用website静态构建。',
11
11
  base: 'PUBLIC_BASE_PATH须为空或以/开头的站内目录;不允许外部地址、末尾/、查询、片段或路径逃逸。',
12
- modules: '静态website不能启用服务端业务模块。请使用--features none创建纯展示站点,或继续使用默认SSR。',
12
+ modules: '静态website不能启用服务端业务模块。请移除服务端模块及其初始化后再构建,或继续使用默认SSR。',
13
13
  routes: '静态website不能包含自定义API、表单actions或未预渲染页面;请移除动态入口或使用SSR。',
14
14
  assets: 'static是公开资产目录,不能放入.env私有环境文件或符号链接;请移动私有内容后重新构建。',
15
15
  },
16
16
  'en-US': {
17
17
  flag: 'HAI_BUILD_STATIC accepts only 0 or 1: omitted/0 keeps SSR, and 1 enables a static website build.',
18
18
  base: 'PUBLIC_BASE_PATH must be empty or a site-local directory beginning with /. External URLs, trailing /, queries, fragments and path traversal are not allowed.',
19
- modules: 'A static website cannot enable server-side business modules. Create a presentation site with --features none, or keep the default SSR mode.',
19
+ modules: 'A static website cannot enable server-side business modules. Remove server modules and their initialization before building, or keep the default SSR mode.',
20
20
  routes: 'A static website cannot contain custom APIs, form actions or pages that were not prerendered. Remove dynamic routes or use SSR.',
21
21
  assets: 'static is a public asset directory and cannot contain private .env files or symbolic links. Move private content before building.',
22
22
  },
23
23
  'ja-JP': {
24
24
  flag: 'HAI_BUILD_STATICは0または1です。省略/0はSSR、1はwebsiteの静的構築を有効にします。',
25
25
  base: 'PUBLIC_BASE_PATHは空、または/で始まるサイト内ディレクトリーです。外部URL、末尾の/、クエリー、フラグメント、パス逸脱は使えません。',
26
- modules: '静的websiteではサーバー業務モジュールを有効にできません。--features noneで表示専用サイトを作成するか、既定のSSRを使用してください。',
26
+ modules: '静的websiteではサーバー業務モジュールを有効にできません。サーバーモジュールと初期化処理を除去するか、既定のSSRを使用してください。',
27
27
  routes: '静的websiteに独自API、form actions、事前描画されないページを含められません。動的ルートを削除するかSSRを使用してください。',
28
28
  assets: 'staticは公開資産ディレクトリーです。非公開.envファイルやシンボリックリンクを置かず、非公開内容を移動してから構築してください。',
29
29
  },
30
30
  })
31
- const serverFeatures = [{{#each websiteServerFeatures}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}]
32
- const requestedLocale = [process.env.HAI_CORE_DEFAULTLOCALE, process.env.LC_ALL, process.env.LC_MESSAGES, process.env.LANG].find(value => value?.trim())?.split(/[_.-]/)[0]?.toLowerCase()
33
- const locale = requestedLocale === 'en' || requestedLocale === 'c' || requestedLocale === 'posix' ? 'en-US' : requestedLocale === 'ja' ? 'ja-JP' : 'zh-CN'
31
+ const serverFeatures = [
32
+ {{#each websiteServerFeatures}}
33
+ '{{this}}',
34
+ {{/each}}
35
+ ]
36
+ const requestedLocale = [
37
+ process.env.HAI_CORE_DEFAULTLOCALE,
38
+ process.env.LC_ALL,
39
+ process.env.LC_MESSAGES,
40
+ process.env.LANG,
41
+ ].find(value => value?.trim())?.split(/[_.-]/)[0]?.toLowerCase()
42
+ const locale = requestedLocale === 'en' || requestedLocale === 'c' || requestedLocale === 'posix'
43
+ ? 'en-US'
44
+ : requestedLocale === 'ja'
45
+ ? 'ja-JP'
46
+ : 'zh-CN'
34
47
 
35
48
  /** 只读取显式构建值,不加载dotenv和服务端秘密。 */
36
49
  export function resolveStaticWebsite() {