create-aiaui-module 0.1.2-TEST → 0.3.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 (37) hide show
  1. package/README.md +54 -73
  2. package/bin/create-aiaui-module.js +24 -32
  3. package/package.json +4 -3
  4. package/runtime/_runtime/registry.py +9 -2
  5. package/templates/{JobInformation → JobDashboard}/CUSTOMIZATION.md +3 -3
  6. package/templates/{JobInformation → JobDashboard}/README.md +9 -9
  7. package/templates/{JobInformation → JobDashboard/backend}/api.py +49 -24
  8. package/templates/JobDashboard/backend/config.json +1 -0
  9. package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/DefaultPanel.vue +1 -1
  10. /package/templates/{JobInformation/resources → JobDashboard/client}/build.mjs +0 -0
  11. /package/templates/{JobInformation/resources → JobDashboard/client}/dist/manifest.json +0 -0
  12. /package/templates/{JobInformation/resources → JobDashboard/client}/dist/modules.js +0 -0
  13. /package/templates/{JobInformation/resources → JobDashboard/client}/package-lock.json +0 -0
  14. /package/templates/{JobInformation/resources → JobDashboard/client}/package.json +0 -0
  15. /package/templates/{JobInformation/resources → JobDashboard/client}/src/contract.ts +0 -0
  16. /package/templates/{JobInformation/resources → JobDashboard/client}/src/customization.ts +0 -0
  17. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/Icons.vue +0 -0
  18. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/InputCarriers.vue +0 -0
  19. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/JobBottom.vue +0 -0
  20. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/JobCard.vue +0 -0
  21. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/JobHeader.vue +0 -0
  22. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/JobMiddle.vue +0 -0
  23. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/JobName.vue +0 -0
  24. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/LotId.vue +0 -0
  25. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/OutputCarriers.vue +0 -0
  26. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/Recipe.vue +0 -0
  27. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/Status.vue +0 -0
  28. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/Timeline.vue +0 -0
  29. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/board-data.ts +0 -0
  30. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/config-defaults.ts +0 -0
  31. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/format.ts +0 -0
  32. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/slots.ts +0 -0
  33. /package/templates/{JobInformation/resources → JobDashboard/client}/src/defaults/styles.css +0 -0
  34. /package/templates/{JobInformation/resources → JobDashboard/client}/src/index.ts +0 -0
  35. /package/templates/{JobInformation/resources → JobDashboard/client}/src/vite-env.d.ts +0 -0
  36. /package/templates/{JobInformation/resources → JobDashboard/client}/vite.config.mjs +0 -0
  37. /package/templates/{JobInformation → JobDashboard}/contractVersion +0 -0
package/README.md CHANGED
@@ -1,102 +1,83 @@
1
- # create-aiaui-module
1
+ # AIAUI module templates
2
2
 
3
- 在 AIAUI Instance 根目录一键生成可编辑的模块目录(FastAPI 后端路由 + Vue 前端)。
4
- 通过 `npx` 运行,无需全局安装、无需克隆仓库,运行时无外部依赖(除 Instance
5
- 侧已声明的 Python 依赖)。
3
+ Scaffold an editable AIAUI Instance module — a FastAPI `backend/` router
4
+ plus a Vue `client/` frontend — with a single `npx` command. No global
5
+ install, no repository checkout, no `aiaui` package required.
6
6
 
7
- | 步骤 | 命令 | 说明 |
7
+ | Step | Command | Description |
8
8
  | --- | --- | --- |
9
- | 查看可用模块 | `npx create-aiaui-module@latest --list` | 列出模块 ID 与 contractVersion |
10
- | 生成模块 | `npx create-aiaui-module@latest JobInformation` | 在实例根目录生成 `PyAIAUI/JobInformation` |
11
- | 机器可读 | `npx create-aiaui-module@latest JobInformation --json` | 生成结果输出 JSON(接 CI) |
9
+ | List available templates | `npx create-aiaui-module@latest --list` | Allowed template IDs and their contract version |
10
+ | Create a module | `npx create-aiaui-module@latest JobDashboard` | Generates `PyAIAUI/JobDashboard/` in the Instance root |
11
+ | Machine-readable output | `npx create-aiaui-module@latest JobDashboard --json` | Structured result (CI hook) |
12
12
 
13
- ## 环境要求
13
+ ## Requirements
14
14
 
15
- - 执行生成器的机器:Node.js >= 20
16
- - Instance 运行侧:Python + `fastapi`、`uvicorn`(Instance 侧自备,`pip install fastapi uvicorn`)
15
+ - To run the generator: Node.js >= 20
16
+ - To run the generated module: Python + `fastapi`, `uvicorn` on the Instance
17
+ side (`pip install fastapi uvicorn`)
17
18
 
18
- ## 快速开始
19
+ ## Quick start
19
20
 
20
- 在 Instance 根目录(含 `Config/` 的目录)下执行:
21
+ From an Instance root (a directory containing `Config/`):
21
22
 
22
23
  ```bash
23
- npx create-aiaui-module@latest JobInformation
24
+ npx create-aiaui-module@latest JobDashboard
24
25
  ```
25
26
 
26
- 生成后 `PyAIAUI/JobInformation/` 与 `PyAIAUI/_runtime/` 就位,`Config/AIAUIProtocol.json` 中登记该模块(默认 `enabled: false`、`port: null`)。
27
+ Produces `PyAIAUI/JobDashboard/` (module code) and `PyAIAUI/_runtime/` (the
28
+ embedded runtime that loads it), and registers `JobDashboard` (disabled,
29
+ `port: null`) in `Config/AIAUIProtocol.json`.
27
30
 
28
- 启用前需要调整的配置:
31
+ The generated module:
29
32
 
30
- ```json
31
- {
32
- "schemaVersion": 1,
33
- "port": 18901,
34
- "modules": [
35
- { "name": "JobInformation", "enabled": true }
36
- ]
37
- }
33
+ ```
34
+ <Instance root>/
35
+ ├── Config/AIAUIProtocol.json # port + module enable/disable
36
+ └── PyAIAUI/
37
+ ├── __init__.py # AIAUI class entry (start/stop)
38
+ ├── JobDashboard/ # one module = one directory
39
+ │ ├── backend/api.py # create_api(context) -> APIRouter
40
+ │ ├── backend/config.json # panel appearance (display + state color/icon)
41
+ │ ├── client/src/ # Vue frontend source
42
+ │ ├── client/dist/ # prebuilt bundle (manifest.json)
43
+ │ └── README.md / CUSTOMIZATION.md
44
+ └── _runtime/ # embedded runtime (no external deps)
38
45
  ```
39
46
 
40
- 把 Instance 启动/关闭流程接上(生成后 `PyAIAUI` 目录里就是这段代码的宿主实现):
47
+ Enable the module before first boot: pick a free loopback `port` for
48
+ `AIAUIProtocol.json` and flip `enabled` to `true`. Then wire the Instance
49
+ lifecycle:
41
50
 
42
51
  ```python
43
52
  from PyAIAUI import AIAUI
44
53
 
45
- # 业务对象就绪后
46
54
  aiaui = AIAUI.start(services={"job_repository": repo})
47
- # aiaui 为 None 表示没有任何模块启用
48
-
49
- # ... Instance 正常运行 ...
50
-
51
- # 关闭前
52
- if aiaui is not None:
53
- aiaui.stop()
55
+ ...
56
+ aiaui.stop()
54
57
  ```
55
58
 
56
- `AIAUI.start()` 幂等(重复调用返回同一 handle),一个 handle 托管本 Instance 全部启用模块,共用一个 loopback 端口。后端接口由模块自己的 `api.py` 里的 `create_api(context)` 提供,`context.services` 就是上面注入的业务对象。
59
+ `AIAUI.start()` is idempotent; it returns `None` when no modules are enabled.
57
60
 
58
- ## 生成结果
61
+ After enabling, the module answers locally under
62
+ `/api/v1/services/<id>/modules/JobDashboard/...` (via UIService) — `GET /jobs`
63
+ (feed the panel), `GET /config` (panel appearance from `backend/config.json`),
64
+ and any custom routes you add to `create_api(context)`.
59
65
 
60
- ```
61
- <Instance root>/
62
- ├── Config/AIAUIProtocol.json # 运行协议:端口 + 模块启停(生成器只登记,不配置)
63
- └── PyAIAUI/
64
- ├── __init__.py # 入口:AIAUI 类(AIAUI.start / aiaui.stop)
65
- ├── JobInformation/ # 模块目录,目录名 == 模块 ID
66
- │ ├── api.py # 后端契约:create_api(context) -> APIRouter
67
- │ ├── resources/src/ # Vue 前端源码
68
- │ ├── resources/dist/ # 预构建产物(开箱即跑,改源码后 npm run build 重出)
69
- │ └── {README.md, CUSTOMIZATION.md}# 模块说明与定制手册
70
- └── _runtime/ # 内嵌模块宿主(零外部依赖)
71
- ```
66
+ ## Built-in templates
72
67
 
73
- 生成器不选端口、不装依赖、不启动进程、不覆盖已存在的模块目录;无效的既有协议会被拒绝且不留残留。
74
-
75
- ## 内置模块清单
76
-
77
- | 模块 ID | 说明 | 组成 |
68
+ | ID | Description | Contents |
78
69
  | --- | --- | --- |
79
- | `JobInformation` | 工单 / 载具 / 工序信息面板 | FastAPI 后端 + Vue 前端;内置 `header`、`inputCarriers`、`outputCarriers`、`timeline`、`bottom`(含嵌套的 `recipe`)可替换槽位;三级定制(默认 / 局部替换 / 全量自定义) |
80
- | `HelloWorld` | 最小前端模块示例 | 自定义元素;验证挂载链路,无后端 |
81
- 自定义通过 `resources/src/customization.ts` 完成,槽位通信通过 `context.bus`(事件 / expose+peer),详见生成目录里的 `CUSTOMIZATION.md`。
82
-
83
- ## UIService 集成
84
-
85
- ```bash
86
- npm i -g create-aiaui-module
87
- export AIA_UI_MODULE_TEMPLATE_ROOT="$(npm root -g)/create-aiaui-module"
88
- ```
89
-
90
- 此后 UIService 调用包内 `bin/create-aiaui-module.js`(与 `npx` 行为一致),或在业务机上直接 `npx`。
91
-
92
- ## 常用命令
70
+ | `JobDashboard` | Job information / carrier / process panel | `backend/api.py` + `backend/config.json` + Vue `client/`. Implement `GET /jobs` (job data) and `GET /config` (display switches + state color/icon), or adjust nothing at all — a built-in default panel ships and falls back to demo data. Slots are individually replaceable (`board` / `headerRegion` / `middleRegion` / `bottomRegion` + leaf slots like `jobName` / `status` / `timeline` / `inputCarriers` / `outputCarriers` / `recipe`) without touching the panel shell. |
71
+ | `HelloWorld` | Minimal frontend-only example | Custom element for probing the module lifecycle, no backend |
93
72
 
94
- - `npx create-aiaui-module@latest --list` — 查看模板
95
- - `npx create-aiaui-module@latest <ModuleId>` — 生成(`--instance-root` 指定根目录)
96
- - `npx create-aiaui-module@latest --json --list` — 机器可读
73
+ Customize a card's layout by editing `client/src/customization.ts`; every slot
74
+ component receives `{ context, job, config }` as props. See
75
+ `CUSTOMIZATION.md` inside the generated `JobDashboard/` directory for the full
76
+ slot table and contract.
97
77
 
98
- ## 维护者(发布新模板)
78
+ ## What the generator does NOT do
99
79
 
100
- - 新增模块:在 `templates/<ModuleId>/` 下放实现(含 `contractVersion`、`README.md`、`resources/`)
101
- - 前端模板发布前在 `resources/` 跑 `npm install && npm run build`,把产物一起随包发布;发布自动执行 `verify-dist` 校验每个模板的 `contractVersion`、入口、`dist/` 产物
102
- - 发版流程:`npm test` → 改 `package.json` 版本号 → `npm publish`
80
+ - It does not choose a port, install dependencies, start any process, or
81
+ overwrite an existing `PyAIAUI/<ModuleId>` directory.
82
+ - An invalid existing `AIAUIProtocol.json` is refused without leaving a new
83
+ module directory behind.
@@ -35,7 +35,7 @@ function briefDescription(templateId) {
35
35
 
36
36
  function printTemplateList(entries) {
37
37
  console.log();
38
- console.log("AIAUI 模块模板");
38
+ console.log("AIAUI module templates");
39
39
  console.log(DIVIDER);
40
40
  if (entries.length === 0) console.log(" (none)");
41
41
  for (const entry of entries) {
@@ -44,43 +44,35 @@ function printTemplateList(entries) {
44
44
  console.log(` ${briefDescription(entry.id)}`);
45
45
  }
46
46
  console.log(DIVIDER);
47
- console.log("生成: npx create-aiaui-module <模块名> [--list] [--json]");
47
+ console.log("usage: npx create-aiaui-module <ModuleId> [--list] [--json] [--instance-root <path>]");
48
48
  }
49
49
 
50
50
  function printSuccess(result) {
51
51
  console.log();
52
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
53
- console.log(" [AIAUI] 生成成功");
54
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
52
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
53
+ console.log(" [AIAUI] module created");
54
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
55
55
  console.log();
56
- console.log(` ✅ 生成路径 ${result.path} (${result.filesCopied} 个文件)`);
57
- console.log(` ✅ 协议配置 ${result.configPath} (enabled: ${result.enabled})`);
56
+ console.log(` done module ${result.moduleId}`);
57
+ console.log(` done path ${result.path}`);
58
+ console.log(` done files ${result.filesCopied}`);
59
+ console.log(` done protocol ${result.configPath}`);
60
+ console.log(` enabled: ${result.enabled}, port: ${result.port ?? "unset"}`);
58
61
  console.log();
59
- console.log("⚠️ 配置调整(需要你手动做)");
60
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
61
- console.log(" 📌 修改 port 字段为 18901(或你选择的其他空闲端口)");
62
- console.log(" 📌 将 enabled 改为 true");
63
- console.log(" 📌 模块接入 Instance 启动流程后再启用");
64
- console.log();
65
- console.log("🔧 接下来要做:");
66
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
67
- console.log(" 1. 配置端口");
68
- console.log(" 修改 AIAUIProtocol.json 中 port 字段为 18901(示例值)");
69
- console.log(" 2. 配置模块 enable");
70
- console.log(" 修改 AIAUIProtocol.json 中对应模块的 enabled 为 true");
71
- console.log(" 3. 接入 Instance 启动流程");
72
- console.log(" · 启动时: aiaui = AIAUI.start(services={...})");
73
- console.log(" · 关闭时: aiaui.stop()");
74
- console.log(" 4. 打开 UIClient 对应页面查看效果(JobInformation → /jobs)");
75
- console.log();
76
- console.log("❌ 没有做的(请确认)");
77
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
78
- console.log(" · 未启动服务(由 Instance 生命周期调用 AIAUI.start)");
79
- console.log(" · 未安装依赖(模块运行需要 pip install fastapi uvicorn)");
80
- console.log(" · 未选择/配置端口");
81
- console.log(" · 未手动启用任何模块");
82
- console.log();
83
- console.log(" 初始化目录已就绪,按上面的顺序操作即可上线。");
62
+ console.log("next: edit your Config & wire the module (you must do this)");
63
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
64
+ console.log(' 1. Set a free loopback port in AIAUIProtocol.json ("port", e.g. 18900-18999 range)');
65
+ console.log(' and flip "enabled" to true for your module');
66
+ console.log(" 2. Wire the module into your Instance lifecycle");
67
+ console.log(" from PyAIAUI import AIAUI");
68
+ console.log(" aiaui = AIAUI.start(services={...}) # at Instance startup");
69
+ console.log(" aiaui.stop() # at shutdown");
70
+ console.log(' 3. Mount its data: backend/api.py implements create_api(context) -> APIRouter.');
71
+ console.log(" (many templates ship backend/config.json for appearance settings — see README)");
72
+
73
+ console.log(" 4. Rebuild the frontend if you edited it:");
74
+ console.log(" npm install && npm run build # from <module>/client");
75
+ console.log(" no starting services, auto-required deps, or a running Instance is triggered");
84
76
  console.log();
85
77
  }
86
78
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-aiaui-module",
3
- "version": "0.1.2-TEST",
4
- "description": "AIAUI Instance module scaffolder: generate editable module (FastAPI + Vue frontend) with one npx command",
3
+ "version": "0.3.0",
4
+ "description": "AIAUI Instance module scaffolder: npx generates an editable module",
5
5
  "keywords": [
6
6
  "aiaui",
7
7
  "scaffolding",
@@ -9,7 +9,8 @@
9
9
  "vue",
10
10
  "fastapi",
11
11
  "module",
12
- "instance"
12
+ "instance",
13
+ "sharetek"
13
14
  ],
14
15
  "type": "module",
15
16
  "bin": {
@@ -18,8 +18,15 @@ class ModuleDescriptor:
18
18
  def __init__(self, module_id: str, directory: Path):
19
19
  self.id = module_id
20
20
  self.directory = directory
21
- self.api_file = directory / "api.py"
22
- self.frontend_entry = directory / "resources" / "dist"
21
+ # New templates ship backend/api.py, legacy ones api.py at module root.
22
+ backend_api = directory / "backend" / "api.py"
23
+ self.api_file = (
24
+ backend_api if backend_api.is_file() else directory / "api.py"
25
+ )
26
+ client_dist = directory / "client" / "dist"
27
+ self.frontend_entry = (
28
+ client_dist if client_dist.is_dir() else directory / "resources" / "dist"
29
+ )
23
30
 
24
31
  @property
25
32
  def has_backend(self) -> bool:
@@ -1,4 +1,4 @@
1
- # JobInformation customization
1
+ # JobDashboard customization
2
2
 
3
3
  This module ships a built-in job-information panel that reads two local
4
4
  endpoints and renders one card per job. Every part of a card can be replaced
@@ -67,7 +67,7 @@ without touching the platform code; the host contract
67
67
 
68
68
  ## Slots (what you can replace)
69
69
 
70
- Edit `resources/src/customization.ts`. Keys per card:
70
+ Edit `client/src/customization.ts`. Keys per card:
71
71
 
72
72
  | Slot | Replaces | Renders inside |
73
73
  | --- | --- | --- |
@@ -89,7 +89,7 @@ slots ignore the region around them (which stays platform-owned). Replacing
89
89
  sub-slots if set.
90
90
 
91
91
  ```ts
92
- // resources/src/customization.ts
92
+ // client/src/customization.ts
93
93
  import MyJobName from "./custom/MyJobName.vue";
94
94
  import type { Customization } from "./contract";
95
95
 
@@ -1,4 +1,4 @@
1
- # JobInformation module
1
+ # JobDashboard module
2
2
 
3
3
  This template scaffolds an editable "job information" UI module. The
4
4
  generator only produces scaffolding plus an inlined runtime
@@ -19,7 +19,7 @@ def create_api(context) -> APIRouter:
19
19
  ```
20
20
 
21
21
  The generated host calls `create_api(context)` once and mounts the returned
22
- router at `/modules/JobInformation`. You receive:
22
+ router at `/modules/JobDashboard`. You receive:
23
23
 
24
24
  - `context.instance_root` — the Instance root directory.
25
25
  - `context.services` — a dict of the live objects your Instance passes in at
@@ -40,11 +40,11 @@ Everything else about how to fetch or present data is yours to implement.
40
40
 
41
41
  The frontend is organized around a small customization contract:
42
42
 
43
- - `resources/src/index.ts` — the stable `mount` / `unmount` bridge the host loads.
44
- - `resources/src/contract.ts` — the shared types for the module context, bus, and customization.
45
- - `resources/src/customization.ts` — the only file you normally edit to customize.
46
- - `resources/src/bus.ts` — a per-mount communication bus shared by all slot components.
47
- - `resources/src/defaults/` — the built-in panel and its replaceable boxes.
43
+ - `client/src/index.ts` — the stable `mount` / `unmount` bridge the host loads.
44
+ - `client/src/contract.ts` — the shared types for the module context, bus, and customization.
45
+ - `client/src/customization.ts` — the only file you normally edit to customize.
46
+ - `client/src/bus.ts` — a per-mount communication bus shared by all slot components.
47
+ - `client/src/defaults/` — the built-in panel and its replaceable boxes.
48
48
 
49
49
  Slot components also communicate through `context.bus` (events or imperative handles); see CUSTOMIZATION.md for the patterns.
50
50
 
@@ -59,7 +59,7 @@ Three levels are supported without changing `index.ts`:
59
59
  2. **Partial** — replace one or more named boxes:
60
60
 
61
61
  ```ts
62
- // resources/src/customization.ts
62
+ // client/src/customization.ts
63
63
  import MyHeader from "./custom/MyHeader.vue";
64
64
  import type { Customization } from "./contract";
65
65
 
@@ -110,7 +110,7 @@ aiaui = AIAUI.start(services={
110
110
  aiaui.stop()
111
111
  ```
112
112
 
113
- `start()` returns `None` when `JobInformation` is disabled in
113
+ `start()` returns `None` when `JobDashboard` is disabled in
114
114
  `Config/AIAUIProtocol.json`; otherwise it returns the running host.
115
115
 
116
116
  ## Dependencies (reminder, not installed automatically)
@@ -1,15 +1,58 @@
1
- """Editable Job Information example, mounted under /modules/JobInformation."""
1
+ """Editable JobDashboard example, mounted under /modules/JobDashboard."""
2
2
 
3
3
  import inspect
4
+ import json
5
+ from pathlib import Path
4
6
 
5
7
  from fastapi import APIRouter
6
8
 
9
+ CONFIG_PATH = Path(__file__).resolve().parent / "config.json"
10
+
11
+ DEFAULT_CONFIG = {
12
+ "display": {
13
+ "lotId": True,
14
+ "recipe": True,
15
+ "inputCarriers": True,
16
+ "outputCarriers": True,
17
+ "timeline": True,
18
+ },
19
+ "states": [
20
+ {"name": "Setup", "color": "#0369a1", "icon": "clock"},
21
+ {"name": "Select", "color": "#526176", "icon": "list"},
22
+ {"name": "WaitingForStart", "color": "#8b95a5", "icon": "stop"},
23
+ {"name": "Executing", "color": "#1d4ed8", "icon": "activity"},
24
+ {"name": "Running", "color": "#1d4ed8", "icon": "activity"},
25
+ {"name": "Complete", "color": "#166534", "icon": "flag"},
26
+ {"name": "Finished", "color": "#166534", "icon": "flag"},
27
+ ],
28
+ }
29
+
30
+
31
+ def default_config() -> dict:
32
+ """Merge ``config.json`` over the shipped default palette.
33
+
34
+ Any missing key is merged over the built-in defaults; unreadable or
35
+ non-object content falls back to the shipped defaults, so the endpoint
36
+ can be left ``{}`` to keep everything visible by default.
37
+ """
38
+ try:
39
+ raw = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
40
+ except OSError:
41
+ return DEFAULT_CONFIG
42
+ if not isinstance(raw, dict):
43
+ return DEFAULT_CONFIG
44
+ merged: dict = {**DEFAULT_CONFIG, **raw}
45
+ if isinstance(raw.get("display"), dict):
46
+ merged["display"] = {**DEFAULT_CONFIG["display"], **raw["display"]}
47
+ if isinstance(raw.get("states"), list) and raw["states"]:
48
+ merged["states"] = raw["states"]
49
+ return merged
50
+
7
51
 
8
52
  def create_api(context) -> APIRouter:
9
53
  """Entry point invoked by the generated PyAIAUI host.
10
54
 
11
- The Instance injects its live objects via ``PyAIAUI.AIAUI.start(services={...})``
12
- and they arrive here through ``context.services``.
55
+ The Instance injects its live objects via ``PyAIAUI.AIAUI.start(services={...})`` and they arrive here through ``context.services``.
13
56
  """
14
57
  router = APIRouter()
15
58
 
@@ -71,27 +114,9 @@ def create_api(context) -> APIRouter:
71
114
 
72
115
  @router.get("/config")
73
116
  async def config():
74
- """Panel appearance contract: which parts to display + the state
75
- colour/icon map. The template already ships a built-in default copy
76
- (``config-defaults.ts``), so this endpoint can stay absent.
117
+ """Panel appearance contract, read from ``config.json`` (this file's
118
+ sibling). Missing keys fall back to the built-in DEFAULT_CONFIG.
77
119
  """
78
- return {
79
- "display": {
80
- "lotId": True,
81
- "recipe": True,
82
- "inputCarriers": True,
83
- "outputCarriers": True,
84
- "timeline": True,
85
- },
86
- "states": [
87
- {"name": "Setup", "color": "#0369a1", "icon": "clock"},
88
- {"name": "Select", "color": "#526176", "icon": "list"},
89
- {"name": "WaitingForStart", "color": "#8b95a5", "icon": "stop"},
90
- {"name": "Executing", "color": "#1d4ed8", "icon": "activity"},
91
- {"name": "Running", "color": "#1d4ed8", "icon": "activity"},
92
- {"name": "Complete", "color": "#166534", "icon": "flag"},
93
- {"name": "Finished", "color": "#166534", "icon": "flag"},
94
- ],
95
- }
120
+ return default_config()
96
121
 
97
122
  return router
@@ -0,0 +1 @@
1
+ {"display": {"lotId": false}}
@@ -80,7 +80,7 @@ const visibleJobs = computed(() =>
80
80
  </script>
81
81
 
82
82
  <template>
83
- <section class="legacy-jobs" aria-label="Job Information">
83
+ <section class="legacy-jobs" aria-label="Job Dashboard">
84
84
  <header class="legacy-toolbar">
85
85
  <div class="legacy-filters">
86
86
  <label class="legacy-search">