Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
213 commits
Select commit Hold shift + click to select a range
e7f99b7
publish wheels containing the native extension
samizdatco Jul 9, 2026
91cff9c
reflect test results in exit code
samizdatco Jul 9, 2026
665d555
test for presence of pyobjc+extension, not just paths
samizdatco Jul 9, 2026
d4baffb
fix regex escape
samizdatco Jul 10, 2026
a7db0a5
fix objc.super use in ConsoleScript
samizdatco Jul 10, 2026
c7cd5db
fix `ordered()` crash on grobs and silent dotted-path failures
samizdatco Jul 10, 2026
2fbb342
add python 3.14 support by bumping pyobjc to 11.1
samizdatco Jul 10, 2026
a28b046
ignore rsrc files resulting from in-place build
samizdatco Jul 10, 2026
e8f238d
generate MANIFEST.in only when needed for sdist
samizdatco Jul 10, 2026
12cb0d3
rename build.py to make.py (to prevent `-m build` shadowing)
samizdatco Jul 10, 2026
576fd98
opt into secure restorable state
samizdatco Jul 10, 2026
211e67a
update embedded python to 3.14.6
samizdatco Jul 10, 2026
27039a9
update python version compat
samizdatco Jul 10, 2026
cad94bd
update 1.0.1 docs
samizdatco Jul 11, 2026
a319c1b
bump copyright year
samizdatco Jul 11, 2026
64d6242
fix typos
samizdatco Jul 11, 2026
4969660
add missing 3.14 classifier
samizdatco Jul 11, 2026
f831aae
use system fonts in typography tests
samizdatco Jul 11, 2026
5f18a22
fixed Text.flow() when invoked w/ callback
samizdatco Jul 11, 2026
b20c941
fix the test for blend-only effects
samizdatco Jul 11, 2026
90a109b
fix MagicNumber's numeric-protocol implementation
samizdatco Jul 11, 2026
5ac1282
fix rasterization of clipping masks based on vector images
samizdatco Jul 13, 2026
669de8c
add clip() test cases that use canvas transforms
samizdatco Jul 13, 2026
e38d8cf
replace deprecated WebView with WKWebView
samizdatco Jul 13, 2026
4b58923
update document contents after each edit
samizdatco Jul 13, 2026
dfb9434
fix license format
samizdatco Jul 13, 2026
00e2dfb
rename `sync_edits` and `flash_menu` methods
samizdatco Jul 13, 2026
7daadf5
add new default editor theme
samizdatco Jul 13, 2026
48b3c78
add `Assets.car` for macOS 26+ icon
samizdatco Jul 14, 2026
bcf1d39
Replace setup.py with direct call to tests
samizdatco Jul 14, 2026
15df096
handle quadratic curves in `path_to_polygon`
samizdatco Jul 14, 2026
628b4a0
Add logo banner to readme
samizdatco Jul 14, 2026
7e56ef6
Fix deprecation warning from pyobjc
samizdatco Jul 14, 2026
76b6815
update module providing UTType*
samizdatco Jul 14, 2026
0d7571b
set exit code on script errors when running `plotdevice` headless
samizdatco Jul 14, 2026
d629adc
replace `xrange` with `range` in code samples
samizdatco Jul 14, 2026
30cdf8a
update markdown docs
samizdatco Jul 14, 2026
3509453
fix crash when canvas is zoomed before script has rendered
samizdatco Jul 15, 2026
ad6331f
local ace modification: filter autocompletions
samizdatco Jul 14, 2026
b78d58e
local ace modification: fix layout race condition in autocomplete
samizdatco Jul 16, 2026
90a6542
add optional `start` and `end` args to Bezier.points()
samizdatco Jul 17, 2026
396b2ac
rework _locate()
samizdatco Jul 17, 2026
312608e
upgrade vendored ace.js to 1.44.0
samizdatco Jul 13, 2026
8c7ed66
fix endpoint bug in point() and speed up points() by caching segments
samizdatco Jul 17, 2026
f283f29
remove segment length caching (it was too expensive for its own good)
samizdatco Jul 17, 2026
8e8632e
add tooltips and documentation links to plotdevice symbols
samizdatco Jul 14, 2026
7fcc39f
add context menu for opening docs
samizdatco Jul 15, 2026
95a1bab
fix refreshdoc command mishandling an empty source string
samizdatco Jul 15, 2026
b5bc653
add syntax coloring to snippet preview in completions
samizdatco Jul 16, 2026
c41714e
use theme colors in popups (replacing inline styles via js)
samizdatco Jul 16, 2026
5458931
replace monolithic setup.py with pyproject.toml + scripts
samizdatco Jul 9, 2026
e3ad48c
bump minimum python version to 3.9
samizdatco Jul 9, 2026
daa1a9e
add channel='outline' to clip()/mask() for vector image stencils
samizdatco Jul 17, 2026
a7c2500
replace gpc with clipper2
samizdatco Jul 17, 2026
b36f22e
add tests for Bezier.trace()
samizdatco Jul 17, 2026
34da913
add tests for clip()/mask() with channel='outline'
samizdatco Jul 17, 2026
aa0c63c
add .trace() method to Bezier to convert stroked paths to outlines
samizdatco Jul 17, 2026
1fe8d86
reorder View > Zoom menu items and fix flash on zoomed re-render
samizdatco Oct 27, 2026
3d95477
use hardware vblank for animations (macOS 14+ only)
samizdatco Oct 15, 2026
2b92d92
merge: display-link animation, hardware zoom and a centering clip view
samizdatco Oct 15, 2026
262d36d
fix export sheet crash
samizdatco Oct 14, 2026
dd43ec1
upgrade vendored SwiftDraw to 0.21.0
samizdatco Oct 14, 2026
7deaa2f
fix upside-down clip()/mask() w/ vector image stencils
samizdatco Oct 14, 2026
5bd2dac
validate image() kwargs
samizdatco Oct 14, 2026
0522413
speed up per-grob draw overhead
samizdatco Oct 17, 2026
351664c
speed up text construction
samizdatco Oct 20, 2026
58e46e0
bump PyObjC to 12.2 to fix a per-draw memory leak
samizdatco Oct 16, 2026
d199d76
merge: overhead optimizations and pyobjc leak fix
samizdatco Oct 16, 2026
77857ef
render each window's frames on its own thread
samizdatco Oct 18, 2026
9075e19
merge: render Frames into IOSurfaces on a separate thread
samizdatco Oct 18, 2026
04fe8b9
hide the window toolbar in full screen mode
samizdatco Oct 19, 2026
894a15f
keep the canvas centered by sizing the view to the viewport
samizdatco Oct 17, 2026
36746b4
fix crash on unknown theme selection
samizdatco Oct 15, 2026
4633165
fix plot(obj, live=True) validation
samizdatco Oct 16, 2026
b561ac7
fix rollback-state memory leaks
samizdatco Oct 16, 2026
47ab846
fix `with` restoration scope
samizdatco Oct 16, 2026
29a8a76
embed free-threaded Python in the app
samizdatco Oct 16, 2026
1209ecd
add make.py dev --frameworks
samizdatco Oct 16, 2026
ada87e3
add -t/--free-threaded switches
samizdatco Oct 16, 2026
442a155
merge: free-threading and embedded Python frameworks
samizdatco Oct 16, 2026
66945da
track grob bounds so out-of-view drawing can be culled
samizdatco Oct 17, 2026
84c6f81
keep shadow size consistent
samizdatco Oct 17, 2026
feba543
merge: culling, scaled shadows, and tiled/viewport-only rendering
samizdatco Oct 20, 2026
e4ecf0b
fix repeat runs of non-animated scripts when holding cmd-r
samizdatco Oct 18, 2026
2577b30
use CGPDFContext for multipage PDF exports
samizdatco Oct 22, 2026
44e1bbe
reuse the window's render thread for exports
samizdatco Oct 19, 2026
9148e89
avoid CoreGraphics glitch by serializing rendering
samizdatco Oct 19, 2026
a570578
only populate the PDF & TIFF pasteboards
samizdatco Oct 19, 2026
69062fa
use the render thread for headless exports
samizdatco Oct 19, 2026
e47efe8
merge: wide-gamut working space, color-managed exports and CMYK profiles
samizdatco Oct 22, 2026
8e21ef2
add proof profiles for color vision deficiencies
samizdatco Oct 23, 2026
4919f1c
add ProRes 4444 for video exports
samizdatco Oct 19, 2026
a56f6e5
cleanly close export files when interrupted
samizdatco Oct 19, 2026
c78bfba
suggest video bitrate automatically
samizdatco Oct 19, 2026
389e6cc
merge: exports use the render thread
samizdatco Oct 19, 2026
4ffb755
speed up transforms and geometry
samizdatco Oct 20, 2026
7fcea3e
refactor the build scripts, CLI launcher, and free-threading support
samizdatco Oct 27, 2026
af1193f
use hardware-backed zoom and update DPI when dragging between monitors
samizdatco Jul 25, 2026
21265a4
replace GraphicsBackdrop with auto-centering ClipView subclass
samizdatco Jul 25, 2026
97774e1
commit each Canvas to a Frame for rendering
samizdatco Oct 16, 2026
242142f
use IOSurfaces for display instead of CGImages
samizdatco Oct 15, 2026
15ef32b
restructure the view's render pipeline
samizdatco Oct 18, 2026
ef54b99
render large/zoomed still frames as tiles
samizdatco Oct 18, 2026
b843180
render only the visible portion of live frames
samizdatco Oct 20, 2026
396e644
use CoreText for single-line runs
samizdatco Oct 20, 2026
2eb3f43
prevent app builds from overlapping
samizdatco Oct 22, 2026
ca108fa
use Display P3 by default
samizdatco Oct 21, 2026
56e73f9
add export(colors=) and --colors
samizdatco Oct 22, 2026
41c3b66
add CMYK profiles to `colors`/`--colors`
samizdatco Oct 23, 2026
6ca685f
add Proof Colors menu and export sheet popup
samizdatco Oct 22, 2026
1d6e23a
rewrap lib/cocoa.py's imports
samizdatco Oct 29, 2026
c233f0d
speed up initial imports
samizdatco Oct 29, 2026
afa0900
add _commit() hook to grobs and containers
samizdatco Oct 29, 2026
d3beca2
snapshot canvases for image() and Pattern() as 2x P3 bitmaps
samizdatco Oct 29, 2026
41691e1
add Rec. 709/2020 profiles for video exports
samizdatco Oct 23, 2026
f069d0d
move ProRes's unpremultiplication into Video.m
samizdatco Oct 29, 2026
cefd01f
reorder GraphicsView's methods
samizdatco Oct 29, 2026
01c50c6
fix font family/weight parsing
samizdatco Oct 29, 2026
62797a5
only release the render lock when at depth 1
samizdatco Oct 29, 2026
dc1126e
tweak --colors/--cmyk error messages
samizdatco Oct 29, 2026
61cdcda
fix Gradient objects passed to fill(), stroke(), and background()
samizdatco Oct 23, 2026
83f3e4c
find installed color profiles by file name
samizdatco Oct 22, 2026
1b05d96
convert canvas patterns to the export's color space
samizdatco Oct 30, 2026
a4780ce
draw svg and canvas patterns as vectors
samizdatco Oct 30, 2026
f7eff94
copy containers' contents if the script holds a reference
samizdatco Oct 30, 2026
d34f8bd
fix cmyk exports of pattern-filled text
samizdatco Oct 31, 2026
73ac604
support position and size args in Pattern()
samizdatco Oct 31, 2026
038e969
add plot(canvas) for drawing a canvas as vectors
samizdatco Oct 31, 2026
b860323
canvas-based Patterns remain vectors, image(canvas) rasterizes
samizdatco Oct 31, 2026
089f2b6
merge: draw canvases as vectors via plot() and Pattern
samizdatco Oct 31, 2026
b3ad646
compare icc tags (not just names) in png export test
samizdatco Oct 31, 2026
ec94f01
avoid unnecessary copies in `with bezier()`
samizdatco Nov 1, 2026
d8cd963
optimize grob creation & copying
samizdatco Nov 1, 2026
34133f4
use canvas units for transforms in `with bezier()` blocks
samizdatco Nov 1, 2026
130ed91
make closepath() idem potent
samizdatco Nov 1, 2026
526934e
fix a heap overrun flattening paths with open contours
samizdatco Nov 1, 2026
d71b2f8
use CGColors for strokes, fills, and text rendering
samizdatco Nov 2, 2026
dfbd6f0
merge: bezier path optimizations
samizdatco Nov 3, 2026
165b8be
replace NSGradient with a native PDGradient
samizdatco Nov 2, 2026
5bb15d9
move CMYK conversion into Press.convert()
samizdatco Nov 3, 2026
9fdac17
text() can now use vector-backed Pattern fills
samizdatco Oct 31, 2026
5df8676
move Canvas & Frame into gfx/canvas.py
samizdatco Nov 4, 2026
62ee5d3
move PDPath & PDSurface into extensions/gfx
samizdatco Nov 4, 2026
768cec5
remove PD prefix from internals
samizdatco Nov 4, 2026
048d82d
merge: move canvas, display, & native sources
samizdatco Nov 4, 2026
34f59f2
refactor color management, press exports, and the Distiller
samizdatco Oct 23, 2026
9a1dd94
merge: misc. refactors
samizdatco Oct 23, 2026
40e8bf1
make xcode builds lazier
samizdatco Nov 4, 2026
969ecf2
use truststore to verify https via the keychain
samizdatco Nov 5, 2026
82eb17d
restore drawing state in finally blocks
samizdatco Nov 4, 2026
79e3243
fix `Transform.apply()` for rotated or skewed regions
samizdatco Nov 7, 2026
3b1ce9c
recognize cli invocations using `-mplotdevice`
samizdatco Nov 7, 2026
dc3366c
set minimum version to macOS 11
samizdatco Nov 7, 2026
b46b5b5
fix `image()` applying its alpha twice
samizdatco Nov 7, 2026
3f1e5bc
don't re-render for proof changes during an export
samizdatco Nov 7, 2026
6565340
raise if `finish()` is called without an `add()`
samizdatco Nov 7, 2026
6e7c108
fix the Parallelism note asterisk
samizdatco Nov 8, 2026
3094b80
refactor drawing, effects, and CoreText layout
samizdatco Oct 27, 2026
35472d9
refactor the view pipeline
samizdatco Oct 27, 2026
d54a6c1
draw svg and pdf images as vectors inside plotted and tiled canvases
samizdatco Oct 31, 2026
bc82adb
clip() and mask() can use a canvas's vector outline
samizdatco Oct 31, 2026
ffa197b
animate at 60 fps by default
samizdatco Nov 8, 2026
28693a0
fix text bounds on canvases in non-px units
samizdatco Nov 8, 2026
cd9c0fb
fix crash when measuring the bounds of empty flowed blocks
samizdatco Nov 8, 2026
5ac034f
skip a zoom's backdrop if one is already rendering
samizdatco Nov 8, 2026
b823c24
use raw pixel values for clip() and mask() stencils
samizdatco Nov 8, 2026
20a67c6
inherit context state in `drawpath()` calls
samizdatco Nov 7, 2026
b48ee34
move .so to plotdevice.lib._native
samizdatco Nov 8, 2026
3d9cfdf
add .offset(), .simplify(), and .area() methods to Bezier
samizdatco Jul 17, 2026
3cd96bd
remove c trig functions
samizdatco Jul 17, 2026
bbaa6b0
make the native extension safe for free-threading
samizdatco Oct 18, 2026
70ea263
add free-threaded mode to cli
samizdatco Oct 16, 2026
a53f983
add a native PDPath type for bezier paths
samizdatco Nov 1, 2026
8a995f9
drop the stale `isEnabled()` check in the Colors menu
samizdatco Nov 7, 2026
c8becc0
use PDPath for Bezier storage
samizdatco Nov 1, 2026
161a35c
fix Bezier.x and .y for paths without a fulcrum
samizdatco Nov 2, 2026
d092800
fail loudly when a venv can't be built
samizdatco Nov 7, 2026
892bf4c
rename `Bezier.offset()` to `inset()`
samizdatco Nov 7, 2026
747e5ec
disable kerning with `tracking=None`
samizdatco Nov 7, 2026
1522000
fix `Text.lines` miscounting hyphenated/multi-line runs
samizdatco Nov 7, 2026
698fb24
find SwiftDraw.o via `swift build --show-bin-path`
samizdatco Nov 7, 2026
ce75eb5
apply black point compensation manually
samizdatco Nov 7, 2026
25cd302
use the render thread for single-image exports
samizdatco Nov 5, 2026
866b6a2
use native scrollbars in the editor
samizdatco Nov 6, 2026
c235d80
reduce per-grob drawing overhead
samizdatco Nov 4, 2026
4cf85c6
replace _cg_port() with gfx._state.port
samizdatco Nov 4, 2026
8549486
move _cg_layer & _cg_bitmap into RenderState
samizdatco Nov 4, 2026
ee8ebbe
collect render state in thread-local gfx._state
samizdatco Nov 4, 2026
04efb11
wait for a vblank after an early frame delivery
samizdatco Nov 3, 2026
9843a9a
store Color's CGColors only in its cache
samizdatco Nov 3, 2026
d5dfe12
collect press and rgb profiles in Profile objects
samizdatco Nov 3, 2026
95cf9f5
group export color settings in Output object
samizdatco Nov 3, 2026
dc33d1d
group proofing settings in Proof object
samizdatco Nov 3, 2026
6b03202
sort colors.py's tables and helpers into Profile, Press, & Output
samizdatco Nov 3, 2026
46dd588
reorganize Profile paths and defaults
samizdatco Nov 8, 2026
ee7d318
move the profile classes into gfx/profiles.py
samizdatco Nov 3, 2026
ad4f6b4
update gfx/profiles.py imports
samizdatco Nov 4, 2026
4138193
merge: refactor colors and profiles
samizdatco Nov 3, 2026
2f0da07
get/set Color.hex in terms of the object's color space
samizdatco Nov 4, 2026
cf65598
merge: collect render state in gfx._state
samizdatco Nov 4, 2026
f3c9130
move gfx's state readers into RenderState
samizdatco Nov 4, 2026
2b981bb
remove leading underscores
samizdatco Nov 4, 2026
8c58931
store the active CGContext at gfx._state.port
samizdatco Nov 4, 2026
a16081a
don't reset clip & shadow state when plotting a canvas
samizdatco Nov 4, 2026
403f1d6
move the on-screen pipeline to gui/display.py
samizdatco Nov 4, 2026
cb1bfec
update gfx/canvas.py & gui/display.py imports
samizdatco Nov 4, 2026
5b3437d
name dev venvs by minor version
samizdatco Nov 8, 2026
570c83c
merge: bug fixes
samizdatco Nov 7, 2026
8ac0d32
add `layer` arg to `effects.applied()`
samizdatco Nov 7, 2026
12c24ab
draw isolated bitmaps without a transparency layer
samizdatco Nov 7, 2026
4636fbe
reject NaN and infinity in path coordinates
samizdatco Nov 7, 2026
8043fbc
pass arguments after `--` to the script
samizdatco Nov 7, 2026
13520fa
add a library fixture so the ximport test can run
samizdatco Nov 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3

- name: Run Tests
- name: Install build dependencies
run: pip3 install -U pip wheel twine build setuptools

- name: Install PlotDevice and run tests
run: |
pip3 install -U pip wheel twine
python3 setup.py test
pip3 install .
python3 -m tests

- name: Build Wheels
uses: pypa/cibuildwheel@v4
with:
output-dir: dist

- name: Build sdist
- name: Build Source Distribution
run: |
python3 setup.py sdist
python3 -m build --sdist

- name: Upload to PyPI
env:
Expand Down
28 changes: 20 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ jobs:
# collect notarization credentials
xcrun notarytool store-credentials "AC_NOTARY" --apple-id "$AC_ID" --team-id "$AC_TEAM" --password "${{ secrets.AC_PASSWORD }}"

- name: Run Tests
- name: Install build dependencies
run: pip3 install -U pip wheel setuptools

- name: Install PlotDevice and run tests
run: |
python3 setup.py test
pip3 install .
python3 -m tests

- name: Build & Notarize App
run: |
python3 setup.py dist
python3 make.py dist

- name: Create Draft Release
uses: softprops/action-gh-release@v1
Expand All @@ -55,21 +59,30 @@ jobs:
files: |
dist/*.zip

sdist:
packages:
name: Post to test.pypi.org
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Run Tests
- name: Install build dependencies
run: pip3 install -U pip wheel twine build setuptools

- name: Install PlotDevice and run tests
run: |
python3 setup.py test
pip3 install .
python3 -m tests

- name: Build Wheels
uses: pypa/cibuildwheel@v4
with:
output-dir: dist

- name: Build Source Distribution
run: |
python3 setup.py sdist
python3 -m build --sdist

- name: Upload to test.pypi.org
env:
Expand All @@ -78,6 +91,5 @@ jobs:
TWINE_REPOSITORY: https://test.pypi.org/legacy/
TWINE_NON_INTERACTIVE: 1
run: |
pip3 install twine
twine check dist/*
twine upload -r testpypi dist/*
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ jobs:
run: brew install python-setuptools

- name: Run tests
run: python3 setup.py test
run: python3 -m tests
12 changes: 8 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ build/
dist/
plotdevice.egg-info
app/Resources/viewer.nib
Manifest.in
plotdevice/rsrc
MANIFEST.in

# local development
deps/local
/*.so
.build
/_*
*.so

# external dependencies
deps/frameworks/*.framework
deps/frameworks/relocatable-python
deps/frameworks/*.framework.pkg
deps/extensions/svg/SwiftDraw*
deps/extensions/clipper2/*.o

# cds garbage
related/
Expand All @@ -31,4 +35,4 @@ xcuserdata
# unit tests
tests/_out
tests/_diff
details.html
details.html
158 changes: 158 additions & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Building PlotDevice from Source

If you’re interested in unreleased code from the repository (or doing development work on the
library/app itself), there are a few different ways to build it.

## Requirements

- macOS 11+ and Xcode or the Xcode command line tools (`xcode-select --install`)
- A Python 3.9+ interpreter with wheel availability for `pyobjc-core==11.1`
(i.e. not a brand-new Python release that predates PyObjC’s own support for it)


## Setting up Python

By default, macOS 12.3 and later only include a `python3` interpreter if you install Xcode (or its command line tools).
There are a few other ways to get a usable Python setup worth considering if you're interested in using
PlotDevice from the command line. A key feature distinguishing the different options is whether they
install Python as a ‘framework’ or ‘stand-alone’:

> A **framework** Python installation allows PlotDevice to give you access to a GUI interface for
> running scripts via the `python3 -m plotdevice` command. **Stand-alone** Python is slightly more limited:
> it supports ‘headless’ use (via the command line tool’s [`--export`](#imageanimation-export) option) and
> can open a window displaying your canvas, but it will not show an icon in the Dock or give you access to
> commands in the menu bar.

### Xcode

The [Xcode command line tools](https://developer.apple.com/documentation/xcode/installing-the-command-line-tools/) provide a
**framework** build of the (now somewhat long in the tooth) 3.9 release of Python. If you're okay running such an old interpreter,
you can install the tools with the command:
```console
xcode-select --install
```

### Homebrew

The [Homebrew](https://docs.brew.sh/Homebrew-and-Python) package manager provides a **framework** build via its more up-to-date `python3` package. You can install it and run `plotdevice` with:
```console
brew install python3
```

### `pyenv`

The [pyenv](https://github.com/pyenv/pyenv) version manager lets you easily switch between multiple python versions on the same system.
You can install a new **stand-alone** interpreter by specifying the version number:
```console
pyenv install 3.14.6
```

If you want a **framework** install, you need to pass a custom option via environment variables:
```console
env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.14.6
```

### `uv`

A newer alternative is [`uv`](https://docs.astral.sh/uv/), which uses its own prebuilt interpreters (but currently does not provide
**framework** builds). If you're okay with the limitations of a **stand-alone** interpreter (see above), `uv` gives you a number of
ways to run PlotDevice.

Run a script without permanently installing `plotdevice`:
```console
uvx --from plotdevice plotdevice <script.pv>
```

Install `plotdevice` as a standalone CLI tool:
```console
uv tool install plotdevice
plotdevice <script.pv>
```

Add `plotdevice` as a dependency in a `uv`-managed project:
```console
uv python install 3.14
uv init myproject && cd myproject
uv add plotdevice
uv run plotdevice <script.pv>
```

## Local dev environment

`python3 make.py dev` sets up a self-contained virtualenv at `deps/local/<python-version>/`
and ensures PlotDevice’s dependencies (including its native-code extensions) are installed.
Once this environment has been set up you can use `python3 -m plotdevice <script.pv>` or
`app/plotdevice <script.pv>` to run scripts directly from the repo.

The dev environment is also useful for doing local builds of the app (since it has all the
necessary dependencies pre-installed), so be sure to ‘activate’ it via the correct invocation
for your shell before running one of the build commands:

```sh
source ./deps/local/<py-version>/bin/activate # bash, zsh, etc.
source ./deps/local/<py-version>/bin/activate.fish # fish
source ./deps/local/<py-version>/bin/activate.csh # tcsh
```

To stop using the dev environment and return to your global python interpreter run:
```sh
deactivate
```

## Application builds

The application can be built and debugged in Xcode with the `PlotDevice.xcodeproj` project.

The `make.py` script contains additional utilities for building **PlotDevice.app** (i.e., the full GUI application)
from the command line. If you have the full Xcode installed on your system, you can build the app
(including its own embedded copy of Python) with:
```sh
python3 make.py app
```

Alternatively (if you only have the Xcode command line tools installed) you can use `py2app`, which can
be especially useful for quick, local tests:
```sh
python3 make.py py2app
```

The resulting binary will appear in the `dist` subdirectory and can be moved to your
Applications folder or any other fixed directory. To install a symlink to the command
line tool, launch the app from its installed location and click the Install button in
the Preferences window.

## Module builds

PlotDevice can also be built as a Python module, allowing you to rely on an external editor
and launch scripts from the command line (or from a ‘shebang’ line at the top of your
script invoking the `plotdevice` tool).

The `plotdevice` Python module requires the compilation of native code in the `deps/extensions` directory
but can be installed like any other `pyproject.toml`-based package. From the repo root, type one of:
```sh
pip3 install . # for a regular install
pip3 install -e . # editable install, for dev work on plotdevice/*.py
```
You can run the test suite with:
```sh
python3 -m tests
```

Redistributable `sdists` and wheels can be built via:
```sh
python3 -m build --sdist
python3 -m build --wheel
```

## Utilities

The `make.py` script contains other subcommands that are primarily useful for packaging:
```sh
python3 make.py clean # remove build artifacts
python3 make.py distclean # also remove the embedded Python.framework and deps/local
python3 make.py icon # regenerate app/Resources/Assets.car from app/art/PlotDevice-app.icon
python3 make.py dist # build app (codesigned, notarized, and zipped) and update release.json
```

Note that `dist` requires a "Developer ID Application" signing identity and a `notarytool` keychain profile
named `AC_NOTARY`, and is intended for release builds only.
38 changes: 29 additions & 9 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
PlotDevice 1.1.0
----------------

##### Misc. Improvements
* Upgraded bundled Python to 3.14 in app
* Added precompiled wheels for module installs (supporting Python 3.9–3.14)
* No longer requires Xcode command line tools in order to install
* `plotdevice` command's exit code now reflects script errors
* Added support for running under `uv`
* Editor now uses a `WKWebView` (replacing the deprecated `WebView`)
* Added new default editor theme
* New app icon should avoid "squircle jail" on macOS 26+

##### Bugfixes
* `ordered()` now works correctly and no longer causes the `Advanced/Sorting.pv` example to crash
* `Text.flow()` will now invoke user-provided callbacks to determine layout of multi-column runs
* Command line tool now parses `{4}`-style padded number blocks in exported filenames correctly
* Silenced a console error by fixing `super()` use in command line tool
* Restorable state in the app now uses the newer 'secure' api

PlotDevice 1.0.0
-----------------

Expand All @@ -24,7 +44,7 @@ PlotDevice 1.0.0
* arguments defined using the command line tool's `--args` options are now passed to the script's `sys.argv`

##### Misc. Improvements
* the command line tool can be exited via ctrl-c in addtion to being Quit from the menu bar
* the command line tool can be exited via ctrl-c in addition to being Quit from the menu bar
* simplified unicode handling (and improved support for normalization of user-provided strings)
* building the module now only requires Xcode command line tools—not a full Xcode.app installation
* the `text()` command will always treat its first argument as content (even if it's not a string) unless a `str`, `xml`, or `src` keyword argument is provided
Expand Down Expand Up @@ -224,7 +244,7 @@ NodeBox 1.8.3

NodeBox 1.8.2
-------------
* Direct view is turned of because it broke vector output.
* Direct view is turned off because it broke vector output.
* AppleScripting is back.

NodeBox 1.8.1
Expand Down Expand Up @@ -267,8 +287,8 @@ NodeBox 1.0rc4
* Uses a scene graph internally for speeding things up. All objects now have an inheritFromContext that copies all state out of the context and into the objects.
* Added animation. Adding a speed method indicates that the script is an animation. Animation scripts have a setup() method that does initial setup, and a draw() method that is called for each frame. You can use the throttle to change parameters while the animation is running.
* Removed document properties and variables. NodeBox doesn't touch the code anymore.
* Added measurements for centimeters, milimeters and inches. Just use 5*cm for 5 centimeters.
* All drawing commands are now methods of the Context object. This makes sure that canvases don't interfere with eachother. NOTE: This will have some problems when drawing from libraries. Most notably, "from DrawingPrimitives import *" doesn't work anymore. However, libraries that do drawing can be imported with ximport("mylibrary") to automatically gain access to the _ctx global that has all drawing methods.
* Added measurements for centimeters, millimeters and inches. Just use 5*cm for 5 centimeters.
* All drawing commands are now methods of the Context object. This makes sure that canvases don't interfere with each other. NOTE: This will have some problems when drawing from libraries. Most notably, "from DrawingPrimitives import *" doesn't work anymore. However, libraries that do drawing can be imported with ximport("mylibrary") to automatically gain access to the _ctx global that has all drawing methods.
* BezierPaths are no longer lists internally. They can still be accessed as a sequence, but the returned PathElements are not bound to the path. To manipulate the PathElements, iterate over the sequence, do the changes and use the BezierPath constructor with a list of PathElements as argument.

NodeBox 1.0rc3
Expand Down Expand Up @@ -304,15 +324,15 @@ Version 1.0rc1
* The BezierPath object got a major overhaul: it now integrates nicely with the state model (using the transformedpath method) and is actually a list containing PathElement objects that contain a command, x and y coordinates and (optionally) control points.
* All 'w' and 'h' parameters were changed to 'width' and 'height' (image, strokewidth)
* All of the major state change commands return their state: font, fontsize, lineheight, align, colormode, fill, stroke, strokewidth, transform.
* Documentation is user-friendler and inside of the DrawBot application, so that it can be accessed through Apple's Help system.
* Documentation is user-friendlier and inside of the DrawBot application, so that it can be accessed through Apple's Help system.

Version 0.9b9
-------------
* ValueLadder now respects type of original value: if the original is an integer, the new value will be an integer as well.
* Fixed booleans in variables interface.
* Shift-dragging the value ladder now adds hundreths of a number instead of tenths.
* Shift-dragging the value ladder now adds hundredths of a number instead of tenths.
* Fixed an obscure bug when using CMYK color objects to set the fill (they used the infamous "DrawBot-black" because the converted from the RGB system instead of CMYK)
* Turned of line highlighting on error because of bugs in PyDETextView.
* Turned off line highlighting on error because of bugs in PyDETextView.

Version 0.9b8
-------------
Expand Down Expand Up @@ -345,7 +365,7 @@ Version 0.9b6
* text() command returns the text bounds. (In the same way as textmetrics() would do)
* size, width and height property for the Image object. You can use imagewidth() and imageheight() also.
* Fixed a bug where outlined text could not be stroked when the fill color was not set.
* The drawing region (DrawBotGraphicsView, a subclass of NSView) is acccessible using the graphicsView variable. This is only intented for hackers and will remain undocumented.
* The drawing region (DrawBotGraphicsView, a subclass of NSView) is accessible using the graphicsView variable. This is only intended for hackers and will remain undocumented.
* Added the 'New with code' command which invokes OttoBot.

Version 0.9b5
Expand Down Expand Up @@ -384,4 +404,4 @@ Version 0.9b1

Version 0.9a1
-------------
* Just's first public release of DrawBot.
* Just's first public release of DrawBot.
Loading
Loading