@office-kit/pptx-dsl 0.8.0 → 0.10.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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -8
- package/dist/index.js.map +1 -1
- package/dist/source-edit.d.ts +48 -1
- package/dist/source-edit.js +142 -1
- package/dist/source-edit.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -91,7 +91,7 @@ interface TextParagraph extends api.ParagraphSpec {
|
|
|
91
91
|
bullet?: api.BulletStyle;
|
|
92
92
|
level?: TextLevel;
|
|
93
93
|
}
|
|
94
|
-
interface TextProps extends Bounds, ShapeStyle, api.TextFormat, Children {
|
|
94
|
+
interface TextProps extends Bounds, ShapeStyle, Omit<api.TextFormat, 'shadow' | 'glow'>, Children {
|
|
95
95
|
paragraphs?: readonly TextParagraph[];
|
|
96
96
|
align?: api.ParagraphAlignment;
|
|
97
97
|
anchor?: api.TextAnchor;
|
package/dist/index.js
CHANGED
|
@@ -60,14 +60,7 @@ function Slide(props) {
|
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
const writableFormat =
|
|
64
|
-
const { color, highlight, ...rest } = format;
|
|
65
|
-
return {
|
|
66
|
-
...rest,
|
|
67
|
-
...color == null ? {} : { color: api.asColor(color) },
|
|
68
|
-
...highlight == null ? {} : { highlight: api.asColor(highlight) }
|
|
69
|
-
};
|
|
70
|
-
};
|
|
63
|
+
const writableFormat = api.toWritableTextFormat;
|
|
71
64
|
function Raw(props) {
|
|
72
65
|
return node("Raw", (context) => {
|
|
73
66
|
if (props.scope !== void 0 && props.scope !== context.scope) throw new Error(`Raw scope="${props.scope}" cannot run inside ${context.scope}.`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import * as api from '@office-kit/pptx';\nimport {\n bindJsxSources,\n created,\n textContent,\n literal,\n node,\n requireSlide,\n visit,\n type Children,\n type Context,\n type Node,\n type PresentationNode,\n type RawContext,\n} from './model.ts';\nexport { getShapeJsxSources } from './model.ts';\nexport type { Child, Children, JsxSource, Node, PresentationNode, RawContext } from './model.ts';\nexport { Fragment } from './jsx-runtime.ts';\n\nexport type PresentationProps = Children &\n (\n | { source: api.PresentationInput; size?: never }\n | { source?: never; size?: api.PresentationSize }\n ) & {\n theme?: Parameters<typeof api.setPresentationTheme>[1];\n /** Compose replaces the slide sequence; edit retains the original sequence. */\n mode?: 'compose' | 'edit';\n };\nexport function Presentation(props: PresentationProps): PresentationNode {\n return {\n kind: 'Presentation',\n evaluate: () => {\n throw new Error('Presentation must be the root.');\n },\n async compile() {\n if (props.source !== undefined && props.size !== undefined)\n throw new Error('Presentation source retains its original size; do not specify size.');\n const presentation =\n props.source === undefined\n ? api.createPresentation(props.size === undefined ? {} : { size: props.size })\n : await api.loadPresentation(props.source);\n if (props.theme) api.setPresentationTheme(presentation, props.theme);\n const originals = [...api.getSlides(presentation)];\n const context: Context = {\n presentation,\n originals,\n mode: props.mode ?? (props.source === undefined ? 'compose' : 'edit'),\n scope: 'presentation',\n sources: [],\n shapeSources: new Map(),\n deferred: [],\n };\n await visit(props.children, context);\n for (const apply of context.deferred) await apply();\n if (context.mode === 'compose') {\n for (const slide of originals) api.removeSlide(presentation, slide);\n }\n bindJsxSources(presentation, context.shapeSources);\n return presentation;\n },\n };\n}\nexport async function compile(root: Node): Promise<api.PresentationData> {\n if (!('compile' in root) || typeof root.compile !== 'function')\n throw new Error('Export a Presentation as the root.');\n return root.compile();\n}\n\nexport type LayoutRef = { name: string } | { part: string } | { type: string };\nexport type SlideRef = { index: number } | { part: string } | { title: string };\nexport type Target =\n | { name: string }\n | { id: number }\n | { placeholder: { type?: string; idx?: number } };\nexport type SlideProps = Children &\n ({ from?: SlideRef; target?: never } | { from?: never; target: SlideRef }) & {\n layout?: LayoutRef;\n background?: api.Color;\n notes?: string;\n };\nfunction selectSlide(context: Context, ref: SlideRef): api.SlideData {\n const matches = context.originals.filter((slide, index) =>\n 'index' in ref\n ? index === ref.index\n : 'part' in ref\n ? api.getSlidePartName(slide) === ref.part\n : api.getSlideTitle(slide) === ref.title,\n );\n if (matches.length !== 1)\n throw new Error(`Slide reference matched ${matches.length} slides: ${JSON.stringify(ref)}`);\n return matches[0]!;\n}\nfunction selectLayout(presentation: api.PresentationData, ref: LayoutRef): api.SlideLayoutData {\n const matches = api\n .getSlideLayouts(presentation)\n .filter((layout) =>\n 'name' in ref\n ? api.getSlideLayoutName(layout) === ref.name\n : 'part' in ref\n ? api.getSlideLayoutPartName(layout) === ref.part\n : api.getSlideLayoutType(layout) === ref.type,\n );\n if (matches.length !== 1)\n throw new Error(`Layout reference matched ${matches.length} layouts: ${JSON.stringify(ref)}`);\n return matches[0]!;\n}\nexport function Slide(props: SlideProps): Node {\n return node('Slide', async (context) => {\n if (context.scope !== 'presentation') throw new Error('Slide must be inside Presentation.');\n if (props.target && context.mode !== 'edit')\n throw new Error('Slide target requires Presentation mode=\"edit\".');\n if (props.from && props.target) throw new Error('Slide cannot specify both from and target.');\n const layout = props.layout ? selectLayout(context.presentation, props.layout) : undefined;\n const slide = props.target\n ? selectSlide(context, props.target)\n : props.from\n ? api.duplicateSlide(context.presentation, selectSlide(context, props.from))\n : layout\n ? api.addSlide(context.presentation, { layout })\n : api.addBlankSlide(context.presentation);\n if (layout && (props.from || props.target)) api.setSlideLayout(slide, layout);\n if (props.background !== undefined) api.setSlideBackground(slide, props.background);\n if (props.notes !== undefined) api.setSlideNotes(slide, props.notes);\n await visit(props.children, { ...context, slide, scope: 'slide' });\n });\n}\n// A format read back from the cell carries colors as plain strings, since a\n// deck can hold a scheme token outside the theme. Re-applying it has to go\n// through the same check the writer would run.\nconst writableFormat = (format: api.ReadTextFormat): api.TextFormat => {\n const { color, highlight, ...rest } = format;\n return {\n ...rest,\n ...(color == null ? {} : { color: api.asColor(color) }),\n ...(highlight == null ? {} : { highlight: api.asColor(highlight) }),\n };\n};\n\nexport type RawProps =\n | { scope?: undefined; apply: (context: RawContext) => void | Promise<void> }\n | {\n scope: 'presentation';\n apply: (context: Pick<RawContext, 'presentation'>) => void | Promise<void>;\n }\n | {\n scope: 'slide';\n apply: (context: RawContext & { slide: api.SlideData }) => void | Promise<void>;\n }\n | { scope: 'shape'; apply: (context: Required<RawContext>) => void | Promise<void> };\n\nexport function Raw(props: RawProps): Node {\n return node('Raw', (context) => {\n if (props.scope !== undefined && props.scope !== context.scope) {\n throw new Error(`Raw scope=\"${props.scope}\" cannot run inside ${context.scope}.`);\n }\n context.deferred.push(() => {\n if (props.scope === 'shape') {\n if (!context.shape || !context.slide) throw new Error('Raw requires a shape context.');\n return props.apply({\n presentation: context.presentation,\n slide: context.slide,\n shape: context.shape,\n });\n }\n if (props.scope === 'slide') {\n if (!context.slide) throw new Error('Raw requires a slide context.');\n return props.apply({ ...context, slide: context.slide });\n }\n return props.apply(context);\n });\n });\n}\nexport interface Bounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\nexport interface ShapeStyle {\n name?: string;\n fill?: api.Color | api.GradientFillOptions;\n stroke?: { color: api.Color; width?: number } | false;\n rotation?: number;\n shadow?: api.ShadowOptions;\n glow?: api.GlowOptions;\n click?: api.ShapeClickAction;\n}\nfunction bounds(props: Bounds) {\n return {\n x: api.inches(props.x),\n y: api.inches(props.y),\n w: api.inches(props.width),\n h: api.inches(props.height),\n };\n}\nfunction style(shape: api.SlideShapeData, props: ShapeStyle) {\n if (props.fill !== undefined) {\n if (typeof props.fill === 'string') api.setShapeFill(shape, props.fill);\n else api.setShapeGradientFill(shape, props.fill);\n }\n if (props.stroke === false) api.setShapeNoStroke(shape);\n else if (props.stroke)\n api.setShapeStroke(shape, {\n color: props.stroke.color,\n widthEmu: api.pt(props.stroke.width ?? 1),\n });\n if (props.rotation !== undefined) api.setShapeRotation(shape, props.rotation);\n if (props.shadow) api.setShapeShadow(shape, props.shadow);\n if (props.glow) api.setShapeGlow(shape, props.glow);\n if (props.click) api.setShapeClickAction(shape, props.click);\n}\n/** A paragraph's indent level: 0 is the top, 8 the deepest PowerPoint has. */\nexport type TextLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;\nexport interface TextParagraph extends api.ParagraphSpec {\n /** This paragraph's own bullet; wins over `bullets`. */\n bullet?: api.BulletStyle;\n level?: TextLevel;\n}\nexport interface TextProps extends Bounds, ShapeStyle, api.TextFormat, Children {\n paragraphs?: readonly TextParagraph[];\n align?: api.ParagraphAlignment;\n anchor?: api.TextAnchor;\n autoFit?: api.TextAutoFit;\n /** Bullet or numbering for every paragraph; a newline in the text starts a paragraph. */\n bullets?: api.BulletStyle;\n /** Space before / after every paragraph, in points. */\n paragraphSpacing?: { before?: number; after?: number };\n}\nexport function Text(props: TextProps): Node {\n return node('Text', async (context) => {\n const slide = requireSlide(context);\n const content = textContent(props.children);\n if (props.paragraphs && content.text.trim())\n throw new Error('Text accepts either text children or paragraphs.');\n const shape = created(\n context,\n api.addSlideTextBox(slide, {\n ...bounds(props),\n ...(props.name ? { name: props.name } : {}),\n text: content.text,\n }),\n );\n if (props.paragraphs) api.setShapeParagraphs(shape, props.paragraphs);\n api.setShapeTextFormat(shape, props);\n if (props.align) api.setShapeAlignment(shape, props.align);\n if (props.anchor) api.setShapeTextAnchor(shape, props.anchor);\n if (props.autoFit) api.setShapeTextAutoFit(shape, props.autoFit);\n // Before any bullet: core 0.17.0, still in the peer range, sizes a bullet's hanging\n // indent from the level at that moment and never moves it afterwards.\n props.paragraphs?.forEach((paragraph, index) => {\n if (paragraph.level !== undefined) api.setParagraphLevel(shape, index, paragraph.level);\n });\n if (props.bullets !== undefined) api.setShapeBulletStyle(shape, props.bullets);\n props.paragraphs?.forEach((paragraph, index) => {\n if (paragraph.bullet !== undefined) api.setParagraphBullet(shape, index, paragraph.bullet);\n });\n if (props.paragraphSpacing) {\n const { before, after } = props.paragraphSpacing;\n const spacing = {\n ...(before !== undefined ? { beforePts: before } : {}),\n ...(after !== undefined ? { afterPts: after } : {}),\n };\n for (let i = 0; i < api.getShapeParagraphCount(shape); i++) {\n api.setParagraphSpacing(shape, i, spacing);\n }\n }\n style(shape, props);\n await visit(content.elements, { ...context, slide, shape, scope: 'shape' });\n });\n}\nexport interface ShapeProps extends Bounds, ShapeStyle, Children {\n preset: Parameters<typeof api.addSlideShape>[1]['preset'];\n text?: string;\n format?: api.TextFormat;\n /** Alignment and anchor of `text` inside the shape. */\n align?: api.ParagraphAlignment;\n anchor?: api.TextAnchor;\n}\nexport function Shape(props: ShapeProps): Node {\n return node('Shape', async (context) => {\n if ((props.align || props.anchor) && props.text === undefined)\n throw new Error('Shape align and anchor position its text; set text as well.');\n const shape = created(\n context,\n api.addSlideShape(requireSlide(context), {\n ...bounds(props),\n preset: props.preset,\n ...(props.name ? { name: props.name } : {}),\n ...(props.text !== undefined ? { text: props.text } : {}),\n }),\n );\n style(shape, props);\n if (props.format) api.setShapeTextFormat(shape, props.format);\n if (props.align) api.setShapeAlignment(shape, props.align);\n if (props.anchor) api.setShapeTextAnchor(shape, props.anchor);\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\nexport interface ImageProps extends Bounds, ShapeStyle, Children {\n data: Uint8Array;\n format?: api.ImageFormat;\n fit?: api.ImageFit;\n}\nexport function Image(props: ImageProps): Node {\n return node('Image', async (context) => {\n const shape = created(\n context,\n api.addSlideImage(requireSlide(context), props.data, {\n ...bounds(props),\n ...(props.format ? { format: props.format } : {}),\n ...(props.fit ? { fit: props.fit } : {}),\n ...(props.name ? { name: props.name } : {}),\n }),\n );\n style(shape, props);\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\n// No ShapeStyle: a clip's picture already carries the `ppaction://media` click\n// action that makes it playable, which `click` would replace.\nexport type MediaProps = Bounds &\n Children &\n api.SlideMediaSource & {\n name?: string;\n poster?: Uint8Array;\n posterFormat?: api.ImageFormat;\n };\nexport function Media(props: MediaProps): Node {\n return node('Media', async (context) => {\n const { x: _x, y: _y, width: _width, height: _height, children, ...media } = props;\n const shape = created(\n context,\n api.addSlideMedia(requireSlide(context), { ...media, ...bounds(props) }),\n );\n await visit(children, { ...context, shape, scope: 'shape' });\n });\n}\nexport interface ChartProps extends Bounds, ShapeStyle, Children {\n spec: api.ChartSpec;\n}\nexport function Chart(props: ChartProps): Node {\n return node('Chart', async (context) => {\n const shape = created(\n context,\n api.addSlideChart(requireSlide(context), {\n ...bounds(props),\n spec: props.spec,\n ...(props.name ? { name: props.name } : {}),\n }),\n );\n style(shape, props);\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\nexport type CellBorderSide = 'left' | 'right' | 'top' | 'bottom';\nexport interface CellStyle {\n fill?: api.Color;\n format?: api.TextFormat;\n anchor?: api.TextAnchor;\n align?: api.ParagraphAlignment;\n /** Border per side. `width` is in points; without it the line has no explicit width. */\n borders?: Partial<Record<CellBorderSide, { color: api.Color; width?: number }>>;\n}\nexport interface TableCellInfo {\n /** Zero-based; row 0 is the header. */\n row: number;\n column: number;\n /** A cell's text; for `paragraphs`, the run texts joined, one line per paragraph. */\n value: string;\n}\n/** Exactly one of `text` and `paragraphs`. An empty cell is the string `''`. */\ntype TableCellContent =\n | { text: string; paragraphs?: never }\n | { text?: never; paragraphs: readonly api.ParagraphSpec[] };\nexport type TableCellSpec = TableCellContent & {\n /** This cell is the top-left of a merge. The positions it covers stay in `rows` as `''`. */\n colSpan?: number;\n rowSpan?: number;\n};\nexport type TableCell = string | TableCellSpec;\nexport interface TableProps extends Bounds, Children {\n rows: readonly (readonly TableCell[])[];\n columnWidths?: readonly number[];\n rowHeights?: readonly number[];\n cellStyle?: CellStyle;\n headerStyle?: CellStyle;\n stripeFill?: api.Color;\n /**\n * Style for one cell, merged over `cellStyle`, `headerStyle` and\n * `stripeFill` — for a status column, a total row, a highlighted figure.\n */\n styleCell?: (cell: TableCellInfo) => CellStyle | undefined;\n}\nfunction cellBorders(borders: NonNullable<CellStyle['borders']>) {\n const sides: Partial<Record<CellBorderSide, { color: api.Color; widthEmu?: number }>> = {};\n for (const side of ['left', 'right', 'top', 'bottom'] as const) {\n const border = borders[side];\n if (!border) continue;\n sides[side] = {\n color: border.color,\n ...(border.width !== undefined ? { widthEmu: api.pt(border.width) } : {}),\n };\n }\n return sides;\n}\ntype CellPosition = { row: number; col: number };\nconst positionKey = (row: number, col: number) => `${row}:${col}`;\nconst cellSpec = (cell: TableCell): TableCellSpec =>\n typeof cell === 'string' ? { text: cell } : cell;\nconst cellValue = (spec: TableCellSpec): string =>\n spec.paragraphs\n ? spec.paragraphs.map((paragraph) => paragraph.runs.map((run) => run.text).join('')).join('\\n')\n : spec.text;\n\n/** Maps every position a merge covers to that merge's top-left cell. */\nfunction coveredPositions(rows: TableProps['rows']): Map<string, CellPosition> {\n const covered = new Map<string, CellPosition>();\n rows.forEach((cells, row) =>\n cells.forEach((cell, col) => {\n const { rowSpan = 1, colSpan = 1 } = cellSpec(cell);\n // Clamped to the grid: the core rejects an oversized span later, and an\n // unbounded loop must not run first.\n const lastRow = Math.min(row + rowSpan, rows.length);\n const lastCol = Math.min(col + colSpan, cells.length);\n for (let r = row; r < lastRow; r++)\n for (let c = col; c < lastCol; c++)\n if (r !== row || c !== col) covered.set(positionKey(r, c), { row, col });\n }),\n );\n return covered;\n}\nfunction checkCells(rows: TableProps['rows'], covered: Map<string, CellPosition>) {\n rows.forEach((cells, row) =>\n cells.forEach((cell, col) => {\n const spec = cellSpec(cell);\n if ((spec.text === undefined) === (spec.paragraphs === undefined))\n throw new Error(`Table cell (${row}, ${col}) accepts either text or paragraphs.`);\n const anchor = covered.get(positionKey(row, col));\n // PowerPoint paints only the merge's top-left cell, so anything written\n // here would vanish from the slide without a trace.\n if (anchor && cell !== '')\n throw new Error(\n `Table cell (${row}, ${col}) is covered by the merge at (${anchor.row}, ${anchor.col}); write it as ''.`,\n );\n }),\n );\n}\nexport function Table(props: TableProps): Node {\n return node('Table', async (context) => {\n const covered = coveredPositions(props.rows);\n checkCells(props.rows, covered);\n const shape = created(\n context,\n api.addSlideTable(requireSlide(context), {\n ...bounds(props),\n rows: props.rows.map((cells) => cells.map((cell) => cellSpec(cell).text ?? '')),\n ...(props.columnWidths ? { colWidths: props.columnWidths.map(api.inches) } : {}),\n ...(props.rowHeights ? { rowHeights: props.rowHeights.map(api.inches) } : {}),\n }),\n );\n props.rows.forEach((row, r) =>\n row.forEach((entry, c) => {\n // A merged block is painted from its top-left cell alone, fill and all\n // four borders, so a covered position gets no style and no styleCell call.\n if (covered.has(positionKey(r, c))) return;\n const spec = cellSpec(entry);\n const cell = api.getTableCell(shape, r, c);\n const header = r === 0 ? props.headerStyle : undefined;\n const own = props.styleCell?.({ row: r, column: c, value: cellValue(spec) });\n const striped = r > 0 && r % 2 === 0 ? props.stripeFill : undefined;\n const appearance: CellStyle = {\n ...props.cellStyle,\n ...header,\n ...(striped !== undefined ? { fill: striped } : {}),\n ...own,\n format: { ...props.cellStyle?.format, ...header?.format, ...own?.format },\n };\n if (appearance.fill) api.setTableCellFill(cell, appearance.fill);\n if (spec.paragraphs) {\n // addSlideTable bakes the deck's body-text color into each run so text\n // stays readable on an inverted color map; new runs must keep it.\n const built = api.getTableCellParagraphs(cell)[0]?.elements[0];\n const base = {\n ...(built?.kind === 'r' && built.format ? writableFormat(built.format) : {}),\n ...appearance.format,\n };\n const { align } = appearance;\n api.setTableCellParagraphs(\n cell,\n spec.paragraphs.map((paragraph) => ({\n ...paragraph,\n ...(paragraph.align === undefined && align ? { align } : {}),\n runs: paragraph.runs.map((run) => ({ ...run, format: { ...base, ...run.format } })),\n })),\n );\n } else {\n if (appearance.format) api.setTableCellTextFormat(cell, appearance.format);\n if (appearance.align) api.setTableCellAlignment(cell, appearance.align);\n }\n if (appearance.anchor) api.setTableCellAnchor(cell, appearance.anchor);\n // Only when a side is set: an empty call would still give every cell a <a:tcPr>.\n const borders = cellBorders({\n ...props.cellStyle?.borders,\n ...header?.borders,\n ...own?.borders,\n });\n if (Object.keys(borders).length > 0) api.setTableCellBorders(cell, borders);\n }),\n );\n props.rows.forEach((row, r) =>\n row.forEach((entry, c) => {\n const { rowSpan = 1, colSpan = 1 } = cellSpec(entry);\n if (rowSpan !== 1 || colSpan !== 1)\n api.mergeTableCells(shape, { row: r, col: c, rowSpan, colSpan });\n }),\n );\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\nexport interface LineProps {\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n color?: api.Color;\n /** Stroke width in points. */\n width?: number;\n name?: string;\n}\nexport function Line(props: LineProps): Node {\n return node('Line', (context) => {\n created(\n context,\n api.addSlideLine(requireSlide(context), {\n from: { x: api.inches(props.x1), y: api.inches(props.y1) },\n to: { x: api.inches(props.x2), y: api.inches(props.y2) },\n ...(props.color !== undefined ? { color: props.color } : {}),\n ...(props.width !== undefined ? { widthEmu: api.pt(props.width) } : {}),\n ...(props.name ? { name: props.name } : {}),\n }),\n );\n });\n}\nexport interface GroupProps extends Children {\n name?: string;\n}\n/** Groups the shapes its children create, so they move and resize as one object. */\nexport function Group(props: GroupProps): Node {\n return node('Group', async (context) => {\n requireSlide(context);\n const members: api.SlideShapeData[] = [];\n await visit(props.children, { ...context, members });\n created(context, api.groupShapes(members, props.name ? { name: props.name } : {}));\n });\n}\nfunction selectShape(slide: api.SlideData, target: Target): api.SlideShapeData {\n const matches = api.getSlideShapes(slide).filter((shape) => {\n if ('name' in target) return api.getShapeName(shape) === target.name;\n if ('id' in target) return api.getShapeId(shape) === target.id;\n const ph = target.placeholder;\n return (\n (ph.type !== undefined || ph.idx !== undefined) &&\n (ph.type === undefined || api.getShapePlaceholderType(shape) === ph.type) &&\n (ph.idx === undefined || api.getShapePlaceholderIdx(shape) === ph.idx)\n );\n });\n if (matches.length !== 1)\n throw new Error(`Shape reference matched ${matches.length} shapes: ${JSON.stringify(target)}`);\n return matches[0]!;\n}\nexport interface FillProps extends Children {\n target: Target;\n text?: string;\n format?: api.TextFormat;\n /** Omitted keeps whatever the template's `<a:bodyPr>` says. */\n autoFit?: api.TextAutoFit;\n}\nexport function Fill(props: FillProps): Node {\n return node('Fill', (context) => {\n const shape = selectShape(requireSlide(context), props.target);\n api.setShapeText(shape, props.text ?? literal(props.children));\n if (props.format) api.setShapeTextFormat(shape, props.format);\n if (props.autoFit) api.setShapeTextAutoFit(shape, props.autoFit);\n });\n}\nexport function Remove(props: { target: Target }): Node {\n return node('Remove', (context) => {\n const slide = requireSlide(context);\n api.removeShape(selectShape(slide, props.target));\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import * as api from '@office-kit/pptx';\nimport {\n bindJsxSources,\n created,\n textContent,\n literal,\n node,\n requireSlide,\n visit,\n type Children,\n type Context,\n type Node,\n type PresentationNode,\n type RawContext,\n} from './model.ts';\nexport { getShapeJsxSources } from './model.ts';\nexport type { Child, Children, JsxSource, Node, PresentationNode, RawContext } from './model.ts';\nexport { Fragment } from './jsx-runtime.ts';\n\nexport type PresentationProps = Children &\n (\n | { source: api.PresentationInput; size?: never }\n | { source?: never; size?: api.PresentationSize }\n ) & {\n theme?: Parameters<typeof api.setPresentationTheme>[1];\n /** Compose replaces the slide sequence; edit retains the original sequence. */\n mode?: 'compose' | 'edit';\n };\nexport function Presentation(props: PresentationProps): PresentationNode {\n return {\n kind: 'Presentation',\n evaluate: () => {\n throw new Error('Presentation must be the root.');\n },\n async compile() {\n if (props.source !== undefined && props.size !== undefined)\n throw new Error('Presentation source retains its original size; do not specify size.');\n const presentation =\n props.source === undefined\n ? api.createPresentation(props.size === undefined ? {} : { size: props.size })\n : await api.loadPresentation(props.source);\n if (props.theme) api.setPresentationTheme(presentation, props.theme);\n const originals = [...api.getSlides(presentation)];\n const context: Context = {\n presentation,\n originals,\n mode: props.mode ?? (props.source === undefined ? 'compose' : 'edit'),\n scope: 'presentation',\n sources: [],\n shapeSources: new Map(),\n deferred: [],\n };\n await visit(props.children, context);\n for (const apply of context.deferred) await apply();\n if (context.mode === 'compose') {\n for (const slide of originals) api.removeSlide(presentation, slide);\n }\n bindJsxSources(presentation, context.shapeSources);\n return presentation;\n },\n };\n}\nexport async function compile(root: Node): Promise<api.PresentationData> {\n if (!('compile' in root) || typeof root.compile !== 'function')\n throw new Error('Export a Presentation as the root.');\n return root.compile();\n}\n\nexport type LayoutRef = { name: string } | { part: string } | { type: string };\nexport type SlideRef = { index: number } | { part: string } | { title: string };\nexport type Target =\n | { name: string }\n | { id: number }\n | { placeholder: { type?: string; idx?: number } };\nexport type SlideProps = Children &\n ({ from?: SlideRef; target?: never } | { from?: never; target: SlideRef }) & {\n layout?: LayoutRef;\n background?: api.Color;\n notes?: string;\n };\nfunction selectSlide(context: Context, ref: SlideRef): api.SlideData {\n const matches = context.originals.filter((slide, index) =>\n 'index' in ref\n ? index === ref.index\n : 'part' in ref\n ? api.getSlidePartName(slide) === ref.part\n : api.getSlideTitle(slide) === ref.title,\n );\n if (matches.length !== 1)\n throw new Error(`Slide reference matched ${matches.length} slides: ${JSON.stringify(ref)}`);\n return matches[0]!;\n}\nfunction selectLayout(presentation: api.PresentationData, ref: LayoutRef): api.SlideLayoutData {\n const matches = api\n .getSlideLayouts(presentation)\n .filter((layout) =>\n 'name' in ref\n ? api.getSlideLayoutName(layout) === ref.name\n : 'part' in ref\n ? api.getSlideLayoutPartName(layout) === ref.part\n : api.getSlideLayoutType(layout) === ref.type,\n );\n if (matches.length !== 1)\n throw new Error(`Layout reference matched ${matches.length} layouts: ${JSON.stringify(ref)}`);\n return matches[0]!;\n}\nexport function Slide(props: SlideProps): Node {\n return node('Slide', async (context) => {\n if (context.scope !== 'presentation') throw new Error('Slide must be inside Presentation.');\n if (props.target && context.mode !== 'edit')\n throw new Error('Slide target requires Presentation mode=\"edit\".');\n if (props.from && props.target) throw new Error('Slide cannot specify both from and target.');\n const layout = props.layout ? selectLayout(context.presentation, props.layout) : undefined;\n const slide = props.target\n ? selectSlide(context, props.target)\n : props.from\n ? api.duplicateSlide(context.presentation, selectSlide(context, props.from))\n : layout\n ? api.addSlide(context.presentation, { layout })\n : api.addBlankSlide(context.presentation);\n if (layout && (props.from || props.target)) api.setSlideLayout(slide, layout);\n if (props.background !== undefined) api.setSlideBackground(slide, props.background);\n if (props.notes !== undefined) api.setSlideNotes(slide, props.notes);\n await visit(props.children, { ...context, slide, scope: 'slide' });\n });\n}\nconst writableFormat = api.toWritableTextFormat;\n\nexport type RawProps =\n | { scope?: undefined; apply: (context: RawContext) => void | Promise<void> }\n | {\n scope: 'presentation';\n apply: (context: Pick<RawContext, 'presentation'>) => void | Promise<void>;\n }\n | {\n scope: 'slide';\n apply: (context: RawContext & { slide: api.SlideData }) => void | Promise<void>;\n }\n | { scope: 'shape'; apply: (context: Required<RawContext>) => void | Promise<void> };\n\nexport function Raw(props: RawProps): Node {\n return node('Raw', (context) => {\n if (props.scope !== undefined && props.scope !== context.scope) {\n throw new Error(`Raw scope=\"${props.scope}\" cannot run inside ${context.scope}.`);\n }\n context.deferred.push(() => {\n if (props.scope === 'shape') {\n if (!context.shape || !context.slide) throw new Error('Raw requires a shape context.');\n return props.apply({\n presentation: context.presentation,\n slide: context.slide,\n shape: context.shape,\n });\n }\n if (props.scope === 'slide') {\n if (!context.slide) throw new Error('Raw requires a slide context.');\n return props.apply({ ...context, slide: context.slide });\n }\n return props.apply(context);\n });\n });\n}\nexport interface Bounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\nexport interface ShapeStyle {\n name?: string;\n fill?: api.Color | api.GradientFillOptions;\n stroke?: { color: api.Color; width?: number } | false;\n rotation?: number;\n shadow?: api.ShadowOptions;\n glow?: api.GlowOptions;\n click?: api.ShapeClickAction;\n}\nfunction bounds(props: Bounds) {\n return {\n x: api.inches(props.x),\n y: api.inches(props.y),\n w: api.inches(props.width),\n h: api.inches(props.height),\n };\n}\nfunction style(shape: api.SlideShapeData, props: ShapeStyle) {\n if (props.fill !== undefined) {\n if (typeof props.fill === 'string') api.setShapeFill(shape, props.fill);\n else api.setShapeGradientFill(shape, props.fill);\n }\n if (props.stroke === false) api.setShapeNoStroke(shape);\n else if (props.stroke)\n api.setShapeStroke(shape, {\n color: props.stroke.color,\n widthEmu: api.pt(props.stroke.width ?? 1),\n });\n if (props.rotation !== undefined) api.setShapeRotation(shape, props.rotation);\n if (props.shadow) api.setShapeShadow(shape, props.shadow);\n if (props.glow) api.setShapeGlow(shape, props.glow);\n if (props.click) api.setShapeClickAction(shape, props.click);\n}\n/** A paragraph's indent level: 0 is the top, 8 the deepest PowerPoint has. */\nexport type TextLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;\nexport interface TextParagraph extends api.ParagraphSpec {\n /** This paragraph's own bullet; wins over `bullets`. */\n bullet?: api.BulletStyle;\n level?: TextLevel;\n}\n// `shadow` and `glow` on a `<Text>` have always meant the box's effects, and\n// `TextFormat` now carries the character-level pair of the same names. The box\n// keeps the short names; per-run effects go through `paragraphs`, where a run\n// states its own format.\nexport interface TextProps\n extends Bounds, ShapeStyle, Omit<api.TextFormat, 'shadow' | 'glow'>, Children {\n paragraphs?: readonly TextParagraph[];\n align?: api.ParagraphAlignment;\n anchor?: api.TextAnchor;\n autoFit?: api.TextAutoFit;\n /** Bullet or numbering for every paragraph; a newline in the text starts a paragraph. */\n bullets?: api.BulletStyle;\n /** Space before / after every paragraph, in points. */\n paragraphSpacing?: { before?: number; after?: number };\n}\nexport function Text(props: TextProps): Node {\n return node('Text', async (context) => {\n const slide = requireSlide(context);\n const content = textContent(props.children);\n if (props.paragraphs && content.text.trim())\n throw new Error('Text accepts either text children or paragraphs.');\n const shape = created(\n context,\n api.addSlideTextBox(slide, {\n ...bounds(props),\n ...(props.name ? { name: props.name } : {}),\n text: content.text,\n }),\n );\n if (props.paragraphs) api.setShapeParagraphs(shape, props.paragraphs);\n api.setShapeTextFormat(shape, props);\n if (props.align) api.setShapeAlignment(shape, props.align);\n if (props.anchor) api.setShapeTextAnchor(shape, props.anchor);\n if (props.autoFit) api.setShapeTextAutoFit(shape, props.autoFit);\n // Before any bullet: core 0.17.0, still in the peer range, sizes a bullet's hanging\n // indent from the level at that moment and never moves it afterwards.\n props.paragraphs?.forEach((paragraph, index) => {\n if (paragraph.level !== undefined) api.setParagraphLevel(shape, index, paragraph.level);\n });\n if (props.bullets !== undefined) api.setShapeBulletStyle(shape, props.bullets);\n props.paragraphs?.forEach((paragraph, index) => {\n if (paragraph.bullet !== undefined) api.setParagraphBullet(shape, index, paragraph.bullet);\n });\n if (props.paragraphSpacing) {\n const { before, after } = props.paragraphSpacing;\n const spacing = {\n ...(before !== undefined ? { beforePts: before } : {}),\n ...(after !== undefined ? { afterPts: after } : {}),\n };\n for (let i = 0; i < api.getShapeParagraphCount(shape); i++) {\n api.setParagraphSpacing(shape, i, spacing);\n }\n }\n style(shape, props);\n await visit(content.elements, { ...context, slide, shape, scope: 'shape' });\n });\n}\nexport interface ShapeProps extends Bounds, ShapeStyle, Children {\n preset: Parameters<typeof api.addSlideShape>[1]['preset'];\n text?: string;\n format?: api.TextFormat;\n /** Alignment and anchor of `text` inside the shape. */\n align?: api.ParagraphAlignment;\n anchor?: api.TextAnchor;\n}\nexport function Shape(props: ShapeProps): Node {\n return node('Shape', async (context) => {\n if ((props.align || props.anchor) && props.text === undefined)\n throw new Error('Shape align and anchor position its text; set text as well.');\n const shape = created(\n context,\n api.addSlideShape(requireSlide(context), {\n ...bounds(props),\n preset: props.preset,\n ...(props.name ? { name: props.name } : {}),\n ...(props.text !== undefined ? { text: props.text } : {}),\n }),\n );\n style(shape, props);\n if (props.format) api.setShapeTextFormat(shape, props.format);\n if (props.align) api.setShapeAlignment(shape, props.align);\n if (props.anchor) api.setShapeTextAnchor(shape, props.anchor);\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\nexport interface ImageProps extends Bounds, ShapeStyle, Children {\n data: Uint8Array;\n format?: api.ImageFormat;\n fit?: api.ImageFit;\n}\nexport function Image(props: ImageProps): Node {\n return node('Image', async (context) => {\n const shape = created(\n context,\n api.addSlideImage(requireSlide(context), props.data, {\n ...bounds(props),\n ...(props.format ? { format: props.format } : {}),\n ...(props.fit ? { fit: props.fit } : {}),\n ...(props.name ? { name: props.name } : {}),\n }),\n );\n style(shape, props);\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\n// No ShapeStyle: a clip's picture already carries the `ppaction://media` click\n// action that makes it playable, which `click` would replace.\nexport type MediaProps = Bounds &\n Children &\n api.SlideMediaSource & {\n name?: string;\n poster?: Uint8Array;\n posterFormat?: api.ImageFormat;\n };\nexport function Media(props: MediaProps): Node {\n return node('Media', async (context) => {\n const { x: _x, y: _y, width: _width, height: _height, children, ...media } = props;\n const shape = created(\n context,\n api.addSlideMedia(requireSlide(context), { ...media, ...bounds(props) }),\n );\n await visit(children, { ...context, shape, scope: 'shape' });\n });\n}\nexport interface ChartProps extends Bounds, ShapeStyle, Children {\n spec: api.ChartSpec;\n}\nexport function Chart(props: ChartProps): Node {\n return node('Chart', async (context) => {\n const shape = created(\n context,\n api.addSlideChart(requireSlide(context), {\n ...bounds(props),\n spec: props.spec,\n ...(props.name ? { name: props.name } : {}),\n }),\n );\n style(shape, props);\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\nexport type CellBorderSide = 'left' | 'right' | 'top' | 'bottom';\nexport interface CellStyle {\n fill?: api.Color;\n format?: api.TextFormat;\n anchor?: api.TextAnchor;\n align?: api.ParagraphAlignment;\n /** Border per side. `width` is in points; without it the line has no explicit width. */\n borders?: Partial<Record<CellBorderSide, { color: api.Color; width?: number }>>;\n}\nexport interface TableCellInfo {\n /** Zero-based; row 0 is the header. */\n row: number;\n column: number;\n /** A cell's text; for `paragraphs`, the run texts joined, one line per paragraph. */\n value: string;\n}\n/** Exactly one of `text` and `paragraphs`. An empty cell is the string `''`. */\ntype TableCellContent =\n | { text: string; paragraphs?: never }\n | { text?: never; paragraphs: readonly api.ParagraphSpec[] };\nexport type TableCellSpec = TableCellContent & {\n /** This cell is the top-left of a merge. The positions it covers stay in `rows` as `''`. */\n colSpan?: number;\n rowSpan?: number;\n};\nexport type TableCell = string | TableCellSpec;\nexport interface TableProps extends Bounds, Children {\n rows: readonly (readonly TableCell[])[];\n columnWidths?: readonly number[];\n rowHeights?: readonly number[];\n cellStyle?: CellStyle;\n headerStyle?: CellStyle;\n stripeFill?: api.Color;\n /**\n * Style for one cell, merged over `cellStyle`, `headerStyle` and\n * `stripeFill` — for a status column, a total row, a highlighted figure.\n */\n styleCell?: (cell: TableCellInfo) => CellStyle | undefined;\n}\nfunction cellBorders(borders: NonNullable<CellStyle['borders']>) {\n const sides: Partial<Record<CellBorderSide, { color: api.Color; widthEmu?: number }>> = {};\n for (const side of ['left', 'right', 'top', 'bottom'] as const) {\n const border = borders[side];\n if (!border) continue;\n sides[side] = {\n color: border.color,\n ...(border.width !== undefined ? { widthEmu: api.pt(border.width) } : {}),\n };\n }\n return sides;\n}\ntype CellPosition = { row: number; col: number };\nconst positionKey = (row: number, col: number) => `${row}:${col}`;\nconst cellSpec = (cell: TableCell): TableCellSpec =>\n typeof cell === 'string' ? { text: cell } : cell;\nconst cellValue = (spec: TableCellSpec): string =>\n spec.paragraphs\n ? spec.paragraphs.map((paragraph) => paragraph.runs.map((run) => run.text).join('')).join('\\n')\n : spec.text;\n\n/** Maps every position a merge covers to that merge's top-left cell. */\nfunction coveredPositions(rows: TableProps['rows']): Map<string, CellPosition> {\n const covered = new Map<string, CellPosition>();\n rows.forEach((cells, row) =>\n cells.forEach((cell, col) => {\n const { rowSpan = 1, colSpan = 1 } = cellSpec(cell);\n // Clamped to the grid: the core rejects an oversized span later, and an\n // unbounded loop must not run first.\n const lastRow = Math.min(row + rowSpan, rows.length);\n const lastCol = Math.min(col + colSpan, cells.length);\n for (let r = row; r < lastRow; r++)\n for (let c = col; c < lastCol; c++)\n if (r !== row || c !== col) covered.set(positionKey(r, c), { row, col });\n }),\n );\n return covered;\n}\nfunction checkCells(rows: TableProps['rows'], covered: Map<string, CellPosition>) {\n rows.forEach((cells, row) =>\n cells.forEach((cell, col) => {\n const spec = cellSpec(cell);\n if ((spec.text === undefined) === (spec.paragraphs === undefined))\n throw new Error(`Table cell (${row}, ${col}) accepts either text or paragraphs.`);\n const anchor = covered.get(positionKey(row, col));\n // PowerPoint paints only the merge's top-left cell, so anything written\n // here would vanish from the slide without a trace.\n if (anchor && cell !== '')\n throw new Error(\n `Table cell (${row}, ${col}) is covered by the merge at (${anchor.row}, ${anchor.col}); write it as ''.`,\n );\n }),\n );\n}\nexport function Table(props: TableProps): Node {\n return node('Table', async (context) => {\n const covered = coveredPositions(props.rows);\n checkCells(props.rows, covered);\n const shape = created(\n context,\n api.addSlideTable(requireSlide(context), {\n ...bounds(props),\n rows: props.rows.map((cells) => cells.map((cell) => cellSpec(cell).text ?? '')),\n ...(props.columnWidths ? { colWidths: props.columnWidths.map(api.inches) } : {}),\n ...(props.rowHeights ? { rowHeights: props.rowHeights.map(api.inches) } : {}),\n }),\n );\n props.rows.forEach((row, r) =>\n row.forEach((entry, c) => {\n // A merged block is painted from its top-left cell alone, fill and all\n // four borders, so a covered position gets no style and no styleCell call.\n if (covered.has(positionKey(r, c))) return;\n const spec = cellSpec(entry);\n const cell = api.getTableCell(shape, r, c);\n const header = r === 0 ? props.headerStyle : undefined;\n const own = props.styleCell?.({ row: r, column: c, value: cellValue(spec) });\n const striped = r > 0 && r % 2 === 0 ? props.stripeFill : undefined;\n const appearance: CellStyle = {\n ...props.cellStyle,\n ...header,\n ...(striped !== undefined ? { fill: striped } : {}),\n ...own,\n format: { ...props.cellStyle?.format, ...header?.format, ...own?.format },\n };\n if (appearance.fill) api.setTableCellFill(cell, appearance.fill);\n if (spec.paragraphs) {\n // addSlideTable bakes the deck's body-text color into each run so text\n // stays readable on an inverted color map; new runs must keep it.\n const built = api.getTableCellParagraphs(cell)[0]?.elements[0];\n const base = {\n ...(built?.kind === 'r' && built.format ? writableFormat(built.format) : {}),\n ...appearance.format,\n };\n const { align } = appearance;\n api.setTableCellParagraphs(\n cell,\n spec.paragraphs.map((paragraph) => ({\n ...paragraph,\n ...(paragraph.align === undefined && align ? { align } : {}),\n runs: paragraph.runs.map((run) => ({ ...run, format: { ...base, ...run.format } })),\n })),\n );\n } else {\n if (appearance.format) api.setTableCellTextFormat(cell, appearance.format);\n if (appearance.align) api.setTableCellAlignment(cell, appearance.align);\n }\n if (appearance.anchor) api.setTableCellAnchor(cell, appearance.anchor);\n // Only when a side is set: an empty call would still give every cell a <a:tcPr>.\n const borders = cellBorders({\n ...props.cellStyle?.borders,\n ...header?.borders,\n ...own?.borders,\n });\n if (Object.keys(borders).length > 0) api.setTableCellBorders(cell, borders);\n }),\n );\n props.rows.forEach((row, r) =>\n row.forEach((entry, c) => {\n const { rowSpan = 1, colSpan = 1 } = cellSpec(entry);\n if (rowSpan !== 1 || colSpan !== 1)\n api.mergeTableCells(shape, { row: r, col: c, rowSpan, colSpan });\n }),\n );\n await visit(props.children, { ...context, shape, scope: 'shape' });\n });\n}\nexport interface LineProps {\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n color?: api.Color;\n /** Stroke width in points. */\n width?: number;\n name?: string;\n}\nexport function Line(props: LineProps): Node {\n return node('Line', (context) => {\n created(\n context,\n api.addSlideLine(requireSlide(context), {\n from: { x: api.inches(props.x1), y: api.inches(props.y1) },\n to: { x: api.inches(props.x2), y: api.inches(props.y2) },\n ...(props.color !== undefined ? { color: props.color } : {}),\n ...(props.width !== undefined ? { widthEmu: api.pt(props.width) } : {}),\n ...(props.name ? { name: props.name } : {}),\n }),\n );\n });\n}\nexport interface GroupProps extends Children {\n name?: string;\n}\n/** Groups the shapes its children create, so they move and resize as one object. */\nexport function Group(props: GroupProps): Node {\n return node('Group', async (context) => {\n requireSlide(context);\n const members: api.SlideShapeData[] = [];\n await visit(props.children, { ...context, members });\n created(context, api.groupShapes(members, props.name ? { name: props.name } : {}));\n });\n}\nfunction selectShape(slide: api.SlideData, target: Target): api.SlideShapeData {\n const matches = api.getSlideShapes(slide).filter((shape) => {\n if ('name' in target) return api.getShapeName(shape) === target.name;\n if ('id' in target) return api.getShapeId(shape) === target.id;\n const ph = target.placeholder;\n return (\n (ph.type !== undefined || ph.idx !== undefined) &&\n (ph.type === undefined || api.getShapePlaceholderType(shape) === ph.type) &&\n (ph.idx === undefined || api.getShapePlaceholderIdx(shape) === ph.idx)\n );\n });\n if (matches.length !== 1)\n throw new Error(`Shape reference matched ${matches.length} shapes: ${JSON.stringify(target)}`);\n return matches[0]!;\n}\nexport interface FillProps extends Children {\n target: Target;\n text?: string;\n format?: api.TextFormat;\n /** Omitted keeps whatever the template's `<a:bodyPr>` says. */\n autoFit?: api.TextAutoFit;\n}\nexport function Fill(props: FillProps): Node {\n return node('Fill', (context) => {\n const shape = selectShape(requireSlide(context), props.target);\n api.setShapeText(shape, props.text ?? literal(props.children));\n if (props.format) api.setShapeTextFormat(shape, props.format);\n if (props.autoFit) api.setShapeTextAutoFit(shape, props.autoFit);\n });\n}\nexport function Remove(props: { target: Target }): Node {\n return node('Remove', (context) => {\n const slide = requireSlide(context);\n api.removeShape(selectShape(slide, props.target));\n });\n}\n"],"mappings":"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"}
|
package/dist/source-edit.d.ts
CHANGED
|
@@ -60,6 +60,53 @@ declare function verifyTextEdit(request: {
|
|
|
60
60
|
before: string;
|
|
61
61
|
after: string;
|
|
62
62
|
}): TextEditVerification;
|
|
63
|
+
/** DSL elements that create a shape; only their attributes are edited in place. */
|
|
64
|
+
declare const SHAPE_ELEMENTS: readonly ["Text", "Shape", "Image", "Line", "Table", "Chart", "Media", "Group"];
|
|
65
|
+
/**
|
|
66
|
+
* The OOXML wire unit behind each numeric prop. Geometry props are inches that
|
|
67
|
+
* `inches()` rounds to whole EMU (`ST_Coordinate`); `rotation` is degrees the
|
|
68
|
+
* core rounds to whole 60000ths of a degree (`ST_Angle`).
|
|
69
|
+
*/
|
|
70
|
+
declare const PROP_WIRE_UNITS: {
|
|
71
|
+
readonly x: 914400;
|
|
72
|
+
readonly y: 914400;
|
|
73
|
+
readonly width: 914400;
|
|
74
|
+
readonly height: 914400;
|
|
75
|
+
readonly rotation: 60000;
|
|
76
|
+
};
|
|
77
|
+
type NumericProp = keyof typeof PROP_WIRE_UNITS;
|
|
78
|
+
interface PropEdit {
|
|
79
|
+
prop: NumericProp;
|
|
80
|
+
/** The wire value the current source produces (whole EMU or 60000ths of a degree). */
|
|
81
|
+
from: number;
|
|
82
|
+
/** The wire value the edited deck has. */
|
|
83
|
+
to: number;
|
|
84
|
+
}
|
|
85
|
+
interface PropEditRequest {
|
|
86
|
+
file: SourceFile;
|
|
87
|
+
/** The element that created the shape: the innermost `getShapeJsxSources` entry. */
|
|
88
|
+
anchor: TextEditAnchor;
|
|
89
|
+
edits: readonly PropEdit[];
|
|
90
|
+
}
|
|
91
|
+
type PropEditPlan = {
|
|
92
|
+
ok: true;
|
|
93
|
+
change: {
|
|
94
|
+
path: string;
|
|
95
|
+
before: string;
|
|
96
|
+
after: string;
|
|
97
|
+
};
|
|
98
|
+
} | {
|
|
99
|
+
ok: false;
|
|
100
|
+
reason: 'invalid' | 'not-element' | 'not-shape-element' | 'spread' | 'not-numeric';
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Plans writing edited numeric props back onto the JSX element that created a
|
|
104
|
+
* shape. A literal is replaced by the shortest decimal that reproduces the
|
|
105
|
+
* wire value; any other expression keeps its meaning and gains a delta
|
|
106
|
+
* (`x={col * 2.5}` → `x={col * 2.5 + 0.3}`), folding into an earlier delta. A
|
|
107
|
+
* missing prop is inserted. Only this element changes, never a shared value.
|
|
108
|
+
*/
|
|
109
|
+
declare function planPropEdit(request: PropEditRequest): PropEditPlan;
|
|
63
110
|
//#endregion
|
|
64
|
-
export { PreviewSnapshot, SourceFile, TEXT_EDIT_MAX_LENGTH, TextEditAnchor, TextEditPlan, TextEditRequest, TextEditVerification, planTextEdit, verifyTextEdit };
|
|
111
|
+
export { NumericProp, PROP_WIRE_UNITS, PreviewSnapshot, PropEdit, PropEditPlan, PropEditRequest, SHAPE_ELEMENTS, SourceFile, TEXT_EDIT_MAX_LENGTH, TextEditAnchor, TextEditPlan, TextEditRequest, TextEditVerification, planPropEdit, planTextEdit, verifyTextEdit };
|
|
65
112
|
//# sourceMappingURL=source-edit.d.ts.map
|
package/dist/source-edit.js
CHANGED
|
@@ -91,7 +91,148 @@ function verifyTextEdit(request) {
|
|
|
91
91
|
};
|
|
92
92
|
return { ok: true };
|
|
93
93
|
}
|
|
94
|
+
/** DSL elements that create a shape; only their attributes are edited in place. */
|
|
95
|
+
const SHAPE_ELEMENTS = [
|
|
96
|
+
"Text",
|
|
97
|
+
"Shape",
|
|
98
|
+
"Image",
|
|
99
|
+
"Line",
|
|
100
|
+
"Table",
|
|
101
|
+
"Chart",
|
|
102
|
+
"Media",
|
|
103
|
+
"Group"
|
|
104
|
+
];
|
|
105
|
+
/**
|
|
106
|
+
* The OOXML wire unit behind each numeric prop. Geometry props are inches that
|
|
107
|
+
* `inches()` rounds to whole EMU (`ST_Coordinate`); `rotation` is degrees the
|
|
108
|
+
* core rounds to whole 60000ths of a degree (`ST_Angle`).
|
|
109
|
+
*/
|
|
110
|
+
const PROP_WIRE_UNITS = {
|
|
111
|
+
x: 914400,
|
|
112
|
+
y: 914400,
|
|
113
|
+
width: 914400,
|
|
114
|
+
height: 914400,
|
|
115
|
+
rotation: 6e4
|
|
116
|
+
};
|
|
117
|
+
const MAX_DECIMALS = 10;
|
|
118
|
+
function wireDecimal(units, wire) {
|
|
119
|
+
for (let digits = 0; digits <= MAX_DECIMALS; digits++) {
|
|
120
|
+
const text = String(Number((wire / units).toFixed(digits)));
|
|
121
|
+
if (Math.round(Number(text) * units) === wire) return text;
|
|
122
|
+
}
|
|
123
|
+
return String(wire / units);
|
|
124
|
+
}
|
|
125
|
+
function deltaDecimal(units, delta) {
|
|
126
|
+
for (let digits = 0; digits <= MAX_DECIMALS; digits++) {
|
|
127
|
+
const text = String(Number((delta / units).toFixed(digits)));
|
|
128
|
+
if (Math.abs(Number(text) * units - delta) < 1e-6) return text;
|
|
129
|
+
}
|
|
130
|
+
return String(delta / units);
|
|
131
|
+
}
|
|
132
|
+
const FULL_TURN = 360 * 6e4;
|
|
133
|
+
function wrappedDelta(prop, from, to) {
|
|
134
|
+
const delta = to - from;
|
|
135
|
+
if (prop !== "rotation") return delta;
|
|
136
|
+
const turned = (delta % FULL_TURN + FULL_TURN) % FULL_TURN;
|
|
137
|
+
return turned > FULL_TURN / 2 ? turned - FULL_TURN : turned;
|
|
138
|
+
}
|
|
139
|
+
function numericLiteral(node) {
|
|
140
|
+
return ts.isNumericLiteral(node) || ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand);
|
|
141
|
+
}
|
|
142
|
+
function bindsTighterThanAdd(node) {
|
|
143
|
+
if (ts.isBinaryExpression(node)) {
|
|
144
|
+
const op = node.operatorToken.kind;
|
|
145
|
+
return op === ts.SyntaxKind.AsteriskToken || op === ts.SyntaxKind.SlashToken || op === ts.SyntaxKind.PercentToken || op === ts.SyntaxKind.AsteriskAsteriskToken;
|
|
146
|
+
}
|
|
147
|
+
return !(ts.isConditionalExpression(node) || ts.isArrowFunction(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isYieldExpression(node) || ts.isAwaitExpression(node) || ts.isPrefixUnaryExpression(node) || ts.isTypeOfExpression(node) || ts.isVoidExpression(node) || ts.isDeleteExpression(node));
|
|
148
|
+
}
|
|
149
|
+
function rewriteExpression(source, expression, edit) {
|
|
150
|
+
const units = PROP_WIRE_UNITS[edit.prop];
|
|
151
|
+
if (numericLiteral(expression)) return wireDecimal(units, edit.to);
|
|
152
|
+
const delta = wrappedDelta(edit.prop, edit.from, edit.to);
|
|
153
|
+
if (ts.isBinaryExpression(expression) && (expression.operatorToken.kind === ts.SyntaxKind.PlusToken || expression.operatorToken.kind === ts.SyntaxKind.MinusToken) && ts.isNumericLiteral(expression.right)) {
|
|
154
|
+
const sign = expression.operatorToken.kind === ts.SyntaxKind.PlusToken ? 1 : -1;
|
|
155
|
+
const total = Math.round(sign * Number(expression.right.text) * units) + delta;
|
|
156
|
+
const left = expression.left.getText(source);
|
|
157
|
+
if (total === 0) return left;
|
|
158
|
+
return `${left} ${total > 0 ? "+" : "-"} ${deltaDecimal(units, Math.abs(total))}`;
|
|
159
|
+
}
|
|
160
|
+
const text = expression.getText(source);
|
|
161
|
+
return `${bindsTighterThanAdd(expression) ? text : `(${text})`} ${delta > 0 ? "+" : "-"} ${deltaDecimal(units, Math.abs(delta))}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Plans writing edited numeric props back onto the JSX element that created a
|
|
165
|
+
* shape. A literal is replaced by the shortest decimal that reproduces the
|
|
166
|
+
* wire value; any other expression keeps its meaning and gains a delta
|
|
167
|
+
* (`x={col * 2.5}` → `x={col * 2.5 + 0.3}`), folding into an earlier delta. A
|
|
168
|
+
* missing prop is inserted. Only this element changes, never a shared value.
|
|
169
|
+
*/
|
|
170
|
+
function planPropEdit(request) {
|
|
171
|
+
const { file, anchor, edits } = request;
|
|
172
|
+
if (edits.length === 0 || edits.some((edit) => !Object.hasOwn(PROP_WIRE_UNITS, edit.prop) || !Number.isInteger(edit.from) || !Number.isInteger(edit.to)) || new Set(edits.map((edit) => edit.prop)).size !== edits.length) return {
|
|
173
|
+
ok: false,
|
|
174
|
+
reason: "invalid"
|
|
175
|
+
};
|
|
176
|
+
const source = ts.createSourceFile(file.path, file.source, ts.ScriptTarget.Latest, true, /[jt]sx$/.test(file.path) ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
177
|
+
const element = anchoredElement(source, anchor);
|
|
178
|
+
if (!element || !(ts.isJsxElement(element) || ts.isJsxSelfClosingElement(element))) return {
|
|
179
|
+
ok: false,
|
|
180
|
+
reason: "not-element"
|
|
181
|
+
};
|
|
182
|
+
const opening = ts.isJsxElement(element) ? element.openingElement : element;
|
|
183
|
+
if (!ts.isIdentifier(opening.tagName) || !SHAPE_ELEMENTS.includes(opening.tagName.text)) return {
|
|
184
|
+
ok: false,
|
|
185
|
+
reason: "not-shape-element"
|
|
186
|
+
};
|
|
187
|
+
const attributes = opening.attributes.properties;
|
|
188
|
+
const replacements = [];
|
|
189
|
+
const inserted = [];
|
|
190
|
+
for (const edit of edits) {
|
|
191
|
+
if (edit.from === edit.to) continue;
|
|
192
|
+
const attribute = attributes.find((item) => ts.isJsxAttribute(item) && item.name.getText(source) === edit.prop);
|
|
193
|
+
if (!attribute) {
|
|
194
|
+
if (attributes.some(ts.isJsxSpreadAttribute)) return {
|
|
195
|
+
ok: false,
|
|
196
|
+
reason: "spread"
|
|
197
|
+
};
|
|
198
|
+
inserted.push(`${edit.prop}={${wireDecimal(PROP_WIRE_UNITS[edit.prop], edit.to)}}`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const initializer = attribute.initializer;
|
|
202
|
+
if (!initializer || !ts.isJsxExpression(initializer) || !initializer.expression) return {
|
|
203
|
+
ok: false,
|
|
204
|
+
reason: "not-numeric"
|
|
205
|
+
};
|
|
206
|
+
replacements.push({
|
|
207
|
+
start: initializer.expression.getStart(source),
|
|
208
|
+
end: initializer.expression.end,
|
|
209
|
+
text: rewriteExpression(source, initializer.expression, edit)
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (replacements.length === 0 && inserted.length === 0) return {
|
|
213
|
+
ok: false,
|
|
214
|
+
reason: "invalid"
|
|
215
|
+
};
|
|
216
|
+
if (inserted.length > 0) {
|
|
217
|
+
const at = attributes.end;
|
|
218
|
+
replacements.push({
|
|
219
|
+
start: at,
|
|
220
|
+
end: at,
|
|
221
|
+
text: " " + inserted.join(" ")
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
let after = file.source;
|
|
225
|
+
for (const { start, end, text } of replacements.sort((a, b) => b.start - a.start)) after = after.slice(0, start) + text + after.slice(end);
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
change: {
|
|
229
|
+
path: file.path,
|
|
230
|
+
before: file.source,
|
|
231
|
+
after
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
94
235
|
//#endregion
|
|
95
|
-
export { TEXT_EDIT_MAX_LENGTH, planTextEdit, verifyTextEdit };
|
|
236
|
+
export { PROP_WIRE_UNITS, SHAPE_ELEMENTS, TEXT_EDIT_MAX_LENGTH, planPropEdit, planTextEdit, verifyTextEdit };
|
|
96
237
|
|
|
97
238
|
//# sourceMappingURL=source-edit.js.map
|
package/dist/source-edit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-edit.js","names":[],"sources":["../src/source-edit.ts"],"sourcesContent":["import ts from 'typescript';\n\n/** Longest `before` / `after` a text edit accepts, in UTF-16 code units. */\nexport const TEXT_EDIT_MAX_LENGTH = 12000;\n\nexport interface SourceFile {\n path: string;\n source: string;\n}\n\nexport type TextEditPlan =\n | { ok: true; change: { path: string; before: string; after: string } }\n | { ok: false; reason: 'invalid' | 'not-unique-on-slide' | 'not-found' | 'ambiguous' };\n\n/** A `getShapeJsxSources` entry, with `fileName` given as the matching `SourceFile.path`. */\nexport interface TextEditAnchor {\n path: string;\n /** 1-based, as in `JsxSource`. */\n lineNumber: number;\n /** 1-based, as in `JsxSource`. */\n columnNumber: number;\n}\n\nexport interface TextEditRequest {\n /** Only these files are searched; pass the slide sources, not shared components. */\n files: readonly SourceFile[];\n /** The rendered text of the slide being edited. `before` must occur in it exactly once. */\n slideText: string;\n before: string;\n after: string;\n /** Searches only the JSX element that starts here. */\n anchor?: TextEditAnchor;\n}\n\n// A JSX text node renders the way JSX collapses it: each line trimmed where\n// it meets a line break, blank lines dropped, the rest joined by one space.\nfunction jsxText(node: ts.JsxText): string {\n return node.text\n .split(/\\r\\n|\\n|\\r/)\n .map((line, index, lines) => {\n let value = line.replaceAll('\\t', ' ');\n if (index) value = value.trimStart();\n if (index < lines.length - 1) value = value.trimEnd();\n return value;\n })\n .filter(Boolean)\n .join(' ');\n}\n\nfunction anchoredElement(source: ts.SourceFile, anchor: TextEditAnchor): ts.Node | undefined {\n const lines = source.getLineStarts();\n const lineStart = lines[anchor.lineNumber - 1];\n // A stale anchor may point past the file or past the end of its line.\n if (lineStart === undefined || anchor.columnNumber < 1) return undefined;\n const position = lineStart + anchor.columnNumber - 1;\n if (position >= (lines[anchor.lineNumber] ?? source.end)) return undefined;\n let found: ts.Node | undefined;\n const visit = (node: ts.Node) => {\n if (found || node.getStart(source) > position || node.end <= position) return;\n if (\n node.getStart(source) === position &&\n (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node))\n )\n found = node;\n else ts.forEachChild(node, visit);\n };\n visit(source);\n return found;\n}\n\n/**\n * Plans replacing the source literal that renders `before` with `after`.\n * Only a JSX text or a string literal written out whole qualifies; computed\n * text and text written in more than one place are refused, so the caller\n * can fall back to a broader edit.\n */\nexport function planTextEdit(request: TextEditRequest): TextEditPlan {\n const { files, slideText, before, after, anchor } = request;\n if (\n !before.trim() ||\n before.length > TEXT_EDIT_MAX_LENGTH ||\n after.length > TEXT_EDIT_MAX_LENGTH ||\n after === before\n )\n return { ok: false, reason: 'invalid' };\n if (slideText.split(before).length !== 2) return { ok: false, reason: 'not-unique-on-slide' };\n const changes: { path: string; before: string; after: string }[] = [];\n for (const file of files) {\n if (anchor && file.path !== anchor.path) continue;\n const source = ts.createSourceFile(\n file.path,\n file.source,\n ts.ScriptTarget.Latest,\n true,\n /[jt]sx$/.test(file.path) ? ts.ScriptKind.TSX : ts.ScriptKind.TS,\n );\n const root = anchor ? anchoredElement(source, anchor) : source;\n if (!root) continue;\n const visit = (node: ts.Node) => {\n const text = ts.isJsxText(node)\n ? jsxText(node)\n : ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)\n ? node.text\n : undefined;\n if (text === before) {\n // Attribute strings use JSX entity rules, not JavaScript string escapes.\n const jsx = ts.isJsxText(node) || ts.isJsxAttribute(node.parent);\n const replacement = jsx ? '{' + JSON.stringify(after) + '}' : JSON.stringify(after);\n const start = ts.isJsxText(node) ? node.pos : node.getStart(source);\n changes.push({\n path: file.path,\n before: file.source,\n after: file.source.slice(0, start) + replacement + file.source.slice(node.end),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(root);\n }\n if (changes.length === 0) return { ok: false, reason: 'not-found' };\n if (changes.length > 1) return { ok: false, reason: 'ambiguous' };\n return { ok: true, change: changes[0]! };\n}\n\nexport type TextEditVerification =\n | { ok: true }\n | { ok: false; reason: 'slide-count-changed' | 'other-slides-changed' | 'text-mismatch' };\n\nexport interface PreviewSnapshot {\n /** One rendering per slide (SVG); compared for equality only. */\n slides: readonly string[];\n slideTexts: readonly string[];\n}\n\n/** Checks that a rebuilt deck differs from the previous one only by `before` → `after` on `slide`. */\nexport function verifyTextEdit(request: {\n previous: PreviewSnapshot;\n next: PreviewSnapshot;\n slide: number;\n before: string;\n after: string;\n}): TextEditVerification {\n const { previous, next, slide, before, after } = request;\n if (next.slides.length !== previous.slides.length)\n return { ok: false, reason: 'slide-count-changed' };\n if (next.slides.some((svg, index) => index !== slide && svg !== previous.slides[index]))\n return { ok: false, reason: 'other-slides-changed' };\n // A function replacement: `after` may contain `$&` and friends.\n if (next.slideTexts[slide] !== previous.slideTexts[slide]?.replace(before, () => after))\n return { ok: false, reason: 'text-mismatch' };\n return { ok: true };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"source-edit.js","names":[],"sources":["../src/source-edit.ts"],"sourcesContent":["import ts from 'typescript';\n\n/** Longest `before` / `after` a text edit accepts, in UTF-16 code units. */\nexport const TEXT_EDIT_MAX_LENGTH = 12000;\n\nexport interface SourceFile {\n path: string;\n source: string;\n}\n\nexport type TextEditPlan =\n | { ok: true; change: { path: string; before: string; after: string } }\n | { ok: false; reason: 'invalid' | 'not-unique-on-slide' | 'not-found' | 'ambiguous' };\n\n/** A `getShapeJsxSources` entry, with `fileName` given as the matching `SourceFile.path`. */\nexport interface TextEditAnchor {\n path: string;\n /** 1-based, as in `JsxSource`. */\n lineNumber: number;\n /** 1-based, as in `JsxSource`. */\n columnNumber: number;\n}\n\nexport interface TextEditRequest {\n /** Only these files are searched; pass the slide sources, not shared components. */\n files: readonly SourceFile[];\n /** The rendered text of the slide being edited. `before` must occur in it exactly once. */\n slideText: string;\n before: string;\n after: string;\n /** Searches only the JSX element that starts here. */\n anchor?: TextEditAnchor;\n}\n\n// A JSX text node renders the way JSX collapses it: each line trimmed where\n// it meets a line break, blank lines dropped, the rest joined by one space.\nfunction jsxText(node: ts.JsxText): string {\n return node.text\n .split(/\\r\\n|\\n|\\r/)\n .map((line, index, lines) => {\n let value = line.replaceAll('\\t', ' ');\n if (index) value = value.trimStart();\n if (index < lines.length - 1) value = value.trimEnd();\n return value;\n })\n .filter(Boolean)\n .join(' ');\n}\n\nfunction anchoredElement(source: ts.SourceFile, anchor: TextEditAnchor): ts.Node | undefined {\n const lines = source.getLineStarts();\n const lineStart = lines[anchor.lineNumber - 1];\n // A stale anchor may point past the file or past the end of its line.\n if (lineStart === undefined || anchor.columnNumber < 1) return undefined;\n const position = lineStart + anchor.columnNumber - 1;\n if (position >= (lines[anchor.lineNumber] ?? source.end)) return undefined;\n let found: ts.Node | undefined;\n const visit = (node: ts.Node) => {\n if (found || node.getStart(source) > position || node.end <= position) return;\n if (\n node.getStart(source) === position &&\n (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node))\n )\n found = node;\n else ts.forEachChild(node, visit);\n };\n visit(source);\n return found;\n}\n\n/**\n * Plans replacing the source literal that renders `before` with `after`.\n * Only a JSX text or a string literal written out whole qualifies; computed\n * text and text written in more than one place are refused, so the caller\n * can fall back to a broader edit.\n */\nexport function planTextEdit(request: TextEditRequest): TextEditPlan {\n const { files, slideText, before, after, anchor } = request;\n if (\n !before.trim() ||\n before.length > TEXT_EDIT_MAX_LENGTH ||\n after.length > TEXT_EDIT_MAX_LENGTH ||\n after === before\n )\n return { ok: false, reason: 'invalid' };\n if (slideText.split(before).length !== 2) return { ok: false, reason: 'not-unique-on-slide' };\n const changes: { path: string; before: string; after: string }[] = [];\n for (const file of files) {\n if (anchor && file.path !== anchor.path) continue;\n const source = ts.createSourceFile(\n file.path,\n file.source,\n ts.ScriptTarget.Latest,\n true,\n /[jt]sx$/.test(file.path) ? ts.ScriptKind.TSX : ts.ScriptKind.TS,\n );\n const root = anchor ? anchoredElement(source, anchor) : source;\n if (!root) continue;\n const visit = (node: ts.Node) => {\n const text = ts.isJsxText(node)\n ? jsxText(node)\n : ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)\n ? node.text\n : undefined;\n if (text === before) {\n // Attribute strings use JSX entity rules, not JavaScript string escapes.\n const jsx = ts.isJsxText(node) || ts.isJsxAttribute(node.parent);\n const replacement = jsx ? '{' + JSON.stringify(after) + '}' : JSON.stringify(after);\n const start = ts.isJsxText(node) ? node.pos : node.getStart(source);\n changes.push({\n path: file.path,\n before: file.source,\n after: file.source.slice(0, start) + replacement + file.source.slice(node.end),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(root);\n }\n if (changes.length === 0) return { ok: false, reason: 'not-found' };\n if (changes.length > 1) return { ok: false, reason: 'ambiguous' };\n return { ok: true, change: changes[0]! };\n}\n\nexport type TextEditVerification =\n | { ok: true }\n | { ok: false; reason: 'slide-count-changed' | 'other-slides-changed' | 'text-mismatch' };\n\nexport interface PreviewSnapshot {\n /** One rendering per slide (SVG); compared for equality only. */\n slides: readonly string[];\n slideTexts: readonly string[];\n}\n\n/** Checks that a rebuilt deck differs from the previous one only by `before` → `after` on `slide`. */\nexport function verifyTextEdit(request: {\n previous: PreviewSnapshot;\n next: PreviewSnapshot;\n slide: number;\n before: string;\n after: string;\n}): TextEditVerification {\n const { previous, next, slide, before, after } = request;\n if (next.slides.length !== previous.slides.length)\n return { ok: false, reason: 'slide-count-changed' };\n if (next.slides.some((svg, index) => index !== slide && svg !== previous.slides[index]))\n return { ok: false, reason: 'other-slides-changed' };\n // A function replacement: `after` may contain `$&` and friends.\n if (next.slideTexts[slide] !== previous.slideTexts[slide]?.replace(before, () => after))\n return { ok: false, reason: 'text-mismatch' };\n return { ok: true };\n}\n\n/** DSL elements that create a shape; only their attributes are edited in place. */\nexport const SHAPE_ELEMENTS = [\n 'Text',\n 'Shape',\n 'Image',\n 'Line',\n 'Table',\n 'Chart',\n 'Media',\n 'Group',\n] as const;\n\n/**\n * The OOXML wire unit behind each numeric prop. Geometry props are inches that\n * `inches()` rounds to whole EMU (`ST_Coordinate`); `rotation` is degrees the\n * core rounds to whole 60000ths of a degree (`ST_Angle`).\n */\nexport const PROP_WIRE_UNITS = {\n x: 914400,\n y: 914400,\n width: 914400,\n height: 914400,\n rotation: 60000,\n} as const;\nexport type NumericProp = keyof typeof PROP_WIRE_UNITS;\n\nexport interface PropEdit {\n prop: NumericProp;\n /** The wire value the current source produces (whole EMU or 60000ths of a degree). */\n from: number;\n /** The wire value the edited deck has. */\n to: number;\n}\n\nexport interface PropEditRequest {\n file: SourceFile;\n /** The element that created the shape: the innermost `getShapeJsxSources` entry. */\n anchor: TextEditAnchor;\n edits: readonly PropEdit[];\n}\n\nexport type PropEditPlan =\n | { ok: true; change: { path: string; before: string; after: string } }\n | {\n ok: false;\n reason: 'invalid' | 'not-element' | 'not-shape-element' | 'spread' | 'not-numeric';\n };\n\n// Six decimals of an inch and five of a degree always pin a wire value; the\n// search returns the shortest decimal that rounds to it.\nconst MAX_DECIMALS = 10;\nfunction wireDecimal(units: number, wire: number): string {\n for (let digits = 0; digits <= MAX_DECIMALS; digits++) {\n const text = String(Number((wire / units).toFixed(digits)));\n if (Math.round(Number(text) * units) === wire) return text;\n }\n return String(wire / units);\n}\n// A delta added to an expression shifts its wire value by exactly `delta`\n// units once the sum is rounded, so the decimal only has to land within a\n// tiny fraction of a unit rather than round to it.\nfunction deltaDecimal(units: number, delta: number): string {\n for (let digits = 0; digits <= MAX_DECIMALS; digits++) {\n const text = String(Number((delta / units).toFixed(digits)));\n if (Math.abs(Number(text) * units - delta) < 1e-6) return text;\n }\n return String(delta / units);\n}\n// Degrees wrap, so a turn is written the short way round: 350° → 10° is −20°.\nconst FULL_TURN = 360 * 60000;\nfunction wrappedDelta(prop: NumericProp, from: number, to: number): number {\n const delta = to - from;\n if (prop !== 'rotation') return delta;\n const turned = ((delta % FULL_TURN) + FULL_TURN) % FULL_TURN;\n return turned > FULL_TURN / 2 ? turned - FULL_TURN : turned;\n}\n\nfunction numericLiteral(node: ts.Expression): boolean {\n return (\n ts.isNumericLiteral(node) ||\n (ts.isPrefixUnaryExpression(node) &&\n node.operator === ts.SyntaxKind.MinusToken &&\n ts.isNumericLiteral(node.operand))\n );\n}\n// Expressions that bind tighter than `+`, so `expr + d` needs no parentheses.\nfunction bindsTighterThanAdd(node: ts.Expression): boolean {\n if (ts.isBinaryExpression(node)) {\n const op = node.operatorToken.kind;\n return (\n op === ts.SyntaxKind.AsteriskToken ||\n op === ts.SyntaxKind.SlashToken ||\n op === ts.SyntaxKind.PercentToken ||\n op === ts.SyntaxKind.AsteriskAsteriskToken\n );\n }\n return !(\n ts.isConditionalExpression(node) ||\n ts.isArrowFunction(node) ||\n ts.isAsExpression(node) ||\n ts.isSatisfiesExpression(node) ||\n ts.isYieldExpression(node) ||\n ts.isAwaitExpression(node) ||\n ts.isPrefixUnaryExpression(node) ||\n ts.isTypeOfExpression(node) ||\n ts.isVoidExpression(node) ||\n ts.isDeleteExpression(node)\n );\n}\n\nfunction rewriteExpression(\n source: ts.SourceFile,\n expression: ts.Expression,\n edit: PropEdit,\n): string {\n const units = PROP_WIRE_UNITS[edit.prop];\n if (numericLiteral(expression)) return wireDecimal(units, edit.to);\n const delta = wrappedDelta(edit.prop, edit.from, edit.to);\n // `expr ± n` from an earlier edit: fold into the same trailing literal.\n if (\n ts.isBinaryExpression(expression) &&\n (expression.operatorToken.kind === ts.SyntaxKind.PlusToken ||\n expression.operatorToken.kind === ts.SyntaxKind.MinusToken) &&\n ts.isNumericLiteral(expression.right)\n ) {\n const sign = expression.operatorToken.kind === ts.SyntaxKind.PlusToken ? 1 : -1;\n const previous = Math.round(sign * Number(expression.right.text) * units);\n const total = previous + delta;\n const left = expression.left.getText(source);\n if (total === 0) return left;\n return `${left} ${total > 0 ? '+' : '-'} ${deltaDecimal(units, Math.abs(total))}`;\n }\n const text = expression.getText(source);\n const base = bindsTighterThanAdd(expression) ? text : `(${text})`;\n return `${base} ${delta > 0 ? '+' : '-'} ${deltaDecimal(units, Math.abs(delta))}`;\n}\n\n/**\n * Plans writing edited numeric props back onto the JSX element that created a\n * shape. A literal is replaced by the shortest decimal that reproduces the\n * wire value; any other expression keeps its meaning and gains a delta\n * (`x={col * 2.5}` → `x={col * 2.5 + 0.3}`), folding into an earlier delta. A\n * missing prop is inserted. Only this element changes, never a shared value.\n */\nexport function planPropEdit(request: PropEditRequest): PropEditPlan {\n const { file, anchor, edits } = request;\n if (\n edits.length === 0 ||\n edits.some(\n (edit) =>\n !Object.hasOwn(PROP_WIRE_UNITS, edit.prop) ||\n !Number.isInteger(edit.from) ||\n !Number.isInteger(edit.to),\n ) ||\n new Set(edits.map((edit) => edit.prop)).size !== edits.length\n )\n return { ok: false, reason: 'invalid' };\n const source = ts.createSourceFile(\n file.path,\n file.source,\n ts.ScriptTarget.Latest,\n true,\n /[jt]sx$/.test(file.path) ? ts.ScriptKind.TSX : ts.ScriptKind.TS,\n );\n const element = anchoredElement(source, anchor);\n if (!element || !(ts.isJsxElement(element) || ts.isJsxSelfClosingElement(element)))\n return { ok: false, reason: 'not-element' };\n const opening = ts.isJsxElement(element) ? element.openingElement : element;\n if (\n !ts.isIdentifier(opening.tagName) ||\n !(SHAPE_ELEMENTS as readonly string[]).includes(opening.tagName.text)\n )\n return { ok: false, reason: 'not-shape-element' };\n const attributes = opening.attributes.properties;\n const replacements: { start: number; end: number; text: string }[] = [];\n const inserted: string[] = [];\n for (const edit of edits) {\n if (edit.from === edit.to) continue;\n const attribute = attributes.find(\n (item): item is ts.JsxAttribute =>\n ts.isJsxAttribute(item) && item.name.getText(source) === edit.prop,\n );\n if (!attribute) {\n // A spread could be what sets the prop now; an inserted attribute would\n // silently win over it or be overridden by it depending on order.\n if (attributes.some(ts.isJsxSpreadAttribute)) return { ok: false, reason: 'spread' };\n inserted.push(`${edit.prop}={${wireDecimal(PROP_WIRE_UNITS[edit.prop], edit.to)}}`);\n continue;\n }\n const initializer = attribute.initializer;\n if (!initializer || !ts.isJsxExpression(initializer) || !initializer.expression)\n return { ok: false, reason: 'not-numeric' };\n replacements.push({\n start: initializer.expression.getStart(source),\n end: initializer.expression.end,\n text: rewriteExpression(source, initializer.expression, edit),\n });\n }\n if (replacements.length === 0 && inserted.length === 0) return { ok: false, reason: 'invalid' };\n if (inserted.length > 0) {\n const at = attributes.end;\n replacements.push({ start: at, end: at, text: ' ' + inserted.join(' ') });\n }\n let after = file.source;\n for (const { start, end, text } of replacements.sort((a, b) => b.start - a.start))\n after = after.slice(0, start) + text + after.slice(end);\n return { ok: true, change: { path: file.path, before: file.source, after } };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@office-kit/pptx-dsl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Typed TSX authoring for editable PowerPoint presentations",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"provenance": true
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@office-kit/pptx": "0.
|
|
41
|
+
"@office-kit/pptx": "0.23.0",
|
|
42
42
|
"tsdown": "^0.22.2",
|
|
43
43
|
"typescript": "^6.0.3"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@office-kit/pptx": "^0.
|
|
46
|
+
"@office-kit/pptx": "^0.23.0",
|
|
47
47
|
"typescript": ">=5.0.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|