@h-ai/cli 1.0.0-beta.18 → 1.0.0-beta.19
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.en.md +4 -0
- package/README.ja.md +4 -0
- package/README.md +4 -0
- package/dist/{chunk-6YZBC5MO.js → chunk-2ZL5ORQ2.js} +85 -25
- package/dist/chunk-2ZL5ORQ2.js.map +1 -0
- package/dist/{chunk-7NVRYYHN.js → chunk-L53P43B2.js} +129 -31
- package/dist/chunk-L53P43B2.js.map +1 -0
- package/dist/cli-config-templates-LWMD2I6V.js +5 -0
- package/dist/{cli-config-templates-QG5HOYR5.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
- package/dist/{cli-skill-templates-W4I6ZKYW.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
- package/dist/{cli-skill-templates-W4I6ZKYW.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2747 -671
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +22 -22
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
- package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +71 -32
- package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +131 -54
- package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
- package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +78 -31
- package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
- package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
- package/templates/apps/api/eslint.config.js.hbs +3 -1
- package/templates/apps/api/images/serv.Dockerfile.hbs +3 -4
- package/templates/apps/api/playwright.config.ts.hbs +23 -19
- package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -3
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +111 -26
- package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
- package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
- package/templates/apps/fullstack/images/serv.Dockerfile.hbs +3 -4
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +0 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +139 -49
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +139 -28
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +141 -25
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
- package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
- package/templates/apps/fullstack/playwright.config.ts.hbs +9 -3
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -15
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -21
- package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +14 -3
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
- package/templates/apps/website/scripts/static-website.mjs.hbs +16 -3
- package/templates/apps/website/src/routes/+layout.svelte.hbs +44 -23
- package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
- package/templates/base/e2e/app.spec.ts.hbs +49 -16
- package/templates/base/eslint-rules.js +44 -25
- package/templates/base/eslint.config.js.hbs +3 -1
- package/templates/base/images/web.Dockerfile.hbs +3 -4
- package/templates/base/package.json.hbs +5 -4
- package/templates/base/patches/daisyui-5.7.50.patch +8 -0
- package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
- package/templates/base/playwright.config.ts +3 -1
- package/templates/base/pnpm-workspace.yaml.hbs +33 -16
- package/templates/base/src/app.d.ts.hbs +1 -1
- package/templates/base/src/hooks.server.ts.hbs +36 -27
- package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
- package/templates/base/src/lib/server/init.ts.hbs +124 -51
- package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
- package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
- package/templates/base/svelte.config.js.hbs +10 -100
- package/templates/base/tests/scaffold.test.ts.hbs +12 -3
- package/templates/base/tsconfig.json.hbs +7 -4
- package/templates/base/vite.config.ts.hbs +108 -14
- package/templates/base/vitest.config.ts.hbs +6 -2
- package/templates/features/ai/routes/api/chat/+server.ts +3 -1
- package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +11 -2
- package/templates/shared/browser-check/startup.js +32 -6
- package/templates/shared/browser-startup.spec.ts +3 -1
- package/templates/shared/build-metadata.ts +18 -4
- package/templates/shared/e2e-env.ts +22 -6
- package/templates/shared/native-name.mjs +9 -2
- package/templates/shared/public-base-path.mjs +17 -4
- package/templates/shared/run-e2e.mjs +40 -9
- package/templates/shared/run-node.mjs +15 -4
- package/templates/shared/run-tauri.mjs +37 -8
- package/templates/shared/vite-runtime-config.ts +9 -3
- package/templates/skills/AGENTS.md +10 -73
- package/templates/skills/bridges/admin/AGENTS.md +9 -65
- package/templates/skills/bridges/api/AGENTS.md +8 -59
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
- package/templates/skills/bridges/h5/AGENTS.md +8 -63
- package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
- package/templates/skills/bridges/website/AGENTS.md +8 -63
- package/templates/skills/hai-ai/acp.md +2 -0
- package/templates/skills/hai-ai/guide.md +17 -6
- package/templates/skills/hai-ai/reference.md +21 -12
- package/templates/skills/hai-api-client/reference.md +1 -1
- package/templates/skills/hai-app-create/development.md +3 -1
- package/templates/skills/hai-app-create/project.md +33 -0
- package/templates/skills/hai-app-create/reference.md +8 -8
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-build/SKILL.md +2 -2
- package/templates/skills/hai-build/paths.md +1 -1
- package/templates/skills/hai-build/reference.md +11 -7
- package/templates/skills/hai-capacitor/reference.md +2 -0
- package/templates/skills/hai-ci/SKILL.md +1 -0
- package/templates/skills/hai-core/reference.md +3 -0
- package/templates/skills/hai-crypto/reference.md +2 -0
- package/templates/skills/hai-deploy/SKILL.md +7 -1
- package/templates/skills/hai-deploy/middleware.md +3 -1
- package/templates/skills/hai-framework-sync/sync.md +2 -0
- package/templates/skills/hai-kit/reference.md +16 -4
- package/templates/skills/hai-payment/reference.md +2 -0
- package/templates/skills/hai-reach/SKILL.md +2 -1
- package/templates/skills/hai-reach/reference.md +1 -1
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-reldb/reference.md +4 -0
- package/templates/skills/hai-serv/reference.md +12 -2
- package/templates/skills/hai-storage/reference.md +12 -3
- package/templates/skills/hai-ui/SKILL.md +3 -2
- package/templates/skills/hai-ui/guide.md +1 -1
- package/templates/skills/hai-ui/reference.md +23 -35
- package/templates/skills/hai-vecdb/reference.md +1 -1
- package/dist/chunk-6YZBC5MO.js.map +0 -1
- package/dist/chunk-7NVRYYHN.js.map +0 -1
- package/dist/cli-config-templates-QG5HOYR5.js +0 -5
|
@@ -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: [
|
|
6
|
-
|
|
5
|
+
plugins: [
|
|
6
|
+
svelte(),
|
|
7
|
+
],
|
|
8
|
+
test: {
|
|
9
|
+
environment: 'node',
|
|
10
|
+
include: ['tests/**/*.test.ts'],
|
|
11
|
+
},
|
|
7
12
|
})
|
|
@@ -44,7 +44,9 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
|
|
|
44
44
|
return url.port
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
return url.protocol === 'https:'
|
|
47
|
+
return url.protocol === 'https:'
|
|
48
|
+
? '443'
|
|
49
|
+
: '80'
|
|
48
50
|
}
|
|
49
51
|
catch {
|
|
50
52
|
return fallbackPort
|
|
@@ -54,7 +56,9 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
|
|
|
54
56
|
export default defineConfig({
|
|
55
57
|
testDir: './e2e',
|
|
56
58
|
{{#if fullstack.e2eFrontend}}
|
|
57
|
-
...(sessionClose
|
|
59
|
+
...(sessionClose
|
|
60
|
+
? { testMatch: '**/session-close.spec.ts' }
|
|
61
|
+
: { testIgnore: '**/session-close.spec.ts' }),
|
|
58
62
|
{{/if}}
|
|
59
63
|
// 多个界面验收共用管理员资料与引导状态,保留串行以免互相覆盖。
|
|
60
64
|
fullyParallel: false,
|
|
@@ -116,7 +120,9 @@ export default defineConfig({
|
|
|
116
120
|
timeout: 120_000,
|
|
117
121
|
},
|
|
118
122
|
...frontends.map((frontend) => {
|
|
119
|
-
const url = frontend.target === primaryFrontend
|
|
123
|
+
const url = frontend.target === primaryFrontend
|
|
124
|
+
? baseURL
|
|
125
|
+
: frontend.url
|
|
120
126
|
return {
|
|
121
127
|
name: frontend.name,
|
|
122
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
|
-
|
|
2
|
+
minimumReleaseAgeExcludePrune: true
|
|
3
3
|
|
|
4
4
|
trustPolicy: no-downgrade
|
|
5
5
|
# 上游构建工具仍要求 4.x;4.0.3 tarball 已核对原 lockfile 的 SHA-512。
|
|
@@ -8,32 +8,47 @@ trustPolicyExclude:
|
|
|
8
8
|
|
|
9
9
|
# pnpm 12 配置;原生依赖仅运行明确列出的安装脚本。
|
|
10
10
|
nodeLinker: hoisted
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
esbuild: true
|
|
17
|
-
core-js: false
|
|
11
|
+
shellEmulator: true
|
|
12
|
+
|
|
13
|
+
packages:
|
|
14
|
+
- packages/*
|
|
15
|
+
- apps/*
|
|
18
16
|
|
|
17
|
+
# 构建与浏览器依赖的安全修复;版本与框架工作区同步维护。
|
|
19
18
|
overrides:
|
|
20
|
-
|
|
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
|
|
21
22
|
cookie@<0.7.0: 0.7.2
|
|
23
|
+
dompurify@>=3 <3.4.17: 3.4.17
|
|
22
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
|
|
23
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
|
|
24
34
|
sharp@<0.35.5: 0.35.5
|
|
25
35
|
tinypool@<2.1.2: 2.2.0
|
|
26
|
-
'@qdrant/js-client-rest>undici': 7.30.0
|
|
27
36
|
xcode>uuid: 11.1.1
|
|
28
|
-
"@types/node": "{{versions.nodeTypes}}"
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
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
|
|
33
48
|
|
|
34
49
|
{{#if useCatalogProtocol}}
|
|
35
50
|
catalog:
|
|
36
51
|
{{#each haiCatalogPackages}}
|
|
37
|
-
|
|
52
|
+
{{{yaml @key this}}}
|
|
38
53
|
{{/each}}
|
|
39
54
|
{{/if}}
|
|
@@ -5,14 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { BottomNavItem } from '@h-ai/ui'
|
|
7
7
|
import type { Snippet } from 'svelte'
|
|
8
|
-
import { browser } from '$app/
|
|
8
|
+
import { browser } from '$app/env'
|
|
9
9
|
import { goto } from '$app/navigation'
|
|
10
|
-
import {
|
|
10
|
+
import { resolve } from '$app/paths'
|
|
11
11
|
import { page } from '$app/state'
|
|
12
|
-
{{#if hasI18n}}
|
|
13
|
-
import * as m from '$lib/paraglide/messages'
|
|
14
|
-
import { getLocale, setLocale } from '$lib/paraglide/runtime.js'
|
|
15
|
-
{{/if}}
|
|
16
12
|
{{#if features.crypto}}
|
|
17
13
|
import { crypto } from '@h-ai/crypto'
|
|
18
14
|
import { kit } from '@h-ai/kit'
|
|
@@ -29,6 +25,10 @@
|
|
|
29
25
|
import { applyTheme, getSavedTheme } from '@h-ai/ui/theme'
|
|
30
26
|
{{#if features.crypto}}
|
|
31
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'
|
|
32
32
|
{{/if}}
|
|
33
33
|
import '../app.css'
|
|
34
34
|
|
|
@@ -37,7 +37,9 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const { children }: Props = $props()
|
|
40
|
-
const initialTheme = browser
|
|
40
|
+
const initialTheme = browser
|
|
41
|
+
? getSavedTheme()
|
|
42
|
+
: 'light'
|
|
41
43
|
let currentTheme = $state(initialTheme)
|
|
42
44
|
if (browser)
|
|
43
45
|
applyTheme(initialTheme, false)
|
|
@@ -46,37 +48,71 @@
|
|
|
46
48
|
currentTheme = theme
|
|
47
49
|
applyTheme(theme)
|
|
48
50
|
}
|
|
49
|
-
|
|
50
51
|
{{#if features.crypto}}
|
|
51
|
-
if (browser)
|
|
52
|
-
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), {
|
|
52
|
+
if (browser) {
|
|
53
|
+
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), {
|
|
54
|
+
crypto,
|
|
55
|
+
basePath: resolve('/').replace(/\/$/, ''),
|
|
56
|
+
})
|
|
57
|
+
}
|
|
53
58
|
{{/if}}
|
|
54
59
|
{{#if features.iam}}
|
|
55
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
60
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
56
61
|
$effect(() => authActions.bindPageExit({ enabled: page.data.iamPublicConfig?.session.endOnPageHide }))
|
|
57
62
|
{{/if}}
|
|
58
63
|
|
|
59
|
-
const navItems: (BottomNavItem & { path:
|
|
64
|
+
const navItems: (BottomNavItem & { path: ReturnType<typeof resolve> })[] = [
|
|
60
65
|
{{#if hasI18n}}
|
|
61
|
-
{
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
},
|
|
64
84
|
{{else}}
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
},
|
|
68
103
|
{{/if}}
|
|
69
104
|
]
|
|
70
105
|
|
|
71
|
-
const localPath = $derived(page.url.pathname.slice(
|
|
72
|
-
const activeNav = $derived(navItems.find(item => item.path ===
|
|
106
|
+
const localPath = $derived(page.url.pathname.slice(resolve('/').replace(/\/$/, '').length) || '/')
|
|
107
|
+
const activeNav = $derived(navItems.find(item => item.path === page.url.pathname)?.id)
|
|
73
108
|
// 所有页面共用页头页脚;仅业务页面显示底部导航。
|
|
74
109
|
const isAuthPage = $derived(localPath.startsWith('/auth'))
|
|
75
110
|
|
|
76
111
|
function handleNavigation(id: string) {
|
|
77
112
|
const item = navItems.find(item => item.id === id)
|
|
78
113
|
if (item)
|
|
79
|
-
|
|
114
|
+
// eslint-disable-next-line svelte/no-navigation-without-resolve -- path 仅来自上方 resolve 构造的导航表,不能二次添加挂载前缀。
|
|
115
|
+
void goto(item.path)
|
|
80
116
|
}
|
|
81
117
|
{{#if hasI18n}}
|
|
82
118
|
|
|
@@ -48,9 +48,21 @@
|
|
|
48
48
|
let nativeTokenStorage = $state(false)
|
|
49
49
|
|
|
50
50
|
const navItems = $derived([
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
|
|
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>
|
|
@@ -151,9 +151,13 @@ async function initialize(): Promise<void> {
|
|
|
151
151
|
const result = await apiClient.init({
|
|
152
152
|
baseUrl: resolveApiBase(),
|
|
153
153
|
clientName: 'hai-mobile-app',
|
|
154
|
-
headers: native
|
|
154
|
+
headers: native
|
|
155
|
+
? { 'x-hai-token-transport': 'body' }
|
|
156
|
+
: {},
|
|
155
157
|
auth: { storage },
|
|
156
|
-
...(transport
|
|
158
|
+
...(transport
|
|
159
|
+
? { transport }
|
|
160
|
+
: {}),
|
|
157
161
|
})
|
|
158
162
|
if (!result.success)
|
|
159
163
|
throw new Error(result.error.message)
|
|
@@ -172,7 +176,14 @@ async function initialize(): Promise<void> {
|
|
|
172
176
|
throw error
|
|
173
177
|
}
|
|
174
178
|
{{else}}
|
|
175
|
-
const result = await apiClient.init({
|
|
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
|
+
})
|
|
176
187
|
if (!result.success)
|
|
177
188
|
throw new Error(result.error.message)
|
|
178
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 = {
|
|
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
|
|
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
|
|
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>
|
|
@@ -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 [
|
|
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>
|
|
@@ -28,9 +28,22 @@ const message = core.i18n.createMessageGetter({
|
|
|
28
28
|
assets: 'staticは公開資産ディレクトリーです。非公開.envファイルやシンボリックリンクを置かず、非公開内容を移動してから構築してください。',
|
|
29
29
|
},
|
|
30
30
|
})
|
|
31
|
-
const serverFeatures = [
|
|
32
|
-
|
|
33
|
-
|
|
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() {
|
|
@@ -4,13 +4,9 @@
|
|
|
4
4
|
* 官网根布局
|
|
5
5
|
*/
|
|
6
6
|
import type { Snippet } from 'svelte'
|
|
7
|
-
import { browser } from '$app/
|
|
8
|
-
import {
|
|
7
|
+
import { browser } from '$app/env'
|
|
8
|
+
import { resolve } from '$app/paths'
|
|
9
9
|
import { page } from '$app/state'
|
|
10
|
-
{{#if hasI18n}}
|
|
11
|
-
import * as m from '$lib/paraglide/messages'
|
|
12
|
-
import { getLocale, setLocale } from '$lib/paraglide/runtime.js'
|
|
13
|
-
{{/if}}
|
|
14
10
|
{{#if features.crypto}}
|
|
15
11
|
import { crypto } from '@h-ai/crypto'
|
|
16
12
|
import { kit } from '@h-ai/kit'
|
|
@@ -27,6 +23,10 @@
|
|
|
27
23
|
import { applyTheme, getSavedTheme } from '@h-ai/ui/theme'
|
|
28
24
|
{{#if features.crypto}}
|
|
29
25
|
import { untrack } from 'svelte'
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if hasI18n}}
|
|
28
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
29
|
+
import { getLocale, setLocale } from '#lib/paraglide/runtime.js'
|
|
30
30
|
{{/if}}
|
|
31
31
|
import '../app.css'
|
|
32
32
|
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const { children }: Props = $props()
|
|
38
|
-
const initialTheme = browser
|
|
38
|
+
const initialTheme = browser
|
|
39
|
+
? getSavedTheme()
|
|
40
|
+
: 'light'
|
|
39
41
|
let currentTheme = $state(initialTheme)
|
|
40
42
|
if (browser)
|
|
41
43
|
applyTheme(initialTheme, false)
|
|
@@ -44,27 +46,42 @@
|
|
|
44
46
|
currentTheme = theme
|
|
45
47
|
applyTheme(theme)
|
|
46
48
|
}
|
|
47
|
-
|
|
48
49
|
{{#if features.crypto}}
|
|
49
|
-
if (browser)
|
|
50
|
-
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), {
|
|
50
|
+
if (browser) {
|
|
51
|
+
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), {
|
|
52
|
+
crypto,
|
|
53
|
+
basePath: resolve('/').replace(/\/$/, ''),
|
|
54
|
+
})
|
|
55
|
+
}
|
|
51
56
|
{{/if}}
|
|
52
57
|
{{#if features.iam}}
|
|
53
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
58
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
54
59
|
$effect(() => authActions.bindPageExit({ enabled: page.data.iamPublicConfig?.session.endOnPageHide }))
|
|
55
60
|
{{/if}}
|
|
56
61
|
|
|
57
62
|
const navItems = [
|
|
58
63
|
{{#if hasI18n}}
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
{
|
|
65
|
+
path: resolve('/'),
|
|
66
|
+
label: m.nav_home(),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
path: resolve('about'),
|
|
70
|
+
label: m.nav_about(),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
path: resolve('services'),
|
|
74
|
+
label: m.nav_services(),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
path: resolve('contact'),
|
|
78
|
+
label: m.nav_contact(),
|
|
79
|
+
},
|
|
63
80
|
{{else}}
|
|
64
|
-
{ path: '/', label: '首页' },
|
|
65
|
-
{ path: '
|
|
66
|
-
{ path: '
|
|
67
|
-
{ path: '
|
|
81
|
+
{ path: resolve('/'), label: '首页' },
|
|
82
|
+
{ path: resolve('about'), label: '关于' },
|
|
83
|
+
{ path: resolve('services'), label: '服务' },
|
|
84
|
+
{ path: resolve('contact'), label: '联系我们' },
|
|
68
85
|
{{/if}}
|
|
69
86
|
] as const
|
|
70
87
|
{{#if hasI18n}}
|
|
@@ -89,15 +106,17 @@
|
|
|
89
106
|
<header class='navbar flex-wrap bg-base-100 shadow-sm'>
|
|
90
107
|
<div class='flex-1'>
|
|
91
108
|
{{#if hasI18n}}
|
|
92
|
-
<a href={resolve('/'
|
|
109
|
+
<a href={resolve('/')} class='btn btn-ghost text-xl font-black tracking-tight'>{page.data.appConfig.name}</a>
|
|
93
110
|
{{else}}
|
|
94
|
-
<a href={resolve('/'
|
|
111
|
+
<a href={resolve('/')} class='btn btn-ghost text-xl font-black tracking-tight'>{page.data.appConfig.name}</a>
|
|
95
112
|
{{/if}}
|
|
96
113
|
</div>
|
|
97
114
|
<nav class='w-full flex-none sm:w-auto'>
|
|
98
115
|
<ul class='menu menu-horizontal flex-wrap px-1'>
|
|
99
116
|
{#each navItems as item (item.path)}
|
|
100
|
-
<li
|
|
117
|
+
<li><!-- eslint-disable svelte/no-navigation-without-resolve -- 菜单字段已由 resolve 生成完整挂载地址;规则无法追踪派生/筛选后的集合,不能二次添加前缀。 -->
|
|
118
|
+
<a href={item.path}>{item.label}</a>
|
|
119
|
+
<!-- eslint-enable svelte/no-navigation-without-resolve --></li>
|
|
101
120
|
{/each}
|
|
102
121
|
</ul>
|
|
103
122
|
</nav>
|
|
@@ -114,7 +133,9 @@
|
|
|
114
133
|
<footer class='footer footer-center p-4 bg-base-300 text-base-content'>
|
|
115
134
|
<span>{page.data.appConfig.name} · {page.data.appConfig.version} · {page.data.appConfig.env}</span>
|
|
116
135
|
{{#if hasI18n}}
|
|
117
|
-
<p>{m.footer_copyright({
|
|
136
|
+
<p>{m.footer_copyright({
|
|
137
|
+
year: new Date().getFullYear(),
|
|
138
|
+
})}</p>
|
|
118
139
|
{{else}}
|
|
119
140
|
<p>Copyright © 2025 - All rights reserved</p>
|
|
120
141
|
{{/if}}
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { resolve } from '$app/paths'
|
|
7
7
|
import { page } from '$app/state'
|
|
8
|
+
import { Badge } from '@h-ai/ui'
|
|
8
9
|
{{#if hasI18n}}
|
|
9
|
-
import * as m from '
|
|
10
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
10
11
|
{{/if}}
|
|
11
|
-
import { Badge } from '@h-ai/ui'
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<svelte:head>
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
{{#if hasI18n}}
|
|
36
36
|
<h1 class='mt-5 text-5xl font-black tracking-tight'>{m.page_home_title()}</h1>
|
|
37
37
|
<p class='py-6 text-base-content/60'>{m.page_home_subtitle()}</p>
|
|
38
|
-
<a href={resolve('
|
|
38
|
+
<a href={resolve('about')} class='btn btn-primary'>
|
|
39
39
|
<span class='icon-[tabler--arrow-right] size-5'></span>
|
|
40
40
|
{m.page_home_cta()}
|
|
41
41
|
</a>
|
|
42
42
|
{{else}}
|
|
43
43
|
<h1 class='mt-5 text-5xl font-black tracking-tight'>欢迎</h1>
|
|
44
44
|
<p class='py-6 text-base-content/60'>基于 hai Agent Framework 构建</p>
|
|
45
|
-
<a href={resolve('
|
|
45
|
+
<a href={resolve('about')} class='btn btn-primary'>
|
|
46
46
|
<span class='icon-[tabler--arrow-right] size-5'></span>
|
|
47
47
|
了解更多
|
|
48
48
|
</a>
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* 关于页面
|
|
5
5
|
*/
|
|
6
|
+
import { Card, PageHeader } from '@h-ai/ui'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
|
-
import * as m from '
|
|
8
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
8
9
|
{{/if}}
|
|
9
|
-
import { Card, PageHeader } 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 { Button, Card, FormField, FormLayout, Input, notifyWarning, PageHeader, Textarea } from '@h-ai/ui'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
|
-
import * as m from '
|
|
8
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
8
9
|
{{/if}}
|
|
9
|
-
import { Button, Card, FormField, FormLayout, Input, notifyWarning, PageHeader, Textarea } from '@h-ai/ui'
|
|
10
10
|
|
|
11
11
|
let name = $state('')
|
|
12
12
|
let email = $state('')
|
|
@@ -3,16 +3,31 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* 服务页面
|
|
5
5
|
*/
|
|
6
|
+
import { Card, PageHeader } from '@h-ai/ui'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
|
-
import * as m from '
|
|
8
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
8
9
|
{{/if}}
|
|
9
|
-
import { Card, PageHeader } from '@h-ai/ui'
|
|
10
10
|
|
|
11
11
|
{{#if hasI18n}}
|
|
12
12
|
const services = $derived([
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
{
|
|
14
|
+
id: 'consulting',
|
|
15
|
+
title: m.page_services_item1(),
|
|
16
|
+
desc: m.page_services_desc(),
|
|
17
|
+
icon: 'icon-[tabler--message-circle]',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'development',
|
|
21
|
+
title: m.page_services_item2(),
|
|
22
|
+
desc: m.page_services_desc(),
|
|
23
|
+
icon: 'icon-[tabler--code]',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'operations',
|
|
27
|
+
title: m.page_services_item3(),
|
|
28
|
+
desc: m.page_services_desc(),
|
|
29
|
+
icon: 'icon-[tabler--server-cog]',
|
|
30
|
+
},
|
|
16
31
|
])
|
|
17
32
|
{{else}}
|
|
18
33
|
const services = [
|