@ww_nero/skills 3.1.0 → 3.2.1
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/assets/ppt_prompt.txt +25 -0
- package/assets/snippets.json +73 -0
- package/index.js +27 -164
- package/package.json +2 -1
- package/scripts/combine_stls.py +45 -0
- package/scripts/step_to_stl.py +55 -0
- /package/{assets → scripts}/html_to_png.py +0 -0
- /package/{assets → scripts}/pdf_to_png.py +0 -0
- /package/{assets → scripts}/pptx_to_png.py +0 -0
- /package/{assets → scripts}/svg_to_pptx.py +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
制作PPT的完整步骤如下(可根据当前实际状态,跳过部分已经完成的环节):
|
|
2
|
+
|
|
3
|
+
1. **规划PPT内容**,存储到markdown文件中:
|
|
4
|
+
- 每页Slide的规划需要包含:内容、布局方式、插图(只有用户明确提出要求时才添加插图)
|
|
5
|
+
- 不同页面应尽可能使用不同的布局方式,避免过于单调,可适当使用图表来呈现信息,例如折线图、柱状图、饼状图等;各正文页面标题样式、背景和文字色调**必须保持一致**。
|
|
6
|
+
- 如果用户未明确提出要求,则页面默认比例为宽1280px、高720px,默认背景为弱渐变的浅灰蓝(#F2F4F7 → #FCFCFC),现代科技感、扁平化风格。
|
|
7
|
+
- 如果需要插入图片,则按照以下方式:
|
|
8
|
+
- 收集图片素材,优先使用用户提供的图片;如未提供,则使用图像生成工具生成合适的图片
|
|
9
|
+
- 将图片素材保存到`images`文件夹中,使用内容和宽高比例作为图片名称,例如`main_background_16_9.png`表示该图片是主背景图,宽高比为16:9;对于用户提供的图片素材,可调用工具解读图片内容
|
|
10
|
+
|
|
11
|
+
2. **通过SVG代码实现Slides的排版**:
|
|
12
|
+
- 如需Image,统一使用`rect`或`circle`元素进行占位(必须符合图片宽高比例),占位元素应设置合适的位置,可添加浅色填充或边框以便预览时识别
|
|
13
|
+
- 如需Icon,优先使用Emoji,其次使用纯SVG代码的方式绘制
|
|
14
|
+
- 如需Chart,如折线图、柱状图等,应使用纯SVG代码的方式绘制,而不是使用图片占位元素
|
|
15
|
+
- 每页Slide的保存在一个独立HTML文件中,例如`slide_01.html`、`slide_02.html`等。
|
|
16
|
+
|
|
17
|
+
3. **把SVG转成PPTX文件**:
|
|
18
|
+
- 参考snippet工具中提供的`svg_to_pptx`代码示例,编写临时的python脚本,把svg转化成可编辑的pptx文件,不要遗漏任何元素(例如线条、装饰等)
|
|
19
|
+
- 转化的方法是把svg代码转化成使用python创建pptx中可操作元素的代码,示例中的创建元素的工具函数均可以使用
|
|
20
|
+
- 同时对于每个slide务必创建单独的create_slide函数,例如`create_slide1`、`create_slide2`等
|
|
21
|
+
- **不要使用`BeautifulSoup`这种通用的解析元素的方式,而是逐个元素从svg代码转化为python创建元素的代码**
|
|
22
|
+
- 转换过程中,需要把图片占位元素替换成前面准备的图片素材
|
|
23
|
+
- 每个页面编写一个单独的python脚本进行转化,最后再写一个python脚本,把所有单页的pptx文件合并成一个。
|
|
24
|
+
|
|
25
|
+
**注意**:完成后**需要保留**规划文档、html页面文件和python转化脚本,方便进一步根据反馈进行修改
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"html_to_png": {
|
|
3
|
+
"title": "HTML 转 PNG",
|
|
4
|
+
"description": "Playwright 渲染 HTML 后截图,自动按主体尺寸设置 viewport,适合静态页面或组件 demo。",
|
|
5
|
+
"codeFile": "html_to_png.py",
|
|
6
|
+
"notes": [
|
|
7
|
+
"默认等待 1500ms 以确保样式/异步资源加载完成,可通过脚本中的 wait 参数调节。",
|
|
8
|
+
"使用 device_scale_factor=4 输出高清图,如需更快速度可降低倍率。",
|
|
9
|
+
"截图时优先取 body 内的顶层单节点,否则回退到整个 body,保证尺寸合理。"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"pdf_to_png": {
|
|
13
|
+
"title": "PDF 转 PNG",
|
|
14
|
+
"description": "调用 pdf2image 将 PDF 分页转成位图,并按序号命名输出。",
|
|
15
|
+
"codeFile": "pdf_to_png.py",
|
|
16
|
+
"notes": [
|
|
17
|
+
"支持自定义 dpi 与输出格式(png/jpg 等),默认 300dpi 用于打印级清晰度。",
|
|
18
|
+
"输出目录会自动创建,文件名形如 page_0001.png,便于批量处理。",
|
|
19
|
+
"如需大批量转换,可将 convert_from_path 的线程参数打开以提升性能。"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"pptx_to_png": {
|
|
23
|
+
"title": "PPTX 转 PNG",
|
|
24
|
+
"description": "使用 LibreOffice 无头模式转换为 PDF,再通过 pdf2image 转成 PNG 图片,兼容 Windows/macOS/Linux。",
|
|
25
|
+
"codeFile": "pptx_to_png.py",
|
|
26
|
+
"notes": [
|
|
27
|
+
"脚本会自动探测 soffice 路径,如未安装需先安装 LibreOffice。",
|
|
28
|
+
"内部先转 PDF 再转 PNG,中间文件在临时目录处理,不会残留。",
|
|
29
|
+
"支持自定义 dpi,默认 300dpi 用于打印级清晰度。",
|
|
30
|
+
"输出目录会自动创建,文件名形如 page_0001.png,便于批量处理。"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"svg_to_pptx": {
|
|
34
|
+
"title": "SVG 转 PPTX",
|
|
35
|
+
"description": "把 SVG 转化成在 PPTX 中创建可操作的元素。",
|
|
36
|
+
"codeFile": "svg_to_pptx.py",
|
|
37
|
+
"notes": [
|
|
38
|
+
"【Icon 元素处理】对于复杂的 SVG 图标(如 <g id=\"icon-*\"> 元素或包含 path 的小图标),应先将其独立保存为小的 PNG 图片,再插入到 PPTX 中。**不能遗漏任何图标**。",
|
|
39
|
+
"【文本颜色丢失】python-pptx 设置字体名称时会添加 a:latin/a:ea 等元素,如果颜色填充放在 rPr 末尾会被 PowerPoint 忽略。解决:用 rPr.insert(0, solidFill/gradFill) 将颜色填充插入到 rPr 开头,确保优先级最高。",
|
|
40
|
+
"【渐变文本失效】设置渐变前必须先清理 a:solidFill、a:gradFill、a:noFill 三种标签,否则会冲突。同时添加 rotWithShape=\"1\" 和 scaled=\"1\" 属性确保渐变方向正确。",
|
|
41
|
+
"【背景渐变被覆盖】直接操作 slide._element 时,若 p:bg 已存在需先 clear() 再重建,否则新旧元素混杂导致渲染异常。用 cSld.insert(0, bg) 确保背景节点在正确位置。",
|
|
42
|
+
"【透明度不生效】设置透明度前需检查 fill.type == 1(纯色填充),然后定位到 srgbClr 节点追加 a:alpha 子元素,添加前先移除已有的 a:alpha 避免重复。",
|
|
43
|
+
"【中文字体回退】仅设置 run.font.name 对中文无效,需额外通过 XML 插入 a:ea 元素并指定 typeface=\"Microsoft YaHei\",且 a:ea 要插入到 rPr 开头防止被后续元素覆盖。",
|
|
44
|
+
"【形状渐变填充】设置形状渐变时,必须先移除 spPr 下已有的 a:solidFill/a:gradFill/a:noFill,否则渐变不生效。渐变位置 pos 使用 0-100000 范围(百分比*1000),角度 ang 使用度数*60000。",
|
|
45
|
+
"【渐变透明度】每个渐变停止点的透明度需在对应 srgbClr 下追加 a:alpha 元素,val 值为透明度*100000(如 0.5 透明度对应 50000)。",
|
|
46
|
+
"【曲线绘制】使用 build_freeform 绘制曲线时,起点坐标在构造器中指定,后续点通过 add_line_segments 添加。若需平滑曲线,可用 Catmull-Rom 样条插值生成中间点。",
|
|
47
|
+
"【线宽单位换算】线条宽度不能使用 Pt,应使用 EMU 单位:width * 9525(1px 约等于 9525 EMU)。",
|
|
48
|
+
"【移除阴影效果】如果 SVG 代码中没有显式设置阴影(如 box-shadow 或 filter: drop-shadow),插入元素后需要移除默认阴影,方法是在 spPr 下添加空的 a:effectLst 元素。",
|
|
49
|
+
"【Emoji 处理】Emoji 可以像普通文本一样直接插入到 PPT 中,使用 add_text_box 函数即可,无需特殊处理。"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"step_to_stl": {
|
|
53
|
+
"title": "STEP 转 STL",
|
|
54
|
+
"description": "使用 pythonocc-core 将 STEP/STP 文件转换为 STL 网格文件,适合 3D 打印或可视化。",
|
|
55
|
+
"codeFile": "step_to_stl.py",
|
|
56
|
+
"notes": [
|
|
57
|
+
"依赖 pythonocc-core 库,需通过 conda 或 pip 安装。",
|
|
58
|
+
"网格精度由 BRepMesh_IncrementalMesh 的参数控制,默认 0.1 适合一般用途。",
|
|
59
|
+
"输出为二进制 STL 格式,文件更小,加载更快。",
|
|
60
|
+
"命令行用法: python step_to_stl.py <input_file> [output_file]"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"combine_stls": {
|
|
64
|
+
"title": "合并 STL 文件",
|
|
65
|
+
"description": "使用 gmsh 将多个 STL 文件合并成一个 STL 文件。",
|
|
66
|
+
"codeFile": "combine_stls.py",
|
|
67
|
+
"notes": [
|
|
68
|
+
"依赖 gmsh 库,可通过 pip install gmsh 安装。",
|
|
69
|
+
"输出为二进制 STL 格式。",
|
|
70
|
+
"命令行用法: python combine_stls.py <output_file> <input1.stl> <input2.stl> ..."
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
|
6
6
|
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
7
7
|
const { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
|
|
8
8
|
|
|
9
|
+
const SCRIPTS_ROOT = path.join(__dirname, 'scripts');
|
|
9
10
|
const ASSETS_ROOT = path.join(__dirname, 'assets');
|
|
10
11
|
|
|
11
12
|
const convertPath = (filePath) => {
|
|
@@ -61,151 +62,34 @@ const resolveWorkingDirectory = (rawPath) => {
|
|
|
61
62
|
return resolved;
|
|
62
63
|
};
|
|
63
64
|
|
|
64
|
-
const
|
|
65
|
-
const target = path.resolve(
|
|
66
|
-
const rel = path.relative(
|
|
65
|
+
const resolveScript = (relativePath) => {
|
|
66
|
+
const target = path.resolve(SCRIPTS_ROOT, relativePath);
|
|
67
|
+
const rel = path.relative(SCRIPTS_ROOT, target);
|
|
67
68
|
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
68
69
|
throw new Error('非法资源路径');
|
|
69
70
|
}
|
|
70
71
|
if (!fs.existsSync(target)) {
|
|
71
|
-
throw new Error(
|
|
72
|
+
throw new Error(`未找到脚本: ${relativePath}`);
|
|
72
73
|
}
|
|
73
74
|
return target;
|
|
74
75
|
};
|
|
75
76
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
description: 'Playwright 渲染 HTML 后截图,自动按主体尺寸设置 viewport,适合静态页面或组件 demo。',
|
|
82
|
-
codeFile: 'html_to_png.py',
|
|
83
|
-
notes: [
|
|
84
|
-
'默认等待 1500ms 以确保样式/异步资源加载完成,可通过脚本中的 wait 参数调节。',
|
|
85
|
-
'使用 device_scale_factor=4 输出高清图,如需更快速度可降低倍率。',
|
|
86
|
-
'截图时优先取 body 内的顶层单节点,否则回退到整个 body,保证尺寸合理。'
|
|
87
|
-
]
|
|
88
|
-
},
|
|
89
|
-
pdf_to_png: {
|
|
90
|
-
title: 'PDF 转 PNG',
|
|
91
|
-
description: '调用 pdf2image 将 PDF 分页转成位图,并按序号命名输出。',
|
|
92
|
-
codeFile: 'pdf_to_png.py',
|
|
93
|
-
notes: [
|
|
94
|
-
'支持自定义 dpi 与输出格式(png/jpg 等),默认 300dpi 用于打印级清晰度。',
|
|
95
|
-
'输出目录会自动创建,文件名形如 page_0001.png,便于批量处理。',
|
|
96
|
-
'如需大批量转换,可将 convert_from_path 的线程参数打开以提升性能。'
|
|
97
|
-
]
|
|
98
|
-
},
|
|
99
|
-
pptx_to_png: {
|
|
100
|
-
title: 'PPTX 转 PNG',
|
|
101
|
-
description: '使用 LibreOffice 无头模式转换为 PDF,再通过 pdf2image 转成 PNG 图片,兼容 Windows/macOS/Linux。',
|
|
102
|
-
codeFile: 'pptx_to_png.py',
|
|
103
|
-
notes: [
|
|
104
|
-
'脚本会自动探测 soffice 路径,如未安装需先安装 LibreOffice。',
|
|
105
|
-
'内部先转 PDF 再转 PNG,中间文件在临时目录处理,不会残留。',
|
|
106
|
-
'支持自定义 dpi,默认 300dpi 用于打印级清晰度。',
|
|
107
|
-
'输出目录会自动创建,文件名形如 page_0001.png,便于批量处理。'
|
|
108
|
-
]
|
|
109
|
-
},
|
|
110
|
-
svg_to_pptx: {
|
|
111
|
-
title: 'SVG 转 PPTX',
|
|
112
|
-
description: '把 SVG 转化成在 PPTX 中创建可操作的元素。',
|
|
113
|
-
codeFile: 'svg_to_pptx.py',
|
|
114
|
-
notes: [
|
|
115
|
-
'【Icon 元素处理】对于复杂的 SVG 图标(如 <g id="icon-*"> 元素或包含 path 的小图标),应先将其独立保存为小的 PNG 图片,再插入到 PPTX 中。**不能遗漏任何图标**。',
|
|
116
|
-
'【文本颜色丢失】python-pptx 设置字体名称时会添加 a:latin/a:ea 等元素,如果颜色填充放在 rPr 末尾会被 PowerPoint 忽略。解决:用 rPr.insert(0, solidFill/gradFill) 将颜色填充插入到 rPr 开头,确保优先级最高。',
|
|
117
|
-
'【渐变文本失效】设置渐变前必须先清理 a:solidFill、a:gradFill、a:noFill 三种标签,否则会冲突。同时添加 rotWithShape="1" 和 scaled="1" 属性确保渐变方向正确。',
|
|
118
|
-
'【背景渐变被覆盖】直接操作 slide._element 时,若 p:bg 已存在需先 clear() 再重建,否则新旧元素混杂导致渲染异常。用 cSld.insert(0, bg) 确保背景节点在正确位置。',
|
|
119
|
-
'【透明度不生效】设置透明度前需检查 fill.type == 1(纯色填充),然后定位到 srgbClr 节点追加 a:alpha 子元素,添加前先移除已有的 a:alpha 避免重复。',
|
|
120
|
-
'【中文字体回退】仅设置 run.font.name 对中文无效,需额外通过 XML 插入 a:ea 元素并指定 typeface="Microsoft YaHei",且 a:ea 要插入到 rPr 开头防止被后续元素覆盖。',
|
|
121
|
-
'【形状渐变填充】设置形状渐变时,必须先移除 spPr 下已有的 a:solidFill/a:gradFill/a:noFill,否则渐变不生效。渐变位置 pos 使用 0-100000 范围(百分比*1000),角度 ang 使用度数*60000。',
|
|
122
|
-
'【渐变透明度】每个渐变停止点的透明度需在对应 srgbClr 下追加 a:alpha 元素,val 值为透明度*100000(如 0.5 透明度对应 50000)。',
|
|
123
|
-
'【曲线绘制】使用 build_freeform 绘制曲线时,起点坐标在构造器中指定,后续点通过 add_line_segments 添加。若需平滑曲线,可用 Catmull-Rom 样条插值生成中间点。',
|
|
124
|
-
'【线宽单位换算】线条宽度不能使用 Pt,应使用 EMU 单位:width * 9525(1px 约等于 9525 EMU)。',
|
|
125
|
-
'【移除阴影效果】如果 SVG 代码中没有显式设置阴影(如 box-shadow 或 filter: drop-shadow),插入元素后需要移除默认阴影,方法是在 spPr 下添加空的 a:effectLst 元素。',
|
|
126
|
-
'【Emoji 处理】Emoji 可以像普通文本一样直接插入到 PPT 中,使用 add_text_box 函数即可,无需特殊处理。'
|
|
127
|
-
]
|
|
128
|
-
}
|
|
77
|
+
const loadScript = (relativePath) => fs.readFileSync(resolveScript(relativePath), 'utf8');
|
|
78
|
+
|
|
79
|
+
const loadJson = (filename) => {
|
|
80
|
+
const filePath = path.join(ASSETS_ROOT, filename);
|
|
81
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
129
82
|
};
|
|
130
83
|
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
## 方法一:使用命令行工具(推荐用于简单场景)
|
|
137
|
-
|
|
138
|
-
以下工具均已安装,可直接在终端调用:
|
|
139
|
-
|
|
140
|
-
- **LibreOffice (soffice)**:支持 Office 文档互转
|
|
141
|
-
- PPTX → PDF: \`soffice --headless --convert-to pdf input.pptx --outdir ./output\`
|
|
142
|
-
- DOCX → PDF: \`soffice --headless --convert-to pdf input.docx --outdir ./output\`
|
|
143
|
-
- XLSX → PDF: \`soffice --headless --convert-to pdf input.xlsx --outdir ./output\`
|
|
144
|
-
|
|
145
|
-
- **Pandoc**:支持多种文档格式转换
|
|
146
|
-
- Markdown → DOCX: \`pandoc input.md -o output.docx\`
|
|
147
|
-
- Markdown → PDF(中文): \`pandoc input.md -o output.pdf --pdf-engine=xelatex -V CJKmainfont="Microsoft YaHei"\`
|
|
148
|
-
- HTML → Markdown: \`pandoc input.html -o output.md\`
|
|
149
|
-
|
|
150
|
-
- **LaTeX (pdflatex/xelatex)**:用于高质量 PDF 生成
|
|
151
|
-
- TEX → PDF: \`xelatex input.tex\`
|
|
152
|
-
|
|
153
|
-
## 方法二:编写临时 Python 脚本(推荐用于复杂场景)
|
|
154
|
-
|
|
155
|
-
参考 \`snippet\` 工具中提供的转化脚本示例(已验证正确性),编写临时 Python 脚本:
|
|
156
|
-
|
|
157
|
-
| 转化类型 | snippet 名称 | 说明 |
|
|
158
|
-
|---------|-------------|------|
|
|
159
|
-
| HTML → PNG | \`html_to_png\` | 使用 Playwright 渲染并截图 |
|
|
160
|
-
| PDF → PNG | \`pdf_to_png\` | 使用 pdf2image 分页转换 |
|
|
161
|
-
| PPTX → PNG | \`pptx_to_png\` | 使用 LibreOffice + pdf2image |
|
|
162
|
-
| SVG → PPTX | \`svg_to_pptx\` | 转化为可编辑的 PPTX 元素 |
|
|
163
|
-
|
|
164
|
-
调用示例:使用 \`snippet\` 工具获取参考代码,如 \`snippet({ title: "pdf_to_png" })\`
|
|
165
|
-
|
|
166
|
-
## 解读 PPTX 或 PDF 文档
|
|
167
|
-
|
|
168
|
-
当用户需要解读 PPTX 或 PDF 文档内容时:
|
|
169
|
-
1. 编写临时 Python 脚本,将 PPTX/PDF 转化为 PNG 图片
|
|
170
|
-
2. 对生成的图片进行视觉分析或内容解读
|
|
171
|
-
3. 完成后删除临时脚本和中间文件
|
|
172
|
-
|
|
173
|
-
## 注意事项
|
|
174
|
-
|
|
175
|
-
- 临时脚本执行完成后应删除,除非用户明确要求保留
|
|
176
|
-
- Python 环境已全局安装 \`playwright\`、\`pdf2image\`、\`python-pptx\`、\`Pillow\` 等常用库
|
|
177
|
-
- 如需其他库,可使用 \`pip install\` 安装`
|
|
178
|
-
},
|
|
179
|
-
make_ppt: {
|
|
180
|
-
title: '制作PPT',
|
|
181
|
-
content: `制作PPT的完整步骤如下(可根据当前实际状态,跳过部分已经完成的环节):
|
|
182
|
-
|
|
183
|
-
1. **规划PPT内容**,存储到markdown文件中:
|
|
184
|
-
- 每页Slide的规划需要包含:内容、布局方式、插图(只有用户明确提出要求时才添加插图)
|
|
185
|
-
- 不同页面应尽可能使用不同的布局方式,避免过于单调,可适当使用图表来呈现信息,例如折线图、柱状图、饼状图等;各正文页面标题样式、背景和文字色调**必须保持一致**。
|
|
186
|
-
- 如果用户未明确提出要求,则页面默认比例为宽1280px、高720px,默认背景为弱渐变的浅灰蓝(#F2F4F7 → #FCFCFC),现代科技感、扁平化风格。
|
|
187
|
-
- 如果需要插入图片,则按照以下方式:
|
|
188
|
-
- 收集图片素材,优先使用用户提供的图片;如未提供,则使用图像生成工具生成合适的图片
|
|
189
|
-
- 将图片素材保存到\`images\`文件夹中,使用内容和宽高比例作为图片名称,例如\`main_background_16_9.png\`表示该图片是主背景图,宽高比为16:9;对于用户提供的图片素材,可调用工具解读图片内容
|
|
190
|
-
|
|
191
|
-
2. **通过SVG代码实现Slides的排版**:
|
|
192
|
-
- 如需Image,统一使用\`rect\`或\`circle\`元素进行占位(必须符合图片宽高比例),占位元素应设置合适的位置,可添加浅色填充或边框以便预览时识别
|
|
193
|
-
- 如需Icon,优先使用Emoji,其次使用纯SVG代码的方式绘制
|
|
194
|
-
- 如需Chart,如折线图、柱状图等,应使用纯SVG代码的方式绘制,而不是使用图片占位元素
|
|
195
|
-
- 每页Slide的保存在一个独立HTML文件中,例如\`slide_01.html\`、\`slide_02.html\`等。
|
|
196
|
-
|
|
197
|
-
3. **把SVG转成PPTX文件**:
|
|
198
|
-
- 参考snippet工具中提供的\`svg_to_pptx\`代码示例,编写临时的python脚本,把svg转化成可编辑的pptx文件,不要遗漏任何元素(例如线条、装饰等)
|
|
199
|
-
- 转化的方法是把svg代码转化成使用python创建pptx中可操作元素的代码,示例中的创建元素的工具函数均可以使用
|
|
200
|
-
- 同时对于每个slide务必创建单独的create_slide函数,例如\`create_slide1\`、\`create_slide2\`等
|
|
201
|
-
- **不要使用\`BeautifulSoup\`这种通用的解析元素的方式,而是逐个元素从svg代码转化为python创建元素的代码**
|
|
202
|
-
- 转换过程中,需要把图片占位元素替换成前面准备的图片素材
|
|
203
|
-
- 每个页面编写一个单独的python脚本进行转化,最后再写一个python脚本,把所有单页的pptx文件合并成一个。
|
|
204
|
-
|
|
205
|
-
**注意**:完成后**需要保留**规划文档、html页面文件和python转化脚本,方便进一步根据反馈进行修改`
|
|
206
|
-
}
|
|
84
|
+
const SNIPPETS = loadJson('snippets.json');
|
|
85
|
+
|
|
86
|
+
const loadText = (filename) => {
|
|
87
|
+
const filePath = path.join(ASSETS_ROOT, filename);
|
|
88
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
207
89
|
};
|
|
208
90
|
|
|
91
|
+
const PPT_PROMPT = loadText('ppt_prompt.txt');
|
|
92
|
+
|
|
209
93
|
const buildSnippet = (title, workingDirectory) => {
|
|
210
94
|
const entry = SNIPPETS[title];
|
|
211
95
|
if (!entry) {
|
|
@@ -214,7 +98,7 @@ const buildSnippet = (title, workingDirectory) => {
|
|
|
214
98
|
}
|
|
215
99
|
|
|
216
100
|
const workingDir = resolveWorkingDirectory(workingDirectory);
|
|
217
|
-
const code =
|
|
101
|
+
const code = loadScript(entry.codeFile);
|
|
218
102
|
const outputPath = path.join(workingDir, entry.codeFile);
|
|
219
103
|
fs.writeFileSync(outputPath, code);
|
|
220
104
|
|
|
@@ -231,19 +115,6 @@ const buildSnippet = (title, workingDirectory) => {
|
|
|
231
115
|
return lines.join('\n');
|
|
232
116
|
};
|
|
233
117
|
|
|
234
|
-
const buildGuide = (title) => {
|
|
235
|
-
const entry = GUIDES[title];
|
|
236
|
-
if (!entry) {
|
|
237
|
-
const supported = Object.keys(GUIDES).join(', ');
|
|
238
|
-
throw new Error(`title 仅支持: ${supported}`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const lines = [];
|
|
242
|
-
lines.push(`【${entry.title}】技能指导`);
|
|
243
|
-
lines.push(entry.content);
|
|
244
|
-
|
|
245
|
-
return lines.join('\n');
|
|
246
|
-
};
|
|
247
118
|
|
|
248
119
|
const listTools = () => ({
|
|
249
120
|
tools: [
|
|
@@ -255,11 +126,11 @@ const listTools = () => ({
|
|
|
255
126
|
properties: {
|
|
256
127
|
working_directory: {
|
|
257
128
|
type: 'string',
|
|
258
|
-
description: '
|
|
129
|
+
description: '工作目录的绝对路径,示例脚本文件将保存到此目录'
|
|
259
130
|
},
|
|
260
131
|
title: {
|
|
261
132
|
type: 'string',
|
|
262
|
-
description: '示例类型,可选: html_to_png | pdf_to_png | pptx_to_png | svg_to_pptx',
|
|
133
|
+
description: '示例类型,可选: html_to_png | pdf_to_png | pptx_to_png | svg_to_pptx | step_to_stl | combine_stls',
|
|
263
134
|
enum: Object.keys(SNIPPETS)
|
|
264
135
|
}
|
|
265
136
|
},
|
|
@@ -267,24 +138,18 @@ const listTools = () => ({
|
|
|
267
138
|
}
|
|
268
139
|
},
|
|
269
140
|
{
|
|
270
|
-
name: '
|
|
271
|
-
description: '
|
|
141
|
+
name: 'make_ppt',
|
|
142
|
+
description: '制作PPT的全流程指导说明及注意事项',
|
|
272
143
|
inputSchema: {
|
|
273
144
|
type: 'object',
|
|
274
|
-
properties: {
|
|
275
|
-
|
|
276
|
-
type: 'string',
|
|
277
|
-
description: '技能类型,可选: convert_doc_types | make_ppt',
|
|
278
|
-
enum: Object.keys(GUIDES)
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
required: ['title']
|
|
145
|
+
properties: {},
|
|
146
|
+
required: []
|
|
282
147
|
}
|
|
283
148
|
}
|
|
284
149
|
]
|
|
285
150
|
});
|
|
286
151
|
|
|
287
|
-
const server = new Server({ name: 'skills', version: '3.1
|
|
152
|
+
const server = new Server({ name: 'skills', version: '3.2.1' }, { capabilities: { tools: {} } });
|
|
288
153
|
|
|
289
154
|
server.setRequestHandler(ListToolsRequestSchema, async () => listTools());
|
|
290
155
|
|
|
@@ -301,10 +166,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
301
166
|
return { content: [{ type: 'text', text: result }] };
|
|
302
167
|
}
|
|
303
168
|
|
|
304
|
-
if (name === '
|
|
305
|
-
|
|
306
|
-
const payload = buildGuide(title);
|
|
307
|
-
return { content: [{ type: 'text', text: payload }] };
|
|
169
|
+
if (name === 'make_ppt') {
|
|
170
|
+
return { content: [{ type: 'text', text: PPT_PROMPT }] };
|
|
308
171
|
}
|
|
309
172
|
|
|
310
173
|
return { content: [{ type: 'text', text: `未知工具: ${name}` }], isError: true };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ww_nero/skills",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "MCP server that returns Python reference snippets and skill guides",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"index.js",
|
|
11
|
+
"scripts",
|
|
11
12
|
"assets"
|
|
12
13
|
],
|
|
13
14
|
"dependencies": {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from typing import List
|
|
4
|
+
import gmsh
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def combine_stl_files(input_files: List[str], output_file: str) -> bool:
|
|
8
|
+
"""合并多个STL文件成一个STL文件"""
|
|
9
|
+
try:
|
|
10
|
+
gmsh.initialize()
|
|
11
|
+
gmsh.clear()
|
|
12
|
+
|
|
13
|
+
for stl_file in input_files:
|
|
14
|
+
print(f"正在加载: {stl_file}")
|
|
15
|
+
gmsh.merge(stl_file)
|
|
16
|
+
|
|
17
|
+
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
18
|
+
gmsh.option.setNumber("Mesh.Binary", 1)
|
|
19
|
+
gmsh.write(output_file)
|
|
20
|
+
print(f"合并后的STL文件已保存至: {output_file}")
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
except Exception as e:
|
|
24
|
+
print(f"合并过程中出现错误: {e}")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
finally:
|
|
28
|
+
gmsh.finalize()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
if len(sys.argv) < 3:
|
|
33
|
+
print("用法: python combine_stls.py <output_file> <input_file1> <input_file2> ...")
|
|
34
|
+
print("示例: python combine_stls.py combined.stl part1.stl part2.stl part3.stl")
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
|
|
37
|
+
output_file = sys.argv[1]
|
|
38
|
+
input_files = sys.argv[2:]
|
|
39
|
+
|
|
40
|
+
success = combine_stl_files(input_files, output_file)
|
|
41
|
+
if success:
|
|
42
|
+
print("合并成功!")
|
|
43
|
+
else:
|
|
44
|
+
print("合并失败!")
|
|
45
|
+
sys.exit(1)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from OCC.Extend.DataExchange import read_step_file, write_stl_file
|
|
4
|
+
from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def convert_step_to_stl(input_file: str, output_file: str = None) -> str:
|
|
8
|
+
"""
|
|
9
|
+
将STEP文件转换为STL文件
|
|
10
|
+
|
|
11
|
+
参数:
|
|
12
|
+
input_file: STEP文件路径
|
|
13
|
+
output_file: 输出STL文件路径,默认为输入文件同目录下同名.stl文件
|
|
14
|
+
返回:
|
|
15
|
+
输出文件路径
|
|
16
|
+
"""
|
|
17
|
+
if output_file is None:
|
|
18
|
+
filename_without_ext = os.path.splitext(input_file)[0]
|
|
19
|
+
output_file = f"{filename_without_ext}.stl"
|
|
20
|
+
|
|
21
|
+
output_dir = os.path.dirname(output_file)
|
|
22
|
+
if output_dir and not os.path.exists(output_dir):
|
|
23
|
+
os.makedirs(output_dir)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
shape = read_step_file(input_file)
|
|
27
|
+
except Exception as e:
|
|
28
|
+
raise Exception(f"读取STEP文件失败: {str(e)}")
|
|
29
|
+
|
|
30
|
+
mesh = BRepMesh_IncrementalMesh(shape, 0.1, False, 0.1, True)
|
|
31
|
+
mesh.Perform()
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
write_stl_file(shape, output_file, mode='binary')
|
|
35
|
+
print(f"转换成功: {output_file}")
|
|
36
|
+
return output_file
|
|
37
|
+
except Exception as e:
|
|
38
|
+
raise Exception(f"STL文件写入失败: {str(e)}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
if len(sys.argv) < 2:
|
|
43
|
+
print("用法: python step_to_stl.py <input_file> [output_file]")
|
|
44
|
+
print("示例: python step_to_stl.py model.step")
|
|
45
|
+
print(" python step_to_stl.py model.step output/model.stl")
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
input_file = sys.argv[1]
|
|
49
|
+
output_file = sys.argv[2] if len(sys.argv) > 2 else None
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
convert_step_to_stl(input_file, output_file)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
print(f"转换失败: {str(e)}")
|
|
55
|
+
sys.exit(1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|