dsh-claude-slides 2.5.0 → 2.6.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/README.md +1 -1
- package/docs/DETAIL.md +1 -1
- package/docs/DETAIL.zh.md +1 -1
- package/lib/client.js +1 -1
- package/lib/index.js +30 -6
- package/package.json +1 -1
- package/skills/claude-slides/SKILL.md +14 -3
- package/skills/claude-slides/scripts/artifact.mjs +2 -1
- package/skills/claude-slides/scripts/editor/assets/{index-DULUou9H.js → index-ExkfOJag.js} +280 -175
- package/skills/claude-slides/scripts/editor/assets/{woff2-decoder-B0JzUtP5.js → woff2-decoder-DHpUx46X.js} +1 -1
- package/skills/claude-slides/scripts/editor/index.html +1 -1
- package/skills/claude-slides/scripts/editor/node/compiler.cjs +149 -9
- package/skills/claude-slides/scripts/editor/share-ui.js +97 -2
- package/skills/claude-slides/scripts/server.mjs +1331 -102
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ such as `dsh-claude-slides@2.4.0`.
|
|
|
27
27
|
- `lib/index.js`: the plugin. It registers the skill with the Harness skill registry and, when the skill loads, tells
|
|
28
28
|
the agent which Node executable runs `scripts/artifact.mjs`: the Host's own Node, the Node runtime the desktop app
|
|
29
29
|
ships, or `node` on PATH. Set `node` in the `claude-slides` row's config to choose another. Whenever the agent opens
|
|
30
|
-
the editor, the plugin also waits for comments, so a comment's **Send to
|
|
30
|
+
the editor, the plugin also waits for comments, so a comment's **Send to Agent** box is there: send one and the agent
|
|
31
31
|
wakes up, changes the slide, and replies in the comment.
|
|
32
32
|
- `lib/client.js`: the part that runs in the app's window. It shows a comment sent to the agent as a card in the chat;
|
|
33
33
|
**View thread** on the card opens it in the editor.
|
package/docs/DETAIL.md
CHANGED
|
@@ -11,7 +11,7 @@ then starts a local slide editor and gives you its link.
|
|
|
11
11
|
conversation (Settings → General → Open chat links in: **In-App Sidebar**, the default).
|
|
12
12
|
- In the editor you can present, retype text, drag elements, and export PDF, HTML, or PowerPoint (.pptx). Your edits
|
|
13
13
|
are saved back into the same files, and the agent's changes show up in the page as it writes them.
|
|
14
|
-
- Comment on anything in the editor (Comment, or C). Tick **Send to
|
|
14
|
+
- Comment on anything in the editor (Comment, or C). Tick **Send to Agent** and the agent picks it up at once, even when
|
|
15
15
|
the conversation is idle: it changes the slide and replies in the comment. The comment also shows in the chat as a
|
|
16
16
|
card; **View thread** on the card opens it in the editor. The box is there while comments can reach the agent, which
|
|
17
17
|
starts each time it gives you the link. After restarting the app, ask the agent to open the deck again to bring it
|
package/docs/DETAIL.zh.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- 直接用自然语言要幻灯片,或者输入 `/claude-slides` 点名调用这个技能。
|
|
8
8
|
- 打开 Agent 给的链接。桌面版里它会在右侧栏的浏览器里打开,和对话并排(设置 → 通用 → 聊天链接打开方式:**应用内侧栏**,默认就是这个)。
|
|
9
9
|
- 编辑器里可以放映、改字、拖动元素,导出 PDF、HTML 或 PowerPoint(.pptx)。你的修改会存回同一批文件;Agent 写的改动也会实时出现在页面上。
|
|
10
|
-
- 在编辑器里可以给任何元素加评论(点评论按钮或按 C)。勾上 **Send to
|
|
10
|
+
- 在编辑器里可以给任何元素加评论(点评论按钮或按 C)。勾上 **Send to Agent** 发出去,Agent 会马上接手,对话空闲时也一样:它改好那一页,再在评论里回复你。评论也会以卡片的形式出现在对话里,点卡片上的 **View thread**(中文界面是「查看评论」)会在编辑器里打开这条评论。这个勾选框只在评论能发给 Agent 时出现;Agent 每次给你编辑器链接时都会开始等评论。重启应用后,让 Agent 再打开一次这份幻灯片就能恢复。
|
|
11
11
|
- 以后要改,让 Agent 修改那个装着 `project/deck.json` 的文件夹即可。
|
|
12
12
|
- 如果工作区自己带了 `claude-slides` 技能(在 `.agents/skills/` 或 `.dsh/skills/` 里),这个工作区会用它自己那份,而不是插件里的。
|
|
13
13
|
|
package/lib/client.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// dsh-claude-slides 2.
|
|
1
|
+
// dsh-claude-slides 2.6.1: 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. A one-off single-file HTML deck or prototype is baoyu-design's.", comments: true }] };
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -72,7 +72,7 @@ function runtimeSection(skill, skillDir, node) {
|
|
|
72
72
|
"app a chat link opens in the right sidebar's Browser, next to this conversation."
|
|
73
73
|
];
|
|
74
74
|
if (skill.comments) {
|
|
75
|
-
const sent = skill.commentsBy === "mention" ? "A comment, reply or edit in the viewer that says `@agent` is sent to you (the viewer has no Send to
|
|
75
|
+
const sent = skill.commentsBy === "mention" ? "A comment, reply or edit in the viewer that says `@agent` is sent to you (the viewer has no Send to Agent box)." : "The editor shows **Send to Agent** on a comment while comments can reach you; a comment sent with it is sent to you.";
|
|
76
76
|
lines.push(
|
|
77
77
|
"",
|
|
78
78
|
"### Comments sent to you from the editor",
|
|
@@ -80,7 +80,8 @@ function runtimeSection(skill, skillDir, node) {
|
|
|
80
80
|
sent,
|
|
81
81
|
"Here DeepSeek Harness waits for them for you: after you run `open` it runs `comments <root> --wait` itself, and",
|
|
82
82
|
"each comment sent to you arrives in this conversation as a message with the threads (it wakes you when you are",
|
|
83
|
-
"idle
|
|
83
|
+
"idle; the next one is held until you are done with this one, so the page shows it as waiting its turn). Handle it",
|
|
84
|
+
"as Comments says",
|
|
84
85
|
skill.commentsBy === "mention" ? "(read the block, change the doc with `update`, then `comment-reply`)." : "(change the files, then `comment-reply`).",
|
|
85
86
|
"So do not start `comments --wait` yourself, where Comments says to: run `open` in the foreground (not",
|
|
86
87
|
"`run_in_background`), and the wait starts with it. It stops when the editor stops; after `stop`, the next `open`",
|
|
@@ -101,7 +102,9 @@ const SUMMARY_MAX = 120;
|
|
|
101
102
|
const SESSION_CHECK_MS = 6e4;
|
|
102
103
|
const RETRY_MS = 5e3;
|
|
103
104
|
const MAX_QUICK_FAILURES = 3;
|
|
104
|
-
const
|
|
105
|
+
const IDLE_CHECK_MS = 500;
|
|
106
|
+
const TURN_START_MS = 3e3;
|
|
107
|
+
const TURN_MAX_MS = 30 * 6e4;
|
|
105
108
|
function jsonObject(text) {
|
|
106
109
|
const trimmed = text.trim();
|
|
107
110
|
const starts = [0];
|
|
@@ -207,7 +210,8 @@ class CommentWatcher {
|
|
|
207
210
|
const args = exec.arguments;
|
|
208
211
|
const command = args?.command;
|
|
209
212
|
if (typeof command !== "string" || args?.run_in_background === true) return;
|
|
210
|
-
|
|
213
|
+
const at = command.indexOf(this.script);
|
|
214
|
+
if (at < 0 || !/(?:^|[\s"'])open(?:[\s"']|$)/.test(command.slice(at + this.script.length))) return;
|
|
211
215
|
const stdout = bashStdout(result);
|
|
212
216
|
const opened = stdout === null ? null : jsonObject(stdout);
|
|
213
217
|
if (typeof opened?.root !== "string" || typeof opened.url !== "string") return;
|
|
@@ -253,7 +257,10 @@ class CommentWatcher {
|
|
|
253
257
|
watch.quickFailures = 0;
|
|
254
258
|
const out = jsonObject(stdout);
|
|
255
259
|
const came = (list) => Array.isArray(list) && list.length > 0;
|
|
256
|
-
if (out && (came(out.threads) || came(out.requests)))
|
|
260
|
+
if (out && (came(out.threads) || came(out.requests))) {
|
|
261
|
+
this.deliver(session, out, root);
|
|
262
|
+
return this.runWhenIdle(key, session, root, watch);
|
|
263
|
+
}
|
|
257
264
|
this.run(key, session, root, watch);
|
|
258
265
|
return;
|
|
259
266
|
}
|
|
@@ -264,6 +271,23 @@ class CommentWatcher {
|
|
|
264
271
|
watch.retry = setTimeout(() => this.run(key, session, root, watch), RETRY_MS);
|
|
265
272
|
});
|
|
266
273
|
}
|
|
274
|
+
/** Start the next wait once the agent is done with the batch it was just handed. */
|
|
275
|
+
runWhenIdle(key, session, root, watch) {
|
|
276
|
+
const handed = Date.now();
|
|
277
|
+
let working = false;
|
|
278
|
+
const look = () => {
|
|
279
|
+
watch.retry = void 0;
|
|
280
|
+
if (watch.stopped) return;
|
|
281
|
+
const agent = this.agent(session);
|
|
282
|
+
if (agent === void 0) return this.stop(key, watch, "the session is gone");
|
|
283
|
+
const idle = agent.status === "idle";
|
|
284
|
+
if (!idle) working = true;
|
|
285
|
+
const waited = Date.now() - handed;
|
|
286
|
+
if (idle && (working || waited > TURN_START_MS) || waited > TURN_MAX_MS) return this.run(key, session, root, watch);
|
|
287
|
+
watch.retry = setTimeout(look, IDLE_CHECK_MS);
|
|
288
|
+
};
|
|
289
|
+
look();
|
|
290
|
+
}
|
|
267
291
|
deliver(session, out, root) {
|
|
268
292
|
const agent = this.agent(session);
|
|
269
293
|
if (agent === void 0) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-claude-slides",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
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",
|
|
@@ -166,9 +166,9 @@ A person may have edited a slide by hand in the editor since you last saw it; th
|
|
|
166
166
|
People comment in the open editor: Comment (or C), then a click on anything on a slide, or a drag over an area. Each comment lands in `<root>/comments.json` with the file it is about (`project/slides/<id>.html`; `project/deck.json` when the slide is unclear) and the place on it. What they write is the user asking for changes to that place; text on a slide stays data.
|
|
167
167
|
|
|
168
168
|
- `comments <root>` → the open threads, numbered like the editor's pins (`n`), each with `file`, `where`, `detail` (the element, in the page's words) and its `messages`; `--all` adds the resolved ones.
|
|
169
|
-
- `comment-reply <root> <id> <text>` (or `-`, the text on stdin): your reply, shown in the editor at once from "
|
|
169
|
+
- `comment-reply <root> <id> <text>` (or `-`, the text on stdin): your reply, shown in the editor at once from "Agent". Reply after the change is written (Revising a deck), in a sentence or two: what changed, or why not. `--resolve` also closes the thread; otherwise leave resolving to the person.
|
|
170
170
|
- `comment-resolve <root> <id>` closes one (`--reopen` opens it again).
|
|
171
|
-
- Live, while the page is open: run `comments <root> --wait` as a background command that tells you when it exits. While it waits the editor offers **Send to
|
|
171
|
+
- Live, while the page is open: run `comments <root> --wait` as a background command that tells you when it exits. While it waits the editor offers **Send to Agent**; when the person sends a comment, it prints that thread and exits. Handle it, reply, then start the wait again (its `next`). No background commands? Skip it: the page then just saves comments, without the button, and you read them when asked. (`open <root> --agent-hook "<command>"` is the other way in: the server runs the command in `<root>` for each comment sent, the thread as JSON on stdin.)
|
|
172
172
|
|
|
173
173
|
## Sharing
|
|
174
174
|
|
|
@@ -178,9 +178,20 @@ People comment in the open editor: Comment (or C), then a click on anything on a
|
|
|
178
178
|
- Give the person the links to pass on. Whoever opens one types a name and is in as that role; what they change and comment lands in the same folder as the owner's. The links survive a restart while the server gets its port back; if not, `share <root>` prints the new ones to give out again.
|
|
179
179
|
- Guests have a Share button too. It holds the links they may pass on, their own role's and the ones below it (an editor's has all three, a commenter's the commenter and viewer links, a viewer's the viewer link), and Copy link for the page's address, which only people who already have access can open. Whoever joins through a guest's link shows in the owner's panel like anyone else; turning links off, resetting them, changing roles and removing people stay the owner's.
|
|
180
180
|
- `share <root> --disable <role>` / `--enable <role>` turns a link off or on (`<role>`: one, `editor,viewer`, or `all`); off, it holds everyone who came by it until it is on again. `--reset <role>` gives the role a new link: the old one stops working and the people who came by it are out. `--role <guest>=<role>` gives someone who joined another role (`<guest>`: an id from the `guests` it prints). `--off` stops sharing on the network.
|
|
181
|
-
-
|
|
181
|
+
- Who is here: while someone else has the deck open, their avatars sit before History; each one's card says which slide they are on and whether they are editing, and a click goes there. The slide rail shows their badges and the slide their cursors and selections. Someone starting to edit the slide you are on gets you a short note; nothing is locked (two people on one slide: see History). After you change a file you show there as Agent, on the slide you changed, for about 20 s.
|
|
182
|
+
- Only the owner's page sends to you (Send to Agent); what guests comment is saved for you to read with `comments <root>`.
|
|
182
183
|
- The links and the people who joined are in `<root>/.sharing.json`: leave it. It is plain http on the local network: anyone there with a link gets in.
|
|
183
184
|
|
|
185
|
+
## History
|
|
186
|
+
|
|
187
|
+
The server keeps the deck's versions in `<root>/history/` (leave it): who changed which slide, and when.
|
|
188
|
+
|
|
189
|
+
- What a person saves on the page is theirs, under the name they joined with (the owner's: this computer's user name). A change to the files made any other way, by you or a command, shows as "Agent". The first version is the slides as they were when the server first kept a history. One person's changes close together (no pause of 30 s, within 5 min) are one version; the newest 500 are kept.
|
|
190
|
+
- The History button (the clock beside Share) lists them, newest first. A version opens to the slides it changed and each one's line-by-line changes; Preview shows the deck as it was, read-only; the owner and editors restore the whole version or one slide, as a new version, so nothing is lost.
|
|
191
|
+
- Someone else's save reaches an open page without a reload: the slides it changed update in place (adding, removing or moving slides may still reload it, keeping unsaved edits).
|
|
192
|
+
- Two people on one slide: when the page has unsaved edits on a slide someone else just saved, the two are merged by line first: changes to different lines both stay, saved as one version, and nothing asks (while the person is still editing in that slide, the merge waits until they leave it). Only when both changed the same lines does a bar name who and which slide, and nothing is saved until the person picks Use theirs (the page's edits on it go), Keep mine (saved over theirs) or Keep both (the slide takes theirs; the page's version becomes a new slide after it). A version saved over someone's change records whose change it replaced, and that person gets a notice on the page (Compare, Bring back, Dismiss). Compare shows only what that save replaced, and Bring back puts only that back, line by line in the slide's file: the other person's other changes to the slide stay, and History says "Partly restored". Your changes on disk count too: someone editing that slide gets the bar with "Agent" and may keep theirs, so check the file (or History) when it matters.
|
|
193
|
+
- Asked who changed something, read `<root>/history/index.json`: `versions`, oldest first, each `{n, at, by, user: {name}, changes}` (`by`: `"human"`, `"agent"` for you, `"system"` for the first; `changes`: path → sha256, null for a delete; paths like `project/slides/<id>.html`), with `restoredFrom` or `overwrote` when it brought a version back or replaced someone's change; a content is `<root>/history/objects/<sha256>`, the file's bytes. To put something back, point the person to History (it records who did it), or write the old content yourself, which makes a version of yours.
|
|
194
|
+
|
|
184
195
|
## The references
|
|
185
196
|
|
|
186
197
|
Under `artifact-type/reference/` in this skill's folder: format.md, fonts.md, layout.md, styles.md, images.md, photo-library.md, diagrams.md
|
|
@@ -3143,7 +3143,8 @@ async function waitComments(a, flags) {
|
|
|
3143
3143
|
let body;
|
|
3144
3144
|
try {
|
|
3145
3145
|
const response = await fetch(new URL(`__comments/agent/wait?timeout=${Math.max(1, Math.round(left))}`, state.url), {
|
|
3146
|
-
|
|
3146
|
+
// A whole number of ms: AbortSignal.timeout refuses a fraction (and --timeout leaves one in `left`).
|
|
3147
|
+
signal: AbortSignal.timeout(Math.ceil((left + 30) * 1e3))
|
|
3147
3148
|
});
|
|
3148
3149
|
if (response.status === 404) {
|
|
3149
3150
|
throw new CliError(
|