@deppon/deppon-prd-mcp 0.1.2 → 2.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -25
- package/dist/core/canvas-handoff.d.ts +72 -0
- package/dist/core/canvas-handoff.js +238 -0
- package/dist/core/prd-generator.js +3 -9
- package/dist/core/project-store.d.ts +12 -0
- package/dist/core/project-store.js +17 -0
- package/dist/core/prototype-generator.d.ts +1 -1
- package/dist/core/prototype-generator.js +93 -89
- package/dist/core/prototype-layout.d.ts +13 -0
- package/dist/core/prototype-layout.js +219 -0
- package/dist/core/tldraw-wireframe.d.ts +11 -0
- package/dist/core/tldraw-wireframe.js +205 -0
- package/dist/core/types.d.ts +52 -0
- package/dist/core/types.js +13 -4
- package/dist/http/server.js +12 -1
- package/dist/mcp/server.js +59 -2
- package/package.json +5 -2
- package/templates/examples/README.md +8 -8
- package/templates/examples/app-shell-navigation/layout-spec.md +21 -21
- package/templates/examples/app-shell-navigation/prd.md +56 -56
- package/templates/examples/backend-list/prd.md +32 -32
- package/templates/examples/data-dashboard/prd.md +46 -46
- package/templates/examples/form-edit/prd.md +33 -33
- package/templates/examples/form-preview/prd.md +21 -21
- package/templates/examples/user-frontend/prd.md +19 -19
- package/web/app.js +80 -45
- package/web/index.html +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
1. **Web 控制台**(`http://127.0.0.1:3847`):填写 PRD 要素(标题、背景、筛选字段、列表列等)→ 生成标准化 `prd.md`
|
|
8
8
|
2. **原型生成**:根据同一套配置生成 Tailwind 高保真 `prototype.html`(含 PRD § 标注弹框)
|
|
9
|
-
3.
|
|
9
|
+
3. **在线动态调整(类墨刀)**:设计模式 `?design=1` 下可 **拖拽** 区块/筛选项/列表列/工具栏按钮排序,改文案,移除不合适项,保存后写入 `project.config.json` 并同步 PRD
|
|
10
10
|
4. **MCP 工具**:供 Cursor Agent 调用 `generate_prd`、`generate_prototype`、`open_prd_studio` 等
|
|
11
11
|
|
|
12
12
|
## 安装(消费方)
|
|
@@ -28,20 +28,27 @@ npx @deppon/deppon-prd-mcp
|
|
|
28
28
|
|
|
29
29
|
浏览器打开 Web 控制台:**http://127.0.0.1:3847**
|
|
30
30
|
|
|
31
|
+
### 设计模式(拖拽调整布局)
|
|
32
|
+
|
|
33
|
+
1. Web Studio → **③ 原型编辑** → 点 **🎨 设计模式(拖拽)**
|
|
34
|
+
2. 或直接在原型 URL 加 `?design=1`
|
|
35
|
+
3. 拖拽 **⠿** 手柄调整:筛选区/列表区块顺序、筛选项、表头列、工具栏按钮
|
|
36
|
+
4. 点击 **保存布局** → 写入 `project.config.json`,并重新生成 `prd.md` + `prototype.html`
|
|
37
|
+
|
|
31
38
|
## Cursor / Claude Desktop MCP 配置
|
|
32
39
|
|
|
33
40
|
```json
|
|
34
41
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"deppon-prd": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["-y", "@deppon/deppon-prd-mcp@latest"],
|
|
46
|
+
"env": {
|
|
47
|
+
"DEPPON_PRD_WORKSPACE": "/绝对路径/到你的项目根目录",
|
|
48
|
+
"DEPPON_PRD_HTTP_PORT": "3847"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
43
51
|
}
|
|
44
|
-
}
|
|
45
52
|
}
|
|
46
53
|
```
|
|
47
54
|
|
|
@@ -58,9 +65,20 @@ npm run publish:registry # MCP 官方 Registry(需先 mcp-publisher login
|
|
|
58
65
|
|
|
59
66
|
### MCP 官方 Registry
|
|
60
67
|
|
|
61
|
-
1.
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
1. **安装 CLI**(Homebrew **没有** `mcp-publisher` formula,须从 GitHub Releases 下载):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Apple Silicon Mac
|
|
72
|
+
curl -fL "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_darwin_arm64.tar.gz" \
|
|
73
|
+
| tar xz mcp-publisher
|
|
74
|
+
sudo mv mcp-publisher /usr/local/bin/
|
|
75
|
+
# 若无 sudo,可放到 ~/.local/bin 并确保在 PATH 中
|
|
76
|
+
|
|
77
|
+
mcp-publisher --help
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
2. 登录:`mcp-publisher login github`(须用 GitHub 账号 **chriswong1103**)
|
|
81
|
+
3. 发布:`cd packages/deppon-prd-mcp && mcp-publisher publish`
|
|
64
82
|
|
|
65
83
|
Registry 名称:`io.github.chriswong1103/deppon-prd-mcp`(与 `package.json` 的 `mcpName` 一致,须用 GitHub 账号 **chriswong1103** 登录)
|
|
66
84
|
|
|
@@ -75,18 +93,27 @@ src/prototypes/<slug>/
|
|
|
75
93
|
|
|
76
94
|
## MCP 工具
|
|
77
95
|
|
|
78
|
-
| 工具
|
|
79
|
-
|
|
80
|
-
| `generate_prd`
|
|
81
|
-
| `generate_prototype`
|
|
82
|
-
| `list_prd_projects`
|
|
83
|
-
| `get_prd_project`
|
|
84
|
-
| `open_prd_studio`
|
|
96
|
+
| 工具 | 说明 |
|
|
97
|
+
| ----------------------- | ------------------------------------------------------------- |
|
|
98
|
+
| `generate_prd` | 从 pageName 等要素生成 PRD 并落盘 |
|
|
99
|
+
| `generate_prototype` | 从已有 slug 重新生成 prototype.html |
|
|
100
|
+
| `list_prd_projects` | 列出已生成项目 |
|
|
101
|
+
| `get_prd_project` | 读取项目配置与 PRD 摘要 |
|
|
102
|
+
| `open_prd_studio` | 返回 Web 控制台 URL |
|
|
103
|
+
| `export_canvas_handoff` | 导出 Penpot/Reframe/**tldraw** handoff(含 `wireframe.tldr`) |
|
|
85
104
|
|
|
86
105
|
## 环境变量
|
|
87
106
|
|
|
88
|
-
| 变量
|
|
89
|
-
|
|
90
|
-
| `DEPPON_PRD_WORKSPACE` | `process.cwd()` | 仓库根目录
|
|
91
|
-
| `DEPPON_PRD_HTTP_PORT` | `3847`
|
|
92
|
-
| `DEPPON_PRD_HTTP_HOST` | `127.0.0.1`
|
|
107
|
+
| 变量 | 默认 | 说明 |
|
|
108
|
+
| ---------------------- | --------------- | -------------- |
|
|
109
|
+
| `DEPPON_PRD_WORKSPACE` | `process.cwd()` | 仓库根目录 |
|
|
110
|
+
| `DEPPON_PRD_HTTP_PORT` | `3847` | Web 控制台端口 |
|
|
111
|
+
| `DEPPON_PRD_HTTP_HOST` | `127.0.0.1` | 绑定地址 |
|
|
112
|
+
|
|
113
|
+
## 文档
|
|
114
|
+
|
|
115
|
+
| 文档 | 说明 |
|
|
116
|
+
| ---------------------------------------------------------------- | --------------------------------------------------- |
|
|
117
|
+
| [Cursor + MCP 使用指南](./docs/cursor-mcp-workflow.md) | Agent 生成 PRD/原型、设计模式、常见问题 |
|
|
118
|
+
| [**Penpot/Reframe 画布联调**](./docs/canvas-handoff-workflow.md) | PRD 走 deppon-prd,原型交 Penpot/Reframe MCP |
|
|
119
|
+
| [墨刀分析 & 演进路线图](./docs/modao-analysis-and-roadmap.md) | 墨刀 Proto2 技术反推、scene.json 规划、Phase 1 ~ 3 |
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { PrdProject } from './types.js';
|
|
2
|
+
export interface CanvasHandoff {
|
|
3
|
+
version: '1.0';
|
|
4
|
+
source: 'deppon-prd-mcp';
|
|
5
|
+
slug: string;
|
|
6
|
+
pageName: string;
|
|
7
|
+
pageType: PrdProject['pageType'];
|
|
8
|
+
generatedAt: string;
|
|
9
|
+
paths: {
|
|
10
|
+
prd: string;
|
|
11
|
+
config: string;
|
|
12
|
+
referencePrototype: string;
|
|
13
|
+
handoff: string;
|
|
14
|
+
tldrawWireframe?: string;
|
|
15
|
+
};
|
|
16
|
+
device: {
|
|
17
|
+
name: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
spec: {
|
|
22
|
+
background: string;
|
|
23
|
+
menuPath: string;
|
|
24
|
+
filters: PrdProject['filters'];
|
|
25
|
+
columns: PrdProject['columns'];
|
|
26
|
+
toolbarButtons: string[];
|
|
27
|
+
rowActions: string[];
|
|
28
|
+
features: PrdProject['features'];
|
|
29
|
+
};
|
|
30
|
+
widgets: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
type: string;
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
w: number;
|
|
36
|
+
h: number;
|
|
37
|
+
props: Record<string, unknown>;
|
|
38
|
+
}>;
|
|
39
|
+
penpot: {
|
|
40
|
+
frameName: string;
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
prompt: string;
|
|
44
|
+
};
|
|
45
|
+
reframe: {
|
|
46
|
+
projectSlug: string;
|
|
47
|
+
brandHint: string;
|
|
48
|
+
prompt: string;
|
|
49
|
+
};
|
|
50
|
+
tldraw: {
|
|
51
|
+
relativePath: string;
|
|
52
|
+
prompt: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export declare function buildCanvasHandoff(project: PrdProject): CanvasHandoff;
|
|
56
|
+
export declare function saveCanvasHandoff(project: PrdProject): {
|
|
57
|
+
handoffPath: string;
|
|
58
|
+
penpotPromptPath: string;
|
|
59
|
+
reframePromptPath: string;
|
|
60
|
+
tldrawPromptPath: string;
|
|
61
|
+
tldrawWireframePath: string;
|
|
62
|
+
handoff: CanvasHandoff;
|
|
63
|
+
nextSteps: {
|
|
64
|
+
penpot: string[];
|
|
65
|
+
reframe: string[];
|
|
66
|
+
tldraw: string[];
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export declare function getHandoffSummary(slug: string): {
|
|
70
|
+
exists: boolean;
|
|
71
|
+
paths: string[];
|
|
72
|
+
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getPrototypesRoot } from '../config.js';
|
|
4
|
+
import { getProjectDir } from './project-store.js';
|
|
5
|
+
import { buildTldrawFile, buildTldrawPrompt } from './tldraw-wireframe.js';
|
|
6
|
+
function buildWidgets(project) {
|
|
7
|
+
const sidebarW = 220;
|
|
8
|
+
const headerH = 56;
|
|
9
|
+
const pad = 24;
|
|
10
|
+
const contentX = sidebarW + pad;
|
|
11
|
+
const filterH = project.filters.length > 0 ? 160 : 0;
|
|
12
|
+
const tableTop = headerH + pad + filterH + (filterH ? 16 : 0);
|
|
13
|
+
const widgets = [
|
|
14
|
+
{
|
|
15
|
+
id: 'shell_sidebar',
|
|
16
|
+
type: 'ShellSidebar',
|
|
17
|
+
x: 0,
|
|
18
|
+
y: 0,
|
|
19
|
+
w: sidebarW,
|
|
20
|
+
h: 900,
|
|
21
|
+
props: { menuPath: project.menuPath, activeItem: project.pageName },
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'shell_header',
|
|
25
|
+
type: 'PageHeader',
|
|
26
|
+
x: sidebarW,
|
|
27
|
+
y: 0,
|
|
28
|
+
w: 1440 - sidebarW,
|
|
29
|
+
h: headerH,
|
|
30
|
+
props: { title: project.pageName, breadcrumb: project.menuPath },
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
if (project.filters.length > 0) {
|
|
34
|
+
widgets.push({
|
|
35
|
+
id: 'filter_bar',
|
|
36
|
+
type: 'FilterBar',
|
|
37
|
+
x: contentX,
|
|
38
|
+
y: headerH + pad,
|
|
39
|
+
w: 1440 - sidebarW - pad * 2,
|
|
40
|
+
h: filterH,
|
|
41
|
+
props: {
|
|
42
|
+
title: '筛选条件',
|
|
43
|
+
fields: project.filters.map(f => ({
|
|
44
|
+
name: f.name,
|
|
45
|
+
type: f.type,
|
|
46
|
+
matchRule: f.matchRule,
|
|
47
|
+
})),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
widgets.push({
|
|
52
|
+
id: 'data_table',
|
|
53
|
+
type: 'DataTable',
|
|
54
|
+
x: contentX,
|
|
55
|
+
y: tableTop,
|
|
56
|
+
w: 1440 - sidebarW - pad * 2,
|
|
57
|
+
h: 900 - tableTop - pad,
|
|
58
|
+
props: {
|
|
59
|
+
title: `${project.pageName.replace(/列表|管理/g, '').trim() || project.pageName}列表`,
|
|
60
|
+
columns: project.columns.map(c => c.name),
|
|
61
|
+
toolbarButtons: project.toolbarButtons,
|
|
62
|
+
rowActions: project.rowActions,
|
|
63
|
+
mockRows: project.mockRows?.length ? project.mockRows : undefined,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
return widgets;
|
|
67
|
+
}
|
|
68
|
+
function buildPenpotPrompt(project, handoff) {
|
|
69
|
+
const filterLines = project.filters.map(f => `- ${f.name}(${f.type},${f.matchRule})`).join('\n');
|
|
70
|
+
const columnLines = project.columns.map(c => `- ${c.name}${c.description ? `:${c.description}` : ''}`).join('\n');
|
|
71
|
+
return `# Penpot 原型任务:${project.pageName}
|
|
72
|
+
|
|
73
|
+
请在当前 Penpot 文件中创建一帧 **${handoff.penpot.frameName}**(${handoff.penpot.width}×${handoff.penpot.height}),绘制 B 端后台列表页高保真原型。
|
|
74
|
+
|
|
75
|
+
## 页面背景
|
|
76
|
+
${project.background}
|
|
77
|
+
|
|
78
|
+
## 布局结构(绝对坐标参考)
|
|
79
|
+
${handoff.widgets.map(w => `- **${w.type}** @ (${w.x}, ${w.y}) ${w.w}×${w.h}`).join('\n')}
|
|
80
|
+
|
|
81
|
+
## 筛选区
|
|
82
|
+
${filterLines || '(无)'}
|
|
83
|
+
|
|
84
|
+
## 列表列
|
|
85
|
+
${columnLines}
|
|
86
|
+
|
|
87
|
+
## 工具栏按钮
|
|
88
|
+
${project.toolbarButtons.join('、')}
|
|
89
|
+
|
|
90
|
+
## 行操作
|
|
91
|
+
${project.rowActions.join('、')}
|
|
92
|
+
|
|
93
|
+
## 视觉规范
|
|
94
|
+
- 风格:Arco Design / 企业后台(白底、slate 边框、blue-600 主色)
|
|
95
|
+
- 左侧固定侧栏 220px,顶栏面包屑
|
|
96
|
+
- 参考 HTML 原型:${handoff.paths.referencePrototype}
|
|
97
|
+
|
|
98
|
+
## 关联 PRD
|
|
99
|
+
${handoff.paths.prd}
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
function buildReframePrompt(project, handoff) {
|
|
103
|
+
const filterSummary = project.filters.map(f => f.name).join('、') || '无';
|
|
104
|
+
const columnSummary = project.columns.map(c => c.name).join('、');
|
|
105
|
+
return `# Reframe 原型任务:${project.pageName}
|
|
106
|
+
|
|
107
|
+
基于 deppon-prd-mcp 已生成的 PRD,在 Reframe 画布上创建 B 端后台 **${project.pageName}** 页面。
|
|
108
|
+
|
|
109
|
+
## 设计要求
|
|
110
|
+
- 品牌/风格:${handoff.reframe.brandHint}
|
|
111
|
+
- 设备:${handoff.device.width}×${handoff.device.height}(MacBook Pro 视口)
|
|
112
|
+
- 页面类型:${project.pageType}
|
|
113
|
+
|
|
114
|
+
## 内容规格
|
|
115
|
+
- 背景:${project.background}
|
|
116
|
+
- 菜单路径:${project.menuPath}
|
|
117
|
+
- 筛选项:${filterSummary}
|
|
118
|
+
- 表格列:${columnSummary}
|
|
119
|
+
- 工具栏:${project.toolbarButtons.join('、')}
|
|
120
|
+
- 行操作:${project.rowActions.join('、')}
|
|
121
|
+
|
|
122
|
+
## 布局区块
|
|
123
|
+
${handoff.widgets.map(w => `- ${w.type}:${JSON.stringify(w.props)}`).join('\n')}
|
|
124
|
+
|
|
125
|
+
## 工作流
|
|
126
|
+
1. 读取参考 HTML:${handoff.paths.referencePrototype}
|
|
127
|
+
2. 使用 reframe_compile 将结构编译为 INode 场景
|
|
128
|
+
3. reframe_inspect 检查对比度与布局
|
|
129
|
+
4. reframe_export format=html 导出
|
|
130
|
+
|
|
131
|
+
## PRD 文档
|
|
132
|
+
${handoff.paths.prd}
|
|
133
|
+
|
|
134
|
+
项目 slug:\`${handoff.reframe.projectSlug}\`
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
export function buildCanvasHandoff(project) {
|
|
138
|
+
const dir = getProjectDir(project.slug);
|
|
139
|
+
const widgets = buildWidgets(project);
|
|
140
|
+
const handoff = {
|
|
141
|
+
version: '1.0',
|
|
142
|
+
source: 'deppon-prd-mcp',
|
|
143
|
+
slug: project.slug,
|
|
144
|
+
pageName: project.pageName,
|
|
145
|
+
pageType: project.pageType,
|
|
146
|
+
generatedAt: new Date().toISOString(),
|
|
147
|
+
paths: {
|
|
148
|
+
prd: path.join(dir, 'prd.md'),
|
|
149
|
+
config: path.join(dir, 'project.config.json'),
|
|
150
|
+
referencePrototype: path.join(dir, 'prototype.html'),
|
|
151
|
+
handoff: path.join(dir, 'canvas-handoff.json'),
|
|
152
|
+
tldrawWireframe: path.join(dir, 'wireframe.tldr'),
|
|
153
|
+
},
|
|
154
|
+
device: { name: 'MacBook Pro', width: 1440, height: 900 },
|
|
155
|
+
spec: {
|
|
156
|
+
background: project.background,
|
|
157
|
+
menuPath: project.menuPath,
|
|
158
|
+
filters: project.filters,
|
|
159
|
+
columns: project.columns,
|
|
160
|
+
toolbarButtons: project.toolbarButtons,
|
|
161
|
+
rowActions: project.rowActions,
|
|
162
|
+
features: project.features,
|
|
163
|
+
},
|
|
164
|
+
widgets,
|
|
165
|
+
penpot: {
|
|
166
|
+
frameName: `${project.pageName} · 原型`,
|
|
167
|
+
width: 1440,
|
|
168
|
+
height: 900,
|
|
169
|
+
prompt: '',
|
|
170
|
+
},
|
|
171
|
+
reframe: {
|
|
172
|
+
projectSlug: project.slug,
|
|
173
|
+
brandHint: 'Arco Design Pro · 企业 B 端后台',
|
|
174
|
+
prompt: '',
|
|
175
|
+
},
|
|
176
|
+
tldraw: {
|
|
177
|
+
relativePath: `${project.slug}/wireframe.tldr`,
|
|
178
|
+
prompt: '',
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
handoff.penpot.prompt = buildPenpotPrompt(project, handoff);
|
|
182
|
+
handoff.reframe.prompt = buildReframePrompt(project, handoff);
|
|
183
|
+
handoff.tldraw.prompt = buildTldrawPrompt(project, handoff, handoff.tldraw.relativePath);
|
|
184
|
+
return handoff;
|
|
185
|
+
}
|
|
186
|
+
export function saveCanvasHandoff(project) {
|
|
187
|
+
const handoff = buildCanvasHandoff(project);
|
|
188
|
+
const dir = getProjectDir(project.slug);
|
|
189
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
190
|
+
const handoffPath = path.join(dir, 'canvas-handoff.json');
|
|
191
|
+
const penpotPromptPath = path.join(dir, 'penpot-prompt.md');
|
|
192
|
+
const reframePromptPath = path.join(dir, 'reframe-prompt.md');
|
|
193
|
+
const tldrawPromptPath = path.join(dir, 'tldraw-prompt.md');
|
|
194
|
+
const tldrawWireframePath = path.join(dir, 'wireframe.tldr');
|
|
195
|
+
fs.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2), 'utf8');
|
|
196
|
+
fs.writeFileSync(penpotPromptPath, handoff.penpot.prompt, 'utf8');
|
|
197
|
+
fs.writeFileSync(reframePromptPath, handoff.reframe.prompt, 'utf8');
|
|
198
|
+
fs.writeFileSync(tldrawPromptPath, handoff.tldraw.prompt, 'utf8');
|
|
199
|
+
fs.writeFileSync(tldrawWireframePath, JSON.stringify(buildTldrawFile(project, handoff.widgets), null, 2), 'utf8');
|
|
200
|
+
return {
|
|
201
|
+
handoffPath,
|
|
202
|
+
penpotPromptPath,
|
|
203
|
+
reframePromptPath,
|
|
204
|
+
tldrawPromptPath,
|
|
205
|
+
tldrawWireframePath,
|
|
206
|
+
handoff,
|
|
207
|
+
nextSteps: {
|
|
208
|
+
penpot: [
|
|
209
|
+
'终端:yarn penpot:mcp',
|
|
210
|
+
'浏览器 design.penpot.app → 插件 → http://localhost:4400/manifest.json → Connect',
|
|
211
|
+
'Agent 读 penpot-prompt.md,用 penpot MCP 绘制高保真原型',
|
|
212
|
+
],
|
|
213
|
+
reframe: [
|
|
214
|
+
'终端:yarn reframe:platform(需 yarn reframe:setup)',
|
|
215
|
+
'Agent 读 reframe-prompt.md,调用 reframe MCP',
|
|
216
|
+
`预览:http://127.0.0.1:4100/platform/project/${project.slug}`,
|
|
217
|
+
],
|
|
218
|
+
tldraw: [
|
|
219
|
+
'Cursor 已配置 tldraw MCP(TLDRAW_DIR=src/prototypes)',
|
|
220
|
+
`打开线框:src/prototypes/${project.slug}/wireframe.tldr(tldraw.com 或 VS Code)`,
|
|
221
|
+
'Agent 读 tldraw-prompt.md,用 tldraw_add_shape 补充箭头/备注',
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
export function getHandoffSummary(slug) {
|
|
227
|
+
const dir = path.join(getPrototypesRoot(), slug);
|
|
228
|
+
const files = [
|
|
229
|
+
'canvas-handoff.json',
|
|
230
|
+
'penpot-prompt.md',
|
|
231
|
+
'reframe-prompt.md',
|
|
232
|
+
'tldraw-prompt.md',
|
|
233
|
+
'wireframe.tldr',
|
|
234
|
+
];
|
|
235
|
+
const paths = files.map(f => path.join(dir, f));
|
|
236
|
+
const exists = paths.every(p => fs.existsSync(p));
|
|
237
|
+
return { exists, paths };
|
|
238
|
+
}
|
|
@@ -6,15 +6,9 @@ export function generatePrdMarkdown(project) {
|
|
|
6
6
|
const title = project.useDpItTitle ? buildDpItTitle(project) : `${project.pageName} PRD(产品需求文档)`;
|
|
7
7
|
const moduleName = project.pageName;
|
|
8
8
|
const listTitle = `${moduleName.replace(/列表|管理/g, '').trim() || moduleName}列表`;
|
|
9
|
-
const featureRows = project.features
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const permissionRows = project.permissions
|
|
13
|
-
.map(p => `| ${p.point} | ${p.scope} | ${p.dataScope} |`)
|
|
14
|
-
.join('\n');
|
|
15
|
-
const filterRows = project.filters
|
|
16
|
-
.map(f => `| ${f.name} | ${f.type} | ${f.limit} | ${f.matchRule} |`)
|
|
17
|
-
.join('\n');
|
|
9
|
+
const featureRows = project.features.map(f => `| ${f.module} | ${f.feature} | ${f.description} |`).join('\n');
|
|
10
|
+
const permissionRows = project.permissions.map(p => `| ${p.point} | ${p.scope} | ${p.dataScope} |`).join('\n');
|
|
11
|
+
const filterRows = project.filters.map(f => `| ${f.name} | ${f.type} | ${f.limit} | ${f.matchRule} |`).join('\n');
|
|
18
12
|
const columnRows = project.columns
|
|
19
13
|
.map(c => `| ${c.name} | ${c.description || '—'} | ${c.sortable ? '是' : '否'} |`)
|
|
20
14
|
.join('\n');
|
|
@@ -16,6 +16,18 @@ export declare function applyOverrides(slug: string, overrides: Partial<Pick<Prd
|
|
|
16
16
|
listTitle?: string;
|
|
17
17
|
filterTitle?: string;
|
|
18
18
|
}): PrdProject | null;
|
|
19
|
+
export interface DesignSavePayload {
|
|
20
|
+
layout?: PrdProject['layout'];
|
|
21
|
+
filters?: PrdProject['filters'];
|
|
22
|
+
columns?: PrdProject['columns'];
|
|
23
|
+
toolbarButtons?: string[];
|
|
24
|
+
overrides?: {
|
|
25
|
+
pageName?: string;
|
|
26
|
+
listTitle?: string;
|
|
27
|
+
filterTitle?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function saveDesignLayout(slug: string, payload: DesignSavePayload): PrdProject | null;
|
|
19
31
|
export declare function readProjectFiles(slug: string): {
|
|
20
32
|
prd: string;
|
|
21
33
|
prototype: string;
|
|
@@ -64,6 +64,23 @@ export function applyOverrides(slug, overrides) {
|
|
|
64
64
|
saveProject(project);
|
|
65
65
|
return project;
|
|
66
66
|
}
|
|
67
|
+
export function saveDesignLayout(slug, payload) {
|
|
68
|
+
const project = loadProject(slug);
|
|
69
|
+
if (!project)
|
|
70
|
+
return null;
|
|
71
|
+
if (payload.layout)
|
|
72
|
+
project.layout = payload.layout;
|
|
73
|
+
if (payload.filters?.length)
|
|
74
|
+
project.filters = payload.filters;
|
|
75
|
+
if (payload.columns?.length)
|
|
76
|
+
project.columns = payload.columns;
|
|
77
|
+
if (payload.toolbarButtons?.length)
|
|
78
|
+
project.toolbarButtons = payload.toolbarButtons;
|
|
79
|
+
if (payload.overrides?.pageName)
|
|
80
|
+
project.pageName = payload.overrides.pageName;
|
|
81
|
+
saveProject(project);
|
|
82
|
+
return project;
|
|
83
|
+
}
|
|
67
84
|
export function readProjectFiles(slug) {
|
|
68
85
|
const project = loadProject(slug);
|
|
69
86
|
if (!project)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type PrdProject } from './types.js';
|
|
2
|
-
export declare function generatePrototypeHtml(
|
|
2
|
+
export declare function generatePrototypeHtml(rawProject: PrdProject): string;
|