@office-kit/pptx-editor 0.1.1 → 0.2.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.
@@ -0,0 +1,232 @@
1
+ import { a as generatedTools, i as shapeSummary, n as runTool, r as schemaErrors } from "./agent-tools-EU7Eb-FV.js";
2
+ import * as pptx from "@office-kit/pptx";
3
+ //#region src/core/agent-edits.ts
4
+ const MAX_EDITS = 50;
5
+ const generated = new Map(generatedTools.tools.map((tool) => [tool.name, tool]));
6
+ function generatedTool(name) {
7
+ const tool = generated.get(name);
8
+ if (!tool) throw new Error(`The editor has no tool ${name}.`);
9
+ return tool;
10
+ }
11
+ const propertiesOf = (schema) => schema.properties ?? {};
12
+ /** `schema` (an object schema) with only the properties `keep` names. */
13
+ function pick(schema, keep) {
14
+ const properties = propertiesOf(schema);
15
+ const narrowed = {
16
+ ...schema,
17
+ properties: Object.fromEntries(keep.map((key) => [key, properties[key]]))
18
+ };
19
+ delete narrowed.$defs;
20
+ if (Array.isArray(schema.required)) narrowed.required = schema.required.filter((key) => keep.includes(key));
21
+ return narrowed;
22
+ }
23
+ const inputOf = (name, keep) => pick(generatedTool(name).input_schema, keep);
24
+ const PRESERVE_FORMATTING = { options: { preserveFormatting: true } };
25
+ const OPERATIONS = {
26
+ setShapeText: {
27
+ target: "shape",
28
+ input: inputOf("setShapeText", ["shape", "value"]),
29
+ description: "Replaces a shape's whole text (`\\n` between paragraphs) and keeps the formatting of the text before and after what changed. To change two separate places in one shape, apply it twice, each time with the whole text changed in one place: a change in between loses the formatting of the text between them. `{ case }` changes letter case instead.",
30
+ fixed: PRESERVE_FORMATTING
31
+ },
32
+ setTableCellText: {
33
+ target: "cell",
34
+ input: inputOf("setTableCellText", ["cell", "text"]),
35
+ description: "Replaces a table cell's whole text (`\\n` between paragraphs) and keeps the formatting of the text before and after what changed. To change two separate places in one cell, apply it twice, each time with the whole text changed in one place. `{ case }` changes letter case instead.",
36
+ fixed: PRESERVE_FORMATTING
37
+ },
38
+ setChartData: {
39
+ target: "chart",
40
+ input: inputOf("setChartData", ["chart", "data"]),
41
+ description: generatedTool("setChartData").description
42
+ },
43
+ setShapeTextFormat: {
44
+ target: "shape",
45
+ input: (() => {
46
+ const input = inputOf("setShapeTextFormat", ["shape", "format"]);
47
+ const format = propertiesOf(input).format;
48
+ return {
49
+ ...input,
50
+ properties: {
51
+ ...propertiesOf(input),
52
+ format: pick(format, [
53
+ "size",
54
+ "bold",
55
+ "italic",
56
+ "color"
57
+ ])
58
+ }
59
+ };
60
+ })(),
61
+ description: "Sets the font size (points), bold, italic or color of all the text in a shape. Properties left out stay as they are."
62
+ },
63
+ setShapeFill: {
64
+ target: "shape",
65
+ input: inputOf("setShapeFill", ["shape", "color"]),
66
+ description: generatedTool("setShapeFill").description
67
+ },
68
+ setShapeBounds: {
69
+ target: "shape",
70
+ input: inputOf("setShapeBounds", ["shape", "bounds"]),
71
+ description: "Moves and resizes a shape: `bounds` is its position and size in EMU."
72
+ },
73
+ removeShape: {
74
+ target: "shape",
75
+ input: inputOf("removeShape", ["shape"]),
76
+ description: generatedTool("removeShape").description
77
+ },
78
+ moveSlide: {
79
+ target: "slide",
80
+ input: (() => {
81
+ const input = inputOf("moveSlide", ["slide", "toIndex"]);
82
+ return {
83
+ ...input,
84
+ properties: {
85
+ ...propertiesOf(input),
86
+ toIndex: {
87
+ type: "integer",
88
+ minimum: 0
89
+ }
90
+ }
91
+ };
92
+ })(),
93
+ description: "Moves a slide to the 0-based position `toIndex`."
94
+ },
95
+ removeSlide: {
96
+ target: "slide",
97
+ input: inputOf("removeSlide", ["slide"]),
98
+ description: generatedTool("removeSlide").description
99
+ }
100
+ };
101
+ function collectDefs() {
102
+ const available = {};
103
+ for (const name of Object.keys(OPERATIONS)) Object.assign(available, generatedTool(name).input_schema.$defs);
104
+ const defs = {};
105
+ const visit = (schema) => {
106
+ for (const [, ref] of JSON.stringify(schema).matchAll(/"#\/\$defs\/([A-Za-z0-9]+)"/g)) {
107
+ if (ref in defs) continue;
108
+ defs[ref] = available[ref];
109
+ visit(defs[ref]);
110
+ }
111
+ };
112
+ for (const operation of Object.values(OPERATIONS)) visit(operation.input);
113
+ return Object.fromEntries(Object.entries(defs).sort(([a], [b]) => a.localeCompare(b)));
114
+ }
115
+ /**
116
+ * The JSON Schema (draft 2020-12) of a batch of edits: `{ edits: [{ tool,
117
+ * input }, …] }`, applied in order. Give it to a model as one tool's input.
118
+ */
119
+ const agentEditSchema = {
120
+ type: "object",
121
+ properties: { edits: {
122
+ type: "array",
123
+ minItems: 1,
124
+ maxItems: MAX_EDITS,
125
+ items: { anyOf: Object.entries(OPERATIONS).map(([name, { input, description }]) => ({
126
+ type: "object",
127
+ description,
128
+ properties: {
129
+ tool: {
130
+ type: "string",
131
+ const: name
132
+ },
133
+ input
134
+ },
135
+ required: ["tool", "input"],
136
+ additionalProperties: false
137
+ })) }
138
+ } },
139
+ required: ["edits"],
140
+ additionalProperties: false,
141
+ $defs: collectDefs()
142
+ };
143
+ /** Why a batch of edits could not be applied. */
144
+ var AgentEditError = class extends Error {
145
+ /** The failing edit's position in the batch; `null` when the batch did not match the schema. */
146
+ index;
147
+ constructor(index, message, options) {
148
+ super(message, options);
149
+ this.name = "AgentEditError";
150
+ this.index = index;
151
+ }
152
+ };
153
+ const decoded = Symbol("decoded");
154
+ /**
155
+ * Checks a model's `edits` (the `edits` of `agentEditSchema`) without
156
+ * touching a presentation. Throws an `AgentEditError` with `index: null`
157
+ * that lists every problem by its JSON Pointer into `agentEditSchema`'s
158
+ * input (`/edits/0/input/…`). `touchedSlides` are the
159
+ * part names of the slides the edits change, in order, each once.
160
+ */
161
+ function decodeAgentEdits(edits) {
162
+ const errors = schemaErrors(agentEditSchema, { edits });
163
+ if (errors.length > 0) throw new AgentEditError(null, `Invalid edits:\n${errors.map((error) => `- ${error}`).join("\n")}`);
164
+ const touched = /* @__PURE__ */ new Set();
165
+ const out = edits.map(({ tool, input }) => {
166
+ const operation = OPERATIONS[tool];
167
+ const target = input[operation.target];
168
+ touched.add(typeof target === "string" ? target : target.slide);
169
+ return {
170
+ tool,
171
+ input: {
172
+ ...input,
173
+ ...operation.fixed
174
+ }
175
+ };
176
+ });
177
+ return {
178
+ edits: Object.assign(out, { [decoded]: true }),
179
+ touchedSlides: [...touched]
180
+ };
181
+ }
182
+ /**
183
+ * Applies `edits` to `presentation` in order, each reading references
184
+ * against the presentation as the edits before it left it, and resolves
185
+ * with each edit's result. When edit `i` fails it rejects with an
186
+ * `AgentEditError` of `index: i`; the edits before it have been applied,
187
+ * so discard the presentation.
188
+ */
189
+ async function applyAgentEdits(presentation, edits) {
190
+ const host = {
191
+ presentation: () => presentation,
192
+ apply: async (_, edit) => edit(presentation)
193
+ };
194
+ const results = [];
195
+ for (const [index, { tool, input }] of edits.entries()) try {
196
+ results.push(await runTool(host, tool, input));
197
+ } catch (error) {
198
+ throw new AgentEditError(index, error instanceof Error ? error.message : String(error), { cause: error });
199
+ }
200
+ return results;
201
+ }
202
+ /**
203
+ * What a model reads before editing: for each slide (all of them, or the
204
+ * ones `slides` names by part name, in presentation order) its part name,
205
+ * 0-based index, title, whether it is hidden, its speaker notes and its
206
+ * shapes as the editor's `listShapes` reports them.
207
+ */
208
+ function readForAgent(presentation, slides) {
209
+ const all = pptx.getSlides(presentation);
210
+ const wanted = slides && new Set(slides);
211
+ if (wanted) {
212
+ const known = new Set(all.map((slide) => pptx.getSlidePartName(slide)));
213
+ const missing = [...wanted].filter((name) => !known.has(name));
214
+ if (missing.length > 0) throw new Error(`There is no slide ${missing.join(", ")}.`);
215
+ }
216
+ return { slides: all.flatMap((slide, index) => {
217
+ const partName = pptx.getSlidePartName(slide);
218
+ if (wanted && !wanted.has(partName)) return [];
219
+ return [{
220
+ slide: partName,
221
+ index,
222
+ title: pptx.getSlideTitle(slide),
223
+ hidden: pptx.isSlideHidden(slide),
224
+ notes: pptx.getSlideNotes(slide),
225
+ shapes: pptx.getSlideShapes(slide).map((shape) => shapeSummary(presentation, shape))
226
+ }];
227
+ }) };
228
+ }
229
+ //#endregion
230
+ export { AgentEditError, agentEditSchema, applyAgentEdits, decodeAgentEdits, readForAgent };
231
+
232
+ //# sourceMappingURL=headless.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headless.js","names":[],"sources":["../src/core/agent-edits.ts"],"sourcesContent":["// A small set of edits a language model applies to a presentation without an\n// editor: the `@office-kit/pptx-editor/headless` entry.\n//\n// The operations are nine of the editor's tools, run by the same `runTool`.\n// Their schemas are the generated ones narrowed to what a small revision\n// needs, so the whole set stays a few kilobytes in a model's context. A\n// narrowed schema is a subset of the generated one, so whatever passes it is\n// also valid input for the library function.\n\nimport * as pptx from '@office-kit/pptx';\nimport type { PresentationData } from '@office-kit/pptx';\nimport { generatedTools } from '../manifest/tools.generated.ts';\nimport {\n type JsonSchema,\n type JsonValue,\n type ToolHost,\n runTool,\n schemaErrors,\n shapeSummary,\n} from './agent-tools.ts';\n\nconst MAX_EDITS = 50;\n\ntype Properties = Readonly<Record<string, JsonSchema>>;\n\ninterface Operation {\n /** The input property holding the reference to what the edit changes. */\n readonly target: 'shape' | 'cell' | 'chart' | 'slide';\n readonly input: JsonSchema;\n readonly description: string;\n /** Arguments set after decoding, which the model cannot choose. */\n readonly fixed?: Readonly<Record<string, JsonValue>>;\n}\n\nconst generated = new Map(generatedTools.tools.map((tool) => [tool.name, tool]));\n\nfunction generatedTool(name: string) {\n const tool = generated.get(name);\n // The tests keep these names in the generated manifest.\n if (!tool) throw new Error(`The editor has no tool ${name}.`);\n return tool;\n}\n\nconst propertiesOf = (schema: JsonSchema): Properties => (schema.properties ?? {}) as Properties;\n\n/** `schema` (an object schema) with only the properties `keep` names. */\nfunction pick(schema: JsonSchema, keep: readonly string[]): JsonSchema {\n const properties = propertiesOf(schema);\n const narrowed: Record<string, JsonValue> = {\n ...schema,\n properties: Object.fromEntries(keep.map((key) => [key, properties[key]!])),\n };\n // The definitions are collected once for the whole set.\n delete narrowed.$defs;\n if (Array.isArray(schema.required))\n narrowed.required = schema.required.filter((key) => keep.includes(key as string));\n return narrowed;\n}\n\nconst inputOf = (name: string, keep: readonly string[]): JsonSchema =>\n pick(generatedTool(name).input_schema, keep);\n\n// A whole-text edit that keeps the formatting of what it does not change\n// (`setShapeText` without it rebuilds the text as one run per paragraph).\nconst PRESERVE_FORMATTING = { options: { preserveFormatting: true } } as const;\n\nconst OPERATIONS: Readonly<Record<string, Operation>> = {\n setShapeText: {\n target: 'shape',\n input: inputOf('setShapeText', ['shape', 'value']),\n description:\n \"Replaces a shape's whole text (`\\\\n` between paragraphs) and keeps the formatting of the text before and after what changed. To change two separate places in one shape, apply it twice, each time with the whole text changed in one place: a change in between loses the formatting of the text between them. `{ case }` changes letter case instead.\",\n fixed: PRESERVE_FORMATTING,\n },\n setTableCellText: {\n target: 'cell',\n input: inputOf('setTableCellText', ['cell', 'text']),\n description:\n \"Replaces a table cell's whole text (`\\\\n` between paragraphs) and keeps the formatting of the text before and after what changed. To change two separate places in one cell, apply it twice, each time with the whole text changed in one place. `{ case }` changes letter case instead.\",\n fixed: PRESERVE_FORMATTING,\n },\n setChartData: {\n target: 'chart',\n input: inputOf('setChartData', ['chart', 'data']),\n description: generatedTool('setChartData').description,\n },\n setShapeTextFormat: {\n target: 'shape',\n input: (() => {\n const input = inputOf('setShapeTextFormat', ['shape', 'format']);\n const format = propertiesOf(input).format!;\n return {\n ...input,\n properties: {\n ...propertiesOf(input),\n format: pick(format, ['size', 'bold', 'italic', 'color']),\n },\n };\n })(),\n description:\n 'Sets the font size (points), bold, italic or color of all the text in a shape. Properties left out stay as they are.',\n },\n setShapeFill: {\n target: 'shape',\n input: inputOf('setShapeFill', ['shape', 'color']),\n description: generatedTool('setShapeFill').description,\n },\n setShapeBounds: {\n target: 'shape',\n input: inputOf('setShapeBounds', ['shape', 'bounds']),\n description: 'Moves and resizes a shape: `bounds` is its position and size in EMU.',\n },\n removeShape: {\n target: 'shape',\n input: inputOf('removeShape', ['shape']),\n description: generatedTool('removeShape').description,\n },\n moveSlide: {\n target: 'slide',\n input: (() => {\n const input = inputOf('moveSlide', ['slide', 'toIndex']);\n return {\n ...input,\n properties: { ...propertiesOf(input), toIndex: { type: 'integer', minimum: 0 } },\n };\n })(),\n description: 'Moves a slide to the 0-based position `toIndex`.',\n },\n removeSlide: {\n target: 'slide',\n input: inputOf('removeSlide', ['slide']),\n description: generatedTool('removeSlide').description,\n },\n};\n\n// One `$defs` for the whole set, with only the definitions the narrowed\n// schemas still reach: the generated ones repeat them per tool.\nfunction collectDefs(): Record<string, JsonSchema> {\n const available: Record<string, JsonSchema> = {};\n for (const name of Object.keys(OPERATIONS))\n Object.assign(available, generatedTool(name).input_schema.$defs);\n const defs: Record<string, JsonSchema> = {};\n const visit = (schema: JsonSchema) => {\n for (const [, ref] of JSON.stringify(schema).matchAll(/\"#\\/\\$defs\\/([A-Za-z0-9]+)\"/g)) {\n if (ref! in defs) continue;\n defs[ref!] = available[ref!]!;\n visit(defs[ref!]!);\n }\n };\n for (const operation of Object.values(OPERATIONS)) visit(operation.input);\n return Object.fromEntries(Object.entries(defs).sort(([a], [b]) => a.localeCompare(b)));\n}\n\nconst editsSchema = {\n type: 'array',\n minItems: 1,\n maxItems: MAX_EDITS,\n items: {\n anyOf: Object.entries(OPERATIONS).map(([name, { input, description }]) => ({\n type: 'object',\n description,\n properties: { tool: { type: 'string', const: name }, input },\n required: ['tool', 'input'],\n additionalProperties: false,\n })),\n },\n} satisfies JsonSchema;\n\n/**\n * The JSON Schema (draft 2020-12) of a batch of edits: `{ edits: [{ tool,\n * input }, …] }`, applied in order. Give it to a model as one tool's input.\n */\nexport const agentEditSchema: JsonSchema & { readonly type: 'object' } = {\n type: 'object',\n properties: { edits: editsSchema },\n required: ['edits'],\n additionalProperties: false,\n $defs: collectDefs(),\n};\n\n/** Why a batch of edits could not be applied. */\nexport class AgentEditError extends Error {\n /** The failing edit's position in the batch; `null` when the batch did not match the schema. */\n readonly index: number | null;\n\n constructor(index: number | null, message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'AgentEditError';\n this.index = index;\n }\n}\n\ninterface DecodedAgentEdit {\n readonly tool: string;\n readonly input: Readonly<Record<string, unknown>>;\n}\n\n// Only `decodeAgentEdits` makes the type, so the fixed arguments cannot be skipped.\nconst decoded: unique symbol = Symbol('decoded');\n\n/** Edits `decodeAgentEdits` has checked, ready for `applyAgentEdits`. */\nexport type DecodedAgentEdits = readonly DecodedAgentEdit[] & { readonly [decoded]: true };\n\n/**\n * Checks a model's `edits` (the `edits` of `agentEditSchema`) without\n * touching a presentation. Throws an `AgentEditError` with `index: null`\n * that lists every problem by its JSON Pointer into `agentEditSchema`'s\n * input (`/edits/0/input/…`). `touchedSlides` are the\n * part names of the slides the edits change, in order, each once.\n */\nexport function decodeAgentEdits(edits: unknown): {\n edits: DecodedAgentEdits;\n touchedSlides: readonly string[];\n} {\n // Pointers start at the tool input (`/edits/0/…`), where the model wrote them.\n const errors = schemaErrors(agentEditSchema, { edits });\n if (errors.length > 0)\n throw new AgentEditError(\n null,\n `Invalid edits:\\n${errors.map((error) => `- ${error}`).join('\\n')}`,\n );\n const touched = new Set<string>();\n const out = (edits as readonly DecodedAgentEdit[]).map(({ tool, input }) => {\n const operation = OPERATIONS[tool]!;\n const target = input[operation.target] as string | { readonly slide: string };\n touched.add(typeof target === 'string' ? target : target.slide);\n return { tool, input: { ...input, ...operation.fixed } };\n });\n return { edits: Object.assign(out, { [decoded]: true as const }), touchedSlides: [...touched] };\n}\n\n/**\n * Applies `edits` to `presentation` in order, each reading references\n * against the presentation as the edits before it left it, and resolves\n * with each edit's result. When edit `i` fails it rejects with an\n * `AgentEditError` of `index: i`; the edits before it have been applied,\n * so discard the presentation.\n */\nexport async function applyAgentEdits(\n presentation: PresentationData,\n edits: DecodedAgentEdits,\n): Promise<readonly JsonValue[]> {\n const host: ToolHost = {\n presentation: () => presentation,\n apply: async (_, edit) => edit(presentation),\n };\n const results: JsonValue[] = [];\n for (const [index, { tool, input }] of edits.entries()) {\n try {\n // Each edit reads what the previous one left, so they run one by one.\n results.push(await runTool(host, tool, input));\n } catch (error) {\n throw new AgentEditError(index, error instanceof Error ? error.message : String(error), {\n cause: error,\n });\n }\n }\n return results;\n}\n\n/**\n * What a model reads before editing: for each slide (all of them, or the\n * ones `slides` names by part name, in presentation order) its part name,\n * 0-based index, title, whether it is hidden, its speaker notes and its\n * shapes as the editor's `listShapes` reports them.\n */\nexport function readForAgent(\n presentation: PresentationData,\n slides: readonly string[] | null,\n): JsonValue {\n const all = pptx.getSlides(presentation);\n const wanted = slides && new Set(slides);\n if (wanted) {\n const known = new Set(all.map((slide) => pptx.getSlidePartName(slide)));\n const missing = [...wanted].filter((name) => !known.has(name));\n if (missing.length > 0) throw new Error(`There is no slide ${missing.join(', ')}.`);\n }\n return {\n slides: all.flatMap((slide, index) => {\n const partName = pptx.getSlidePartName(slide);\n if (wanted && !wanted.has(partName)) return [];\n return [\n {\n slide: partName,\n index,\n title: pptx.getSlideTitle(slide),\n hidden: pptx.isSlideHidden(slide),\n notes: pptx.getSlideNotes(slide),\n shapes: pptx.getSlideShapes(slide).map((shape) => shapeSummary(presentation, shape)),\n },\n ];\n }),\n };\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { r as JsonValue, t as EditorTool } from "./agent-tools-bDB2Jq41.js";
1
2
  import { t as DeckConflict } from "./deck-merge-r5VCQNH_.js";
2
3
  import { PresentationData, SlideShapeData } from "@office-kit/pptx";
3
4
 
@@ -9,30 +10,6 @@ import { PresentationData, SlideShapeData } from "@office-kit/pptx";
9
10
  */
10
11
  type ChangeSource = "user" | "agent" | "source";
11
12
  //#endregion
12
- //#region src/core/agent-tools.d.ts
13
- /** A JSON value: what a tool takes as input and gives back. */
14
- type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
15
- readonly [key: string]: JsonValue;
16
- };
17
- /** A JSON Schema (draft 2020-12) object. */
18
- type JsonSchema = {
19
- readonly [key: string]: JsonValue;
20
- };
21
- /**
22
- * A tool definition in the shape model APIs take: Anthropic's `tools` entries
23
- * as they are; for OpenAI, `{ type: 'function', function: { name, description,
24
- * parameters: input_schema } }`.
25
- */
26
- interface EditorTool {
27
- /** The `@office-kit/pptx` export it calls, or a read tool's name. */
28
- readonly name: string;
29
- readonly description: string;
30
- /** The input, as a self-contained JSON Schema (draft 2020-12) object. */
31
- readonly input_schema: JsonSchema & {
32
- readonly type: "object";
33
- };
34
- }
35
- //#endregion
36
13
  //#region src/core/proposal.d.ts
37
14
  /**
38
15
  * Where a proposed version comes from: an agent that worked on a copy, or the
@@ -239,5 +216,5 @@ interface EditorHandle {
239
216
  */
240
217
  declare function mountEditor(target: HTMLElement, options?: EditorOptions): EditorHandle;
241
218
  //#endregion
242
- export { mountEditor as a, ProposalOrigin as c, EditorTool as d, JsonSchema as f, EditorOptions as i, ProposeOptions as l, ChangeSource as m, EditorHandle as n, ShapeRef as o, JsonValue as p, EditorLocale as r, resolveShape as s, EditorEventMap as t, ProposeResult as u };
243
- //# sourceMappingURL=index-BFQN6sI6.d.ts.map
219
+ export { mountEditor as a, ProposalOrigin as c, ChangeSource as d, EditorOptions as i, ProposeOptions as l, EditorHandle as n, ShapeRef as o, EditorLocale as r, resolveShape as s, EditorEventMap as t, ProposeResult as u };
220
+ //# sourceMappingURL=index-cdzLrufG.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import { a as mountEditor, c as ProposalOrigin, d as EditorTool, f as JsonSchema, i as EditorOptions, l as ProposeOptions, m as ChangeSource, n as EditorHandle, o as ShapeRef, p as JsonValue, r as EditorLocale, s as resolveShape, t as EditorEventMap, u as ProposeResult } from "./index-BFQN6sI6.js";
1
+ import { n as JsonSchema, r as JsonValue, t as EditorTool } from "./agent-tools-bDB2Jq41.js";
2
+ import { a as mountEditor, c as ProposalOrigin, d as ChangeSource, i as EditorOptions, l as ProposeOptions, n as EditorHandle, o as ShapeRef, r as EditorLocale, s as resolveShape, t as EditorEventMap, u as ProposeResult } from "./index-cdzLrufG.js";
2
3
  import { t as DeckConflict } from "./deck-merge-r5VCQNH_.js";
3
4
  export { type ChangeSource, type DeckConflict, EditorEventMap, EditorHandle, EditorLocale, EditorOptions, type EditorTool, type JsonSchema, type JsonValue, type ProposalOrigin, type ProposeOptions, type ProposeResult, type ShapeRef, mountEditor, resolveShape };
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- import { n as resolveShape, t as mountEditor } from "./src-BrBOgen_.js";
1
+ import { t as mountEditor } from "./src-CJWOpCx6.js";
2
+ import { n as resolveShape } from "./shape-ref-B5iNfYWh.js";
2
3
  export { mountEditor, resolveShape };
@@ -1,4 +1,4 @@
1
- import { i as EditorOptions, n as EditorHandle } from "./index-BFQN6sI6.js";
1
+ import { i as EditorOptions, n as EditorHandle } from "./index-cdzLrufG.js";
2
2
 
3
3
  //#region src/core/dev-host.d.ts
4
4
  /** What `@office-kit/pptx-editor/internal` adds to a mount for `@office-kit/pptx-dev`. */
package/dist/internal.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as devHostOptions, t as mountEditor } from "./src-BrBOgen_.js";
1
+ import { n as devHostOptions, t as mountEditor } from "./src-CJWOpCx6.js";
2
2
  //#region src/internal.ts
3
3
  /**
4
4
  * `mountEditor` for `pptx-dev`'s editor page. Inside the preview's frame
package/dist/merge.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as describeConflict, r as mergeDecks, t as comparablePart } from "./deck-merge-B_Damj97.js";
1
+ import { n as describeConflict, r as mergeDecks, t as comparablePart } from "./deck-merge-Dj_XHQQM.js";
2
2
  export { comparablePart, describeConflict, mergeDecks };
@@ -0,0 +1,109 @@
1
+ import { findShapeById, findSlideByPartName, getGroupChildren, getShapeId, getShapeName, getShapeSlide, getSlidePartName, getSlideShapes, getSlides } from "@office-kit/pptx";
2
+ //#region src/core/selection.ts
3
+ /** Ordered slide selection; older single-slide snapshots remain compatible. */
4
+ function selectedSlideIndices(sel) {
5
+ return sel.kind === "slide" ? [...new Set(sel.slideIndices ?? [sel.slideIndex])].sort((a, b) => a - b) : [sel.slideIndex];
6
+ }
7
+ /** The primary (first) selected shape id, if any. */
8
+ function selectedShapeId(sel) {
9
+ if (sel.kind === "shape") return sel.shapeIds[0] ?? null;
10
+ if (sel.kind === "cell") return sel.shapeId;
11
+ return null;
12
+ }
13
+ /** All selected shape ids (empty unless a shape/cell selection). */
14
+ function selectedShapeIds(sel) {
15
+ if (sel.kind === "shape") return [...sel.shapeIds];
16
+ if (sel.kind === "cell") return [sel.shapeId];
17
+ return [];
18
+ }
19
+ /** Which operands the current selection can satisfy. Used to enable/disable
20
+ * commands and to route a command to the right target object. */
21
+ function availableOperands(sel) {
22
+ const set = new Set(["presentation"]);
23
+ set.add("slide");
24
+ if (sel.kind === "shape" && sel.shapeIds.length > 0) set.add("shape");
25
+ if (sel.kind === "cell") {
26
+ set.add("shape");
27
+ set.add("cell");
28
+ }
29
+ return set;
30
+ }
31
+ /** The library flattens groups; the canvas selects and transforms their outer frame. */
32
+ function topLevelShapes(slide) {
33
+ const shapes = getSlideShapes(slide);
34
+ const nestedIds = new Set(shapes.flatMap((shape) => getGroupChildren(shape).map(getShapeId)));
35
+ return shapes.filter((shape) => !nestedIds.has(getShapeId(shape)));
36
+ }
37
+ //#endregion
38
+ //#region src/core/shape-ref.ts
39
+ /** The ref for `shape`, which is on a slide of `presentation`. */
40
+ function shapeRef(presentation, shape) {
41
+ const slide = getShapeSlide(shape);
42
+ return {
43
+ slideIndex: getSlides(presentation).indexOf(slide),
44
+ slide: getSlidePartName(slide),
45
+ shapeId: getShapeId(shape),
46
+ name: getShapeName(shape)
47
+ };
48
+ }
49
+ /** Refs for the shapes `selection` names (the table, for a cell selection). */
50
+ function selectionShapeRefs(presentation, selection) {
51
+ const slide = getSlides(presentation)[selection.slideIndex];
52
+ if (!slide) return [];
53
+ const partName = getSlidePartName(slide);
54
+ return selectedShapeIds(selection).flatMap((shapeId) => {
55
+ const shape = findShapeById(slide, shapeId);
56
+ return shape ? [{
57
+ slideIndex: selection.slideIndex,
58
+ slide: partName,
59
+ shapeId,
60
+ name: getShapeName(shape)
61
+ }] : [];
62
+ });
63
+ }
64
+ /**
65
+ * The shape `ref` names in `presentation`. Throws when its slide or the shape
66
+ * no longer exists, so an agent acting on an old selection fails instead of
67
+ * editing something else.
68
+ */
69
+ function resolveShape(presentation, ref) {
70
+ const slide = findSlideByPartName(presentation, ref.slide);
71
+ if (!slide) throw new Error(`Shape "${ref.name}" (id ${ref.shapeId}) is gone: its slide was deleted.`);
72
+ const shape = findShapeById(slide, ref.shapeId);
73
+ if (!shape) throw new Error(`Shape "${ref.name}" (id ${ref.shapeId}) is gone: it was deleted from slide ${ref.slide}.`);
74
+ return shape;
75
+ }
76
+ /**
77
+ * `selection` without the shapes and slides an edit removed. Selections are
78
+ * kept by index and id, so after an agent's edit they may name things that are
79
+ * gone, while the canvas, panes and ribbon expect them to exist.
80
+ */
81
+ function reconcileSelection(presentation, selection) {
82
+ const slides = getSlides(presentation);
83
+ const slide = slides[selection.slideIndex];
84
+ if (!slide) return {
85
+ kind: "none",
86
+ slideIndex: Math.max(0, slides.length - 1)
87
+ };
88
+ if (selection.kind === "none") return selection;
89
+ if (selection.kind === "slide") return (selection.slideIndices ?? [selection.slideIndex]).every((index) => index < slides.length) ? selection : {
90
+ kind: "slide",
91
+ slideIndex: selection.slideIndex
92
+ };
93
+ const live = new Set(getSlideShapes(slide).map(getShapeId));
94
+ const none = {
95
+ kind: "none",
96
+ slideIndex: selection.slideIndex
97
+ };
98
+ if (selection.kind === "cell") return live.has(selection.shapeId) ? selection : none;
99
+ const shapeIds = selection.shapeIds.filter((id) => live.has(id));
100
+ if (shapeIds.length === selection.shapeIds.length) return selection;
101
+ return shapeIds.length > 0 ? {
102
+ ...selection,
103
+ shapeIds
104
+ } : none;
105
+ }
106
+ //#endregion
107
+ export { availableOperands as a, selectedSlideIndices as c, shapeRef as i, topLevelShapes as l, resolveShape as n, selectedShapeId as o, selectionShapeRefs as r, selectedShapeIds as s, reconcileSelection as t };
108
+
109
+ //# sourceMappingURL=shape-ref-B5iNfYWh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-ref-B5iNfYWh.js","names":[],"sources":["../src/core/selection.ts","../src/core/shape-ref.ts"],"sourcesContent":["// The editor selection model.\n//\n// A capability's `operand` (presentation / slide / shape / cell) determines\n// which selection makes it applicable — this mirrors the reference desktop app's contextual\n// behaviour, where the ribbon lights up different controls depending on whether\n// you have a slide, a shape, or a table cell selected.\n//\n// Selections are stored by *stable identity* (slide index + shape id + cell\n// coordinates), never by object reference, so they survive re-renders, undo\n// snapshots, and structural clones.\n\nimport {\n getSlideShapes,\n getGroupChildren,\n getShapeId,\n type SlideData,\n type SlideShapeData,\n} from '@office-kit/pptx';\nimport type { Operand } from '../manifest/types.ts';\n\nexport interface SlideSelection {\n readonly kind: 'slide';\n readonly slideIndex: number;\n readonly slideIndices?: readonly number[];\n readonly anchorIndex?: number;\n}\n\nexport interface ShapeSelection {\n readonly kind: 'shape';\n readonly slideIndex: number;\n /** `getShapeId` value; unique within the slide. Multiple for multi-select. */\n readonly shapeIds: readonly number[];\n}\n\nexport interface CellSelection {\n readonly kind: 'cell';\n readonly slideIndex: number;\n /** The table shape holding the cell. */\n readonly shapeId: number;\n readonly row: number;\n readonly col: number;\n readonly end?: { readonly row: number; readonly col: number };\n}\n\nexport interface NoneSelection {\n readonly kind: 'none';\n /** Even with nothing selected we track which slide is shown. */\n readonly slideIndex: number;\n}\n\nexport type Selection = SlideSelection | ShapeSelection | CellSelection | NoneSelection;\n\nexport function selectionSlideIndex(sel: Selection): number {\n return sel.slideIndex;\n}\n\n/** Ordered slide selection; older single-slide snapshots remain compatible. */\nexport function selectedSlideIndices(sel: Selection): number[] {\n return sel.kind === 'slide'\n ? [...new Set(sel.slideIndices ?? [sel.slideIndex])].sort((a, b) => a - b)\n : [sel.slideIndex];\n}\n\n/** The primary (first) selected shape id, if any. */\nexport function selectedShapeId(sel: Selection): number | null {\n if (sel.kind === 'shape') return sel.shapeIds[0] ?? null;\n if (sel.kind === 'cell') return sel.shapeId;\n return null;\n}\n\n/** All selected shape ids (empty unless a shape/cell selection). */\nexport function selectedShapeIds(sel: Selection): number[] {\n if (sel.kind === 'shape') return [...sel.shapeIds];\n if (sel.kind === 'cell') return [sel.shapeId];\n return [];\n}\n\n/** Which operands the current selection can satisfy. Used to enable/disable\n * commands and to route a command to the right target object. */\nexport function availableOperands(sel: Selection): ReadonlySet<Operand> {\n const set = new Set<Operand>(['presentation']);\n // A presentation always has slides once one exists.\n set.add('slide');\n if (sel.kind === 'shape' && sel.shapeIds.length > 0) set.add('shape');\n if (sel.kind === 'cell') {\n set.add('shape');\n set.add('cell');\n }\n return set;\n}\n\nexport function isOperandAvailable(sel: Selection, operand: Operand): boolean {\n return availableOperands(sel).has(operand);\n}\n\n/** The library flattens groups; the canvas selects and transforms their outer frame. */\nexport function topLevelShapes(slide: SlideData): SlideShapeData[] {\n const shapes = getSlideShapes(slide);\n const nestedIds = new Set(shapes.flatMap((shape) => getGroupChildren(shape).map(getShapeId)));\n return shapes.filter((shape) => !nestedIds.has(getShapeId(shape)));\n}\n","// References to shapes that outlive the editor's in-memory objects.\n//\n// Undo, Redo and a failed edit's rollback replace the whole presentation with\n// one loaded from bytes, so a `SlideShapeData` held across edits may belong to\n// a discarded object graph. A ref names a shape by what the file itself keeps\n// stable instead: the slide's part name and the shape's `cNvPr` id.\n\nimport {\n findShapeById,\n findSlideByPartName,\n getShapeId,\n getShapeName,\n getShapeSlide,\n getSlidePartName,\n getSlideShapes,\n getSlides,\n type PresentationData,\n type SlideShapeData,\n} from '@office-kit/pptx';\nimport { selectedShapeIds, type Selection } from './selection.ts';\n\n/** A shape on a slide, identified independently of the editor's live objects. */\nexport interface ShapeRef {\n /** The slide's 0-based position when the ref was taken. Informational: slides can move. */\n readonly slideIndex: number;\n /** The slide's package part name (`/ppt/slides/slide3.xml`), which identifies the slide. */\n readonly slide: string;\n /** The shape's id (`getShapeId`), unique within its slide. */\n readonly shapeId: number;\n /** The shape's name when the ref was taken, as in the Selection Pane. */\n readonly name: string;\n}\n\n/** The ref for `shape`, which is on a slide of `presentation`. */\nexport function shapeRef(presentation: PresentationData, shape: SlideShapeData): ShapeRef {\n const slide = getShapeSlide(shape);\n return {\n slideIndex: getSlides(presentation).indexOf(slide),\n slide: getSlidePartName(slide),\n shapeId: getShapeId(shape),\n name: getShapeName(shape),\n };\n}\n\n/** Refs for the shapes `selection` names (the table, for a cell selection). */\nexport function selectionShapeRefs(\n presentation: PresentationData,\n selection: Selection,\n): ShapeRef[] {\n const slide = getSlides(presentation)[selection.slideIndex];\n if (!slide) return [];\n const partName = getSlidePartName(slide);\n return selectedShapeIds(selection).flatMap((shapeId) => {\n // The selection is UI state, and not every edit that removes a shape\n // updates it; a shape that is gone is simply not selected.\n const shape = findShapeById(slide, shapeId);\n return shape\n ? [{ slideIndex: selection.slideIndex, slide: partName, shapeId, name: getShapeName(shape) }]\n : [];\n });\n}\n\n/**\n * The shape `ref` names in `presentation`. Throws when its slide or the shape\n * no longer exists, so an agent acting on an old selection fails instead of\n * editing something else.\n */\nexport function resolveShape(presentation: PresentationData, ref: ShapeRef): SlideShapeData {\n const slide = findSlideByPartName(presentation, ref.slide);\n if (!slide)\n throw new Error(`Shape \"${ref.name}\" (id ${ref.shapeId}) is gone: its slide was deleted.`);\n const shape = findShapeById(slide, ref.shapeId);\n if (!shape)\n throw new Error(\n `Shape \"${ref.name}\" (id ${ref.shapeId}) is gone: it was deleted from slide ${ref.slide}.`,\n );\n return shape;\n}\n\n/**\n * `selection` without the shapes and slides an edit removed. Selections are\n * kept by index and id, so after an agent's edit they may name things that are\n * gone, while the canvas, panes and ribbon expect them to exist.\n */\nexport function reconcileSelection(\n presentation: PresentationData,\n selection: Selection,\n): Selection {\n const slides = getSlides(presentation);\n const slide = slides[selection.slideIndex];\n if (!slide) return { kind: 'none', slideIndex: Math.max(0, slides.length - 1) };\n if (selection.kind === 'none') return selection;\n if (selection.kind === 'slide') {\n const indices = selection.slideIndices ?? [selection.slideIndex];\n return indices.every((index) => index < slides.length)\n ? selection\n : { kind: 'slide', slideIndex: selection.slideIndex };\n }\n const live = new Set(getSlideShapes(slide).map(getShapeId));\n const none: Selection = { kind: 'none', slideIndex: selection.slideIndex };\n if (selection.kind === 'cell') return live.has(selection.shapeId) ? selection : none;\n const shapeIds = selection.shapeIds.filter((id) => live.has(id));\n if (shapeIds.length === selection.shapeIds.length) return selection;\n return shapeIds.length > 0 ? { ...selection, shapeIds } : none;\n}\n"],"mappings":"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"}