
Automating Numbers
- 25 installs
- 39 repo stars
- Updated January 14, 2026
- spillwavesolutions/automating-mac-apps-plugin
Helps with ai & agent building tasks during AI-assisted development.
About
automating-numbers is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- automating-numbers
- AI & Agent Building
- AI-coding skill
Automating Numbers by the numbers
- 25 all-time installs (skills.sh)
- Ranked #9,800 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/spillwavesolutions/automating-mac-apps-plugin --skill automating-numbersAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 25 |
|---|---|
| repo stars | ★ 39 |
| Last updated | January 14, 2026 |
| Repository | spillwavesolutions/automating-mac-apps-plugin ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Automating Numbers (JXA-first, AppleScript discovery)
Relationship to the macOS automation skill
- Standalone for Numbers, aligned with
automating-mac-appspatterns. - Use
automating-mac-appsfor permissions, shell, and UI scripting guidance. - PyXA Installation: To use PyXA examples in this skill, see the installation instructions in
automating-mac-appsskill (PyXA Installation section).
Core Framing
- Numbers AppleScript dictionary is AppleScript-first; discover there.
- JXA provides logic and data processing.
- Objects are specifiers; read via methods, write via assignments.
- Handle errors from Numbers operations using try/catch blocks and Application error checking.
Workflow (default)
1) [ ] Discover terms in Script Editor (Numbers dictionary). 2) [ ] Prototype minimal AppleScript commands. 3) [ ] Port to JXA and add defensive checks. 4) [ ] Prefer batch reads and clipboard shim for writes. 5) [ ] Use UI scripting only for dictionary gaps.
Validation Checklist
- [ ] Empty document handling works without errors
- [ ] Data integrity verified after batch operations
- [ ] Numbers UI remains responsive after automation runs
- [ ] Errors logged with specific Numbers object paths
- [ ] Sheet/table indices validated before access
- [ ] Clipboard shim restores original clipboard contents
Examples
Basic table read (JXA - Legacy):
const numbers = Application('Numbers');
const doc = numbers.documents[0];
const sheet = doc.sheets[0];
const table = sheet.tables[0];
const data = table.rows.whose({_not: [{cells: []}]})().map(row => row.cells().map(c => c.value()));Basic table read (PyXA - Recommended):
import PyXA
numbers = PyXA.Numbers()
# Get first document, sheet, and table
doc = numbers.documents()[0]
sheet = doc.sheets()[0]
table = sheet.tables()[0]
# Read all rows with data
rows = table.rows()
data = []
for row in rows:
cells = row.cells()
if cells: # Skip empty rows
row_data = [cell.value() for cell in cells]
data.append(row_data)
print("Table data:", data)PyObjC with Scripting Bridge:
from ScriptingBridge import SBApplication
numbers = SBApplication.applicationWithBundleIdentifier_("com.apple.Numbers")
# Access document and table
doc = numbers.documents()[0]
sheet = doc.sheets()[0]
table = sheet.tables()[0]
# Read table data
rows = table.rows()
data = []
for row in rows:
cells = row.cells()
if cells:
row_data = [cell.value() for cell in cells]
data.append(row_data)
print("Table data:", data)Batch write with clipboard shim (JXA - Legacy):
const numbers = Application('Numbers');
// Prepare data array
const data = [['Name', 'Age'], ['Alice', 25], ['Bob', 30]];
// Use clipboard for bulk insertion
const app = Application.currentApplication();
app.includeStandardAdditions = true;
app.setTheClipboardTo(data.map(row => row.join('\t')).join('\n'));
numbers.activate();
delay(0.5);
// UI scripting to paste
SystemEvents = Application('System Events');
SystemEvents.keystroke('v', {using: 'command down'});Batch write (PyXA - Modern):
import PyXA
numbers = PyXA.Numbers()
# Prepare data
data = [
['Name', 'Age'],
['Alice', 25],
['Bob', 30]
]
# Get table to write to
doc = numbers.documents()[0]
sheet = doc.sheets()[0]
table = sheet.tables()[0]
# Clear existing data and write new data
table.clear() # Clear table first
for i, row_data in enumerate(data):
# Add row if needed
if i >= len(table.rows()):
table.rows().push({})
# Set cell values
row = table.rows()[i]
for j, value in enumerate(row_data):
if j >= len(row.cells()):
row.cells().push({})
cell = row.cells()[j]
cell.value = valuePyObjC Batch Write:
from ScriptingBridge import SBApplication
numbers = SBApplication.applicationWithBundleIdentifier_("com.apple.Numbers")
# Prepare data
data = [
['Name', 'Age'],
['Alice', 25],
['Bob', 30]
]
# Get table
doc = numbers.documents()[0]
sheet = doc.sheets()[0]
table = sheet.tables()[0]
# Clear and write data
table.clear()
for i, row_data in enumerate(data):
# Ensure row exists
while len(table.rows()) <= i:
table.rows().push({})
row = table.rows()[i]
for j, value in enumerate(row_data):
# Ensure cell exists
while len(row.cells()) <= j:
row.cells().push({})
cell = row.cells()[j]
cell.value = valueWhen Not to Use
- General macOS automation without Numbers involvement
- AppleScript alone suffices (no JXA logic needed)
- Complex UI interactions beyond data operations (use
automating-mac-apps) - Cross-platform compatibility required (use CSV/pandas)
- Real-time collaborative editing scenarios
What to load
- JXA Numbers basics:
automating-numbers/references/numbers-basics.md - Recipes (tables, ranges, formatting):
automating-numbers/references/numbers-recipes.md - Advanced patterns (clipboard, performance, ObjC):
automating-numbers/references/numbers-advanced.md - Dictionary translation table:
automating-numbers/references/numbers-dictionary.md - Formulas and locale notes:
automating-numbers/references/numbers-formulas.md - Sorting patterns:
automating-numbers/references/numbers-sorting.md - UI scripting patterns:
automating-numbers/references/numbers-ui-scripting.md - PyXA API Reference (complete class/method docs):
automating-numbers/references/numbers-pyxa-api-reference.md
Numbers JXA advanced patterns
Clipboard shim for bulk writes
function bulkWrite(table, dataMatrix) {
const app = Application.currentApplication();
app.includeStandardAdditions = true;
const tsv = dataMatrix.map(r => r.join("\t")).join("\n");
app.setTheClipboardTo(tsv);
Numbers.activate();
delay(0.2);
const se = Application("System Events");
se.keystroke("v", { using: "command down" });
}16-bit color conversion
function toJXAColor(r, g, b) {
return [r * 257, g * 257, b * 257];
}ObjC bridge (file existence)
ObjC.import("Foundation");
const fm = $.NSFileManager.defaultManager;
const exists = fm.fileExistsAtPath("/Users/you/Documents/report.numbers");Numbers JXA basics
Bootstrapping
'use strict';
const Numbers = Application("Numbers");
Numbers.includeStandardAdditions = true;
Numbers.activate();Open document
const doc = Numbers.open(Path("/Users/you/Documents/report.numbers"));Sheets and tables
const sheet = doc.sheets.byName("Sheet 1");
const table = sheet.tables.byName("Table 1");Numbers dictionary translation table
AppleScript JXA
----------------------------------- ------------------------------------------
front document Numbers.documents[0]
active sheet doc.activeSheet
sheet "Sheet 1" doc.sheets.byName("Sheet 1")
table "Table 1" sheet.tables.byName("Table 1")
value of cell "A1" table.cells["A1"].value()
value of range "A1:C10" table.ranges["A1:C10"].value()
selection range table.selectionRangeNotes:
- Collections are specifiers; call methods to read values.
- Prefer byName access for sheets/tables.
Numbers formulas
Set a formula
const cell = table.cells["B2"];
cell.value = "=SUM(A1:A10)";Freeze a formula (value only)
const val = cell.value();
cell.value = val;Locale note
- Formula separators may be
,or;depending on system locale.
PyXA Numbers Module API Reference
New in PyXA version 0.0.8 - Control macOS Numbers using JXA-like syntax from Python.
This reference documents all classes, methods, properties, and enums in the PyXA Numbers module. Numbers inherits table, cell, row, column, and range functionality from the shared iWork base classes. For practical examples and usage patterns, see numbers-basics.md.
Contents
- Class Hierarchy
- XANumbersApplication
- XANumbersDocument
- XANumbersSheet
- XANumbersTemplate
- XANumbersWindow
- XANumbersContainer
- Table Classes (iWork Base)
- XAiWorkTable
- XAiWorkRange
- XAiWorkRow
- XAiWorkColumn
- XAiWorkCell
- XAiWorkChart
- List Classes
- Enumerations
- Quick Reference Tables
---
Class Hierarchy
XAObject
├── XANumbersApplication (XAiWorkApplication)
│ ├── XANumbersDocument (XAiWorkDocument)
│ │ ├── XANumbersSheet (XANumbersContainer)
│ │ │ ├── XAiWorkTable (XAiWorkiWorkItem)
│ │ │ │ ├── XAiWorkRange
│ │ │ │ │ ├── XAiWorkRow
│ │ │ │ │ ├── XAiWorkColumn
│ │ │ │ │ └── XAiWorkCell
│ │ │ │ └── XAiWorkChart
│ │ │ └── [images, shapes, lines, etc.]
│ │ └── XANumbersTemplate
│ └── XANumbersWindow (XAiWorkWindow)
└── XANumbersContainerList---
XANumbersApplication
Bases: XAiWorkApplication
Main entry point for interacting with Numbers.app.
Properties
| Property | Type | Description |
|---|---|---|
name | str | Application name ("Numbers") |
frontmost | bool | Whether Numbers is frontmost application |
version | str | Application version |
current_document | XANumbersDocument | Currently active document |
Methods
documents(filter=None) -> XANumbersDocumentList
Returns a list of open documents matching the filter.
Parameters:
filter(dict | None) - Property-value pairs to filter by
Example:
import PyXA
numbers = PyXA.Application("Numbers")
docs = numbers.documents()
for doc in docs:
print(doc.name)templates(filter=None) -> XANumbersTemplateList
Returns available templates matching the filter.
Parameters:
filter(dict | None) - Property-value pairs to filter by
Example:
templates = numbers.templates()
print(templates.name())
# ['Blank', 'Checklist', 'Invoice', ...]new_document(file_path='./Untitled.numbers', template=None) -> XANumbersDocument
Creates a new document.
Parameters:
file_path(str | XAPath) - Path to create document attemplate(XANumbersTemplate | None) - Template to initialize with
Returns: The newly created document
Example:
# Create blank document
doc = numbers.new_document("~/Documents/report.numbers")
# Create from template
template = numbers.templates().by_name("Invoice")
doc = numbers.new_document("~/Documents/invoice.numbers", template=template)new_sheet(document, properties=None) -> XANumbersSheet
Creates a new sheet in the specified document.
Parameters:
document(XANumbersDocument) - Document to add sheet toproperties(dict | None) - Properties for the new sheet
Returns: The newly created sheet
make(specifier, properties=None, data=None)
Creates a new element without adding to any list. Use XAList.push() to add.
Parameters:
specifier(str | ObjectType) - Class name to createproperties(dict) - Properties for the objectdata(Any) - Initialization data
Example:
# Create a new table
new_table = numbers.make("table", {"name": "Sales Data", "row_count": 10, "column_count": 5})
doc.sheets()[0].tables().push(new_table)
# Create a new line
new_line = numbers.make("line", {"startPoint": (100, 100), "endPoint": (200, 200)})
doc.sheets()[0].lines().push(new_line)---
XANumbersDocument
Bases: XAiWorkDocument
Represents an open Numbers spreadsheet.
Properties
| Property | Type | Description |
|---|---|---|
name | str | Document name |
id | str | Unique identifier |
file | XAPath | Location on disk (if saved) |
modified | bool | Whether modified since last save |
password_protected | bool | Whether password protected |
document_template | XANumbersTemplate | Assigned template |
active_sheet | XANumbersSheet | Currently active sheet (read/write) |
selection | XAiWorkiWorkItemList | Currently selected items |
properties | dict | All document properties |
Methods
sheets(filter=None) -> XANumbersSheetList
Returns sheets matching the filter.
Example:
doc = numbers.documents()[0]
all_sheets = doc.sheets()
first_sheet = doc.sheets()[0]
named_sheet = doc.sheets().by_name("Sales")new_sheet(properties=None) -> XANumbersSheet
Creates a new sheet at the end of the document.
Parameters:
properties(dict) - Properties for the new sheet (e.g.,{"name": "Q4 Data"})
Returns: The newly created sheet
Example:
new_sheet = doc.new_sheet({"name": "Summary"})export(file_path=None, format=ExportFormat.PDF)
Exports the spreadsheet.
Parameters:
file_path(str | XAPath | None) - Export destinationformat(ExportFormat) - Export format (default: PDF)
Example:
# Export to PDF
doc.export("/path/to/output.pdf", XANumbersApplication.ExportFormat.PDF)
# Export to Excel
doc.export("/path/to/output.xlsx", XANumbersApplication.ExportFormat.MICROSOFT_EXCEL)
# Export to CSV
doc.export("/path/to/output.csv", XANumbersApplication.ExportFormat.CSV)save()
Saves the document in Numbers format.
Example:
doc.save()---
XANumbersSheet
Bases: XANumbersContainer
Represents a single sheet in a Numbers spreadsheet.
Properties
| Property | Type | Description |
|---|---|---|
name | str | Sheet name (read/write) |
properties | dict | All sheet properties |
Methods
tables(filter=None) -> XAiWorkTableList
Returns tables in the sheet.
Example:
sheet = doc.sheets()[0]
tables = sheet.tables()
first_table = tables[0]
named_table = tables.by_name("Data")charts(filter=None) -> XAiWorkChartList
Returns charts in the sheet.
images(filter=None) -> XAiWorkImageList
Returns images in the sheet.
shapes(filter=None) -> XAiWorkShapeList
Returns shapes in the sheet.
lines(filter=None) -> XAiWorkLineList
Returns lines in the sheet.
text_items(filter=None) -> XAiWorkTextItemList
Returns text items in the sheet.
groups(filter=None) -> XAiWorkGroupList
Returns groups in the sheet.
iwork_items(filter=None) -> XAiWorkiWorkItemList
Returns all iWork items in the sheet.
audio_clips(filter=None) -> XAiWorkAudioClipList
Returns audio clips in the sheet.
movies(filter=None) -> XAiWorkMovieList
Returns movies in the sheet.
add_image(file_path) -> XAiWorkImage
Adds an image to the sheet.
Parameters:
file_path(str | XAPath | XAImage) - Path to image file
Returns: The newly created image object
Example:
image = sheet.add_image("/path/to/chart.png")---
XANumbersTemplate
Bases: XAObject
Represents a Numbers template.
Properties
| Property | Type | Description |
|---|---|---|
id | str | Unique identifier |
name | str | Template name |
---
XANumbersWindow
Bases: XAiWorkWindow
Represents a Numbers window.
Properties
| Property | Type | Description |
|---|---|---|
document | XANumbersDocument | Document displayed in window |
---
XANumbersContainer
Bases: XAiWorkContainer
Base class for containers (sheets) in Numbers.
---
Table Classes (iWork Base)
Numbers uses shared iWork base classes for tables, cells, rows, columns, and ranges. These classes are defined in iWorkApplicationBase and inherited by Numbers.
XAiWorkTable
Bases: XAiWorkiWorkItem
Represents a table in a Numbers sheet.
Properties
| Property | Type | Description |
|---|---|---|
name | str | Table identifier (read/write) |
row_count | int | Number of rows (read/write) |
column_count | int | Number of columns (read/write) |
header_row_count | int | Number of header rows (read/write) |
header_column_count | int | Number of header columns (read/write) |
footer_row_count | int | Number of footer rows (read/write) |
cell_range | XAiWorkRange | All cells in the table |
selection_range | XAiWorkRange | Currently selected cells (read/write) |
Methods
cells(filter=None) -> XAiWorkCellList
Returns all cells in the table.
Example:
table = sheet.tables()[0]
all_cells = table.cells()
cell_a1 = table.cells().by_name("A1")rows(filter=None) -> XAiWorkRowList
Returns all rows in the table.
Example:
rows = table.rows()
first_row = rows[0]
row_by_address = rows.by_address(5) # Get row 5columns(filter=None) -> XAiWorkColumnList
Returns all columns in the table.
Example:
columns = table.columns()
first_column = columns[0]
col_by_address = columns.by_address(2) # Get column B (index 2)ranges(filter=None) -> XAiWorkRangeList
Returns named ranges in the table.
sort(by_column, in_rows=None, direction=SortDirection.ASCENDING) -> XAiWorkTable
Sorts the table by the specified column.
Parameters:
by_column(XAiWorkColumn) - Column to sort byin_rows(list[XAiWorkRow] | XAiWorkRowList | None) - Rows to include in sort (None = all rows)direction(SortDirection) - ASCENDING or DESCENDING
Returns: The sorted table
Example:
# Sort by column A ascending
table.sort(table.columns()[0], direction=XAiWorkApplication.SortDirection.ASCENDING)
# Sort by column B descending
table.sort(table.columns()[1], direction=XAiWorkApplication.SortDirection.DESCENDING)---
XAiWorkRange
Bases: XAObject
Represents a range of cells in a table.
Properties
| Property | Type | Description |
|---|---|---|
name | str | Range coordinates (e.g., "A1:C5") (read/write) |
font_name | str | Font of cells in range (read/write) |
font_size | float | Font size of cells (read/write) |
format | CellFormat | Cell format (read/write) |
alignment | Alignment | Horizontal alignment (read/write) |
vertical_alignment | Alignment | Vertical alignment (read/write) |
text_color | XAColor | Text color (read/write) |
background_color | XAColor | Background color (read/write) |
text_wrap | bool | Whether text wraps (read/write) |
properties | dict | All range properties |
Methods
clear() -> XAiWorkRange
Clears the content of every cell in the range.
Returns: The range (for method chaining)
Example:
# Clear a specific range
table.cell_range.clear()
# Clear specific cells
table.rows()[0].clear()merge() -> XAiWorkRange
Merges all cells in the range into one cell.
Returns: The range
Example:
# Merge header cells
header_range = table.rows()[0]
header_range.merge()unmerge() -> XAiWorkRange
Unmerges previously merged cells.
Returns: The range
cells(filter=None) -> XAiWorkCellList
Returns cells within the range.
rows(filter=None) -> XAiWorkRowList
Returns rows within the range.
columns(filter=None) -> XAiWorkColumnList
Returns columns within the range.
---
XAiWorkRow
Bases: XAiWorkRange
Represents a single row in a table. Inherits all properties and methods from XAiWorkRange.
Properties
| Property | Type | Description |
|---|---|---|
address | int | Row index (1-based, read-only) |
height | float | Row height in pixels (read/write) |
Example:
row = table.rows()[0]
print(f"Row {row.address} height: {row.height}")
row.height = 30 # Set row height---
XAiWorkColumn
Bases: XAiWorkRange
Represents a single column in a table. Inherits all properties and methods from XAiWorkRange.
Properties
| Property | Type | Description |
|---|---|---|
address | int | Column index (1-based, read-only) |
width | float | Column width in pixels (read/write) |
Example:
col = table.columns()[0]
print(f"Column {col.address} width: {col.width}")
col.width = 150 # Set column width---
XAiWorkCell
Bases: XAiWorkRange
Represents a single cell in a table. Inherits all properties and methods from XAiWorkRange.
Properties
| Property | Type | Description |
|---|---|---|
value | `int | float |
formatted_value | str | Formatted display value (read-only) |
formula | str | Cell formula as text (read-only) |
row | XAiWorkRow | Cell's containing row (read-only) |
column | XAiWorkColumn | Cell's containing column (read-only) |
Example:
cell = table.cells()[0]
# Read value
print(cell.value)
print(cell.formatted_value)
# Write value
cell.value = 42
cell.value = "Hello"
cell.value = 3.14
# Check for formula
if cell.formula:
print(f"Formula: {cell.formula}")---
XAiWorkChart
Bases: XAiWorkiWorkItem
Represents a chart in a Numbers sheet. Charts are created from table data.
---
List Classes
PyXA provides list wrapper classes with fast enumeration and bulk property access.
XANumbersDocumentList
Bulk methods for document lists:
docs = numbers.documents()
docs.name() # -> list[str]
docs.modified() # -> list[bool]
docs.password_protected() # -> list[bool]
docs.active_sheet() # -> XANumbersSheetList
docs.document_template() # -> XANumbersTemplateList
docs.properties() # -> list[dict]Filter methods:
docs.by_name("Budget")
docs.by_modified(True)
docs.by_active_sheet(sheet)
docs.by_document_template(template)
docs.by_properties({"name": "Budget"})XANumbersSheetList
Bulk methods for sheet lists:
sheets = doc.sheets()
sheets.name() # -> list[str]
sheets.properties() # -> list[dict]Filter methods:
sheets.by_name("Summary")
sheets.by_properties({"name": "Summary"})XANumbersTemplateList
templates = numbers.templates()
templates.id() # -> list[str]
templates.name() # -> list[str]
templates.by_name("Invoice")
templates.by_id("template-id")XAiWorkTableList
tables = sheet.tables()
tables.name() # -> list[str]
tables.row_count() # -> list[int]
tables.column_count() # -> list[int]
tables.header_row_count() # -> list[int]
tables.header_column_count() # -> list[int]
tables.footer_row_count() # -> list[int]
tables.cell_range() # -> XAiWorkRangeList
tables.selection_range() # -> XAiWorkRangeListFilter methods:
tables.by_name("Sales Data")
tables.by_row_count(100)
tables.by_column_count(10)XAiWorkRangeList
ranges = table.ranges()
ranges.name() # -> list[str]
ranges.font_name() # -> list[str]
ranges.font_size() # -> list[float]
ranges.format() # -> list[CellFormat]
ranges.alignment() # -> list[Alignment]
ranges.text_color() # -> list[XAColor]
ranges.background_color() # -> list[XAColor]
ranges.text_wrap() # -> list[bool]
ranges.vertical_alignment() # -> list[Alignment]Filter methods:
ranges.by_name("A1:B5")
ranges.by_font_name("Helvetica")
ranges.by_format(CellFormat.CURRENCY)XAiWorkRowList
rows = table.rows()
rows.address() # -> list[int]
rows.height() # -> list[float]Filter methods:
rows.by_address(5) # Get row 5
rows.by_height(30) # Get rows with height 30XAiWorkColumnList
columns = table.columns()
columns.address() # -> list[int]
columns.width() # -> list[float]Filter methods:
columns.by_address(3) # Get column C (index 3)
columns.by_width(150) # Get columns with width 150XAiWorkCellList
cells = table.cells()
cells.value() # -> list[Any]
cells.formatted_value() # -> list[str]
cells.formula() # -> list[str]
cells.row() # -> XAiWorkRowList
cells.column() # -> XAiWorkColumnListFilter methods:
cells.by_value(100)
cells.by_formatted_value("$100.00")
cells.by_formula("=SUM(A1:A10)")
cells.by_row(row)
cells.by_column(column)---
Enumerations
ExportFormat
Export format options for Numbers documents.
| Value | OSType | Description |
|---|---|---|
NUMBERS | Nuff | Native Numbers format (.numbers) |
PDF | Npdf | PDF document |
MICROSOFT_EXCEL | Nexl | Excel format (.xlsx) |
CSV | Ncsv | Comma-separated values |
NUMBERS_09 | Nnmb | Numbers '09 format (legacy) |
Example:
from PyXA.apps.Numbers import XANumbersApplication
doc.export("/path/output.pdf", XANumbersApplication.ExportFormat.PDF)
doc.export("/path/output.xlsx", XANumbersApplication.ExportFormat.MICROSOFT_EXCEL)
doc.export("/path/output.csv", XANumbersApplication.ExportFormat.CSV)ObjectType
Creatable object types for the make() method.
| Value | Description |
|---|---|
DOCUMENT | Numbers document |
SHEET | Sheet within a document |
TABLE | Table within a sheet |
CHART | Chart |
IMAGE | Image |
SHAPE | Shape |
LINE | Line |
TEXT_ITEM | Text item |
AUDIO_CLIP | Audio clip |
MOVIE | Movie/video |
GROUP | Group of items |
IWORK_ITEM | Generic iWork item |
CellFormat (iWork Base)
Cell format options for table cells.
| Value | OSType | Description |
|---|---|---|
AUTO | faut | Automatic formatting |
CHECKBOX | fcch | Checkbox (boolean) |
CURRENCY | fcur | Currency format |
DATE_AND_TIME | fdtm | Date and time |
FRACTION | ffra | Fraction display |
DECIMAL_NUMBER | nmbr | Decimal number |
PERCENT | fper | Percentage |
POPUP_MENU | fcpp | Popup menu selection |
SCIENTIFIC | fsci | Scientific notation |
SLIDER | fcsl | Slider control |
STEPPER | fcst | Stepper control |
TEXT | ctxt | Plain text |
DURATION | fdur | Duration |
RATING | frat | Star rating |
NUMERAL_SYSTEM | fcns | Numeral system |
Example:
from PyXA.apps.iWorkApplicationBase import XAiWorkApplication
cell.format = XAiWorkApplication.CellFormat.CURRENCY
cell.format = XAiWorkApplication.CellFormat.PERCENTAlignment (iWork Base)
Alignment options for cell content.
| Value | OSType | Description |
|---|---|---|
AUTO | aaut | Automatic alignment |
LEFT | alft | Left aligned |
CENTER_HORIZONTAL | actr | Center aligned (horizontal) |
RIGHT | arit | Right aligned |
JUSTIFY | ajst | Justified |
TOP | avtp | Top aligned (vertical) |
CENTER_VERTICAL | actr | Center aligned (vertical) |
BOTTOM | avbt | Bottom aligned |
Example:
cell.alignment = XAiWorkApplication.Alignment.CENTER_HORIZONTAL
cell.vertical_alignment = XAiWorkApplication.Alignment.TOPSortDirection (iWork Base)
Sort direction options.
| Value | OSType | Description |
|---|---|---|
ASCENDING | ascn | Sort A to Z, 0 to 9 |
DESCENDING | dscn | Sort Z to A, 9 to 0 |
Example:
table.sort(table.columns()[0], direction=XAiWorkApplication.SortDirection.DESCENDING)---
Quick Reference Tables
Common Operations
| Task | Code |
|---|---|
| Get Numbers app | numbers = PyXA.Application("Numbers") |
| Create document | doc = numbers.new_document() |
| Create from template | doc = numbers.new_document(template=numbers.templates().by_name("Invoice")) |
| Get first document | doc = numbers.documents()[0] |
| Get all sheets | sheets = doc.sheets() |
| Get active sheet | sheet = doc.active_sheet |
| Set active sheet | doc.active_sheet = doc.sheets()[1] |
| Add new sheet | sheet = doc.new_sheet({"name": "Data"}) |
| Get tables in sheet | tables = sheet.tables() |
| Get table by name | table = sheet.tables().by_name("Table 1") |
| Export to PDF | doc.export("/path.pdf", ExportFormat.PDF) |
| Export to Excel | doc.export("/path.xlsx", ExportFormat.MICROSOFT_EXCEL) |
| Export to CSV | doc.export("/path.csv", ExportFormat.CSV) |
| Save document | doc.save() |
Table Operations
| Task | Code |
|---|---|
| Get all cells | cells = table.cells() |
| Get all rows | rows = table.rows() |
| Get all columns | columns = table.columns() |
| Get cell by name | cell = table.cells().by_name("A1") |
| Get row by index | row = table.rows()[0] |
| Get column by index | col = table.columns()[0] |
| Get row count | count = table.row_count |
| Get column count | count = table.column_count |
| Set row count | table.row_count = 20 |
| Set column count | table.column_count = 10 |
| Clear table | table.cell_range.clear() |
| Sort ascending | table.sort(table.columns()[0]) |
| Sort descending | table.sort(table.columns()[0], direction=SortDirection.DESCENDING) |
Cell Operations
| Task | Code |
|---|---|
| Read cell value | value = cell.value |
| Write cell value | cell.value = 42 |
| Get formatted value | display = cell.formatted_value |
| Get cell formula | formula = cell.formula |
| Set cell format | cell.format = CellFormat.CURRENCY |
| Set font | cell.font_name = "Helvetica" |
| Set font size | cell.font_size = 14 |
| Set alignment | cell.alignment = Alignment.CENTER_HORIZONTAL |
| Set text color | cell.text_color = XAColor.red() |
| Set background | cell.background_color = XAColor.yellow() |
| Enable text wrap | cell.text_wrap = True |
Row/Column Operations
| Task | Code |
|---|---|
| Get row height | height = row.height |
| Set row height | row.height = 30 |
| Get column width | width = column.width |
| Set column width | column.width = 150 |
| Get row address | index = row.address |
| Get column address | index = column.address |
| Clear row | row.clear() |
| Clear column | column.clear() |
| Merge cells in row | row.merge() |
Range Operations
| Task | Code |
|---|---|
| Get all cells range | range = table.cell_range |
| Get selection | range = table.selection_range |
| Clear range | range.clear() |
| Merge range | range.merge() |
| Unmerge range | range.unmerge() |
| Set range font | range.font_name = "Arial" |
| Set range format | range.format = CellFormat.PERCENT |
Property Access Patterns
# Single object property access
table = sheet.tables()[0]
print(table.name)
print(table.row_count)
# Bulk property access on lists
tables = sheet.tables()
print(tables.name()) # Returns list[str]
print(tables.row_count()) # Returns list[int]
# Filtering lists
large_tables = tables.greater_than("row_count", 100)
currency_cells = cells.by_format(CellFormat.CURRENCY)Reading Table Data
# Read entire table as 2D array
table = sheet.tables()[0]
data = []
for row in table.rows():
row_data = [cell.value for cell in row.cells()]
data.append(row_data)
# Read specific column values
col_values = [cell.value for cell in table.columns()[0].cells()]
# Read specific row values
row_values = [cell.value for cell in table.rows()[0].cells()]Writing Table Data
# Write to specific cell
table.cells().by_name("A1").value = "Header"
# Write row of data
row = table.rows()[0]
values = ["Name", "Age", "City"]
for i, cell in enumerate(row.cells()):
if i < len(values):
cell.value = values[i]
# Write 2D data array
data = [
["Name", "Score"],
["Alice", 95],
["Bob", 87]
]
for i, row_data in enumerate(data):
row = table.rows()[i]
for j, value in enumerate(row_data):
row.cells()[j].value = value---
See Also
- PyXA Numbers Documentation - Official PyXA documentation
- PyXA iWork Base Documentation - Source for table/cell classes
- numbers-basics.md - JXA fundamentals
- numbers-recipes.md - Common automation patterns
- numbers-advanced.md - Advanced techniques
Numbers JXA recipes
Read table values (batch)
const values = table.rows.value();Set a single cell
table.rows[1].cells[1].value = "OK";Basic formatting
const cell = table.rows[1].cells[1];
cell.backgroundColor = [65535, 0, 0];
cell.textColor = [0, 0, 0];Numbers sorting patterns
Recommended approach
- Read table data in one batch.
- Sort using JS.
- Write back via clipboard shim.
const data = table.rows.value();
const header = data[0];
const body = data.slice(1);
body.sort((a, b) => (a[1] || 0) - (b[1] || 0));
const sorted = [header].concat(body);
// Use clipboard shim to paste sorted dataNumbers UI scripting patterns
When to use
- Only when dictionary access is missing or broken.
Basic pattern
const se = Application("System Events");
const n = se.processes.byName("Numbers");
Application("Numbers").activate();
delay(0.2);
// Example: open Format sidebar (path varies by version)
// n.windows[0].toolbars[0].buttons.byName("Format").click();Notes
- Use Accessibility Inspector to find stable element paths.
- Prefer named UI elements; avoid index paths.
- Add wait loops before clicking.
#!/usr/bin/env python3
"""
Create Numbers Spreadsheet Script - PyXA Implementation
Creates a new Numbers spreadsheet with sample data
Usage: python create_numbers_spreadsheet.py "Spreadsheet Name" ["Save Path"]
"""
import sys
import subprocess
from pathlib import Path
def create_numbers_spreadsheet(name, save_path=None):
"""Create a new Numbers spreadsheet with sample data"""
print("Creating Numbers spreadsheet with AppleScript...")
# Use AppleScript for Numbers automation since PyXA Numbers support may be limited
try:
# Build the script based on whether we have a save path
if save_path:
abs_path = str(Path(save_path).resolve())
save_command = f'save newDoc in POSIX file "{abs_path}"'
else:
save_command = '-- no save'
script = f'''
tell application "Numbers"
activate
-- Create new document
set newDoc to make new document
-- Get the first sheet and table
set firstSheet to sheet 1 of newDoc
set firstTable to table 1 of firstSheet
-- Set simple headers
set value of cell 1 of row 1 of firstTable to "Product"
set value of cell 2 of row 1 of firstTable to "Price"
-- Add simple data
set value of cell 1 of row 2 of firstTable to "Widget A"
set value of cell 2 of row 2 of firstTable to 10.99
-- Save if requested, otherwise close
if "{save_command}" is not "-- no save" then
{save_command}
close newDoc saving no
else
close newDoc saving no
end if
end tell
'''
subprocess.run(["osascript", "-e", script], check=True, timeout=30)
print(f"Successfully created Numbers spreadsheet: {name}")
if save_path:
print(f"Saved to: {save_path}")
return True
print(f"Successfully created Numbers spreadsheet: {name}")
if save_path:
print(f"Saved to: {save_path}")
return True
except subprocess.CalledProcessError as e:
print(f"AppleScript failed: {e}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
if __name__ == "__main__":
name = sys.argv[1] if len(sys.argv) > 1 else "Sample Spreadsheet"
save_path = sys.argv[2] if len(sys.argv) > 2 else None
success = create_numbers_spreadsheet(name, save_path)
sys.exit(0 if success else 1)#!/usr/bin/env osascript -l JavaScript
/**
* create_skills_spreadsheet.js
* Creates a Numbers spreadsheet with one tab per skill,
* listing file paths and sizes for each file in the skill.
* THIS IS JUST AN EXAMPLE SCRIPT.
*/
'use strict';
const Numbers = Application("Numbers");
const Finder = Application("Finder");
const app = Application.currentApplication();
app.includeStandardAdditions = true;
Numbers.includeStandardAdditions = true;
const SKILLS_PATH = "/Users/richardhightower/clients/spillwave/src/skill-foundary-agent/using_apple_automation_foundary/automating-mac-apps-plugin/plugins/automating-mac-apps-plugin/skills";
// Get all files in a directory recursively
function getFilesInDir(dirPath) {
const files = [];
try {
const result = app.doShellScript(`find "${dirPath}" -type f 2>/dev/null`);
if (result) {
// JXA shell script returns \r for newlines
const paths = result.split(/[\r\n]+/).filter(p => p.length > 0);
for (const filePath of paths) {
try {
const sizeResult = app.doShellScript(`stat -f%z "${filePath}" 2>/dev/null || echo 0`);
const size = parseInt(sizeResult, 10) || 0;
// Make path relative to skill directory
const relativePath = filePath.replace(dirPath + "/", "");
files.push({
path: relativePath,
size: size
});
} catch (e) {
// Skip files we can't stat
}
}
}
} catch (e) {
// Empty directory or error
}
return files;
}
// Format file size for display
function formatSize(bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
return (bytes / (1024 * 1024)).toFixed(2) + " MB";
}
// Get list of skills
function getSkills() {
const result = app.doShellScript(`ls "${SKILLS_PATH}"`);
// JXA shell script returns \r for newlines
return result.split(/[\r\n]+/).filter(s => s.length > 0);
}
function run() {
try {
// Get all skills
const skills = getSkills();
// Activate Numbers and create new document
Numbers.activate();
delay(0.5);
// Create new document
const doc = Numbers.Document();
Numbers.documents.push(doc);
delay(0.5);
// Get the document we just created
const activeDoc = Numbers.documents[0];
// Process each skill
let isFirstSheet = true;
for (const skillName of skills) {
const skillPath = SKILLS_PATH + "/" + skillName;
const files = getFilesInDir(skillPath);
let sheet;
if (isFirstSheet) {
// Use the default first sheet
sheet = activeDoc.sheets[0];
sheet.name = skillName;
isFirstSheet = false;
} else {
// Add a new sheet
const newSheet = Numbers.Sheet({ name: skillName });
activeDoc.sheets.push(newSheet);
sheet = activeDoc.sheets[activeDoc.sheets.length - 1];
}
delay(0.2);
// Get the table in this sheet
const table = sheet.tables[0];
// Set up header row
table.rows[0].cells[0].value = "Path";
table.rows[0].cells[1].value = "Size";
// Make header bold by selecting and formatting
// (JXA Numbers doesn't have direct bold control, but we set values)
// Add file data
for (let i = 0; i < files.length; i++) {
const rowIndex = i + 1; // Skip header row
// Ensure we have enough rows
while (table.rows.length <= rowIndex) {
table.rows.push(Numbers.Row());
}
// Set cell values
table.rows[rowIndex].cells[0].value = files[i].path;
table.rows[rowIndex].cells[1].value = formatSize(files[i].size);
}
// Resize columns to fit content (approximate)
delay(0.1);
}
return `Created spreadsheet with ${skills.length} sheets (tabs) for skills`;
} catch (error) {
return "Error: " + error.message;
}
}
#!/usr/bin/env python3
"""
Export Numbers Spreadsheet to CSV Script
Exports a Numbers spreadsheet to CSV format
Usage: python export_numbers_to_csv.py "input.numbers" "output.csv"
"""
import sys
import subprocess
import csv
from pathlib import Path
def export_numbers_to_csv(input_file, output_file):
"""Export Numbers spreadsheet to CSV"""
print(f"Exporting Numbers spreadsheet to CSV: {input_file} -> {output_file}")
try:
script = f'''
tell application "Numbers"
set theDoc to open POSIX file "{Path(input_file).resolve()}"
-- Get the first sheet and table
set firstSheet to sheet 1 of theDoc
set firstTable to table 1 of firstSheet
-- Read all cell values and create CSV data
set csvData to ""
-- Get table dimensions
set rowCount to count of rows of firstTable
set colCount to count of columns of firstTable
repeat with i from 1 to rowCount
set rowData to {{}}
repeat with j from 1 to colCount
try
set cellValue to value of cell j of row i of firstTable
-- Convert to string and escape quotes
set cellString to cellValue as string
set end of rowData to cellString
on error
set end of rowData to ""
end try
end repeat
-- Join row with commas
set AppleScript's text item delimiters to ","
set rowString to rowData as string
set AppleScript's text item delimiters to ""
-- Add to CSV data
set csvData to csvData & rowString & linefeed
end repeat
-- Close without saving
close theDoc saving no
return csvData
end tell
'''
result = subprocess.run(["osascript", "-e", script],
capture_output=True, text=True, check=True, timeout=30)
csv_data = result.stdout.strip()
if csv_data:
# Write CSV data to file
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
# Parse the CSV data and write properly
lines = csv_data.split('\n')
for line in lines:
if line.strip(): # Skip empty lines
# Split by comma and handle basic CSV writing
csvfile.write(line + '\n')
print(f"Successfully exported to CSV: {output_file}")
return True
else:
print("No data found to export")
return False
except subprocess.CalledProcessError as e:
print(f"AppleScript failed: {e}")
print(f"Error output: {e.stderr}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python export_numbers_to_csv.py 'input.numbers' 'output.csv'")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
success = export_numbers_to_csv(input_file, output_file)
sys.exit(0 if success else 1)#!/usr/bin/env python3
"""
Read Numbers Spreadsheet Script
Reads data from a Numbers spreadsheet
Usage: python read_numbers_spreadsheet.py "path/to/spreadsheet.numbers"
"""
import sys
import subprocess
import json
from pathlib import Path
def read_numbers_spreadsheet(file_path):
"""Read data from a Numbers spreadsheet"""
print(f"Reading Numbers spreadsheet: {file_path}")
try:
script = f'''
tell application "Numbers"
set theDoc to open POSIX file "{Path(file_path).resolve()}"
-- Get the first sheet and table
set firstSheet to sheet 1 of theDoc
set firstTable to table 1 of firstSheet
-- Read all cell values
set allData to {{}}
-- Get table dimensions (approximate)
set rowCount to count of rows of firstTable
set colCount to count of columns of firstTable
repeat with i from 1 to rowCount
set rowData to {{}}
repeat with j from 1 to colCount
try
set cellValue to value of cell j of row i of firstTable
set end of rowData to cellValue
on error
set end of rowData to ""
end try
end repeat
set end of allData to rowData
end repeat
-- Close without saving
close theDoc saving no
return allData
end tell
'''
result = subprocess.run(["osascript", "-e", script],
capture_output=True, text=True, check=True, timeout=30)
# Parse the AppleScript result
# AppleScript returns data in a format that needs parsing
output = result.stdout.strip()
if output:
print(f"Raw output: {output}")
# For now, just indicate success
print("Successfully read Numbers spreadsheet data")
return True
else:
print("No data found in spreadsheet")
return False
except subprocess.CalledProcessError as e:
print(f"AppleScript failed: {e}")
print(f"Error output: {e.stderr}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python read_numbers_spreadsheet.py 'path/to/spreadsheet.numbers'")
sys.exit(1)
file_path = sys.argv[1]
success = read_numbers_spreadsheet(file_path)
sys.exit(0 if success else 1)