dsh-claude-slides 2.12.1 → 2.12.2
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/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// dsh-claude-slides 2.12.
|
|
1
|
+
// dsh-claude-slides 2.12.2: built from baoyu-design.
|
|
2
2
|
var define_PLUGIN_default = { name: "claude-slides", provider: "dsh-claude-slides", skills: [{ name: "claude-slides", description: "Make and revise slide decks in the Slides artifact format (project/deck.json plus one HTML file per slide) as local files, then open them in a local slide editor where the user can present, retype, drag, and export PDF, HTML or PowerPoint. Use when the user asks for an editable slide deck or presentation in this format, names claude-slides or the Slides type, or wants to revise a folder that holds project/deck.json. Not for a one-off single-file HTML deck or prototype.", comments: true }] };
|
|
3
3
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-claude-slides",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.2",
|
|
4
4
|
"description": "DeepSeek Harness plugin: the claude-slides skill from baoyu-design. The agent writes editable slide decks as files and opens them in a local slide editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"icon": "./icon.svg",
|
|
24
24
|
"baoyuDesign": {
|
|
25
25
|
"releaseNotes": [
|
|
26
|
+
{
|
|
27
|
+
"version": "2.12.2",
|
|
28
|
+
"date": "2026-11-09",
|
|
29
|
+
"en": "### Features\n- **`claude-design`: Export → HTML for Figma.** Capturing the canvas, or the Standalone web page, with Figma's Chrome extension brought the artboards in as empty boxes, because each artboard is drawn inside a frame the extension does not look into. **HTML for Figma** (in the toolbar's Export, an artboard's Export and the Share panel's Export tab) saves `<title>.figma.html`: every artboard's rendered layers, placed where they are on the canvas, with their styles, fonts and images inside the file, and no frames and no scripts. Open it in Chrome and capture it with Figma's extension (**Capture page**, or **Select element** for one artboard). In an artboard's own Export it is that artboard alone. `SKILL.md` tells the agent how to answer when asked to bring a design into Figma (reported by @mufanmu, [#36](https://github.com/JimLiu/baoyu-design/issues/36)).\n\n### Fixes\n- **`claude-design`: PNG export no longer draws some icons as black or solid blocks.** An SVG icon drawn with `<use>` lost the `fill: none` and `stroke: currentColor` its class gave it, and came out filled in black; it now takes the colour of the `<use>`. An icon cut out with a CSS `mask-image` (on an element or on `::before` and `::after`) was drawn as a solid block because the mask's file was not in the exported image; masks are now embedded like background images (reported by @hh0592821, [#35](https://github.com/JimLiu/baoyu-design/issues/35)).\n- **`share` gives out the Wi-Fi's address first on a computer with a VPN, a proxy or a hotspot.** On Windows the links could start with the address of a proxy's virtual adapter (a sing-box TUN named `falemon-tun`, `172.19.0.1`), which nobody else on the network can reach. Addresses of adapters named like a tunnel (`falemon-tun`, OpenVPN's TAP, Wintun, anything with `vpn` in its name), of point-to-point links, and of networks this computer makes itself (the Mobile Hotspot's `192.168.137.1`, a virtual machine's or container's network) now come after the local network's. When a computer is on more than one network, `share` says that a link that does not open can be tried with another of the listed addresses (reported by @Bistard, [#37](https://github.com/JimLiu/baoyu-design/issues/37)).\n- **`open` no longer says the port is in use when a firewall or proxy filters `::1`.** A probe refused with `EACCES` or `EPERM` counted as an answer from something on the port, so `open` gave up on all 100 ports it tries.\n- A page whose skill folder was moved, renamed or removed while its server ran now says so, and names the folder to the owner, instead of telling them to reinstall the skill. A guest is told to ask whoever shared it.",
|
|
30
|
+
"zh": "### 新功能\n- **`claude-design`:新增 Export → HTML for Figma。** 以前用 Figma 的 Chrome 扩展捕捉画布,或捕捉导出的 Standalone web page,画板进到 Figma 里都是空框,因为每个画板都画在一个框架(iframe)里,扩展看不进去。现在工具栏的 Export、画板自己的 Export 和 Share 面板的 Export 标签里都有 **HTML for Figma**,保存成 `<标题>.figma.html`:每个画板渲染出来的图层,摆在它们在画布上的位置,样式、字体和图片都在文件里,没有框架,也没有脚本。用 Chrome 打开这个文件,再用 Figma 的扩展捕捉(**Capture page**,只要一个画板就用 **Select element**)。画板自己的 Export 里,导出的只是这一个画板。`SKILL.md` 也写了用户要把设计带进 Figma 时 Agent 该怎么答(@mufanmu 报告,[#36](https://github.com/JimLiu/baoyu-design/issues/36))。\n\n### 修复\n- **`claude-design`:PNG 导出不再把一部分图标画成黑色或实心块。** 用 `<use>` 画的 SVG 图标,会丢掉 class 给它的 `fill: none` 和 `stroke: currentColor`,导出来被涂成黑色,现在按 `<use>` 的颜色走。用 CSS `mask-image` 抠出来的图标(元素上的,或 `::before`、`::after` 上的)因为遮罩文件没进导出的图片,被画成一整块实心,现在遮罩像背景图一样嵌进去(@hh0592821 报告,[#35](https://github.com/JimLiu/baoyu-design/issues/35))。\n- **装了 VPN、代理或开了热点的电脑上,`share` 先给出 Wi-Fi 的地址。** 在 Windows 上,链接可能以代理虚拟网卡的地址开头(sing-box 的 TUN,叫 `falemon-tun`,`172.19.0.1`),网络上的其他人连不上。现在名字像隧道的网卡(`falemon-tun`、OpenVPN 的 TAP、Wintun、名字里带 `vpn` 的),点对点链路,以及这台电脑自己建的网络(移动热点的 `192.168.137.1`、虚拟机或容器的网络),地址都排在本地网络后面。电脑在多个网络上时,`share` 会提示:链接打不开,可以把地址换成列出的另一个再试(@Bistard 报告,[#37](https://github.com/JimLiu/baoyu-design/issues/37))。\n- **防火墙或代理拦着 `::1` 时,`open` 不再报端口被占用。** 探测被 `EACCES` 或 `EPERM` 拒绝,以前算成端口上有东西应答,`open` 试的 100 个端口全部放弃。\n- 技能文件夹在服务器运行期间被移动、改名或删除,页面现在会说明这一点,并把文件夹告诉主人,而不是让他重装技能;访客看到的是请分享的人重新打开。"
|
|
31
|
+
},
|
|
26
32
|
{
|
|
27
33
|
"version": "2.12.1",
|
|
28
34
|
"date": "2026-11-07",
|
|
@@ -64,12 +70,6 @@
|
|
|
64
70
|
"date": "2026-11-04",
|
|
65
71
|
"en": "### Fixes\n- DeepSeek Harness on Windows: the command the skills told the agent to run was written for a POSIX shell, and PowerShell reads a quoted path at the start of a line as a string, not a command. On Windows they now give `& '<node>' '<script>' <command>`, and say that PowerShell has no heredoc.",
|
|
66
72
|
"zh": "### 修复\n- Windows 上的 DeepSeek Harness:技能告诉 agent 运行的命令是按 POSIX shell 写的,而 PowerShell 会把行首带引号的路径当成字符串,不当成命令。现在 Windows 上给的是 `& '<node>' '<script>' <command>`,并说明 PowerShell 没有 heredoc。"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"version": "2.9.1",
|
|
70
|
-
"date": "2026-11-04",
|
|
71
|
-
"en": "### Fixes\n- DeepSeek Harness on Windows: the plugins only recognized a shell tool named `bash`, and on Windows it is named `pwsh`, so they never noticed an `open`. The folder stayed off the **Artifacts** page, **Send to Agent** never appeared, and comments did not reach the agent. They now recognize both names (by @leil894-collab, [#31](https://github.com/JimLiu/baoyu-design/pull/31); reported by @EmotionTowel, [#30](https://github.com/JimLiu/baoyu-design/issues/30)).",
|
|
72
|
-
"zh": "### 修复\n- Windows 上的 DeepSeek Harness:插件只认名叫 `bash` 的 shell 工具,而它在 Windows 上叫 `pwsh`,所以一直注意不到 `open`。文件夹不会出现在作品页,**Send to Agent** 不出现,评论也到不了 Agent。现在两个名字都认(by @leil894-collab,[#31](https://github.com/JimLiu/baoyu-design/pull/31);@EmotionTowel 报告,[#30](https://github.com/JimLiu/baoyu-design/issues/30))。"
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
},
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"dsh": ">=0.2.0-0"
|
|
96
96
|
},
|
|
97
97
|
"dependencies": {
|
|
98
|
-
"dsh-claude-artifacts": "^2.12.
|
|
98
|
+
"dsh-claude-artifacts": "^2.12.2"
|
|
99
99
|
},
|
|
100
100
|
"dsh": {
|
|
101
101
|
"bundle": {
|
|
@@ -2524,6 +2524,10 @@ function loopbackBlocked(port) {
|
|
|
2524
2524
|
socket.once("error", (error) => done(error.code === "EPERM" || error.code === "EACCES"));
|
|
2525
2525
|
});
|
|
2526
2526
|
}
|
|
2527
|
+
function isTailnet(address) {
|
|
2528
|
+
const m = /^100\.(\d{1,3})\./.exec(address);
|
|
2529
|
+
return !!m && Number(m[1]) >= 64 && Number(m[1]) <= 127;
|
|
2530
|
+
}
|
|
2527
2531
|
function serverOrigin(host, port) {
|
|
2528
2532
|
const shown = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host.includes(":") ? `[${host}]` : host;
|
|
2529
2533
|
return `http://${shown}:${port}`;
|
|
@@ -3125,9 +3129,12 @@ async function shareCommand(o) {
|
|
|
3125
3129
|
const links = view.links;
|
|
3126
3130
|
const shown = (list) => Object.fromEntries(Object.entries(list).map(([role, link]) => [role, link.on ? link.url : "off"]));
|
|
3127
3131
|
const tailnet = view.lan ? view.tailnet : void 0;
|
|
3132
|
+
const addresses = view.lan ? lanUrls(view) : [];
|
|
3133
|
+
const local = view.lan && Array.isArray(view.addresses) ? view.addresses.filter((a) => !isTailnet(String(a))) : [];
|
|
3134
|
+
const others = local.length > 1 ? " This computer is on more than one network (addresses): if a link does not open for someone on the same Wi-Fi or LAN, give them the same link with another of those addresses." : "";
|
|
3128
3135
|
return {
|
|
3129
3136
|
lan: view.lan,
|
|
3130
|
-
...view.lan ? { addresses
|
|
3137
|
+
...view.lan ? { addresses } : {},
|
|
3131
3138
|
links: shown(links),
|
|
3132
3139
|
...tailnet ? {
|
|
3133
3140
|
tailscale: shown(tailnet.links),
|
|
@@ -3135,7 +3142,7 @@ async function shareCommand(o) {
|
|
|
3135
3142
|
} : {},
|
|
3136
3143
|
guests: view.guests,
|
|
3137
3144
|
...view.lan ? {
|
|
3138
|
-
note:
|
|
3145
|
+
note: `Send the link for the role you want to give: editor edits, commenter comments, viewer reads. Anyone on this network with a link joins under a name they type. The owner (this computer) shares from the Share button too.${others}`,
|
|
3139
3146
|
security: "It is plain http: others on the network can read what passes. A link is the permission: whoever has it gets its role, so send it only to the people meant to have it, and reset a role's link if it went further.",
|
|
3140
3147
|
stop: o.offCommand
|
|
3141
3148
|
} : { note: `Not shared on the network: the links work on this computer only. ${NOT_SHARED_HINT}` }
|
|
@@ -918,13 +918,21 @@ function isTailnet(address) {
|
|
|
918
918
|
const m = /^100\.(\d{1,3})\./.exec(address);
|
|
919
919
|
return !!m && Number(m[1]) >= 64 && Number(m[1]) <= 127;
|
|
920
920
|
}
|
|
921
|
-
var isTunnel = (name) =>
|
|
921
|
+
var isTunnel = (name) => /(^|[^a-z0-9])(utun|tun|tap|wg|ppp|ipsec)\d*($|[^a-z0-9])/i.test(name) || /vpn|wintun|tunnel/i.test(name);
|
|
922
|
+
var ipv4Number = (address) => address.split(".").reduce((n, part) => n * 256 + Number(part), 0);
|
|
923
|
+
function isOwnNetwork(entry) {
|
|
924
|
+
if (!/^\d+\.\d+\.\d+\.\d+$/.test(entry.netmask)) return false;
|
|
925
|
+
const mask = ipv4Number(entry.netmask);
|
|
926
|
+
if (mask >= ipv4Number("255.255.255.252")) return true;
|
|
927
|
+
const address = ipv4Number(entry.address);
|
|
928
|
+
return ((address & mask) >>> 0) + 1 === address;
|
|
929
|
+
}
|
|
922
930
|
function lanAddresses() {
|
|
923
931
|
const out = /* @__PURE__ */ new Map();
|
|
924
932
|
for (const [name, list] of Object.entries(os2.networkInterfaces())) {
|
|
925
933
|
for (const entry of list ?? []) {
|
|
926
934
|
if (entry.family !== "IPv4" || entry.internal || entry.address.startsWith("169.254.") || isBenchmarkRange(entry.address)) continue;
|
|
927
|
-
const rank = isTailnet(entry.address) ? 2 : isTunnel(name) ? 1 : 0;
|
|
935
|
+
const rank = isTailnet(entry.address) ? 2 : isTunnel(name) || isOwnNetwork(entry) ? 1 : 0;
|
|
928
936
|
out.set(entry.address, Math.min(rank, out.get(entry.address) ?? rank));
|
|
929
937
|
}
|
|
930
938
|
}
|
|
@@ -939,7 +947,10 @@ function answers(host, port) {
|
|
|
939
947
|
};
|
|
940
948
|
socket.setTimeout(300, () => done(false));
|
|
941
949
|
socket.once("connect", () => done(true));
|
|
942
|
-
socket.once(
|
|
950
|
+
socket.once(
|
|
951
|
+
"error",
|
|
952
|
+
(error) => done(!["ECONNREFUSED", "EADDRNOTAVAIL", "EAFNOSUPPORT", "ENETUNREACH", "EHOSTUNREACH", "EACCES", "EPERM"].includes(error.code ?? ""))
|
|
953
|
+
);
|
|
943
954
|
});
|
|
944
955
|
}
|
|
945
956
|
async function answersAnyPort(address) {
|
|
@@ -985,6 +996,7 @@ function serverOrigin(host, port) {
|
|
|
985
996
|
return `http://${shown2}:${port}`;
|
|
986
997
|
}
|
|
987
998
|
var pagePathOf = (kind, root) => `/${kind}/${createHash("sha256").update(path2.resolve(root)).digest("base64url").slice(0, 22)}/`;
|
|
999
|
+
var pageGone = (dir, owner) => owner ? `This page's files are no longer in ${dir}: the skill was moved, renamed or removed while its server ran. Stop the server and open the artifact again (the agent that opened it can do both).` : "This page's files are gone from the computer that shares it. Ask whoever shared it to open it again.";
|
|
988
1000
|
function sendJson2(response, status, body, headers = {}) {
|
|
989
1001
|
const text = JSON.stringify(body);
|
|
990
1002
|
response.writeHead(status, {
|
|
@@ -4753,7 +4765,8 @@ data: ${JSON.stringify(event)}
|
|
|
4753
4765
|
const info = real === null ? null : await fsp4.lstat(real).catch(() => null);
|
|
4754
4766
|
if (file === null || real !== file || !info?.isFile()) {
|
|
4755
4767
|
if (realDist === null && !invalid && parts[0] === "index.html") {
|
|
4756
|
-
|
|
4768
|
+
stamp2(`the editor's files are gone from ${EDITOR_DIR}: the skill was moved, renamed or removed while this server ran`);
|
|
4769
|
+
response.writeHead(503, { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" }).end(pageGone(EDITOR_DIR, viewer.role === "owner"));
|
|
4757
4770
|
return;
|
|
4758
4771
|
}
|
|
4759
4772
|
response.writeHead(404, { "Cache-Control": "no-store" }).end();
|