@evandu/super-pi 0.1.129 → 0.1.131

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.
Files changed (70) hide show
  1. package/agents/super-pi-documents/index.ts +92 -0
  2. package/agents/super-pi-knowledge/index.ts +32 -1
  3. package/agents/super-pi-task/core.ts +40 -0
  4. package/agents/super-pi-task/index.ts +15 -1
  5. package/agents/super-pi-web/index.ts +32 -0
  6. package/assets/fonts/LICENSE +92 -0
  7. package/assets/fonts/NotoSansSC-Bold.otf +0 -0
  8. package/assets/fonts/NotoSansSC-Regular.otf +0 -0
  9. package/dist/.super-pi-check-inputs.json +2 -2
  10. package/dist/client/.super-pi-client-sources.json +5 -5
  11. package/dist/client/assets/{CodeMirrorEditor-BNVBZZSS.js → CodeMirrorEditor-BlQhH3vs.js} +2 -2
  12. package/dist/client/assets/{dist-DmAOqr0M.js → dist-B1_1Ui9E.js} +1 -1
  13. package/dist/client/assets/{dist-GkIV1LTX.js → dist-B448EWTM.js} +1 -1
  14. package/dist/client/assets/{dist-CiSGSVBZ.js → dist-B68Q8uXX.js} +1 -1
  15. package/dist/client/assets/{dist-DB5M5fZG.js → dist-BFlq-kiF.js} +1 -1
  16. package/dist/client/assets/{dist-BykVFExd.js → dist-BXVFgriA.js} +1 -1
  17. package/dist/client/assets/{dist-CZ9I2JCd.js → dist-BcE6tJvc.js} +1 -1
  18. package/dist/client/assets/{dist-BhJGK8-b.js → dist-ByIqsb5-.js} +1 -1
  19. package/dist/client/assets/{dist-DZnUERoW.js → dist-C6qSkkeN.js} +1 -1
  20. package/dist/client/assets/{dist-BKp4AWEJ.js → dist-C9AkgNbt.js} +1 -1
  21. package/dist/client/assets/{dist-CRzRB9k3.js → dist-COp25nhe.js} +1 -1
  22. package/dist/client/assets/{dist-CMnvChDX.js → dist-CpuexUlh.js} +1 -1
  23. package/dist/client/assets/{dist-ChszWGDv.js → dist-CtdlYgGc.js} +1 -1
  24. package/dist/client/assets/{dist-Dq4-QFAJ.js → dist-Cybsoapc.js} +1 -1
  25. package/dist/client/assets/{dist-Ur37340K.js → dist-D6PoSxR5.js} +1 -1
  26. package/dist/client/assets/{dist-C90MPTnk.js → dist-D7bj81KQ.js} +1 -1
  27. package/dist/client/assets/{dist-BBW2SE_R.js → dist-DP2ygAFF.js} +1 -1
  28. package/dist/client/assets/{dist-B61RSa-K.js → dist-DXNDtyHP.js} +1 -1
  29. package/dist/client/assets/{dist-CWYRjnj-.js → dist-DxoWS5MH.js} +1 -1
  30. package/dist/client/assets/{dist-Cxerc7Bb.js → dist-Fhkmvjdx.js} +1 -1
  31. package/dist/client/assets/{dist-BDygQgZD.js → dist-hfpd-aWa.js} +1 -1
  32. package/dist/client/assets/{dist-B2nPVdKh.js → dist-lQWWWxh9.js} +1 -1
  33. package/dist/client/assets/{dist-HdeIBl2J.js → dist-r5Af94tH.js} +1 -1
  34. package/dist/client/assets/{index-D_c-CYAr.js → index-BApJ3Zbq.js} +3 -3
  35. package/dist/client/index.html +1 -1
  36. package/dist/server/active-memory.d.ts +12 -0
  37. package/dist/server/active-memory.js +25 -1
  38. package/dist/server/application/conversation-service.js +5 -0
  39. package/dist/server/application/document-service.d.ts +68 -0
  40. package/dist/server/application/document-service.js +120 -0
  41. package/dist/server/application/run-service.js +49 -7
  42. package/dist/server/documents.d.ts +152 -0
  43. package/dist/server/documents.js +998 -0
  44. package/dist/server/file-changes.js +6 -4
  45. package/dist/server/files.d.ts +32 -0
  46. package/dist/server/files.js +28 -0
  47. package/dist/server/http-server.js +113 -2
  48. package/dist/server/index.js +13 -1
  49. package/dist/server/inline-thinking.d.ts +90 -0
  50. package/dist/server/inline-thinking.js +181 -0
  51. package/dist/server/knowledge.d.ts +24 -14
  52. package/dist/server/knowledge.js +47 -32
  53. package/dist/server/pi-session-pool.js +2 -1
  54. package/dist/server/pi-session.d.ts +20 -6
  55. package/dist/server/pi-session.js +43 -11
  56. package/dist/server/presentations/schema.d.ts +120 -0
  57. package/dist/server/presentations/schema.js +128 -0
  58. package/dist/server/runtime.d.ts +46 -0
  59. package/dist/server/runtime.js +150 -16
  60. package/dist/server/server-context.d.ts +3 -0
  61. package/dist/server/subagents.js +33 -6
  62. package/dist/server/web.d.ts +24 -0
  63. package/dist/server/web.js +180 -0
  64. package/package.json +33 -5
  65. package/skills/presentation/SKILL.md +30 -0
  66. package/skills/presentation/references/data.md +10 -0
  67. package/skills/presentation/references/quality.md +12 -0
  68. package/skills/presentation/references/story.md +12 -0
  69. package/skills/presentation/references/templates-and-editing.md +11 -0
  70. package/skills/presentation/references/visual-system.md +10 -0
@@ -0,0 +1,180 @@
1
+ import { isIP } from "node:net";
2
+ import { lookup } from "node:dns/promises";
3
+ import { request as httpRequest } from "node:http";
4
+ import { request as httpsRequest } from "node:https";
5
+ import { brotliDecompressSync, gunzipSync, inflateSync } from "node:zlib";
6
+ export const WEB_SEARCH_TOOL_NAME = "web_search";
7
+ export const WEB_FETCH_TOOL_NAME = "web_fetch";
8
+ export const MAX_WEB_REQUEST_BYTES = 64 * 1024;
9
+ const SEARCH_LIMIT = 10;
10
+ const RESPONSE_LIMIT = 2 * 1024 * 1024;
11
+ const TEXT_LIMIT = 30_000;
12
+ const REDIRECT_LIMIT = 5;
13
+ const TIMEOUT_MS = 15_000;
14
+ const USER_AGENT = "Mozilla/5.0 (compatible; SuperPi/1.0; +https://www.npmjs.com/package/@evandu/super-pi)";
15
+ function decodeEntities(value) {
16
+ const named = { amp: "&", quot: '"', apos: "'", lt: "<", gt: ">", nbsp: " " };
17
+ return value.replace(/&(#x[\da-f]+|#\d+|[a-z]+);/gi, (_all, entity) => {
18
+ if (entity[0] === "#") {
19
+ const code = entity[1]?.toLowerCase() === "x" ? Number.parseInt(entity.slice(2), 16) : Number.parseInt(entity.slice(1), 10);
20
+ return Number.isFinite(code) ? String.fromCodePoint(code) : "";
21
+ }
22
+ return named[entity.toLowerCase()] ?? `&${entity};`;
23
+ });
24
+ }
25
+ function plainText(html) {
26
+ return decodeEntities(html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " "))
27
+ .replace(/\s+/g, " ").trim();
28
+ }
29
+ /** Parse Bing's ordinary result page. Kept separate so captured fixtures can guard markup changes. */
30
+ export function parseBingResults(html, limit = 5) {
31
+ const results = [];
32
+ const blocks = html.match(/<li\b[^>]*class=["'][^"']*\bb_algo\b[^"']*["'][^>]*>[\s\S]*?<\/li>/gi) ?? [];
33
+ for (const block of blocks) {
34
+ const anchor = /<h2\b[^>]*>[\s\S]*?<a\b[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/i.exec(block);
35
+ if (!anchor)
36
+ continue;
37
+ let url;
38
+ try {
39
+ url = new URL(decodeEntities(anchor[1]));
40
+ }
41
+ catch {
42
+ continue;
43
+ }
44
+ if (!/^https?:$/.test(url.protocol))
45
+ continue;
46
+ const caption = /<p\b[^>]*>([\s\S]*?)<\/p>/i.exec(block)?.[1] ?? "";
47
+ results.push({ title: plainText(anchor[2]), url: url.href, snippet: plainText(caption) });
48
+ if (results.length >= Math.min(SEARCH_LIMIT, Math.max(1, limit)))
49
+ break;
50
+ }
51
+ return results;
52
+ }
53
+ function blockedAddress(address) {
54
+ if (address === "::" || address === "::1")
55
+ return true;
56
+ const v4 = address.startsWith("::ffff:") ? address.slice(7) : address;
57
+ if (isIP(v4) === 4) {
58
+ const [a, b] = v4.split(".").map(Number);
59
+ return a === 0 || a === 10 || a === 127 || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || a >= 224;
60
+ }
61
+ const normalized = address.toLowerCase();
62
+ return normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb") || normalized.startsWith("ff");
63
+ }
64
+ async function publicAddress(hostname) {
65
+ if (hostname.toLowerCase() === "localhost" || hostname.endsWith(".localhost"))
66
+ throw new Error("不允许访问本机或私网地址");
67
+ const records = isIP(hostname) ? [{ address: hostname, family: isIP(hostname) }] : await lookup(hostname, { all: true, verbatim: true });
68
+ const record = records.find(item => !blockedAddress(item.address));
69
+ if (!record || records.some(item => blockedAddress(item.address)))
70
+ throw new Error("不允许访问本机或私网地址");
71
+ return record;
72
+ }
73
+ async function download(input, signal, redirects = 0) {
74
+ const url = new URL(input);
75
+ if (!/^https?:$/.test(url.protocol) || url.username || url.password)
76
+ throw new Error("只允许无凭据的 HTTP/HTTPS URL");
77
+ if (redirects > REDIRECT_LIMIT)
78
+ throw new Error("网页重定向次数过多");
79
+ const resolved = await publicAddress(url.hostname);
80
+ const requester = url.protocol === "https:" ? httpsRequest : httpRequest;
81
+ // Node may ask a custom lookup for one address or an array (`all: true`). Honor both overloads;
82
+ // returning the wrong shape makes net.connect report the misleading "Invalid IP address: undefined".
83
+ const pinnedLookup = ((_hostname, options, callback) => {
84
+ if (options?.all)
85
+ callback(null, [resolved]);
86
+ else
87
+ callback(null, resolved.address, resolved.family);
88
+ });
89
+ return new Promise((resolve, reject) => {
90
+ const req = requester(url, {
91
+ headers: { "user-agent": USER_AGENT, accept: "text/html,text/plain,application/xhtml+xml", "accept-encoding": "gzip, deflate, br" },
92
+ lookup: pinnedLookup,
93
+ servername: url.hostname,
94
+ timeout: TIMEOUT_MS,
95
+ signal,
96
+ }, response => {
97
+ const status = response.statusCode ?? 0;
98
+ const location = response.headers.location;
99
+ if (status >= 300 && status < 400 && location) {
100
+ response.resume();
101
+ download(new URL(location, url).href, signal, redirects + 1).then(resolve, reject);
102
+ return;
103
+ }
104
+ if (status < 200 || status >= 300) {
105
+ response.resume();
106
+ reject(new Error(`网页请求失败(HTTP ${status})`));
107
+ return;
108
+ }
109
+ const chunks = [];
110
+ let bytes = 0;
111
+ response.on("data", chunk => {
112
+ bytes += chunk.length;
113
+ if (bytes > RESPONSE_LIMIT) {
114
+ req.destroy(new Error("网页响应超过 2 MB 限制"));
115
+ return;
116
+ }
117
+ chunks.push(chunk);
118
+ });
119
+ response.on("end", () => {
120
+ try {
121
+ let body = Buffer.concat(chunks);
122
+ const encoding = String(response.headers["content-encoding"] ?? "").toLowerCase();
123
+ if (encoding === "gzip")
124
+ body = gunzipSync(body);
125
+ else if (encoding === "deflate")
126
+ body = inflateSync(body);
127
+ else if (encoding === "br")
128
+ body = brotliDecompressSync(body);
129
+ resolve({ url: url.href, body, contentType: String(response.headers["content-type"] ?? "application/octet-stream") });
130
+ }
131
+ catch (error) {
132
+ reject(error);
133
+ }
134
+ });
135
+ });
136
+ req.on("timeout", () => req.destroy(new Error("网页请求超时")));
137
+ req.on("error", reject);
138
+ req.end();
139
+ });
140
+ }
141
+ function extractHtml(html) {
142
+ const title = plainText(/<title\b[^>]*>([\s\S]*?)<\/title>/i.exec(html)?.[1] ?? "");
143
+ let body = /<article\b[^>]*>([\s\S]*?)<\/article>/i.exec(html)?.[1]
144
+ ?? /<main\b[^>]*>([\s\S]*?)<\/main>/i.exec(html)?.[1]
145
+ ?? /<body\b[^>]*>([\s\S]*?)<\/body>/i.exec(html)?.[1]
146
+ ?? html;
147
+ body = body.replace(/<(nav|aside|footer|form|noscript|svg)\b[^>]*>[\s\S]*?<\/\1>/gi, " ")
148
+ .replace(/<(br|p|div|li|h[1-6]|tr)\b[^>]*>/gi, "\n")
149
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ")
150
+ .replace(/<[^>]+>/g, " ");
151
+ const text = decodeEntities(body).replace(/[ \t]+/g, " ").replace(/\n\s*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
152
+ return { title, text };
153
+ }
154
+ export class WebService {
155
+ async search(query, limit = 5, signal) {
156
+ const clean = query.trim();
157
+ if (!clean)
158
+ throw new Error("搜索词不能为空");
159
+ if (clean.length > 500)
160
+ throw new Error("搜索词不能超过 500 个字符");
161
+ const url = new URL("https://www.bing.com/search");
162
+ url.searchParams.set("q", clean);
163
+ url.searchParams.set("count", String(Math.min(SEARCH_LIMIT, Math.max(1, Math.floor(limit)))));
164
+ url.searchParams.set("setlang", "zh-Hans");
165
+ const response = await download(url.href, signal);
166
+ const results = parseBingResults(response.body.toString("utf8"), limit);
167
+ if (!results.length)
168
+ throw new Error("搜索引擎没有返回可解析的结果,可能暂时限流或页面格式已变化");
169
+ return { query: clean, results };
170
+ }
171
+ async fetch(url, signal) {
172
+ const response = await download(url, signal);
173
+ if (!/^(text\/html|application\/xhtml\+xml|text\/plain)\b/i.test(response.contentType))
174
+ throw new Error(`不支持的网页类型:${response.contentType}`);
175
+ const decoded = response.body.toString("utf8");
176
+ const extracted = /^text\/plain\b/i.test(response.contentType) ? { title: "", text: decoded.trim() } : extractHtml(decoded);
177
+ const truncated = extracted.text.length > TEXT_LIMIT;
178
+ return { title: extracted.title, url: response.url, text: extracted.text.slice(0, TEXT_LIMIT), truncated, contentType: response.contentType };
179
+ }
180
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.129",
3
+ "version": "0.1.131",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,12 +16,14 @@
16
16
  "files": [
17
17
  "dist",
18
18
  "agents",
19
+ "skills",
19
20
  "bin",
20
21
  "scripts/restart-server.mjs",
21
22
  "README.md",
22
23
  "assets/knowledge-model",
23
24
  "scripts/patch-brace-expansion.mjs",
24
- "scripts/diagnose.mjs"
25
+ "scripts/diagnose.mjs",
26
+ "assets/fonts"
25
27
  ],
26
28
  "engines": {
27
29
  "node": ">=22.19.0"
@@ -42,7 +44,11 @@
42
44
  "check:logic": [
43
45
  "scripts/stream-checkpoint.test.mjs",
44
46
  "scripts/stream-transport.test.mjs",
47
+ "scripts/inline-thinking.test.mjs",
48
+ "scripts/inline-thinking-close-tag.test.mjs",
45
49
  "scripts/pi-session.test.mjs",
50
+ "scripts/pi-session-isolation.test.mjs",
51
+ "scripts/installed-layout.test.mjs",
46
52
  "scripts/pi-model-switch.test.mjs",
47
53
  "scripts/pi-thinking-levels.test.mjs",
48
54
  "scripts/diagnose-cli.test.mjs",
@@ -79,6 +85,10 @@
79
85
  "scripts/queue-store.test.mjs",
80
86
  "scripts/followup.test.mjs",
81
87
  "scripts/pi-followup-queue.test.mjs",
88
+ "scripts/background-result-delivery.test.mjs",
89
+ "scripts/question-stop.test.mjs",
90
+ "scripts/background-result-body.test.mjs",
91
+ "scripts/question-request-size.test.mjs",
82
92
  "scripts/md-render.test.mjs",
83
93
  "scripts/revert-session-format.test.mjs",
84
94
  "scripts/revert-session-file.test.mjs",
@@ -122,10 +132,17 @@
122
132
  "scripts/cli.test.mjs",
123
133
  "scripts/check-runner.test.mjs",
124
134
  "scripts/skills-discovery.test.mjs",
135
+ "scripts/built-in-skills.test.mjs",
136
+ "scripts/presentation-schema.test.mjs",
125
137
  "scripts/session-state-icon.test.mjs",
126
138
  "scripts/provider-fetch-models.test.mjs",
127
139
  "scripts/shared-chrome.test.mjs",
128
- "scripts/prompt-templates.test.mjs"
140
+ "scripts/prompt-templates.test.mjs",
141
+ "scripts/documents.test.mjs",
142
+ "scripts/web-tools.test.mjs",
143
+ "scripts/document-service.test.mjs",
144
+ "scripts/document-block-parse.test.mjs",
145
+ "scripts/document-write-overwrite.test.mjs"
129
146
  ],
130
147
  "check:browser": [
131
148
  "scripts/interrupt-browser.test.mjs",
@@ -175,7 +192,8 @@
175
192
  "scripts/knowledge-base-browser.test.mjs",
176
193
  "scripts/terminal-browser.test.mjs",
177
194
  "scripts/ux-polish-browser.test.mjs",
178
- "scripts/prompt-templates-browser.test.mjs"
195
+ "scripts/prompt-templates-browser.test.mjs",
196
+ "scripts/documents-browser.test.mjs"
179
197
  ],
180
198
  "dependencies": {
181
199
  "@codemirror/commands": "^6.11.1",
@@ -210,12 +228,14 @@
210
228
  },
211
229
  "devDependencies": {
212
230
  "@types/node": "latest",
231
+ "@types/pdfkit": "^0.17.7",
213
232
  "@types/react": "latest",
214
233
  "@types/react-dom": "latest",
215
234
  "@types/ws": "latest",
216
235
  "@vitejs/plugin-react": "latest",
217
236
  "concurrently": "latest",
218
237
  "cross-env": "latest",
238
+ "pdf-lib": "^1.17.1",
219
239
  "react": "latest",
220
240
  "react-dom": "latest",
221
241
  "tsx": "latest",
@@ -223,6 +243,14 @@
223
243
  "vite": "latest"
224
244
  },
225
245
  "optionalDependencies": {
226
- "@huggingface/transformers": "^4.3.1"
246
+ "@huggingface/transformers": "^4.3.1",
247
+ "cfb": "^1.2.2",
248
+ "docx": "^9.14.0",
249
+ "exceljs": "^4.4.0",
250
+ "fflate": "^0.8.3",
251
+ "mammoth": "^1.13.0",
252
+ "pdfjs-dist": "^6.4.299",
253
+ "pdfkit": "^0.20.2",
254
+ "pptxgenjs": "^4.0.1"
227
255
  }
228
256
  }
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: presentation
3
+ description: Create, revise, or review PowerPoint presentations when the user needs a coherent story, deliberate visual design, charts or imagery, template fidelity, and a validated PPTX deliverable.
4
+ ---
5
+
6
+ # Presentation production
7
+
8
+ Treat a deck as a communication artifact, not a document split across slides. Preserve the user's audience, purpose, evidence, brand constraints, language, and requested file path.
9
+
10
+ ## Workflow
11
+
12
+ 1. Inspect supplied source material and any existing deck or template before proposing a structure.
13
+ 2. Establish the audience, decision or learning outcome, presentation setting, and approximate duration. Infer ordinary defaults when they do not materially change the result; ask only for consequential missing choices.
14
+ 3. Write a slide-level story before building. Each slide needs one job and a conclusion-oriented title.
15
+ 4. Choose a small visual system and a layout appropriate to each slide's job. Do not make every slide a title plus bullets.
16
+ 5. Build with the presentation tools available in the session. Never claim that an image, chart, template, or visual validation was applied if the available tools cannot perform it.
17
+ 6. Render or inspect the finished deck when the tools support it. Fix overflow, collisions, illegible text, weak hierarchy, misleading charts, and visual repetition before delivery.
18
+ 7. Report the output path, what was validated, and any limitation that remains observable to the user.
19
+
20
+ ## Progressive references
21
+
22
+ - Read [references/story.md](references/story.md) when developing or restructuring the narrative.
23
+ - Read [references/visual-system.md](references/visual-system.md) when choosing layouts, typography, colour, imagery, or information hierarchy.
24
+ - Read [references/data.md](references/data.md) for tables, quantitative claims, and chart selection.
25
+ - Read [references/templates-and-editing.md](references/templates-and-editing.md) when a template or existing deck must be preserved or edited.
26
+ - Read [references/quality.md](references/quality.md) before final validation and delivery.
27
+
28
+ ## Current tool boundary
29
+
30
+ The legacy `document_write` PPT path accepts Markdown and produces a clean text-first deck. It cannot faithfully implement positioned elements, images, native charts, templates, object-level edits, or visual rendering. When those capabilities are absent, produce the strongest honest text-first deck possible and state the limitation; do not simulate visual completion in prose.
@@ -0,0 +1,10 @@
1
+ # Data, tables, and charts
2
+
3
+ Use quantitative visuals to answer a question, not to decorate a slide.
4
+
5
+ - Bar: compare categories. Line: change over ordered time. Scatter: relationship or distribution. Waterfall: contributions to a change. Pie or donut: only a few parts of one whole when precise comparison is unimportant.
6
+ - Start axes honestly, disclose truncation, preserve units, and name the time period and source.
7
+ - Highlight the series or value that supports the slide's takeaway; mute supporting context.
8
+ - Direct-label when practical. Remove chart furniture that does not help interpretation.
9
+ - Use a table when exact lookup matters; use a chart when pattern or comparison matters.
10
+ - Reconcile values against the source before building and again after rendering.
@@ -0,0 +1,12 @@
1
+ # Presentation quality gate
2
+
3
+ Validate the artifact, not just the source instructions.
4
+
5
+ 1. File integrity: opens without repair, has the requested aspect ratio and slide count, and contains no missing relationships.
6
+ 2. Content: claims match sources; titles communicate takeaways; spelling, units, dates, and names are correct.
7
+ 3. Geometry: no object is outside the canvas; no accidental overlap, clipping, or text overflow; margins and alignment are consistent.
8
+ 4. Readability: type remains legible at presentation scale, contrast is adequate, and charts have readable labels.
9
+ 5. Visual rhythm: layouts vary with the story while retaining one system; no page is needlessly dense or empty.
10
+ 6. Delivery: render every slide when possible, inspect a contact sheet and full-size pages, repair defects, then rerender the affected pages.
11
+
12
+ Report exactly which checks ran. A PPTX that merely reopens and yields its text has passed structural round-trip testing, not visual QA.
@@ -0,0 +1,12 @@
1
+ # Story and slide architecture
2
+
3
+ Start from the outcome: what should the audience understand, believe, decide, or do afterward? Arrange evidence so the answer becomes inevitable rather than merely chronological.
4
+
5
+ - Give every slide one communicative job and a title that states its takeaway.
6
+ - Prefer a visible arc: context or tension, evidence, implication, recommendation, next action.
7
+ - Separate sections when the audience must reset its mental model.
8
+ - Remove slides that neither change understanding nor support a decision.
9
+ - Put detail needed only for questions in an appendix.
10
+ - For a timed talk, budget roughly one substantive slide per one to two minutes, then adjust for demos and discussion.
11
+
12
+ Before building, record for each slide: purpose, takeaway title, evidence, intended visual form, and source.
@@ -0,0 +1,11 @@
1
+ # Templates and existing decks
2
+
3
+ Inspect before editing. Preserve the existing slide size, theme, masters, layouts, placeholders, brand assets, and object relationships unless the user asks for a redesign.
4
+
5
+ - Prefer filling real placeholders over painting look-alike text boxes on top.
6
+ - Duplicate an appropriate existing layout when adding a slide.
7
+ - Make object-level changes for local requests; do not rebuild the whole deck and lose unsupported content.
8
+ - Preserve charts, notes, links, media, accessibility metadata, and animations that are outside the requested change.
9
+ - Require a fresh version token before overwriting an existing file.
10
+
11
+ If the available tools expose only extracted text, state that template-faithful editing is unavailable instead of pretending the regenerated file preserves it.
@@ -0,0 +1,10 @@
1
+ # Visual system and layout
2
+
3
+ Choose a restrained system and apply it consistently: aspect ratio, typefaces, title/body scale, margins, grid, background, primary text, muted text, accent, and semantic status colours.
4
+
5
+ - Use hierarchy, whitespace, alignment, and scale before decoration.
6
+ - Select layouts by content relationship: comparison, sequence, hierarchy, spatial relationship, evidence plus interpretation, or a single focal statement.
7
+ - Prefer diagrams, images, charts, and short labels over dense prose when they communicate the same idea more directly.
8
+ - Avoid repeated card grids, decorative gradients, arbitrary icons, excessive rounded rectangles, and tiny captions that create an automatic “AI deck” look.
9
+ - Keep body copy comfortably readable in the actual presentation setting. Split content instead of shrinking it to fit.
10
+ - Treat accessibility as a design constraint: sufficient contrast, colour-independent meaning, readable labels, and sensible reading order.