@_tc/template-core 0.1.12 → 0.1.14

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 (51) hide show
  1. package/.skills/tc-component-usage-skills/SKILL.md +31 -9
  2. package/.skills/tc-component-usage-skills/reference/component-api.md +63 -42
  3. package/.skills/tc-component-usage-skills/reference/examples.md +34 -24
  4. package/.skills/tc-component-usage-skills/reference/patterns.md +52 -17
  5. package/.skills/tc-component-usage-skills/reference/template-core-frontend.md +104 -0
  6. package/.skills/tc-generator/SKILL.md +11 -3
  7. package/.skills/tc-generator/reference/example.md +1 -1
  8. package/.skills/tc-generator/reference/model-schema.md +4 -2
  9. package/.skills/tc-generator/reference/project-template/config/config.default.js +4 -0
  10. package/.skills/tc-generator/reference/project-template/index.js +1 -1
  11. package/.skills/tc-generator/reference/project-template/model/product/project/default.js +1 -0
  12. package/.skills/tc-generator/reference/project-template/package.json +16 -1
  13. package/.skills/tc-generator/reference/runtime-extensions.md +190 -0
  14. package/README.md +217 -2
  15. package/cjs/app/extend/db.js +20 -1
  16. package/cjs/bundler/utils.js +1 -1
  17. package/esm/app/extend/db.js +138 -3
  18. package/esm/bundler/utils.js +2 -2
  19. package/fe/frontend/apps/dash/Dashboard.js +3 -1
  20. package/fe/frontend/apps/dash/types.d.ts +8 -0
  21. package/fe/frontend/src/components/LanguageSwitch/LanguageSwitch.d.ts +14 -0
  22. package/fe/frontend/src/components/LanguageSwitch/LanguageSwitch.js +32 -0
  23. package/fe/frontend/src/components/LanguageSwitch/index.d.ts +4 -0
  24. package/fe/frontend/src/components/LanguageSwitch/index.js +3 -0
  25. package/fe/frontend/src/components/ThemeSwitch/ThemeSwitch.d.ts +15 -0
  26. package/fe/frontend/src/components/ThemeSwitch/ThemeSwitch.js +59 -0
  27. package/fe/frontend/src/components/ThemeSwitch/index.d.ts +4 -0
  28. package/fe/frontend/src/components/ThemeSwitch/index.js +3 -0
  29. package/fe/frontend/src/components/index.d.ts +4 -0
  30. package/fe/frontend/src/components/index.js +3 -0
  31. package/fe/frontend/src/index.d.ts +1 -0
  32. package/fe/frontend/src/index.js +1 -0
  33. package/fe/frontend/src/language/en-US.d.ts +13 -0
  34. package/fe/frontend/src/language/en-US.js +13 -0
  35. package/fe/frontend/src/language/index.d.ts +7 -0
  36. package/fe/frontend/src/language/index.js +7 -0
  37. package/fe/frontend/src/language/zh-CN.d.ts +13 -0
  38. package/fe/frontend/src/language/zh-CN.js +13 -0
  39. package/fe/packages/ui/react/components/Button/{SumbitButton.d.ts → SubmitButton.d.ts} +5 -5
  40. package/fe/packages/ui/react/components/Button/{SumbitButton.js → SubmitButton.js} +3 -3
  41. package/fe/packages/ui/react/components/Button/index.d.ts +1 -1
  42. package/fe/packages/ui/react/components/Button/index.js +1 -1
  43. package/fe/packages/ui/react/components/Modal/ModalManager.d.ts +3 -3
  44. package/fe/packages/ui/react/components/TableSearch/index.d.ts +1 -0
  45. package/package.json +3 -3
  46. package/types/app/extend/db.d.ts +54 -1
  47. package/types/config/config.default.d.ts +30 -0
  48. package/types/index.d.ts +1 -0
  49. package/cjs/bundler/modulepreload-polyfill.js +0 -1
  50. package/esm/bundler/modulepreload-polyfill.js +0 -11
  51. package/types/bundler/modulepreload-polyfill.d.ts +0 -1
@@ -0,0 +1,104 @@
1
+ # TemplateCore Frontend Helpers
2
+
3
+ Use these when building business UI on top of TemplateCore, especially Dashboard header tools and project pages.
4
+
5
+ ## Import Paths
6
+
7
+ ```tsx
8
+ import { AsyncSelect, LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
9
+ import { Button, DataTable, TableSearch } from '@_tc/template-core/fe/rc'
10
+ ```
11
+
12
+ Inside this repository, `@tc/ui-react` is also valid for the raw UI library. In published business projects, prefer `@_tc/template-core/fe/rc` for UI components and `@_tc/template-core/fe` for frontend runtime helpers.
13
+
14
+ ## LanguageSwitch
15
+
16
+ ```tsx
17
+ <LanguageSwitch />
18
+ ```
19
+
20
+ Default languages are `zh-CN` and `en-US`. Pass `options` to replace the list. `onChange(language)` receives the selected language string. The component calls `i18nStore.getState().setLanguage()`, so it uses the same `tc_language` persistence as the UI i18n runtime.
21
+
22
+ Useful props:
23
+
24
+ ```typescript
25
+ type LanguageSwitchValue = 'zh-CN' | 'en-US' | (string & {})
26
+
27
+ interface LanguageSwitchProps {
28
+ value?: LanguageSwitchValue
29
+ defaultValue?: LanguageSwitchValue
30
+ options?: Array<{ label: ReactNode; value: LanguageSwitchValue; searchText?: string }>
31
+ onChange?: (language: LanguageSwitchValue) => void
32
+ }
33
+ ```
34
+
35
+ ## ThemeSwitch
36
+
37
+ ```tsx
38
+ <ThemeSwitch showLabel />
39
+ ```
40
+
41
+ Toggles `document.documentElement.classList` with the `dark` class and syncs `color-scheme`. By default it persists to `localStorage` key `tc_theme`.
42
+
43
+ Useful props:
44
+
45
+ ```typescript
46
+ type ThemeSwitchMode = 'light' | 'dark'
47
+
48
+ interface ThemeSwitchProps {
49
+ value?: ThemeSwitchMode
50
+ defaultValue?: ThemeSwitchMode
51
+ onChange?: (mode: ThemeSwitchMode) => void
52
+ disabled?: boolean
53
+ showLabel?: boolean
54
+ persist?: boolean
55
+ storageKey?: string
56
+ }
57
+ ```
58
+
59
+ ## AsyncSelect
60
+
61
+ Use `AsyncSelect` when options should be loaded from a TemplateCore API.
62
+
63
+ ```tsx
64
+ <AsyncSelect
65
+ fetchConfig={{ url: '/user/options', method: 'get' }}
66
+ placeholder="Select user"
67
+ />
68
+ ```
69
+
70
+ The options array shape is:
71
+
72
+ ```ts
73
+ Array<{ label: string; value: string | number; searchText?: string }>
74
+ ```
75
+
76
+ The endpoint should return TemplateCore's normal response wrapper. In a controller, use:
77
+
78
+ ```js
79
+ this.success(ctx, [
80
+ { label: "Alice", value: "u1" },
81
+ { label: "Bob", value: "u2" },
82
+ ]);
83
+ ```
84
+
85
+ `AsyncSelect` reads `res.data.code === 0` and then maps `res.data.data`. It forwards most `Select` props except `options`; it owns option loading internally.
86
+
87
+ ## Dashboard Header Tools
88
+
89
+ For a compact header user area, combine the helpers:
90
+
91
+ ```tsx
92
+ import { LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
93
+
94
+ export function HeaderTools() {
95
+ return (
96
+ <div className="flex items-center gap-2">
97
+ <LanguageSwitch className="w-32" />
98
+ <ThemeSwitch />
99
+ </div>
100
+ )
101
+ }
102
+ ```
103
+
104
+ Registering the header slot itself belongs to the generator/runtime extension docs. The supported Dashboard component key is `HeaderView.userArea`.
@@ -18,7 +18,8 @@ Use this skill to generate or update TemplateCore business code.
18
18
  - optional `model/{modelKey}/project/{projectKey}.js`
19
19
  2. Read only the reference needed:
20
20
  - Model/menu/schema rules: `reference/model-schema.md`
21
- - Runnable skeleton: `reference/project-template/`
21
+ - Runtime extensions (db, Dashboard slots/routes, frontend helpers): `reference/runtime-extensions.md`
22
+ - Standalone skeleton: `reference/project-template/`
22
23
  - Complete product CRUD example: `reference/example.md`
23
24
  3. Generate the smallest working slice first: config, entry, controller, router, model.
24
25
  4. Keep keys stable. Array merge and project overrides depend on `key`.
@@ -27,8 +28,10 @@ Use this skill to generate or update TemplateCore business code.
27
28
  ## TemplateCore Conventions
28
29
 
29
30
  - Start the app with `serverStart({ name, baseDir })` from `@_tc/template-core`.
30
- - Start the built-in frontend with `buildFE("dev")` from `@_tc/template-core/bundler` when the user needs the admin UI.
31
+ - Start the built-in frontend with `buildFE("dev")` from `@_tc/template-core/bundler` when the user needs the admin UI. Do not `await` it; the current API starts the build/dev process and returns `void`.
32
+ - If `buildFE("dev")` is used in a standalone project, include the TemplateCore peer dependencies or use the package manager's peer dependency auto-install behavior.
31
33
  - Business code lives under the user's `baseDir`, not inside TemplateCore framework folders.
34
+ - The framework provides `app.extends.db` as a default SQLite-backed KV/SQL helper. Use it for small framework/business settings; override `app/extend/db.js` only when the project needs a custom storage implementation.
32
35
  - Backend loaders map filenames to camelCase fields:
33
36
  - `app/controller/product.js` -> `app.controller.product`
34
37
  - `app/service/user-profile.js` -> `app.service.userProfile`
@@ -43,12 +46,17 @@ Use this skill to generate or update TemplateCore business code.
43
46
 
44
47
  ## Output Rules
45
48
 
46
- - Make generated code directly runnable.
49
+ - Make generated code directly runnable when package dependencies are installed.
47
50
  - Include realistic in-memory data only for demos or scaffolds.
48
51
  - Use `$i18n::...` only when also adding or pointing to language resources; otherwise write plain labels.
49
52
  - For Schema CRUD, include `tableOption`, `createFormOption`, `editFormOption`, and `detailPanelOption` only where the field should appear.
50
53
  - For edit/detail components, set `fetchKey` to the primary key field.
51
54
  - For remove buttons, use `$schema::{field}` to pull values from the current row.
55
+ - Generate frontend extensions only when needed:
56
+ - Dashboard header UI: `frontend/extended/dash/components.tsx` with `HeaderView.userArea`
57
+ - Dashboard custom routes: `frontend/extended/dash/customRoutes.tsx`
58
+ - SchemaForm field types: `frontend/extended/SchemaForm/data.ts`
59
+ - SchemaPage custom call components: `frontend/extended/SchemaPage/CallCom/data.ts`
52
60
 
53
61
  ## Validation
54
62
 
@@ -34,7 +34,7 @@ async function main() {
34
34
  baseDir: join(__dirname, "."),
35
35
  });
36
36
 
37
- await buildFE("dev");
37
+ buildFE("dev");
38
38
  }
39
39
 
40
40
  main().catch((error) => {
@@ -45,7 +45,7 @@ module.exports = {
45
45
  name: "Product Admin",
46
46
  desc: "Product management",
47
47
  icon: "",
48
- homePage: "/_sidebar_/product?projk=default",
48
+ homePage: "/_sidebar_/product",
49
49
  menuLayout: "left",
50
50
  menu: [],
51
51
  };
@@ -63,7 +63,7 @@ Fields:
63
63
  | `menuLayout` | no | `"left"` default, or `"top"`. |
64
64
  | `menu` | yes | Menu tree. |
65
65
 
66
- For `left` layout, built-in menu routes are under `/_sidebar_/{menuKey}`. For `top` layout, built-in menu routes are `/{menuKey}`. Put project-specific query values, such as `projk`, in the project override when they differ per project.
66
+ For `left` layout, built-in menu routes are under `/_sidebar_/{menuKey}`. For `top` layout, built-in menu routes are `/{menuKey}`. Keep shared model `homePage` project-neutral when multiple projects inherit the same model; put project-specific query values, such as `projk`, in the project override when they differ per project.
67
67
 
68
68
  ## Menu Types
69
69
 
@@ -173,6 +173,8 @@ Table list should return:
173
173
 
174
174
  Framework response wrapper is handled by `this.success(ctx, data)`.
175
175
 
176
+ Controllers can use `app.extends.db` for simple persisted data instead of in-memory arrays. See `reference/runtime-extensions.md` for the default DB contract and override pattern.
177
+
176
178
  ## Field Schema
177
179
 
178
180
  Each field is JSON Schema plus TemplateCore options:
@@ -1,3 +1,7 @@
1
1
  module.exports = {
2
2
  port: 9000,
3
+ // Optional: override the default `.template-core/template-core.sqlite` path.
4
+ // db: {
5
+ // path: "data/app.sqlite",
6
+ // },
3
7
  };
@@ -8,7 +8,7 @@ async function main() {
8
8
  baseDir: join(__dirname, "."),
9
9
  });
10
10
 
11
- await buildFE("dev");
11
+ buildFE("dev");
12
12
  }
13
13
 
14
14
  main().catch((error) => {
@@ -1,4 +1,5 @@
1
1
  module.exports = {
2
2
  name: "Default Product Project",
3
3
  desc: "Project-level overrides can be added here.",
4
+ homePage: "/_sidebar_/product?projk=default",
4
5
  };
@@ -5,6 +5,21 @@
5
5
  "dev": "node index.js"
6
6
  },
7
7
  "dependencies": {
8
- "@_tc/template-core": "0.0.3"
8
+ "@_tc/template-core": "latest",
9
+ "@vitejs/plugin-react": "6.0.1",
10
+ "clsx": "^2.1.0",
11
+ "date-fns": "^3.6.0",
12
+ "koa": "^2.15.0",
13
+ "koa-router": "^12.0.1",
14
+ "lodash": "^4.17.21",
15
+ "lucide-react": "^0.468.0",
16
+ "rc-field-form": "^1.44.0",
17
+ "react": "^19.0.0",
18
+ "react-dom": "^19.0.0",
19
+ "react-router-dom": "^6.28.0",
20
+ "tailwind-merge": "^2.4.0",
21
+ "vite": "8.0.10",
22
+ "xlsx": "^0.18.5",
23
+ "zustand": "^5.0.0"
9
24
  }
10
25
  }
@@ -0,0 +1,190 @@
1
+ # TemplateCore Runtime Extensions
2
+
3
+ Use this reference when generating business code that needs persistence, Dashboard customization, or frontend helper components.
4
+
5
+ ## Default DB
6
+
7
+ `app.extends.db` is available by default. It stores framework/business data in SQLite at:
8
+
9
+ ```text
10
+ .template-core/template-core.sqlite
11
+ ```
12
+
13
+ The default path is relative to the business project `baseDir`. Configure it in `config/config.default.js`:
14
+
15
+ ```js
16
+ module.exports = {
17
+ port: 9000,
18
+ db: {
19
+ path: "data/app.sqlite",
20
+ },
21
+ };
22
+ ```
23
+
24
+ Prefer `db.path` for new projects. The current runtime also accepts `db` as a string and object aliases `filename`, `sqlitePath`, or `sqlite: string | { path, filename }` for compatibility.
25
+
26
+ Common methods:
27
+
28
+ ```ts
29
+ app.extends.db.dbPath
30
+ app.extends.db.getDBConnection()
31
+ app.extends.db.execDB(sql)
32
+ app.extends.db.getDBData(key, { namespace })
33
+ app.extends.db.getDBDataRecord(key, { namespace })
34
+ app.extends.db.setDBData(key, value, { namespace })
35
+ app.extends.db.hasDBData(key, { namespace })
36
+ app.extends.db.deleteDBData(key, { namespace })
37
+ app.extends.db.listDBData({ namespace, limit, offset })
38
+ app.extends.db.countDBData({ namespace })
39
+ app.extends.db.clearDBData({ namespace })
40
+ app.extends.db.queryDB(sql, params)
41
+ app.extends.db.runDB(sql, params)
42
+ app.extends.db.getDBFirst(sql, params)
43
+ app.extends.db.transactionDB((db) => ...)
44
+ app.extends.db.closeDB()
45
+ ```
46
+
47
+ SQL safety rule: put user input in `params`; do not concatenate it into SQL strings. `execDB(sql)` is only for fixed SQL such as migrations.
48
+
49
+ Example controller storage:
50
+
51
+ ```js
52
+ const { baseFn } = require("@_tc/template-core");
53
+
54
+ module.exports = (app) => {
55
+ const BaseController = baseFn.baseControllerFn(app);
56
+
57
+ return class SettingController extends BaseController {
58
+ get = async (ctx) => {
59
+ const data = app.extends.db.getDBData("site-setting", {
60
+ namespace: "settings",
61
+ });
62
+
63
+ this.success(ctx, data || {});
64
+ };
65
+
66
+ save = async (ctx) => {
67
+ app.extends.db.setDBData("site-setting", ctx.request.body, {
68
+ namespace: "settings",
69
+ });
70
+
71
+ this.success(ctx, {});
72
+ };
73
+ };
74
+ };
75
+ ```
76
+
77
+ If the runtime does not support the built-in SQLite module or the project needs another store, generate `app/extend/db.js` or `app/extend/db.ts` and preserve the same method names so existing controllers keep working.
78
+
79
+ ## Dashboard Component Slot
80
+
81
+ Use `frontend/extended/dash/components.tsx` to fill supported Dashboard slots. The current slot is:
82
+
83
+ ```text
84
+ HeaderView.userArea
85
+ ```
86
+
87
+ Example:
88
+
89
+ ```tsx
90
+ import { LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
91
+ import type { DashComponentsMap } from '@_tc/template-core/fe'
92
+
93
+ const components = {
94
+ 'HeaderView.userArea': ({ projectInfo }) => {
95
+ return (
96
+ <div className="flex items-center gap-2">
97
+ <span className="max-w-32 truncate text-xs text-muted-foreground">
98
+ {projectInfo?.name}
99
+ </span>
100
+ <LanguageSwitch />
101
+ <ThemeSwitch />
102
+ </div>
103
+ )
104
+ },
105
+ } satisfies DashComponentsMap
106
+
107
+ export default components
108
+ ```
109
+
110
+ Keep header content compact; the default header already owns brand, title, and menu layout.
111
+
112
+ ## Dashboard Custom Routes
113
+
114
+ Use `frontend/extended/dash/customRoutes.tsx` when a `custom` menu item needs a route inside the built-in Dashboard.
115
+
116
+ ```tsx
117
+ import type { DashRoutesExtender } from '@_tc/template-core/fe'
118
+
119
+ const extendDashRoutes: DashRoutesExtender = ({ topRoutes, sidebarRoutes }) => {
120
+ topRoutes.push({
121
+ path: '/custom-page',
122
+ component: <div>Custom Page</div>,
123
+ })
124
+
125
+ sidebarRoutes.push({
126
+ path: 'custom-sidebar-page',
127
+ component: <div>Custom Sidebar Page</div>,
128
+ })
129
+ }
130
+
131
+ export default extendDashRoutes
132
+ ```
133
+
134
+ `topRoutes` are appended under `/dash`. `sidebarRoutes` are appended inside the default sidebar containers.
135
+
136
+ ## Frontend Helpers
137
+
138
+ Import TemplateCore frontend helpers from `@_tc/template-core/fe`:
139
+
140
+ ```tsx
141
+ import { AsyncSelect, LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
142
+ ```
143
+
144
+ Import UI components from the published UI re-export:
145
+
146
+ ```tsx
147
+ import { Button, DataTable, TableSearch } from '@_tc/template-core/fe/rc'
148
+ ```
149
+
150
+ `LanguageSwitch` updates the shared i18n language and persists through `tc_language`. `ThemeSwitch` toggles the root `dark` class and persists through `tc_theme`. `AsyncSelect` loads options from a `fetchConfig` endpoint returning `{ label, value, searchText? }[]`.
151
+
152
+ ## SchemaForm Extensions
153
+
154
+ TemplateCore already registers a framework SchemaForm type named `asyncSelect`. Use it in Schema CRUD model config when a field should fetch options from an API:
155
+
156
+ ```js
157
+ owner_id: {
158
+ type: "string",
159
+ label: "Owner",
160
+ createFormOption: {
161
+ comType: "asyncSelect",
162
+ fetchConfig: {
163
+ url: "/user/options",
164
+ method: "get",
165
+ },
166
+ },
167
+ editFormOption: {
168
+ comType: "asyncSelect",
169
+ fetchConfig: {
170
+ url: "/user/options",
171
+ method: "get",
172
+ },
173
+ },
174
+ }
175
+ ```
176
+
177
+ The options controller should return the normal TemplateCore response wrapper:
178
+
179
+ ```js
180
+ this.success(ctx, [
181
+ { label: "Alice", value: "u1" },
182
+ { label: "Bob", value: "u2" },
183
+ ]);
184
+ ```
185
+
186
+ For project-specific field types, create `frontend/extended/SchemaForm/data.ts` and default-export a component map keyed by field type.
187
+
188
+ ## SchemaPage CallCom Extensions
189
+
190
+ Use `frontend/extended/SchemaPage/CallCom/data.ts` when model `componentConfig` should open a custom runtime component instead of built-in create/edit/detail components. Custom components can import `schemaEventBus`, `eventsInfo`, and `merge` from `@_tc/template-core/fe` to close panels or refresh Schema tables.