@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.
- package/README.md +10 -7
- package/dist/{chunk-GSMXGUYF.js → chunk-5IGMJVV5.js} +56 -38
- package/dist/chunk-5IGMJVV5.js.map +1 -0
- package/dist/{chunk-BPO34NRL.js → chunk-CFCXOKGV.js} +27 -5
- package/dist/chunk-CFCXOKGV.js.map +1 -0
- package/dist/cli-config-templates-5J5VKWWD.js +5 -0
- package/dist/{cli-config-templates-JCS7T3NU.js.map → cli-config-templates-5J5VKWWD.js.map} +1 -1
- package/dist/cli-skill-templates-JMSV2SVW.js +5 -0
- package/dist/{cli-skill-templates-IMYZPQRG.js.map → cli-skill-templates-JMSV2SVW.js.map} +1 -1
- package/dist/index.js +627 -107
- package/dist/index.js.map +1 -1
- package/dist/{src-N4NPS7AG.js → src-QNEJJ3BV.js} +261 -103
- package/dist/src-QNEJJ3BV.js.map +1 -0
- package/package.json +3 -2
- package/templates/README.md +40 -29
- package/templates/apps/admin/messages/en-US.json +5 -0
- package/templates/apps/admin/messages/zh-CN.json +5 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +5 -5
- package/templates/apps/admin/src/routes/+page.svelte.hbs +32 -12
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +37 -20
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +16 -0
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +38 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +33 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +3 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +12 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
- package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +17 -0
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +19 -0
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +26 -0
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +55 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +98 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +21 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +184 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +49 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +18 -0
- package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +168 -0
- package/templates/apps/api/apps/{{projectName}}-service/tsconfig.json.hbs +16 -0
- package/templates/apps/api/apps/{{projectName}}-service/tsup.config.ts.hbs +11 -0
- package/templates/apps/api/apps/{{projectName}}-service/vitest.config.ts.hbs +9 -0
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +18 -0
- package/templates/apps/api/eslint.config.js +32 -0
- package/templates/apps/api/package.json.hbs +29 -0
- package/templates/apps/api/playwright.config.ts.hbs +58 -0
- package/templates/apps/api/pnpm-workspace.yaml.hbs +14 -0
- package/templates/apps/api/tsconfig.base.json +15 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -7
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +51 -29
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +186 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +14 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +1 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +10 -4
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +4 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +20 -6
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +15 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +33 -30
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +152 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +1 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +13 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +22 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +41 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +2 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +9 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +44 -21
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +15 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +15 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +25 -27
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +153 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +1 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +10 -4
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -2
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +2 -1
- package/templates/apps/fullstack/eslint.config.js +0 -1
- package/templates/apps/fullstack/package.json.hbs +18 -17
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +14 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +13 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/{server-app.ts.hbs → app-server.ts.hbs} +23 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +4 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/{server-app.test.ts.hbs → app-server.test.ts.hbs} +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +9 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +5 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +1 -1
- package/templates/apps/fullstack/playwright.config.ts.hbs +41 -4
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +15 -0
- package/templates/apps/h5/messages/en-US.json +3 -0
- package/templates/apps/h5/messages/zh-CN.json +3 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +30 -29
- package/templates/apps/h5/src/routes/+page.svelte.hbs +17 -6
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
- package/templates/apps/mobile-app/.env.hbs +3 -0
- package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
- package/templates/apps/mobile-app/index.html.hbs +13 -0
- package/templates/apps/mobile-app/messages/en-US.json +36 -0
- package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +162 -0
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +54 -0
- package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
- package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +35 -26
- package/templates/apps/website/src/routes/+page.svelte.hbs +28 -10
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +37 -34
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
- package/templates/base/.gitignore.hbs +16 -0
- package/templates/base/{eslint.config.js → eslint.config.js.hbs} +4 -1
- package/templates/base/package.json.hbs +55 -31
- package/templates/base/playwright.config.ts +30 -3
- package/templates/base/pnpm-workspace.yaml.hbs +14 -0
- package/templates/base/src/app.d.ts.hbs +3 -0
- package/templates/base/src/app.html.hbs +7 -0
- package/templates/base/src/hooks.server.ts.hbs +17 -7
- package/templates/base/src/lib/server/init.ts.hbs +20 -12
- package/templates/base/static/favicon.png +0 -0
- package/templates/base/svelte.config.js.hbs +43 -3
- package/templates/base/tests/scaffold.test.ts.hbs +16 -1
- package/templates/base/tsconfig.json.hbs +50 -0
- package/templates/base/vite.config.ts.hbs +13 -3
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +5 -5
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +34 -32
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +46 -48
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +35 -31
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +41 -38
- package/templates/skills/AGENTS.md +60 -17
- package/templates/skills/CLAUDE.md +18 -6
- package/templates/skills/bridges/admin/AGENTS.md +66 -0
- package/templates/skills/bridges/admin/CLAUDE.md +19 -0
- package/templates/skills/bridges/admin/copilot-instructions.md +46 -0
- package/templates/skills/bridges/api/AGENTS.md +65 -0
- package/templates/skills/bridges/api/CLAUDE.md +19 -0
- package/templates/skills/bridges/api/copilot-instructions.md +46 -0
- package/templates/skills/bridges/fullstack/AGENTS.md +84 -0
- package/templates/skills/bridges/fullstack/CLAUDE.md +19 -0
- package/templates/skills/bridges/fullstack/copilot-instructions.md +54 -0
- package/templates/skills/bridges/h5/AGENTS.md +65 -0
- package/templates/skills/bridges/h5/CLAUDE.md +19 -0
- package/templates/skills/bridges/h5/copilot-instructions.md +44 -0
- package/templates/skills/bridges/mobile-app/AGENTS.md +77 -0
- package/templates/skills/bridges/mobile-app/CLAUDE.md +19 -0
- package/templates/skills/bridges/mobile-app/copilot-instructions.md +45 -0
- package/templates/skills/bridges/website/AGENTS.md +65 -0
- package/templates/skills/bridges/website/CLAUDE.md +19 -0
- package/templates/skills/bridges/website/copilot-instructions.md +44 -0
- package/templates/skills/copilot-instructions.md +29 -50
- package/templates/skills/hai-ai/SKILL.md +115 -648
- package/templates/skills/hai-ai/reference.md +197 -0
- package/templates/skills/hai-api-client/SKILL.md +3 -1
- package/templates/skills/hai-app-create/SKILL.md +2 -1
- package/templates/skills/hai-app-review/SKILL.md +2 -2
- package/templates/skills/hai-app-tests/SKILL.md +28 -3
- package/templates/skills/hai-capacitor/SKILL.md +14 -22
- package/templates/skills/hai-deploy/SKILL.md +12 -9
- package/templates/skills/hai-kit/SKILL.md +5 -3
- package/templates/skills/hai-serv/SKILL.md +2 -0
- package/templates/skills/hai-ui/SKILL.md +25 -11
- package/templates/skills/opencode.json +1 -6
- package/dist/chunk-BPO34NRL.js.map +0 -1
- package/dist/chunk-GSMXGUYF.js.map +0 -1
- package/dist/cli-config-templates-JCS7T3NU.js +0 -5
- package/dist/cli-skill-templates-IMYZPQRG.js +0 -5
- package/dist/src-N4NPS7AG.js.map +0 -1
- package/templates/apps/android-app/.env.hbs +0 -3
- package/templates/apps/android-app/messages/en-US.json +0 -24
- package/templates/apps/android-app/messages/zh-CN.json +0 -24
- package/templates/apps/android-app/src/lib/api.ts.hbs +0 -31
- package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
- package/templates/apps/android-app/src/routes/+layout.ts +0 -7
- package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
- package/templates/apps/api/src/routes/+layout.svelte +0 -14
- package/templates/apps/api/src/routes/+page.svelte +0 -11
- package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
- package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
- package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
- package/templates/apps/api/tests/health.test.ts.hbs +0 -26
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.html.hbs +0 -14
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+layout.svelte.hbs +0 -18
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+page.svelte.hbs +0 -162
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.html.hbs +0 -14
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+layout.svelte.hbs +0 -18
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+page.svelte.hbs +0 -162
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.html.hbs +0 -14
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+layout.svelte.hbs +0 -18
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+page.svelte.hbs +0 -162
- package/templates/apps/fullstack/pnpm-workspace.yaml +0 -7
- package/templates/base/tsconfig.json +0 -20
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/{theme.svelte.ts.hbs → theme-store.svelte.ts.hbs} +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{defaultLocale}}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
6
|
+
<meta name="theme-color" content="#2563eb" />
|
|
7
|
+
<title>{{projectName}}</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
3
|
+
"app_title": "Mobile Workspace",
|
|
4
|
+
"app_description": "A mobile app built with hai Agent Framework + Capacitor + Svelte 5",
|
|
5
|
+
"boot_failed_title": "Boot failed",
|
|
6
|
+
"common_retry": "Retry",
|
|
7
|
+
"common_refresh": "Refresh",
|
|
8
|
+
"common_enabled": "Enabled",
|
|
9
|
+
"common_disabled": "Disabled",
|
|
10
|
+
"nav_home": "Workspace",
|
|
11
|
+
"nav_tasks": "Tasks",
|
|
12
|
+
"nav_profile": "Profile",
|
|
13
|
+
"hero_eyebrow": "Mobile Workspace",
|
|
14
|
+
"hero_title": "A real mobile-first starting point",
|
|
15
|
+
"hero_subtitle": "Capacitor native capabilities, secure token storage, unified API client and @h-ai/ui mobile components are wired in.",
|
|
16
|
+
"feature_native_title": "Native ready",
|
|
17
|
+
"feature_native_desc": "Status bar, safe areas and device runtime are ready for push, camera and more plugins.",
|
|
18
|
+
"feature_api_title": "Unified API",
|
|
19
|
+
"feature_api_desc": "Connect to your backend through PUBLIC_API_BASE. Tokens use secure storage on native devices.",
|
|
20
|
+
"status_title": "Runtime status",
|
|
21
|
+
"status_api_label": "API base",
|
|
22
|
+
"status_runtime_label": "Runtime",
|
|
23
|
+
"status_runtime_native": "Native app",
|
|
24
|
+
"status_runtime_web": "Browser preview",
|
|
25
|
+
"status_token_label": "Token storage",
|
|
26
|
+
"status_token_native": "Secure storage",
|
|
27
|
+
"status_token_memory": "Memory storage",
|
|
28
|
+
"tasks_title": "Today's tasks",
|
|
29
|
+
"tasks_sync": "Wire backend APIs",
|
|
30
|
+
"tasks_polish": "Polish mobile UX",
|
|
31
|
+
"tasks_release": "Prepare native packaging",
|
|
32
|
+
"profile_title": "App settings",
|
|
33
|
+
"profile_language": "Locale follows Paraglide",
|
|
34
|
+
"profile_theme": "Theme follows @h-ai/ui",
|
|
35
|
+
"profile_secure": "Sensitive tokens avoid localStorage"
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
3
|
+
"app_title": "移动工作台",
|
|
4
|
+
"app_description": "基于 hai Agent Framework + Capacitor + Svelte 5 构建的移动应用",
|
|
5
|
+
"boot_failed_title": "启动失败",
|
|
6
|
+
"common_retry": "重试",
|
|
7
|
+
"common_refresh": "刷新",
|
|
8
|
+
"common_enabled": "已启用",
|
|
9
|
+
"common_disabled": "未启用",
|
|
10
|
+
"nav_home": "工作台",
|
|
11
|
+
"nav_tasks": "任务",
|
|
12
|
+
"nav_profile": "我的",
|
|
13
|
+
"hero_eyebrow": "Mobile Workspace",
|
|
14
|
+
"hero_title": "真正可用的移动端起点",
|
|
15
|
+
"hero_subtitle": "Capacitor 原生能力、安全 Token 存储、统一 API Client 与 @h-ai/ui 移动组件已经接好。",
|
|
16
|
+
"feature_native_title": "原生能力",
|
|
17
|
+
"feature_native_desc": "状态栏、安全区与设备信息就绪,可继续接入推送、相机等插件。",
|
|
18
|
+
"feature_api_title": "统一 API",
|
|
19
|
+
"feature_api_desc": "通过 PUBLIC_API_BASE 连接服务端,Token 在原生端走安全存储。",
|
|
20
|
+
"status_title": "运行状态",
|
|
21
|
+
"status_api_label": "API 地址",
|
|
22
|
+
"status_runtime_label": "运行环境",
|
|
23
|
+
"status_runtime_native": "原生 App",
|
|
24
|
+
"status_runtime_web": "浏览器预览",
|
|
25
|
+
"status_token_label": "Token 存储",
|
|
26
|
+
"status_token_native": "安全存储",
|
|
27
|
+
"status_token_memory": "内存存储",
|
|
28
|
+
"tasks_title": "今日任务",
|
|
29
|
+
"tasks_sync": "同步后端接口",
|
|
30
|
+
"tasks_polish": "打磨移动端体验",
|
|
31
|
+
"tasks_release": "准备原生打包",
|
|
32
|
+
"profile_title": "应用设置",
|
|
33
|
+
"profile_language": "语言跟随 Paraglide",
|
|
34
|
+
"profile_theme": "主题跟随 @h-ai/ui",
|
|
35
|
+
"profile_secure": "敏感 Token 不落 localStorage"
|
|
36
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<script lang='ts'>
|
|
2
|
+
import { applyTheme, getSavedTheme, setGlobalLocale } from '@h-ai/ui'
|
|
3
|
+
import { onMount } from 'svelte'
|
|
4
|
+
import { closeApi, initApi, resolveApiBase, usesNativeTokenStorage } from './lib/api.js'
|
|
5
|
+
import { initCapacitor, isNativeApp } from './lib/capacitor.js'
|
|
6
|
+
import * as m from './lib/paraglide/messages.js'
|
|
7
|
+
import { getLocale } from './lib/paraglide/runtime.js'
|
|
8
|
+
|
|
9
|
+
type TabId = 'home' | 'tasks' | 'profile'
|
|
10
|
+
|
|
11
|
+
let booting = $state(true)
|
|
12
|
+
let bootError = $state<string | null>(null)
|
|
13
|
+
let activeTab = $state<TabId>('home')
|
|
14
|
+
let nativeRuntime = $state(false)
|
|
15
|
+
let nativeTokenStorage = $state(false)
|
|
16
|
+
|
|
17
|
+
const navItems = $derived([
|
|
18
|
+
{ id: 'home', label: m.nav_home(), iconClass: 'icon-[tabler--layout-dashboard]' },
|
|
19
|
+
{ id: 'tasks', label: m.nav_tasks(), iconClass: 'icon-[tabler--checkbox]' },
|
|
20
|
+
{ id: 'profile', label: m.nav_profile(), iconClass: 'icon-[tabler--user-circle]' },
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
async function bootstrap(): Promise<void> {
|
|
24
|
+
try {
|
|
25
|
+
applyTheme(getSavedTheme(), false)
|
|
26
|
+
setGlobalLocale(getLocale())
|
|
27
|
+
await initCapacitor()
|
|
28
|
+
await initApi()
|
|
29
|
+
nativeRuntime = await isNativeApp()
|
|
30
|
+
nativeTokenStorage = await usesNativeTokenStorage()
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
bootError = error instanceof Error ? error.message : String(error)
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
booting = false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function handleTabChange(id: string): void {
|
|
41
|
+
if (id === 'home' || id === 'tasks' || id === 'profile')
|
|
42
|
+
activeTab = id
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onMount(() => {
|
|
46
|
+
void bootstrap()
|
|
47
|
+
|
|
48
|
+
return () => {
|
|
49
|
+
void closeApi()
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
{#if booting}
|
|
55
|
+
<div class='hai-mobile-viewport'>
|
|
56
|
+
<div class='hai-mobile-shell max-w-lg'>
|
|
57
|
+
<div class='flex min-h-screen items-center justify-center'>
|
|
58
|
+
<span class='loading loading-spinner loading-lg'></span>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
{:else if bootError}
|
|
63
|
+
<div class='hai-mobile-viewport'>
|
|
64
|
+
<div class='hai-mobile-shell max-w-lg'>
|
|
65
|
+
<div class='flex min-h-screen flex-col items-center justify-center gap-3 p-6 text-center'>
|
|
66
|
+
<span class='icon-[tabler--alert-triangle] text-error text-4xl'></span>
|
|
67
|
+
<h1 class='text-lg font-bold'>{m.boot_failed_title()}</h1>
|
|
68
|
+
<p class='text-base-content/60 text-sm'>{bootError}</p>
|
|
69
|
+
<Button variant='primary' size='sm' onclick={() => location.reload()}>{m.common_retry()}</Button>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
{:else}
|
|
74
|
+
<div class='hai-mobile-viewport'>
|
|
75
|
+
<div class='hai-mobile-shell max-w-lg'>
|
|
76
|
+
<AppBar title={m.app_title()} safeArea fixed={false} class='bg-base-100/95 backdrop-blur border-base-content/8' />
|
|
77
|
+
|
|
78
|
+
<main class='hai-mobile-main'>
|
|
79
|
+
{#if activeTab === 'home'}
|
|
80
|
+
<section class='space-y-4 p-4 pb-6'>
|
|
81
|
+
<div class='bg-primary text-primary-content rounded-2xl p-5 shadow-sm'>
|
|
82
|
+
<p class='text-sm opacity-75'>{m.hero_eyebrow()}</p>
|
|
83
|
+
<h1 class='mt-1 text-2xl font-bold'>{m.hero_title()}</h1>
|
|
84
|
+
<p class='mt-2 text-sm opacity-80'>{m.hero_subtitle()}</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class='grid grid-cols-2 gap-3'>
|
|
88
|
+
<Card padding='sm' shadow='sm'>
|
|
89
|
+
<div class='flex flex-col gap-2'>
|
|
90
|
+
<span class='icon-[tabler--device-mobile-code] text-primary text-2xl'></span>
|
|
91
|
+
<span class='text-sm font-medium'>{m.feature_native_title()}</span>
|
|
92
|
+
<span class='text-base-content/60 text-xs'>{m.feature_native_desc()}</span>
|
|
93
|
+
</div>
|
|
94
|
+
</Card>
|
|
95
|
+
<Card padding='sm' shadow='sm'>
|
|
96
|
+
<div class='flex flex-col gap-2'>
|
|
97
|
+
<span class='icon-[tabler--api-app] text-primary text-2xl'></span>
|
|
98
|
+
<span class='text-sm font-medium'>{m.feature_api_title()}</span>
|
|
99
|
+
<span class='text-base-content/60 text-xs'>{m.feature_api_desc()}</span>
|
|
100
|
+
</div>
|
|
101
|
+
</Card>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<Card title={m.status_title()} shadow='sm'>
|
|
105
|
+
<div class='grid grid-cols-2 gap-3 text-sm'>
|
|
106
|
+
<div>
|
|
107
|
+
<p class='text-base-content/50'>{m.status_api_label()}</p>
|
|
108
|
+
<p class='font-medium break-all'>{resolveApiBase()}</p>
|
|
109
|
+
</div>
|
|
110
|
+
<div>
|
|
111
|
+
<p class='text-base-content/50'>{m.status_runtime_label()}</p>
|
|
112
|
+
<Badge variant={nativeRuntime ? 'success' : 'warning'} size='sm'>
|
|
113
|
+
{nativeRuntime ? m.status_runtime_native() : m.status_runtime_web()}
|
|
114
|
+
</Badge>
|
|
115
|
+
</div>
|
|
116
|
+
<div class='col-span-2'>
|
|
117
|
+
<p class='text-base-content/50'>{m.status_token_label()}</p>
|
|
118
|
+
<p class='font-medium'>{nativeTokenStorage ? m.status_token_native() : m.status_token_memory()}</p>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</Card>
|
|
122
|
+
</section>
|
|
123
|
+
{:else if activeTab === 'tasks'}
|
|
124
|
+
<section class='space-y-4 p-4 pb-6'>
|
|
125
|
+
<Card title={m.tasks_title()} shadow='sm'>
|
|
126
|
+
<div class='space-y-3'>
|
|
127
|
+
{#each [m.tasks_sync(), m.tasks_polish(), m.tasks_release()] as task (task)}
|
|
128
|
+
<div class='bg-base-200 rounded-xl flex items-center gap-3 p-3'>
|
|
129
|
+
<span class='icon-[tabler--circle-check] text-success text-xl'></span>
|
|
130
|
+
<span class='text-sm font-medium'>{task}</span>
|
|
131
|
+
</div>
|
|
132
|
+
{/each}
|
|
133
|
+
</div>
|
|
134
|
+
</Card>
|
|
135
|
+
</section>
|
|
136
|
+
{:else}
|
|
137
|
+
<section class='space-y-4 p-4 pb-6'>
|
|
138
|
+
<Card title={m.profile_title()} shadow='sm'>
|
|
139
|
+
<div class='space-y-3 text-sm'>
|
|
140
|
+
<div class='bg-base-200 rounded-xl p-3'>{m.profile_language()}</div>
|
|
141
|
+
<div class='bg-base-200 rounded-xl p-3'>{m.profile_theme()}</div>
|
|
142
|
+
<div class='bg-base-200 rounded-xl p-3'>{m.profile_secure()}</div>
|
|
143
|
+
</div>
|
|
144
|
+
</Card>
|
|
145
|
+
</section>
|
|
146
|
+
{/if}
|
|
147
|
+
</main>
|
|
148
|
+
|
|
149
|
+
<BottomNav
|
|
150
|
+
items={navItems}
|
|
151
|
+
active={activeTab}
|
|
152
|
+
onchange={handleTabChange}
|
|
153
|
+
safeArea
|
|
154
|
+
centered
|
|
155
|
+
maxWidth='lg'
|
|
156
|
+
class='border-base-content/8 shadow-[0_-12px_32px_rgba(15,23,42,0.08)]'
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
{/if}
|
|
161
|
+
|
|
162
|
+
<ToastContainer />
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{projectName}} — API 客户端初始化。
|
|
3
|
+
* Web 预览仅使用内存 TokenStore;原生端使用 Capacitor 安全存储。
|
|
4
|
+
*/
|
|
5
|
+
import { apiClient } from '@h-ai/api-client'
|
|
6
|
+
import { createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
7
|
+
|
|
8
|
+
const DEFAULT_API_BASE = 'http://localhost:3000/api/v1'
|
|
9
|
+
|
|
10
|
+
let initialized = false
|
|
11
|
+
|
|
12
|
+
export function resolveApiBase(): string {
|
|
13
|
+
return import.meta.env.PUBLIC_API_BASE ?? DEFAULT_API_BASE
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function usesNativeTokenStorage(): Promise<boolean> {
|
|
17
|
+
const { Capacitor } = await import('@capacitor/core')
|
|
18
|
+
return Capacitor.isNativePlatform()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function createTokenStorage() {
|
|
22
|
+
if (await usesNativeTokenStorage())
|
|
23
|
+
return createCapacitorTokenStorage()
|
|
24
|
+
|
|
25
|
+
return apiClient.tokenStorage.memory()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function initApi(): Promise<void> {
|
|
29
|
+
if (initialized)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
const initResult = await apiClient.init({
|
|
33
|
+
baseUrl: resolveApiBase(),
|
|
34
|
+
auth: {
|
|
35
|
+
storage: await createTokenStorage(),
|
|
36
|
+
refreshPath: '/auth/refresh',
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
if (!initResult.success)
|
|
41
|
+
throw new Error(initResult.error.message)
|
|
42
|
+
|
|
43
|
+
initialized = true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function closeApi(): Promise<void> {
|
|
47
|
+
if (!initialized)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
await apiClient.close()
|
|
51
|
+
initialized = false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { apiClient }
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Capacitor
|
|
3
|
-
*
|
|
4
|
-
* 在应用启动时调用,配置状态栏等原生能力。
|
|
2
|
+
* Capacitor 初始化。
|
|
5
3
|
*/
|
|
6
4
|
import { capacitor } from '@h-ai/capacitor'
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
export async function isNativeApp(): Promise<boolean> {
|
|
7
|
+
return capacitor.isNative()
|
|
8
|
+
}
|
|
9
|
+
|
|
11
10
|
export async function initCapacitor(): Promise<void> {
|
|
12
11
|
await capacitor.init()
|
|
13
12
|
|
|
14
13
|
if (capacitor.isNative()) {
|
|
15
14
|
await capacitor.statusBar.configure({
|
|
16
15
|
backgroundColor: '#ffffff',
|
|
17
|
-
style: '
|
|
16
|
+
style: 'light',
|
|
18
17
|
overlay: false,
|
|
19
18
|
})
|
|
20
19
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mount } from 'svelte'
|
|
2
|
+
import App from './App.svelte'
|
|
3
|
+
import './app.css'
|
|
4
|
+
|
|
5
|
+
const target = document.getElementById('app')
|
|
6
|
+
if (!target)
|
|
7
|
+
throw new Error('Root element #app not found')
|
|
8
|
+
|
|
9
|
+
const app = mount(App, { target })
|
|
10
|
+
|
|
11
|
+
export default app
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
{{!-- 官网根布局 --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* 官网根布局
|
|
5
5
|
*/
|
|
6
6
|
import type { Snippet } from 'svelte'
|
|
7
|
-
import '../app.css'
|
|
8
7
|
{{#if hasI18n}}
|
|
9
8
|
import { browser } from '$app/environment'
|
|
10
|
-
|
|
11
|
-
import {
|
|
9
|
+
{{/if}}
|
|
10
|
+
import { resolve } from '$app/paths'
|
|
11
|
+
{{#if hasI18n}}
|
|
12
12
|
import * as m from '$lib/paraglide/messages'
|
|
13
|
+
import { getLocale } from '$lib/paraglide/runtime.js'
|
|
14
|
+
import { setGlobalLocale } from '@h-ai/ui'
|
|
13
15
|
{{/if}}
|
|
16
|
+
import '../app.css'
|
|
14
17
|
|
|
15
18
|
interface Props {
|
|
16
19
|
children: Snippet
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
const { children }: Props = $props()
|
|
23
|
+
|
|
24
|
+
const navItems = [
|
|
25
|
+
{{#if hasI18n}}
|
|
26
|
+
{ path: '/', label: m.nav_home() },
|
|
27
|
+
{ path: '/about', label: m.nav_about() },
|
|
28
|
+
{ path: '/services', label: m.nav_services() },
|
|
29
|
+
{ path: '/contact', label: m.nav_contact() },
|
|
30
|
+
{{else}}
|
|
31
|
+
{ path: '/', label: '首页' },
|
|
32
|
+
{ path: '/about', label: '关于' },
|
|
33
|
+
{ path: '/services', label: '服务' },
|
|
34
|
+
{ path: '/contact', label: '联系我们' },
|
|
35
|
+
{{/if}}
|
|
36
|
+
] as const
|
|
20
37
|
{{#if hasI18n}}
|
|
21
38
|
|
|
22
39
|
if (browser) {
|
|
@@ -26,39 +43,31 @@
|
|
|
26
43
|
{{/if}}
|
|
27
44
|
</script>
|
|
28
45
|
|
|
29
|
-
<div class=
|
|
30
|
-
<header class=
|
|
31
|
-
<div class=
|
|
46
|
+
<div class='min-h-screen flex flex-col'>
|
|
47
|
+
<header class='navbar bg-base-100 shadow-sm'>
|
|
48
|
+
<div class='flex-1'>
|
|
32
49
|
{{#if hasI18n}}
|
|
33
|
-
<a href=
|
|
50
|
+
<a href={resolve('/', {})} class='btn btn-ghost text-xl font-black tracking-tight'>{m.app_title()}</a>
|
|
34
51
|
{{else}}
|
|
35
|
-
<a href=
|
|
52
|
+
<a href={resolve('/', {})} class='btn btn-ghost text-xl font-black tracking-tight'>官网</a>
|
|
36
53
|
{{/if}}
|
|
37
54
|
</div>
|
|
38
|
-
<nav class=
|
|
39
|
-
<ul class=
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<li><a href="/services">{m.nav_services()}</a></li>
|
|
44
|
-
<li><a href="/contact">{m.nav_contact()}</a></li>
|
|
45
|
-
{{else}}
|
|
46
|
-
<li><a href="/">首页</a></li>
|
|
47
|
-
<li><a href="/about">关于</a></li>
|
|
48
|
-
<li><a href="/services">服务</a></li>
|
|
49
|
-
<li><a href="/contact">联系我们</a></li>
|
|
50
|
-
{{/if}}
|
|
55
|
+
<nav class='flex-none'>
|
|
56
|
+
<ul class='menu menu-horizontal px-1'>
|
|
57
|
+
{#each navItems as item (item.path)}
|
|
58
|
+
<li><a href={resolve(item.path, {})}>{item.label}</a></li>
|
|
59
|
+
{/each}
|
|
51
60
|
</ul>
|
|
52
61
|
</nav>
|
|
53
62
|
</header>
|
|
54
63
|
|
|
55
|
-
<main class=
|
|
64
|
+
<main class='flex-1'>
|
|
56
65
|
{@render children()}
|
|
57
66
|
</main>
|
|
58
67
|
|
|
59
|
-
<footer class=
|
|
68
|
+
<footer class='footer footer-center p-4 bg-base-300 text-base-content'>
|
|
60
69
|
{{#if hasI18n}}
|
|
61
|
-
<p>{m.footer_copyright()}</p>
|
|
70
|
+
<p>{m.footer_copyright({ year: new Date().getFullYear() })}</p>
|
|
62
71
|
{{else}}
|
|
63
72
|
<p>Copyright © 2025 - All rights reserved</p>
|
|
64
73
|
{{/if}}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{{!-- 官网首页 --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* 官网首页
|
|
5
5
|
*/
|
|
6
|
+
import { resolve } from '$app/paths'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
8
|
import * as m from '$lib/paraglide/messages'
|
|
8
9
|
{{/if}}
|
|
10
|
+
import { Badge } from '@h-ai/ui'
|
|
9
11
|
</script>
|
|
10
12
|
|
|
11
13
|
<svelte:head>
|
|
@@ -16,17 +18,33 @@
|
|
|
16
18
|
{{/if}}
|
|
17
19
|
</svelte:head>
|
|
18
20
|
|
|
19
|
-
<section class=
|
|
20
|
-
<div class=
|
|
21
|
-
<div class=
|
|
21
|
+
<section class='hero min-h-[60vh] bg-gradient-to-b from-base-200 to-base-100'>
|
|
22
|
+
<div class='hero-content text-center'>
|
|
23
|
+
<div class='max-w-xl'>
|
|
24
|
+
<div class='flex justify-center'>
|
|
25
|
+
<Badge variant='primary'>
|
|
26
|
+
<span class='icon-[tabler--sparkles] size-4'></span>
|
|
22
27
|
{{#if hasI18n}}
|
|
23
|
-
|
|
24
|
-
<p class="py-6">{m.page_home_subtitle()}</p>
|
|
25
|
-
<a href="/about" class="btn btn-primary">{m.page_home_cta()}</a>
|
|
28
|
+
{m.app_title()}
|
|
26
29
|
{{else}}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
由 hai 驱动
|
|
31
|
+
{{/if}}
|
|
32
|
+
</Badge>
|
|
33
|
+
</div>
|
|
34
|
+
{{#if hasI18n}}
|
|
35
|
+
<h1 class='mt-5 text-5xl font-black tracking-tight'>{m.page_home_title()}</h1>
|
|
36
|
+
<p class='py-6 text-base-content/60'>{m.page_home_subtitle()}</p>
|
|
37
|
+
<a href={resolve('/about', {})} class='btn btn-primary'>
|
|
38
|
+
<span class='icon-[tabler--arrow-right] size-5'></span>
|
|
39
|
+
{m.page_home_cta()}
|
|
40
|
+
</a>
|
|
41
|
+
{{else}}
|
|
42
|
+
<h1 class='mt-5 text-5xl font-black tracking-tight'>欢迎</h1>
|
|
43
|
+
<p class='py-6 text-base-content/60'>基于 hai Agent Framework 构建</p>
|
|
44
|
+
<a href={resolve('/about', {})} class='btn btn-primary'>
|
|
45
|
+
<span class='icon-[tabler--arrow-right] size-5'></span>
|
|
46
|
+
了解更多
|
|
47
|
+
</a>
|
|
30
48
|
{{/if}}
|
|
31
49
|
</div>
|
|
32
50
|
</div>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{{!-- 关于页面 --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* 关于页面
|
|
5
5
|
*/
|
|
6
6
|
{{#if hasI18n}}
|
|
7
7
|
import * as m from '$lib/paraglide/messages'
|
|
8
8
|
{{/if}}
|
|
9
|
+
import { Card, PageHeader } from '@h-ai/ui'
|
|
9
10
|
</script>
|
|
10
11
|
|
|
11
12
|
<svelte:head>
|
|
@@ -16,12 +17,16 @@
|
|
|
16
17
|
{{/if}}
|
|
17
18
|
</svelte:head>
|
|
18
19
|
|
|
19
|
-
<section class=
|
|
20
|
+
<section class='py-16 px-4 max-w-4xl mx-auto'>
|
|
20
21
|
{{#if hasI18n}}
|
|
21
|
-
<
|
|
22
|
-
<
|
|
22
|
+
<PageHeader title={m.page_about_title()} />
|
|
23
|
+
<Card shadow='sm' class='bg-base-100'>
|
|
24
|
+
<p class='text-lg text-base-content/70'>{m.page_about_desc()}</p>
|
|
25
|
+
</Card>
|
|
23
26
|
{{else}}
|
|
24
|
-
<
|
|
25
|
-
<
|
|
27
|
+
<PageHeader title='关于我们' />
|
|
28
|
+
<Card shadow='sm' class='bg-base-100'>
|
|
29
|
+
<p class='text-lg text-base-content/70'>了解我们的团队与使命。</p>
|
|
30
|
+
</Card>
|
|
26
31
|
{{/if}}
|
|
27
32
|
</section>
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
{{!-- 联系页面 --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* 联系我们页面
|
|
5
5
|
*/
|
|
6
6
|
{{#if hasI18n}}
|
|
7
7
|
import * as m from '$lib/paraglide/messages'
|
|
8
8
|
{{/if}}
|
|
9
|
+
import { Button, Card, FormField, Input, PageHeader } from '@h-ai/ui'
|
|
10
|
+
|
|
11
|
+
let name = $state('')
|
|
12
|
+
let email = $state('')
|
|
13
|
+
let message = $state('')
|
|
14
|
+
|
|
15
|
+
function handleSubmit(event: SubmitEvent) {
|
|
16
|
+
event.preventDefault()
|
|
17
|
+
}
|
|
9
18
|
</script>
|
|
10
19
|
|
|
11
20
|
<svelte:head>
|
|
@@ -16,44 +25,38 @@
|
|
|
16
25
|
{{/if}}
|
|
17
26
|
</svelte:head>
|
|
18
27
|
|
|
19
|
-
<section class=
|
|
28
|
+
<section class='py-16 px-4 max-w-2xl mx-auto'>
|
|
20
29
|
{{#if hasI18n}}
|
|
21
|
-
<
|
|
22
|
-
<p class="text-lg text-gray-600 mb-8">{m.page_contact_desc()}</p>
|
|
30
|
+
<PageHeader title={m.page_contact_title()} description={m.page_contact_desc()} />
|
|
23
31
|
{{else}}
|
|
24
|
-
<
|
|
25
|
-
<p class="text-lg text-gray-600 mb-8">有任何疑问?欢迎联系我们。</p>
|
|
32
|
+
<PageHeader title='联系我们' description='有任何疑问?欢迎联系我们。' />
|
|
26
33
|
{{/if}}
|
|
27
34
|
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
{{#if hasI18n}}
|
|
31
|
-
<label class="label" for="name"><span class="label-text">{m.page_contact_name()}</span></label>
|
|
32
|
-
{{else}}
|
|
33
|
-
<label class="label" for="name"><span class="label-text">姓名</span></label>
|
|
34
|
-
{{/if}}
|
|
35
|
-
<input id="name" type="text" class="input input-bordered w-full" />
|
|
36
|
-
</div>
|
|
37
|
-
<div class="form-control">
|
|
38
|
-
{{#if hasI18n}}
|
|
39
|
-
<label class="label" for="email"><span class="label-text">{m.page_contact_email()}</span></label>
|
|
40
|
-
{{else}}
|
|
41
|
-
<label class="label" for="email"><span class="label-text">邮箱</span></label>
|
|
42
|
-
{{/if}}
|
|
43
|
-
<input id="email" type="email" class="input input-bordered w-full" />
|
|
44
|
-
</div>
|
|
45
|
-
<div class="form-control">
|
|
46
|
-
{{#if hasI18n}}
|
|
47
|
-
<label class="label" for="message"><span class="label-text">{m.page_contact_message()}</span></label>
|
|
48
|
-
{{else}}
|
|
49
|
-
<label class="label" for="message"><span class="label-text">留言</span></label>
|
|
50
|
-
{{/if}}
|
|
51
|
-
<textarea id="message" class="textarea textarea-bordered w-full" rows="4"></textarea>
|
|
52
|
-
</div>
|
|
35
|
+
<Card shadow='sm' class='bg-base-100'>
|
|
36
|
+
<form class='space-y-4' onsubmit={handleSubmit}>
|
|
53
37
|
{{#if hasI18n}}
|
|
54
|
-
|
|
38
|
+
<FormField label={m.page_contact_name()} required>
|
|
39
|
+
<Input id='name' type='text' bind:value={name} required />
|
|
40
|
+
</FormField>
|
|
41
|
+
<FormField label={m.page_contact_email()} required>
|
|
42
|
+
<Input id='email' type='email' bind:value={email} required />
|
|
43
|
+
</FormField>
|
|
44
|
+
<FormField label={m.page_contact_message()} required>
|
|
45
|
+
<textarea id='message' class='textarea textarea-bordered w-full' rows='4' bind:value={message}></textarea>
|
|
46
|
+
</FormField>
|
|
47
|
+
<Button type='submit' variant='primary' class='w-full'>{m.page_contact_submit()}</Button>
|
|
55
48
|
{{else}}
|
|
56
|
-
|
|
49
|
+
<FormField label='姓名' required>
|
|
50
|
+
<Input id='name' type='text' bind:value={name} required />
|
|
51
|
+
</FormField>
|
|
52
|
+
<FormField label='邮箱' required>
|
|
53
|
+
<Input id='email' type='email' bind:value={email} required />
|
|
54
|
+
</FormField>
|
|
55
|
+
<FormField label='留言' required>
|
|
56
|
+
<textarea id='message' class='textarea textarea-bordered w-full' rows='4' bind:value={message}></textarea>
|
|
57
|
+
</FormField>
|
|
58
|
+
<Button type='submit' variant='primary' class='w-full'>发送</Button>
|
|
57
59
|
{{/if}}
|
|
58
|
-
|
|
60
|
+
</form>
|
|
61
|
+
</Card>
|
|
59
62
|
</section>
|