@lark-apaas/coding-template-vite-react 0.1.0-alpha.1 → 0.1.0

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 (77) hide show
  1. package/package.json +1 -1
  2. package/template/.spark_project +12 -0
  3. package/template/README.md +10 -10
  4. package/template/components.json +1 -1
  5. package/template/package.json +5 -3
  6. package/template/scripts/dev.mjs +125 -0
  7. package/template/shared/capabilities/README.md +5 -0
  8. package/template/shared/static/README.md +16 -0
  9. package/template/{client/src → src}/hooks/use-mobile.ts +4 -3
  10. package/template/{client/src → src}/index.tsx +9 -2
  11. package/template/tsconfig.app.json +3 -3
  12. package/template/tsconfig.json +1 -1
  13. package/template/vite.config.ts +1 -2
  14. package/template/package-lock.json +0 -10533
  15. package/template/shared/types.ts +0 -1
  16. /package/template/{client/index.html → index.html} +0 -0
  17. /package/template/{client/public → public}/favicon.svg +0 -0
  18. /package/template/{client/public → public}/icons.svg +0 -0
  19. /package/template/{client/src → src}/app.tsx +0 -0
  20. /package/template/{client/src → src}/components/layout.tsx +0 -0
  21. /package/template/{client/src → src}/components/ui/accordion.tsx +0 -0
  22. /package/template/{client/src → src}/components/ui/alert-dialog.tsx +0 -0
  23. /package/template/{client/src → src}/components/ui/alert.tsx +0 -0
  24. /package/template/{client/src → src}/components/ui/aspect-ratio.tsx +0 -0
  25. /package/template/{client/src → src}/components/ui/avatar.tsx +0 -0
  26. /package/template/{client/src → src}/components/ui/badge.tsx +0 -0
  27. /package/template/{client/src → src}/components/ui/breadcrumb.tsx +0 -0
  28. /package/template/{client/src → src}/components/ui/button-group.tsx +0 -0
  29. /package/template/{client/src → src}/components/ui/button.tsx +0 -0
  30. /package/template/{client/src → src}/components/ui/calendar.tsx +0 -0
  31. /package/template/{client/src → src}/components/ui/card.tsx +0 -0
  32. /package/template/{client/src → src}/components/ui/carousel.tsx +0 -0
  33. /package/template/{client/src → src}/components/ui/chart.tsx +0 -0
  34. /package/template/{client/src → src}/components/ui/checkbox.tsx +0 -0
  35. /package/template/{client/src → src}/components/ui/collapsible.tsx +0 -0
  36. /package/template/{client/src → src}/components/ui/command.tsx +0 -0
  37. /package/template/{client/src → src}/components/ui/context-menu.tsx +0 -0
  38. /package/template/{client/src → src}/components/ui/dialog.tsx +0 -0
  39. /package/template/{client/src → src}/components/ui/drawer.tsx +0 -0
  40. /package/template/{client/src → src}/components/ui/dropdown-menu.tsx +0 -0
  41. /package/template/{client/src → src}/components/ui/empty.tsx +0 -0
  42. /package/template/{client/src → src}/components/ui/field.tsx +0 -0
  43. /package/template/{client/src → src}/components/ui/form.tsx +0 -0
  44. /package/template/{client/src → src}/components/ui/hover-card.tsx +0 -0
  45. /package/template/{client/src → src}/components/ui/image.tsx +0 -0
  46. /package/template/{client/src → src}/components/ui/input-group.tsx +0 -0
  47. /package/template/{client/src → src}/components/ui/input-otp.tsx +0 -0
  48. /package/template/{client/src → src}/components/ui/input.tsx +0 -0
  49. /package/template/{client/src → src}/components/ui/item.tsx +0 -0
  50. /package/template/{client/src → src}/components/ui/kbd.tsx +0 -0
  51. /package/template/{client/src → src}/components/ui/label.tsx +0 -0
  52. /package/template/{client/src → src}/components/ui/menubar.tsx +0 -0
  53. /package/template/{client/src → src}/components/ui/native-select.tsx +0 -0
  54. /package/template/{client/src → src}/components/ui/navigation-menu.tsx +0 -0
  55. /package/template/{client/src → src}/components/ui/pagination.tsx +0 -0
  56. /package/template/{client/src → src}/components/ui/popover.tsx +0 -0
  57. /package/template/{client/src → src}/components/ui/progress.tsx +0 -0
  58. /package/template/{client/src → src}/components/ui/radio-group.tsx +0 -0
  59. /package/template/{client/src → src}/components/ui/resizable.tsx +0 -0
  60. /package/template/{client/src → src}/components/ui/scroll-area.tsx +0 -0
  61. /package/template/{client/src → src}/components/ui/select.tsx +0 -0
  62. /package/template/{client/src → src}/components/ui/separator.tsx +0 -0
  63. /package/template/{client/src → src}/components/ui/sheet.tsx +0 -0
  64. /package/template/{client/src → src}/components/ui/sidebar.tsx +0 -0
  65. /package/template/{client/src → src}/components/ui/skeleton.tsx +0 -0
  66. /package/template/{client/src → src}/components/ui/slider.tsx +0 -0
  67. /package/template/{client/src → src}/components/ui/sonner.tsx +0 -0
  68. /package/template/{client/src → src}/components/ui/spinner.tsx +0 -0
  69. /package/template/{client/src → src}/components/ui/switch.tsx +0 -0
  70. /package/template/{client/src → src}/components/ui/table.tsx +0 -0
  71. /package/template/{client/src → src}/components/ui/tabs.tsx +0 -0
  72. /package/template/{client/src → src}/components/ui/textarea.tsx +0 -0
  73. /package/template/{client/src → src}/components/ui/toggle-group.tsx +0 -0
  74. /package/template/{client/src → src}/components/ui/toggle.tsx +0 -0
  75. /package/template/{client/src → src}/components/ui/tooltip.tsx +0 -0
  76. /package/template/{client/src → src}/index.css +0 -0
  77. /package/template/{client/src → src}/pages/NotFoundPage/NotFoundPage.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-template-vite-react",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0",
4
4
  "description": "Vite + React client template for miaoda coding",
5
5
  "type": "module",
6
6
  "files": ["template", "hooks"],
@@ -0,0 +1,12 @@
1
+ run = ["npm", "run", "dev"]
2
+ lint = ["npm", "run", "lint"]
3
+ hidden = [".config", ".git", "scripts", "node_modules", "dist", ".spark", ".agent", ".agents", "tmp", ".spark_project", ".playwright-cli"]
4
+
5
+ [deployment]
6
+ build = ["npm", "run", "build"]
7
+
8
+ [files]
9
+ [files.restrict]
10
+ pathPatterns = ["package.json", ".spark_project", ".spark/meta.json", ".gitignore"]
11
+ [files.hidden]
12
+ pathPatterns = [".config", ".git", "scripts", "node_modules", "dist", ".spark", ".agent", ".agents", "tmp", ".spark_project", ".playwright-cli"]
@@ -15,7 +15,7 @@
15
15
  ## 目录结构
16
16
 
17
17
  ```
18
- client/src/
18
+ src/
19
19
  ├── index.tsx # 入口(勿修改)
20
20
  ├── app.tsx # 路由配置(仅在 <Routes> 内增删 <Route>)
21
21
  ├── index.css # 全局样式 + 主题变量
@@ -51,8 +51,8 @@ shared/
51
51
 
52
52
  | 文件 | 原因 |
53
53
  |------|------|
54
- | `client/src/index.tsx` | Provider 层级 + 样式引入,由模板管理 |
55
- | `client/src/components/ui/*` | shadcn/ui 内置组件,版本锁定 |
54
+ | `src/index.tsx` | Provider 层级 + 样式引入,由模板管理 |
55
+ | `src/components/ui/*` | shadcn/ui 内置组件,版本锁定 |
56
56
 
57
57
  ---
58
58
 
@@ -60,10 +60,10 @@ shared/
60
60
 
61
61
  | 内容类型 | 放置位置 |
62
62
  |---------|---------|
63
- | 新页面 | `client/src/pages/<PageName>/PageName.tsx` |
64
- | 页面专属组件 | `client/src/pages/<PageName>/components/` |
65
- | 自定义 Hooks | `client/src/hooks/` |
66
- | 工具函数 | `client/src/lib/` |
63
+ | 新页面 | `src/pages/<PageName>/PageName.tsx` |
64
+ | 页面专属组件 | `src/pages/<PageName>/components/` |
65
+ | 自定义 Hooks | `src/hooks/` |
66
+ | 工具函数 | `src/lib/` |
67
67
  | 静态数据文件 | `shared/static/data/` |
68
68
  | 静态图片 | `shared/static/images/` |
69
69
 
@@ -72,7 +72,7 @@ shared/
72
72
  ## 导入路径
73
73
 
74
74
  ```typescript
75
- // @/ 别名 → client/src/
75
+ // @/ 别名 → src/
76
76
  import { cn } from "@/lib/utils";
77
77
  import { useIsMobile } from "@/hooks/use-mobile";
78
78
 
@@ -85,14 +85,14 @@ import configData from "@shared/static/config.json";
85
85
 
86
86
  ## 路由配置
87
87
 
88
- - 新增页面需在 `client/src/app.tsx` 的 `<Routes>` 内注册 `<Route>`
88
+ - 新增页面需在 `src/app.tsx` 的 `<Routes>` 内注册 `<Route>`
89
89
  - `BrowserRouter` 已在 `index.tsx` 中配置,`app.tsx` 中**禁止**再包裹 Router
90
90
 
91
91
  ---
92
92
 
93
93
  ## 主题变量
94
94
 
95
- 主题色定义在 `client/src/index.css`,通过 `:root` CSS 变量 + `@theme inline` 注册到 Tailwind。
95
+ 主题色定义在 `src/index.css`,通过 `:root` CSS 变量 + `@theme inline` 注册到 Tailwind。
96
96
 
97
97
  | 用途 | Tailwind 类 | CSS 变量 |
98
98
  |------|------------|----------|
@@ -5,7 +5,7 @@
5
5
  "tsx": true,
6
6
  "tailwind": {
7
7
  "config": "",
8
- "css": "client/src/index.css",
8
+ "css": "src/index.css",
9
9
  "baseColor": "neutral",
10
10
  "cssVariables": true,
11
11
  "prefix": ""
@@ -3,10 +3,11 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
+ "dev": "node scripts/dev.mjs",
6
7
  "build": "bash scripts/build.sh",
7
8
  "typecheck": "tsc -p tsconfig.app.json",
8
9
  "lint": "concurrently npm:typecheck npm:lint:eslint",
9
- "lint:eslint": "eslint client/src"
10
+ "lint:eslint": "eslint src"
10
11
  },
11
12
  "dependencies": {
12
13
  "@hookform/resolvers": "^5.2.2",
@@ -36,7 +37,7 @@
36
37
  "@radix-ui/react-toggle": "^1.1.9",
37
38
  "@radix-ui/react-toggle-group": "^1.1.10",
38
39
  "@radix-ui/react-tooltip": "^1.1.18",
39
- "@lark-apaas/client-toolkit-lite": "^1.1.0",
40
+ "@lark-apaas/client-toolkit-lite": "1.1.7-alpha.17",
40
41
  "@formkit/auto-animate": "^0.9.0",
41
42
  "@gsap/react": "^2.1.2",
42
43
  "framer-motion": "^12.38.0",
@@ -54,6 +55,7 @@
54
55
  "react": "^19.2.4",
55
56
  "react-day-picker": "^9.14.0",
56
57
  "react-dom": "^19.2.4",
58
+ "react-error-boundary": "^6.0.0",
57
59
  "react-hook-form": "^7.72.0",
58
60
  "react-resizable-panels": "^4.7.6",
59
61
  "react-router-dom": "^7.13.2",
@@ -71,7 +73,7 @@
71
73
  "@types/react": "^19",
72
74
  "@types/react-dom": "^19",
73
75
  "@lark-apaas/coding-presets": "^1.0.0",
74
- "@lark-apaas/coding-vite-preset": "^1.0.0",
76
+ "@lark-apaas/coding-vite-preset": "1.0.7-alpha.13",
75
77
  "concurrently": "^9",
76
78
  "eslint": "^9",
77
79
  "eslint-plugin-react-hooks": "^7",
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dev launcher: 启动 vite dev server,把 stdout/stderr 加时间戳前缀
4
+ * 写到 logs/<name>.std.log + 当前 stdout;负责子进程组管理与端口孤儿清理。
5
+ */
6
+
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import readline from 'node:readline';
10
+ import { spawn, execSync } from 'node:child_process';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ const ROOT = path.resolve(__dirname, '..');
15
+ process.chdir(ROOT);
16
+
17
+ const LOG_DIR = process.env.LOG_DIR || 'logs';
18
+ const CLIENT_DEV_PORT = process.env.CLIENT_DEV_PORT || '8001';
19
+
20
+ fs.mkdirSync(LOG_DIR, { recursive: true });
21
+
22
+ function timestamp() {
23
+ const now = new Date();
24
+ const pad = (n) => String(n).padStart(2, '0');
25
+ return (
26
+ `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ` +
27
+ `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
28
+ );
29
+ }
30
+
31
+ function log(msg) {
32
+ const line = `[${timestamp()}] [dev] ${msg}\n`;
33
+ try { process.stdout.write(line); } catch {}
34
+ }
35
+
36
+ /** 清理端口占用:lsof -ti:<port> | kill -9 */
37
+ function killOrphansByPort(port) {
38
+ try {
39
+ const out = execSync(`lsof -ti:${port}`, { stdio: ['ignore', 'pipe', 'ignore'] })
40
+ .toString()
41
+ .trim();
42
+ if (!out) return [];
43
+ const pids = out.split('\n').filter(Boolean);
44
+ for (const pid of pids) {
45
+ try {
46
+ process.kill(Number(pid), 'SIGKILL');
47
+ log(`killed orphan pid=${pid} on :${port}`);
48
+ } catch {}
49
+ }
50
+ return pids;
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+
56
+ const managed = [];
57
+
58
+ function startProcess({ name, command, args, logFileName }) {
59
+ const logFd = logFileName
60
+ ? fs.openSync(path.join(LOG_DIR, logFileName), 'a')
61
+ : null;
62
+
63
+ const child = spawn(command, args, {
64
+ stdio: ['ignore', 'pipe', 'pipe'],
65
+ shell: false,
66
+ cwd: ROOT,
67
+ env: process.env,
68
+ detached: true,
69
+ });
70
+
71
+ const pipeLines = (stream) => {
72
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
73
+ rl.on('line', (line) => {
74
+ const msg = `[${timestamp()}] [${name}] ${line}\n`;
75
+ try { process.stdout.write(msg); } catch {}
76
+ if (logFd != null) {
77
+ try { fs.writeSync(logFd, msg); } catch {}
78
+ }
79
+ });
80
+ };
81
+ pipeLines(child.stdout);
82
+ pipeLines(child.stderr);
83
+
84
+ managed.push({ name, child });
85
+ return child;
86
+ }
87
+
88
+ killOrphansByPort(CLIENT_DEV_PORT);
89
+
90
+ startProcess({
91
+ name: 'client',
92
+ command: 'npx',
93
+ args: ['vite', '--port', CLIENT_DEV_PORT, '--host', '0.0.0.0'],
94
+ logFileName: 'client.std.log',
95
+ });
96
+
97
+ let stopping = false;
98
+ function cleanup(signal) {
99
+ if (stopping) return;
100
+ stopping = true;
101
+ log(`cleanup triggered by ${signal}`);
102
+
103
+ for (const { child } of managed) {
104
+ if (!child.pid) continue;
105
+ try { process.kill(-child.pid, signal || 'SIGTERM'); } catch {}
106
+ }
107
+ setTimeout(() => {
108
+ for (const { child } of managed) {
109
+ if (!child.pid) continue;
110
+ try { process.kill(-child.pid, 'SIGKILL'); } catch {}
111
+ }
112
+ killOrphansByPort(CLIENT_DEV_PORT);
113
+ process.exit(0);
114
+ }, 2000);
115
+ }
116
+
117
+ process.on('SIGINT', () => cleanup('SIGTERM'));
118
+ process.on('SIGTERM', () => cleanup('SIGTERM'));
119
+ // pkill 杀父 npm 后会收 SIGHUP(controlling tty 关闭);
120
+ // Node 默认直接退出不跑 handler,注册 handler 触发 cleanup
121
+ process.on('SIGHUP', () => cleanup('SIGTERM'));
122
+
123
+ Promise.race(
124
+ managed.map(({ child }) => new Promise((r) => child.on('exit', r))),
125
+ ).then(() => cleanup('SIGTERM'));
@@ -0,0 +1,5 @@
1
+ # shared/capabilities/
2
+
3
+ Capabilities 声明目录。此处文件描述应用对外暴露的能力,由平台运行时消费。
4
+
5
+ 具体 schema 与编写规范见 steering(`.agent/steering/`)。占位目录,按需添加。
@@ -0,0 +1,16 @@
1
+ # shared/static/
2
+
3
+ ⚠️ **此目录资源经鉴权访问**——由平台托管在私有源,仅登录用户可拉取。请勿放公开资源。
4
+
5
+ ## 与其他资源目录的区分
6
+
7
+ | 目录 | 访问 | 部署目标 | 适用场景 |
8
+ |---|---|---|---|
9
+ | `src/assets/` | 代码 `import` | 随 JS 一起进 CDN | 图标、被组件 import 的小图(多数情况选这里) |
10
+ | `public/` | 公网匿名可访问 | 公网 CDN | favicon、被外部按固定 URL 引用的资源 |
11
+ | `shared/static/` | 需应用鉴权 | 私有源 | 仅登录用户可见的图片、PDF、配置文件等 |
12
+
13
+ ## 引用方式
14
+
15
+ `shared/static/` 下文件**不要**用 `/shared/static/foo.png` 这种相对路径直接引用——浏览器无法匿名访问。
16
+ 应通过平台运行时 SDK 拿到带 token 的 URL 后再使用。
@@ -3,7 +3,9 @@ import * as React from "react"
3
3
  const MOBILE_BREAKPOINT = 768
4
4
 
5
5
  export function useIsMobile() {
6
- const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
6
+ const [isMobile, setIsMobile] = React.useState<boolean>(() =>
7
+ typeof window !== "undefined" && window.innerWidth < MOBILE_BREAKPOINT,
8
+ )
7
9
 
8
10
  React.useEffect(() => {
9
11
  const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
@@ -11,9 +13,8 @@ export function useIsMobile() {
11
13
  setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
14
  }
13
15
  mql.addEventListener("change", onChange)
14
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
16
  return () => mql.removeEventListener("change", onChange)
16
17
  }, [])
17
18
 
18
- return !!isMobile
19
+ return isMobile
19
20
  }
@@ -1,7 +1,8 @@
1
1
  import { StrictMode } from "react";
2
2
  import { createRoot } from "react-dom/client";
3
3
  import { BrowserRouter } from "react-router-dom";
4
- import { AppContainer } from "@lark-apaas/client-toolkit-lite";
4
+ import { ErrorBoundary } from "react-error-boundary";
5
+ import { AppContainer, ErrorRender } from "@lark-apaas/client-toolkit-lite";
5
6
  import App from "./app";
6
7
  import "./index.css";
7
8
 
@@ -9,7 +10,13 @@ createRoot(document.getElementById("root")!).render(
9
10
  <StrictMode>
10
11
  <BrowserRouter basename={process.env.CLIENT_BASE_PATH || "/"}>
11
12
  <AppContainer>
12
- <App />
13
+ <ErrorBoundary
14
+ fallbackRender={({ error, resetErrorBoundary }) => (
15
+ <ErrorRender error={error} resetErrorBoundary={resetErrorBoundary} />
16
+ )}
17
+ >
18
+ <App />
19
+ </ErrorBoundary>
13
20
  </AppContainer>
14
21
  </BrowserRouter>
15
22
  </StrictMode>,
@@ -4,10 +4,10 @@
4
4
  "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5
5
  "baseUrl": ".",
6
6
  "paths": {
7
- "@/*": ["./client/src/*"],
7
+ "@/*": ["./src/*"],
8
8
  "@shared/*": ["./shared/*"]
9
9
  }
10
10
  },
11
- "include": ["client/src", "shared"],
12
- "exclude": ["client/src/components/ui"]
11
+ "include": ["src"],
12
+ "exclude": ["src/components/ui"]
13
13
  }
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "baseUrl": ".",
4
4
  "paths": {
5
- "@/*": ["./client/src/*"],
5
+ "@/*": ["./src/*"],
6
6
  "@shared/*": ["./shared/*"]
7
7
  }
8
8
  },
@@ -2,10 +2,9 @@ import path from 'path'
2
2
  import { defineConfig } from '@lark-apaas/coding-vite-preset'
3
3
 
4
4
  export default defineConfig({
5
- root: path.resolve(__dirname, 'client'),
6
5
  resolve: {
7
6
  alias: {
8
- '@': path.resolve(__dirname, 'client/src'),
7
+ '@': path.resolve(__dirname, 'src'),
9
8
  '@shared': path.resolve(__dirname, 'shared'),
10
9
  },
11
10
  },