@office-kit/pptx-dev 0.5.0 → 0.6.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.
- package/README.md +14 -0
- package/dist/cli.mjs +95 -15
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +2 -19
- package/dist/terminal-client.js +15 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -75,6 +75,20 @@ CLI. Navigation while Claude is replying does not change that turn's context.
|
|
|
75
75
|
Stale input is rejected with a retry message. If your Claude settings or managed
|
|
76
76
|
policy disable hooks, automatic context attachment is unavailable; enable the
|
|
77
77
|
preview hook in Claude Code to use it. No project settings file is rewritten.
|
|
78
|
+
|
|
79
|
+
Use **Shift+Enter** to insert a newline in the embedded Claude prompt. On completion,
|
|
80
|
+
the preview checks the actual deck build. Build errors are returned to Claude's
|
|
81
|
+
Stop hook or to the Codex conversation for up to three repair attempts per user
|
|
82
|
+
request. If repair fails, the error stays visible and the last successful preview
|
|
83
|
+
is retained. Stopping the agent cancels further repair attempts.
|
|
84
|
+
|
|
85
|
+
Both agents receive guidance to check text against its actual background, including
|
|
86
|
+
inherited theme colors, and to use native `Text` paragraphs or mapped rows for lists
|
|
87
|
+
and agendas. `Bullets` and `TableOfContents` are not built-in DSL components; define
|
|
88
|
+
or import project helpers if using those names. See the
|
|
89
|
+
[TSX authoring reference](../../skill/references/tsx.md#lists-and-tables-of-contents)
|
|
90
|
+
for a complete agenda example. Contrast guidance still requires visual review;
|
|
91
|
+
a successful build is not a visual accessibility check.
|
|
78
92
|
Dependency paths are source candidates, not an exact slide-to-file mapping.
|
|
79
93
|
“This slide” requests a focused patch; requests about another slide or the
|
|
80
94
|
whole deck can edit other files or shared styling.
|
package/dist/cli.mjs
CHANGED
|
@@ -308,10 +308,16 @@ byId('chat-provider').onchange();
|
|
|
308
308
|
void refreshChat();
|
|
309
309
|
<\/script><script type="module" src="/terminal.js"><\/script></html>`;
|
|
310
310
|
//#endregion
|
|
311
|
+
//#region src/authoring-guidance.ts
|
|
312
|
+
/** Shared guidance for agents editing a deck through the preview. */
|
|
313
|
+
const authoringGuidance = `For lists and tables of contents, use Text paragraphs/bullets or mapped Text rows. Bullets and TableOfContents are not built-in DSL components: inspect installed exports, import existing local helpers or define them. Do not use Raw for these layouts.
|
|
314
|
+
Choose foreground and actual background colors together. Explicitly set readable text colors on slides, cards, tables, chart labels and footnotes; check inherited theme styles. Aim for 4.5:1 contrast for normal text and 3:1 for large text (18pt or 14pt bold). Use a solid backing over images or gradients if needed. Inspect rendered slides; compilation alone cannot verify contrast.`;
|
|
315
|
+
//#endregion
|
|
311
316
|
//#region src/terminal.ts
|
|
312
317
|
const REQUEST_LIMIT = 128e3;
|
|
313
318
|
/** A real interactive Claude session, shared across reloads of its owning tab. */
|
|
314
|
-
function createTerminal(entry, busy = () => false, base = "") {
|
|
319
|
+
function createTerminal(entry, busy = () => false, base = "", verify = async () => null) {
|
|
320
|
+
let repairs = 0;
|
|
315
321
|
let process;
|
|
316
322
|
let owner = "";
|
|
317
323
|
let output = "";
|
|
@@ -352,17 +358,48 @@ function createTerminal(entry, busy = () => false, base = "") {
|
|
|
352
358
|
response.writeHead(code, { "Content-Type": "application/json" });
|
|
353
359
|
response.end(JSON.stringify(value));
|
|
354
360
|
};
|
|
355
|
-
if (request.url === "/terminal/context") {
|
|
361
|
+
if (request.url === "/terminal/context" || request.url === "/terminal/verify") {
|
|
356
362
|
if (request.method !== "POST" || request.headers.authorization !== `Bearer ${token}`) {
|
|
357
363
|
json(403, { error: "Invalid hook credentials" });
|
|
358
364
|
return;
|
|
359
365
|
}
|
|
360
366
|
request.resume();
|
|
367
|
+
if (request.url === "/terminal/verify") {
|
|
368
|
+
if (closing || stopping || !process) {
|
|
369
|
+
json(200, {});
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
let error;
|
|
373
|
+
try {
|
|
374
|
+
error = await verify();
|
|
375
|
+
} catch (cause) {
|
|
376
|
+
error = cause instanceof Error ? cause.message : String(cause);
|
|
377
|
+
}
|
|
378
|
+
if (closing || stopping || !process) {
|
|
379
|
+
json(200, {});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (error && repairs < 3) {
|
|
383
|
+
repairs++;
|
|
384
|
+
status = `Repairing preview (${repairs}/3)…`;
|
|
385
|
+
json(200, {
|
|
386
|
+
decision: "block",
|
|
387
|
+
reason: `The actual preview build failed. Repair the source before finishing (attempt ${repairs}/3). Preserve unrelated edits, inspect the installed DSL exports and local components before changing imports. Do not hide the error or remove requested content. No Raw is needed for lists or an agenda: use Text paragraphs/bullets or mapped Text rows. Build error:\n${error.slice(0, 16e3)}`
|
|
388
|
+
});
|
|
389
|
+
} else {
|
|
390
|
+
status = error ? "Preview still has errors after 3 repair attempts." : "Preview build verified";
|
|
391
|
+
json(200, {});
|
|
392
|
+
}
|
|
393
|
+
emit("state", snapshot());
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
repairs = 0;
|
|
361
397
|
json(200, { hookSpecificOutput: {
|
|
362
398
|
hookEventName: "UserPromptSubmit",
|
|
363
399
|
additionalContext: `You are editing the local PowerPoint TSX project at ${resolve(entry)}.
|
|
364
400
|
Read the project's authoring instructions. Make focused source patches and preserve unrelated work.
|
|
365
|
-
The preview rebuilds on save; do not start another dev server.
|
|
401
|
+
The preview rebuilds on save; do not start another dev server. A Stop hook checks the actual build and returns errors for repair.
|
|
402
|
+
${authoringGuidance}
|
|
366
403
|
The focused slide is context, NOT a restriction. For "this slide", edit only its relevant source. For other slides or deck-wide requests, locate the relevant source or shared theme.
|
|
367
404
|
Slide numbers are 1-based. Verify source before editing; dependency paths are candidates, not an exact slide-to-file mapping.
|
|
368
405
|
Preview context captured with the latest terminal input: ${JSON.stringify(context)}`
|
|
@@ -423,12 +460,20 @@ Preview context captured with the latest terminal input: ${JSON.stringify(contex
|
|
|
423
460
|
}
|
|
424
461
|
const pty = await import("node-pty");
|
|
425
462
|
if (process || closing) throw new Error("Terminal state changed. Try again.");
|
|
426
|
-
const settings = { hooks: {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
463
|
+
const settings = { hooks: {
|
|
464
|
+
Stop: [{ hooks: [{
|
|
465
|
+
type: "http",
|
|
466
|
+
url: `${origin}${base}/terminal/verify`,
|
|
467
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
468
|
+
timeout: 40
|
|
469
|
+
}] }],
|
|
470
|
+
UserPromptSubmit: [{ hooks: [{
|
|
471
|
+
type: "http",
|
|
472
|
+
url: `${origin}${base}/terminal/context`,
|
|
473
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
474
|
+
timeout: 5
|
|
475
|
+
}] }]
|
|
476
|
+
} };
|
|
432
477
|
const env = { ...globalThis.process.env };
|
|
433
478
|
delete env.CLAUDECODE;
|
|
434
479
|
process = pty.spawn("claude", ["--settings", JSON.stringify(settings)], {
|
|
@@ -495,7 +540,7 @@ const markdown = new MarkdownIt({
|
|
|
495
540
|
});
|
|
496
541
|
markdown.renderer.rules.image = (tokens, index) => markdown.utils.escapeHtml(tokens[index]?.content ?? "");
|
|
497
542
|
/** One shared conversation and one editing process per dev server. */
|
|
498
|
-
function createChat(entry, notify, busy = () => false) {
|
|
543
|
+
function createChat(entry, notify, busy = () => false, verify = async () => null) {
|
|
499
544
|
let messages = [];
|
|
500
545
|
let provider = "codex";
|
|
501
546
|
let child;
|
|
@@ -517,6 +562,10 @@ function createChat(entry, notify, busy = () => false) {
|
|
|
517
562
|
stopping = true;
|
|
518
563
|
status = "Stopping…";
|
|
519
564
|
const processToStop = child;
|
|
565
|
+
if (processToStop.exitCode !== null || processToStop.signalCode !== null) {
|
|
566
|
+
notify();
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
520
569
|
const kill = (signal) => {
|
|
521
570
|
try {
|
|
522
571
|
if (process.platform !== "win32" && processToStop.pid) process.kill(-processToStop.pid, signal);
|
|
@@ -527,7 +576,7 @@ function createChat(entry, notify, busy = () => false) {
|
|
|
527
576
|
killTimer = setTimeout(() => kill("SIGKILL"), 2e3);
|
|
528
577
|
notify();
|
|
529
578
|
}
|
|
530
|
-
function start(text, selected, context) {
|
|
579
|
+
function start(text, selected, context, repairs = 0) {
|
|
531
580
|
provider = selected;
|
|
532
581
|
const user = {
|
|
533
582
|
role: "user",
|
|
@@ -541,6 +590,7 @@ function createChat(entry, notify, busy = () => false) {
|
|
|
541
590
|
const prompt = `You are editing a local PowerPoint TSX project in ${dirname(resolve(entry))}.
|
|
542
591
|
Read the project's authoring instructions. Make focused patches to the TSX source, preserving unrelated work.
|
|
543
592
|
The preview rebuilds automatically on save; do not start another dev server.
|
|
593
|
+
${authoringGuidance}
|
|
544
594
|
The attached focus is context, NOT a restriction: use it for references such as "this slide". For other slides or deck-wide requests, find the relevant source or shared theme instead. Do not rewrite the whole deck for a local edit.
|
|
545
595
|
Slide numbers are 1-based and refer to the last successful preview at send time. Source may have changed since then; verify it before editing. Files are dependency candidates, not an exact slide-to-source mapping.
|
|
546
596
|
Respond in the user's language, briefly describing edits or answering their question. If permissions prevent an action, explain that instead of claiming success.
|
|
@@ -616,10 +666,31 @@ Current request and preview context (JSON): ${JSON.stringify(user)}`;
|
|
|
616
666
|
running.on("error", (error) => {
|
|
617
667
|
failure = `Could not run ${selected}. Install it and log in from your terminal first. ${error.message}`;
|
|
618
668
|
});
|
|
619
|
-
running.on("close", (code) => {
|
|
669
|
+
running.on("close", async (code) => {
|
|
620
670
|
if (buffer.trim()) consume(buffer);
|
|
621
671
|
clearTimeout(killTimer);
|
|
622
672
|
status = stopping ? "Stopped. Saved edits remain in the preview." : failure || (code !== 0 ? stderr || `${selected} exited with code ${code}` : !sawResult ? "CLI exited without a completed result." : "Done");
|
|
673
|
+
if (status === "Done") {
|
|
674
|
+
status = "Verifying preview…";
|
|
675
|
+
notify();
|
|
676
|
+
let buildError;
|
|
677
|
+
try {
|
|
678
|
+
buildError = await verify();
|
|
679
|
+
} catch (cause) {
|
|
680
|
+
buildError = cause instanceof Error ? cause.message : String(cause);
|
|
681
|
+
}
|
|
682
|
+
if (!stopping && buildError && repairs < 3) {
|
|
683
|
+
child = void 0;
|
|
684
|
+
start(`The actual preview build failed. Repair the source and preserve unrelated work (attempt ${repairs + 1}/3). Do not remove requested content or hide the error.\n${buildError.slice(0, 16e3)}`, selected, {
|
|
685
|
+
...context,
|
|
686
|
+
buildError
|
|
687
|
+
}, repairs + 1);
|
|
688
|
+
await completion;
|
|
689
|
+
done();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
status = stopping ? "Stopped. Saved edits remain in the preview." : buildError ? "Preview still has errors after 3 repair attempts.\n" + buildError.slice(0, 16e3) : "Done";
|
|
693
|
+
}
|
|
623
694
|
if (status !== "Done") append(status);
|
|
624
695
|
child = void 0;
|
|
625
696
|
notify();
|
|
@@ -718,8 +789,8 @@ async function serveDeck(entry, port = 4173) {
|
|
|
718
789
|
if (!result) {
|
|
719
790
|
const chat = createChat(entry, () => {
|
|
720
791
|
for (const client of clients) client.write("data: chat\n\n");
|
|
721
|
-
}, () => terminal.isRunning());
|
|
722
|
-
const terminal = createTerminal(entry, () => chat.isRunning(), id ? "/agents/" + id : "");
|
|
792
|
+
}, () => terminal.isRunning(), verify);
|
|
793
|
+
const terminal = createTerminal(entry, () => chat.isRunning(), id ? "/agents/" + id : "", verify);
|
|
723
794
|
result = {
|
|
724
795
|
chat,
|
|
725
796
|
terminal,
|
|
@@ -736,7 +807,7 @@ async function serveDeck(entry, port = 4173) {
|
|
|
736
807
|
return;
|
|
737
808
|
}
|
|
738
809
|
response.setHeader("Cache-Control", "no-store");
|
|
739
|
-
const match = request.url?.match(/^\/agents\/([\w-]{1,64})(\/(?:chat(?:\/(?:stop|reset))?|terminal\/(?:start|input|resize|stop|events|context)|close))?$/);
|
|
810
|
+
const match = request.url?.match(/^\/agents\/([\w-]{1,64})(\/(?:chat(?:\/(?:stop|reset))?|terminal\/(?:start|input|resize|stop|events|context|verify)|close))?$/);
|
|
740
811
|
if (request.url?.startsWith("/agents/") && !match) {
|
|
741
812
|
response.writeHead(404).end();
|
|
742
813
|
return;
|
|
@@ -840,6 +911,15 @@ async function serveDeck(entry, port = 4173) {
|
|
|
840
911
|
response.end(page);
|
|
841
912
|
} else response.writeHead(404).end();
|
|
842
913
|
});
|
|
914
|
+
async function verify() {
|
|
915
|
+
rebuild();
|
|
916
|
+
const deadline = Date.now() + 3e4;
|
|
917
|
+
while (!closed && (building || timer || pending)) {
|
|
918
|
+
if (Date.now() >= deadline) return "Preview verification timed out. Inspect the build before claiming success.";
|
|
919
|
+
await new Promise((done) => setTimeout(done, 50));
|
|
920
|
+
}
|
|
921
|
+
return closed ? "Preview server closed." : error;
|
|
922
|
+
}
|
|
843
923
|
async function rebuild() {
|
|
844
924
|
if (closed) return;
|
|
845
925
|
if (building) {
|