mirror of
https://github.com/openai/codex.git
synced 2026-03-05 21:45:28 +03:00
feat: skills for artifacts
This commit is contained in:
137
codex-rs/skills/src/assets/samples/slides/SKILL.md
Normal file
137
codex-rs/skills/src/assets/samples/slides/SKILL.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
---
|
||||||
|
name: slides
|
||||||
|
description: Build, edit, render, import, and export presentation decks with the preloaded @oai/artifact-tool JavaScript surface through the artifacts tool.
|
||||||
|
metadata:
|
||||||
|
short-description: Use the artifacts tool to create and edit slide decks in JavaScript
|
||||||
|
---
|
||||||
|
|
||||||
|
# Slides
|
||||||
|
|
||||||
|
Use this skill when the user wants to create or modify presentation decks with the `artifacts` tool.
|
||||||
|
|
||||||
|
## Tool Contract
|
||||||
|
|
||||||
|
- Use the `artifacts` tool.
|
||||||
|
- Send raw JavaScript only. Do not send JSON objects, quoted code, or markdown fences.
|
||||||
|
- This tool runs plain JavaScript in Node, not TypeScript. Do not use type annotations, `type`, `interface`, or `import type`.
|
||||||
|
- Do not write `import { ... } from "@oai/artifact-tool"`. The `@oai/artifact-tool` module surface is already preloaded on `globalThis`.
|
||||||
|
- Named exports such as `Presentation`, `PresentationFile`, `FileBlob`, `AutoLayoutAlign`, and `AutoLayoutDirection` are available directly.
|
||||||
|
- The full module is also available as `artifactTool`, `artifacts`, and `codexArtifacts`.
|
||||||
|
- You may still import Node built-ins such as `node:fs/promises` when you need to write preview bytes to disk.
|
||||||
|
- Save outputs under a user-visible path such as `artifacts/quarterly-update.pptx` or `artifacts/slide-1.png`.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```js
|
||||||
|
const presentation = Presentation.create({
|
||||||
|
slideSize: { width: 960, height: 540 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const slide = presentation.slides.add();
|
||||||
|
slide.background.fill = "background1";
|
||||||
|
|
||||||
|
const title = slide.shapes.add({
|
||||||
|
geometry: "roundRect",
|
||||||
|
position: { left: 80, top: 72, width: 800, height: 96 },
|
||||||
|
fill: "accent1",
|
||||||
|
});
|
||||||
|
title.text = "Q2 Product Update";
|
||||||
|
|
||||||
|
const subtitle = slide.shapes.add({
|
||||||
|
geometry: "rect",
|
||||||
|
position: { left: 80, top: 196, width: 800, height: 48 },
|
||||||
|
});
|
||||||
|
subtitle.text = "Launch status, reliability, and next milestones";
|
||||||
|
|
||||||
|
const pptxBlob = await PresentationFile.exportPptx(presentation);
|
||||||
|
await pptxBlob.save("artifacts/q2-product-update.pptx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runtime Guardrails
|
||||||
|
|
||||||
|
- Prefer `slide.elements.charts.add("line", { position: ... })` for charts. The runtime chart surface is element-based; `slide.charts.add(...)` is not the reliable entry point for authoring new charts in this skill.
|
||||||
|
- After creating a chart element, set properties on the returned chart object:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const chart = slide.elements.charts.add("line", {
|
||||||
|
position: { left: 80, top: 180, width: 640, height: 320 },
|
||||||
|
});
|
||||||
|
chart.title = "Horsepower";
|
||||||
|
chart.categories = ["1964", "1973", "1989", "2024"];
|
||||||
|
const series = chart.series.add("911");
|
||||||
|
series.values = [130, 210, 247, 518];
|
||||||
|
chart.hasLegend = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
- For local or fetched images that must survive preview rendering, embed bytes rather than passing only a file path or URL. The most reliable pattern is an `ArrayBuffer` plus `contentType`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fs = await import("node:fs/promises");
|
||||||
|
const source = await fs.readFile("artifacts/porsche.jpg");
|
||||||
|
const imageBuffer = source.buffer.slice(
|
||||||
|
source.byteOffset,
|
||||||
|
source.byteOffset + source.byteLength,
|
||||||
|
);
|
||||||
|
|
||||||
|
slide.elements.images.add({
|
||||||
|
blob: imageBuffer,
|
||||||
|
contentType: "image/jpeg",
|
||||||
|
position: { left: 500, top: 0, width: 460, height: 540 },
|
||||||
|
fit: "cover",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- If you fetch an image in-script, save or convert it to bytes first, then pass the `ArrayBuffer` into `slide.elements.images.add(...)`. Do not assume `path`, `url`, `src`, or a Node `Buffer` will preview correctly.
|
||||||
|
- PPTX export can still inherit layout placeholders such as `Title 1`, `Subtitle 2`, date/footer placeholders, or PowerPoint's `Click to add title` boxes. If the deck is meant to be fully custom, strip placeholder shapes before final export:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const placeholderNames = new Set([
|
||||||
|
"Title 1",
|
||||||
|
"Subtitle 2",
|
||||||
|
"Date Placeholder 3",
|
||||||
|
"Footer Placeholder 4",
|
||||||
|
"Slide Number Placeholder 5",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const slide of presentation.slides.items) {
|
||||||
|
const toDelete = slide.shapes.items.filter((shape) => {
|
||||||
|
const name = shape.name ?? "";
|
||||||
|
return placeholderNames.has(name) || Boolean(shape.placeholderType);
|
||||||
|
});
|
||||||
|
for (const shape of toDelete) {
|
||||||
|
shape.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
- Create a new deck with `Presentation.create({ slideSize })`.
|
||||||
|
- Import an existing deck with `await PresentationFile.importPptx(await FileBlob.load("deck.pptx"))`.
|
||||||
|
- Add slides with `presentation.slides.add()` or `presentation.slides.insert({ after, layout })`.
|
||||||
|
- Add content with `slide.shapes.add(...)`, `slide.tables.add(...)`, `slide.elements.charts.add(...)`, and `slide.elements.images.add(...)` when you need preview-safe embedded images.
|
||||||
|
- Render a preview with `await presentation.export({ slide, format: "png", scale: 2 })`, then write `new Uint8Array(await blob.arrayBuffer())` with `node:fs/promises`.
|
||||||
|
- Export a `.pptx` with `await PresentationFile.exportPptx(presentation)`.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
- Start with the smallest script that creates or imports the deck.
|
||||||
|
- Do not begin by checking whether the local artifacts runtime package or cache exists. Assume the `artifacts` tool is ready and start authoring immediately; only investigate runtime installation or packaging if the tool fails before your slide code runs.
|
||||||
|
- If the API surface is unclear, do a tiny probe first: create one slide, add one shape, set `text` or `textStyle`, export one PNG, and inspect the result before scaling up to the full deck.
|
||||||
|
- Save the `.pptx` after meaningful milestones so the user can inspect output.
|
||||||
|
- Prefer short copy and a reusable component system over text-heavy layouts; the preview loop is much faster than rescuing a dense slide after export.
|
||||||
|
- Text boxes do not reliably auto-fit. If copy might wrap, give the shape extra height up front, then shorten the copy or enlarge the box until the rendered PNG shows clear padding on every edge.
|
||||||
|
- Deliberately check text contrast against the actual fill or image behind it. Do not leave dark text on dark fills, light text on light fills, or any pairing that is hard to read at presentation distance.
|
||||||
|
- Treat text containment as a hard requirement. No text should overlap other elements, sit underneath another object, spill outside its geometry, or run off the slide edge.
|
||||||
|
- If text sits inside a box, make the alignment look intentional. Center it only when the layout calls for centered copy; otherwise choose left or right alignment that matches the composition and leaves balanced padding.
|
||||||
|
- If you are using charts or external images, run a tiny end-to-end preview probe before building the full deck. One chart with one series and one embedded image is enough to validate the runtime surface.
|
||||||
|
- If the deck should not use PowerPoint placeholders, inspect imported slide shapes for inherited layout items before the final export. Delete shapes whose names match built-in placeholder labels or that expose `placeholderType`.
|
||||||
|
- If layout is repetitive, use `slide.autoLayout(...)` rather than hand-tuning every coordinate.
|
||||||
|
- QA with rendered PNG previews before handoff. In practice this is a more reliable quick check than importing the generated `.pptx` back into the runtime and inspecting round-tripped objects.
|
||||||
|
- Final QA means checking every rendered slide for contrast, intentional alignment, text superposition, clipped text, overflowing text, and inherited placeholder boxes. If text is hard to read against its background, if one text box overlaps another, if stacked text becomes hard to read, if any line touches a box edge, if text looks misaligned inside its box, or if PowerPoint shows `Click to add ...` placeholders, fix the layout or delete the inherited placeholder shapes and re-export before handoff.
|
||||||
|
- When editing an existing file, load it first, mutate only the requested slides or elements, then export a new `.pptx`.
|
||||||
|
|
||||||
|
## Reference Map
|
||||||
|
|
||||||
|
- [`references/presentation.md`](./references/presentation.md) for the core `Presentation` and `PresentationFile` lifecycle.
|
||||||
|
- [`references/auto-layout.md`](./references/auto-layout.md) for deterministic layout helpers and alignment enums.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Slides"
|
||||||
|
short_description: "Create and edit slide decks with the artifacts tool"
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Auto Layout
|
||||||
|
|
||||||
|
Use auto-layout helpers when several shapes should align or distribute predictably.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const title = slide.shapes.add({ geometry: "rect" });
|
||||||
|
const subtitle = slide.shapes.add({ geometry: "rect" });
|
||||||
|
|
||||||
|
title.position = { width: 720, height: 72 };
|
||||||
|
subtitle.position = { width: 720, height: 40 };
|
||||||
|
|
||||||
|
slide.autoLayout([title, subtitle], {
|
||||||
|
direction: AutoLayoutDirection.vertical,
|
||||||
|
frame: "slide",
|
||||||
|
align: AutoLayoutAlign.topLeft,
|
||||||
|
horizontalPadding: 72,
|
||||||
|
verticalPadding: 64,
|
||||||
|
verticalGap: 12,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful enums:
|
||||||
|
|
||||||
|
- `AutoLayoutDirection.vertical`
|
||||||
|
- `AutoLayoutDirection.horizontal`
|
||||||
|
- `AutoLayoutAlign.topLeft`
|
||||||
|
- `AutoLayoutAlign.center`
|
||||||
|
- `AutoLayoutAlign.bottomLeft`
|
||||||
|
|
||||||
|
Prefer auto-layout for title stacks, card grids, and footer or header placement instead of hand-adjusting every `left` and `top`.
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# Presentation API
|
||||||
|
|
||||||
|
Use `Presentation` as the main facade for authoring and editing decks.
|
||||||
|
|
||||||
|
## Lifecycle
|
||||||
|
|
||||||
|
```js
|
||||||
|
const presentation = Presentation.create({
|
||||||
|
slideSize: { width: 960, height: 540 },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Presentation.create()` creates a new empty deck.
|
||||||
|
- `await PresentationFile.importPptx(await FileBlob.load("deck.pptx"))` imports an existing deck.
|
||||||
|
- `await PresentationFile.exportPptx(presentation)` exports the deck as a saveable blob.
|
||||||
|
- When using this skill operationally, start by authoring with these APIs rather than checking local runtime package directories first. Runtime or package-cache inspection is a fallback for cases where the `artifacts` tool itself fails before deck code executes.
|
||||||
|
|
||||||
|
## Slides
|
||||||
|
|
||||||
|
- `presentation.slides.add()` appends a slide.
|
||||||
|
- `presentation.slides.insert({ after, layout, layoutId })` inserts relative to another slide.
|
||||||
|
- `presentation.slides.getItem(index)` returns a slide by zero-based index.
|
||||||
|
- `presentation.slides.items` exposes the backing collection when you need to iterate. Do not assume the collection is a normal array or that `.get(...)` exists.
|
||||||
|
- `presentation.setActiveSlide(slide)` and `presentation.getActiveSlide()` manage the active pointer.
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
- `slide.shapes.add({ geometry, position, fill, line })`
|
||||||
|
- `slide.elements.images.add(...)`
|
||||||
|
- `slide.tables.add(...)`
|
||||||
|
- `slide.elements.charts.add("line", { position })`
|
||||||
|
|
||||||
|
For the current runtime, charts are most reliable when created as slide elements:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const chart = slide.elements.charts.add("line", {
|
||||||
|
position: { left: 80, top: 180, width: 640, height: 320 },
|
||||||
|
});
|
||||||
|
chart.title = "Representative factory horsepower";
|
||||||
|
chart.categories = ["1964", "1973", "1989", "2024"];
|
||||||
|
const series = chart.series.add("911");
|
||||||
|
series.values = [130, 210, 247, 518];
|
||||||
|
chart.hasLegend = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
For preview-safe images, prefer embedded bytes over a bare path or URL:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fs = await import("node:fs/promises");
|
||||||
|
const source = await fs.readFile("artifacts/porsche.jpg");
|
||||||
|
const imageBuffer = source.buffer.slice(
|
||||||
|
source.byteOffset,
|
||||||
|
source.byteOffset + source.byteLength,
|
||||||
|
);
|
||||||
|
|
||||||
|
slide.elements.images.add({
|
||||||
|
blob: imageBuffer,
|
||||||
|
contentType: "image/jpeg",
|
||||||
|
position: { left: 500, top: 0, width: 460, height: 540 },
|
||||||
|
fit: "cover",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Passing only `path`, `url`, `src`, or a Node `Buffer` may appear to work for export setup but fail during PNG preview rendering. Use an `ArrayBuffer` when the image must render in previews reliably.
|
||||||
|
|
||||||
|
When exporting a fully custom deck, also watch for inherited layout placeholders in the PPTX output. These often show up as empty shapes named `Title 1`, `Subtitle 2`, `Date Placeholder 3`, `Footer Placeholder 4`, or `Slide Number Placeholder 5`, and PowerPoint may render them as `Click to add ...` boxes even if preview PNGs look fine.
|
||||||
|
|
||||||
|
Use a cleanup pass before final export when placeholders are not wanted:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const placeholderNames = new Set([
|
||||||
|
"Title 1",
|
||||||
|
"Subtitle 2",
|
||||||
|
"Date Placeholder 3",
|
||||||
|
"Footer Placeholder 4",
|
||||||
|
"Slide Number Placeholder 5",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const slide of presentation.slides.items) {
|
||||||
|
const toDelete = slide.shapes.items.filter((shape) => {
|
||||||
|
const name = shape.name ?? "";
|
||||||
|
return placeholderNames.has(name) || Boolean(shape.placeholderType);
|
||||||
|
});
|
||||||
|
for (const shape of toDelete) {
|
||||||
|
shape.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Collection access is not fully array-like:
|
||||||
|
|
||||||
|
- Use `slide.shapes.getItem(index)` or `slide.shapes.items[index]` for reads.
|
||||||
|
- Prefer probing with `Object.getOwnPropertyNames(Object.getPrototypeOf(...))` if you are unsure what a collection or element exposes.
|
||||||
|
|
||||||
|
## Text
|
||||||
|
|
||||||
|
Basic text works through the `text` property:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const title = slide.shapes.add({
|
||||||
|
geometry: "rect",
|
||||||
|
position: { left: 80, top: 72, width: 800, height: 96 },
|
||||||
|
});
|
||||||
|
title.text = "Q2 Product Update";
|
||||||
|
```
|
||||||
|
|
||||||
|
Simple styling works through `textStyle`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
title.textStyle = {
|
||||||
|
fontSize: 28,
|
||||||
|
color: "#101820",
|
||||||
|
bold: true,
|
||||||
|
italic: false,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Text boxes do not auto-resize to fit content. If the text might wrap, increase the shape height before you add more copy, and leave visible padding on all sides rather than sizing the box flush to the current line count.
|
||||||
|
|
||||||
|
Treat text placement as a layout quality check, not a best-effort outcome:
|
||||||
|
|
||||||
|
- Make sure the text color has strong contrast against the fill, image, or slide background behind it.
|
||||||
|
- Keep text fully inside the intended geometry with visible padding on each side.
|
||||||
|
- Do not allow text boxes to overlap each other or hide beneath charts, images, or decorative shapes.
|
||||||
|
- When text is inside a box or card, choose alignment intentionally. Center only when the design calls for centered copy; otherwise use the alignment that best matches the surrounding layout.
|
||||||
|
|
||||||
|
When you are authoring a new deck, confirm typography and clipping by rendering a PNG preview rather than assuming the slide will look right after export.
|
||||||
|
|
||||||
|
All geometry uses CSS pixels at 96 DPI.
|
||||||
|
|
||||||
|
## Preview
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fs = await import("node:fs/promises");
|
||||||
|
const preview = await presentation.export({
|
||||||
|
slide,
|
||||||
|
format: "png",
|
||||||
|
scale: 2,
|
||||||
|
});
|
||||||
|
const previewBytes = new Uint8Array(await preview.arrayBuffer());
|
||||||
|
await fs.writeFile("artifacts/slide-1.png", previewBytes);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `format` is usually `"png"` or `"jpeg"`.
|
||||||
|
- `scale` increases output resolution.
|
||||||
|
- `presentation.export(...)` returns an image blob. It is not the same as `FileBlob`, so write it via `arrayBuffer()` and Node file I/O.
|
||||||
|
- For QA, rendered PNGs are the fastest trustworthy check for spacing, contrast, clipping, alignment, and unintended text overlap.
|
||||||
|
- Inspect every slide image before handoff. Small cards, labels, and footer text are the common overflow traps, and stacked text boxes can silently overlap after export. If one text block sits on top of another, if text becomes visually superposed, if text color blends into the background, if text alignment inside a box looks accidental, or if any line touches a box edge, resize the container, move the elements apart, change the styling, or shorten the copy and re-render.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Prefer saving artifacts into an `artifacts/` directory in the current working tree so the user can inspect outputs easily.
|
||||||
67
codex-rs/skills/src/assets/samples/spreadsheets/SKILL.md
Normal file
67
codex-rs/skills/src/assets/samples/spreadsheets/SKILL.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
name: spreadsheets
|
||||||
|
description: Build, edit, recalculate, import, and export spreadsheet workbooks with the preloaded @oai/artifact-tool JavaScript surface through the artifacts tool.
|
||||||
|
metadata:
|
||||||
|
short-description: Use the artifacts tool to create and edit spreadsheets in JavaScript
|
||||||
|
---
|
||||||
|
|
||||||
|
# Spreadsheets
|
||||||
|
|
||||||
|
Use this skill when the user wants to create or modify workbooks with the `artifacts` tool.
|
||||||
|
|
||||||
|
## Tool Contract
|
||||||
|
|
||||||
|
- Use the `artifacts` tool.
|
||||||
|
- Send raw JavaScript only. Do not send JSON objects, quoted code, or markdown fences.
|
||||||
|
- This tool runs plain JavaScript in Node, not TypeScript. Do not use type annotations, `type`, `interface`, or `import type`.
|
||||||
|
- Do not write `import { ... } from "@oai/artifact-tool"`. The package surface is already preloaded.
|
||||||
|
- Named exports such as `Workbook`, `SpreadsheetFile`, and `FileBlob` are available directly.
|
||||||
|
- The full module is also available as `artifactTool`, `artifacts`, and `codexArtifacts`.
|
||||||
|
- Save outputs under a user-visible path such as `artifacts/revenue-model.xlsx`.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```js
|
||||||
|
const workbook = Workbook.create();
|
||||||
|
const sheet = workbook.worksheets.add("Revenue");
|
||||||
|
|
||||||
|
sheet.getRange("A1:C1").values = [["Month", "Bookings", "ARR"]];
|
||||||
|
sheet.getRange("A2:C4").values = [
|
||||||
|
["Jan", 120000, 1440000],
|
||||||
|
["Feb", 135000, 1620000],
|
||||||
|
["Mar", 142000, 1704000],
|
||||||
|
];
|
||||||
|
|
||||||
|
sheet.getRange("E1").values = [["Quarter ARR"]];
|
||||||
|
sheet.getRange("E2").formulas = [["=SUM(C2:C4)"]];
|
||||||
|
|
||||||
|
workbook.recalculate();
|
||||||
|
|
||||||
|
const xlsxBlob = await SpreadsheetFile.exportXlsx(workbook);
|
||||||
|
await xlsxBlob.save("artifacts/revenue-model.xlsx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
- Create a workbook with `Workbook.create()`.
|
||||||
|
- Import an existing workbook with `await SpreadsheetFile.importXlsx(await FileBlob.load("book.xlsx"))`.
|
||||||
|
- Add sheets with `workbook.worksheets.add(name)`.
|
||||||
|
- Address cells and ranges with A1 notation via `sheet.getRange("A1:C10")`.
|
||||||
|
- Set `range.values` and `range.formulas`, then call `workbook.recalculate()` before reading computed values.
|
||||||
|
- For charts, prefer creating the chart first, then populating it directly on the returned object. In practice, the reliable pattern is `const chart = sheet.charts.add("line"); chart.setPosition("A10", "H24"); chart.title = "..."; chart.categories = [...]; const series = chart.series.add("Name"); series.values = [...];`. Some other chart-construction styles can produce workbook objects that look valid in memory but export to empty or hidden charts in the final `.xlsx`.
|
||||||
|
- For worksheet images, prefer `sheet.images.add({ blob, contentType, anchor: { from: ..., to: ... } })`. The `blob` payload shape is the reliable path.
|
||||||
|
- Export an `.xlsx` with `await SpreadsheetFile.exportXlsx(workbook)`.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
- Model the workbook structure first: sheets, headers, and key formulas.
|
||||||
|
- Use formulas instead of copying computed values when the sheet should remain editable.
|
||||||
|
- Recalculate before exporting or reading formula results.
|
||||||
|
- If the workbook includes charts or images, verify layout after export, not just in memory. A sheet-level render pass such as `await workbook.render({ sheet: index, format: "png" })` is a good QA step before handoff.
|
||||||
|
- Check where drawings land on the actual sheet. Merged cells and very tall autofit rows can push visible content far below the fold, so QA should confirm not only that a chart exists, but that it appears in an obvious on-sheet location.
|
||||||
|
- When editing an existing workbook, load it first and preserve unaffected sheets.
|
||||||
|
|
||||||
|
## Reference Map
|
||||||
|
|
||||||
|
- [`references/workbook.md`](./references/workbook.md) for workbook lifecycle and worksheet basics.
|
||||||
|
- [`references/ranges.md`](./references/ranges.md) for A1 addressing, values, formulas, and formatting.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Spreadsheets"
|
||||||
|
short_description: "Create and edit workbooks with the artifacts tool"
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Ranges
|
||||||
|
|
||||||
|
Ranges use A1 notation and are the main surface for reading and writing cells.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const sheet = workbook.worksheets.add("Sheet1");
|
||||||
|
|
||||||
|
sheet.getRange("A1:C1").values = [["Month", "Bookings", "ARR"]];
|
||||||
|
sheet.getRange("A2:C4").values = [
|
||||||
|
["Jan", 120000, 1440000],
|
||||||
|
["Feb", 135000, 1620000],
|
||||||
|
["Mar", 142000, 1704000],
|
||||||
|
];
|
||||||
|
|
||||||
|
sheet.getRange("E2").formulas = [["=SUM(C2:C4)"]];
|
||||||
|
workbook.recalculate();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Use `range.values` for literal values.
|
||||||
|
- Use `range.formulas` for Excel-style formulas.
|
||||||
|
- Call `workbook.recalculate()` before reading formula-driven `range.values`.
|
||||||
|
- Apply formatting through `range.format` when the sheet needs borders, fills, fonts, or number formats.
|
||||||
|
|
||||||
|
## Addressing
|
||||||
|
|
||||||
|
- Single cell: `"B3"`
|
||||||
|
- Rectangle: `"A1:C10"`
|
||||||
|
- Entire row or column ranges are best used sparingly unless the user explicitly wants them.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Workbook API
|
||||||
|
|
||||||
|
Use `Workbook` to create, edit, recalculate, and export spreadsheet artifacts.
|
||||||
|
|
||||||
|
## Lifecycle
|
||||||
|
|
||||||
|
```js
|
||||||
|
const workbook = Workbook.create();
|
||||||
|
const sheet = workbook.worksheets.add("Sheet1");
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Workbook.create()` starts a new workbook.
|
||||||
|
- `await SpreadsheetFile.importXlsx(await FileBlob.load("book.xlsx"))` imports an existing workbook.
|
||||||
|
- `workbook.recalculate()` evaluates formulas.
|
||||||
|
- `await SpreadsheetFile.exportXlsx(workbook)` exports a saveable `.xlsx` blob.
|
||||||
|
|
||||||
|
## Worksheets
|
||||||
|
|
||||||
|
- `workbook.worksheets.add(name)` adds or returns a worksheet.
|
||||||
|
- `workbook.worksheets.getItem(nameOrIndex)` fetches an existing sheet.
|
||||||
|
- `workbook.worksheets.getActiveWorksheet()` returns the active sheet when relevant.
|
||||||
|
|
||||||
|
## Charts And Images
|
||||||
|
|
||||||
|
For charts that must survive `.xlsx` export reliably, prefer mutating the returned chart object directly:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const chart = sheet.charts.add("line");
|
||||||
|
chart.setPosition("A10", "H24");
|
||||||
|
chart.title = "Cash runway";
|
||||||
|
chart.categories = ["Month 0", "Month 1", "Month 2"];
|
||||||
|
|
||||||
|
const series = chart.series.add("Savings");
|
||||||
|
series.values = [6000, 6850, 7700];
|
||||||
|
```
|
||||||
|
|
||||||
|
This authoring path is more reliable than some one-shot chart construction styles, which can produce workbook objects that appear fine in memory but export to empty or misplaced charts.
|
||||||
|
|
||||||
|
For worksheet images, prefer a `blob` payload:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fs = await import("node:fs/promises");
|
||||||
|
const bytes = await fs.readFile("artifacts/chart-preview.png");
|
||||||
|
const blob = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
||||||
|
|
||||||
|
sheet.images.add({
|
||||||
|
blob,
|
||||||
|
contentType: "image/png",
|
||||||
|
anchor: {
|
||||||
|
from: { row: 10, col: 0 },
|
||||||
|
to: { row: 24, col: 8 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## QA
|
||||||
|
|
||||||
|
If the workbook includes charts or images, verify the final sheet layout after export, not just before export:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const png = await workbook.render({ sheet: 0, format: "png" });
|
||||||
|
```
|
||||||
|
|
||||||
|
Use rendered previews to confirm both of these points:
|
||||||
|
|
||||||
|
- The chart or image actually appears in the exported workbook.
|
||||||
|
- It lands where a user will see it without scrolling far past the main table.
|
||||||
|
|
||||||
|
Merged cells and tall autofit rows can push drawings far below the fold even when the drawing exists and is correctly attached to the sheet.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Prefer saving generated files into `artifacts/` so the user can inspect the workbook directly.
|
||||||
Reference in New Issue
Block a user