Skip to content

Releases: eaviles/Ollin

Ollin 0.4.0

Choose a tag to compare

@eaviles eaviles released this 11 Sep 12:31
0.4.0
983f692

Added

  • The API reference is generated from the source as well as written by hand. Every public symbol of the twenty-two library modules now has a page built by DocC, and the core's four hundred and seventy top-level names are grouped on one landing page rather than sorted by kind. A .spi.yml file tells the Swift Package Index which modules to build that documentation for, and Scripts/api-docs.sh builds the same thing locally. It fails on a documentation warning, which is how forty stale doc comments came to light: links pointing at symbols that had been renamed away, and parameters documented under names their signatures no longer carry. It also refuses to pass if a library module of the package is missing from that file, so a new satellite cannot ship with no reference at all.
  • The site can be read by a machine, not just rendered for a person. Every page now has a markdown twin at the same address with .md in place of .html, holding what was actually written rather than navigation and styling wrapped around it, and /llms.txt is a curated index of the whole site pointing at those twins. Each page also says where its own twin is. This follows the llms.txt convention, which is what an agent with a browsing tool looks for; nothing fetches it automatically.

Changed

  • The front page's sketches are a band you page through, and a phone gets them too. Hello, circle now leads a row of sketches you move between with the dots under it, each running beside the whole program that draws it, starting with the circle the README opens on. The separate band of three below it is gone. Moving between them is the browser's own scrolling, so a swipe, a trackpad, and the arrow keys work with no script running, and only the sketch on screen plays: the rest are not fetched until you page to them, which is a megabyte and a half nobody pays for on arrival. One sketch at a time is what puts the band back on a phone, where three of them stacked was a wall of code and the whole thing had to be hidden. The command under Run it names whichever sketch you are looking at, so what you copy runs what you see.

Fixed

  • The Syphon satellite works from a package dependency again. Any project that pulled Ollin in by version, which is what ollin new --remote writes, could not use OllinSyphon at all: the build stopped at resolution with the target 'CSyphon' in product 'OllinSyphon' contains unsafe build flags. The vendored engine needed a prefix header forced into every file, which SwiftPM can only pass as an unsafe flag. Each file now imports that prefix itself, so the flag is gone and nothing about the running code changed.
  • The site's favicon no longer sits in a white box in Safari's tabs. Safari draws a light plate behind an icon whose own tone reads too close to the bar it sits on, which is what an icon carrying a near-black tile gets on a dark tab bar. The mark is drawn on nothing now, in near-black for a light bar and paper for a dark one, and the page picks between the two files: Safari ignores a color-scheme media query written inside an SVG favicon, so the drawing cannot change its own ink.

Ollin 0.3.0

Choose a tag to compare

@eaviles eaviles released this 10 Sep 16:47
0.3.0
e0d6117

Added

  • Every example can show what it does, not just what it says. Scripts/media.sh renders a clip and a still for a sketch and puts them where the site reads them, and the example's own page on the website plays the clip with the still as its poster. A sketch that declares a loop records exactly one lap; everything else records ten seconds, which is what a clip needs to answer what the sketch does before a reader moves on, and an example whose arc wants longer says so in its own row. The clips are H.264 at 4:2:0 tagged for a browser, which is the combination an iPhone plays inline, and they carry no sound. The files live outside the repository behind one address Examples/media.json names once, so moving the host is one edit. Three hundred and fifty-three examples have theirs; the rest are written down with the reason they do not, being a sketch that waits on hardware, one that waits on a hand, or one measured as barely moving.
  • Four vision examples work with no camera plugged in. The mirror that grows Turing patterns on you takes the bundled dancer, the object tracker the wrestler, the depth contours the street, and the contour tracer is told which way round its contrast runs, which is why it had been finding nothing in the bundled film. Each reads the picture its own technique reads best, the way the rest of that folder already did. Two are unchanged and honest about it: the barcode reader has no bundled code to read, and the trajectory tracker draws its own synthetic ball.
  • The guide and the README open on the work. The guide's first picture is now the sketch each of its thirty-two chapters builds, one per cell, in chapter order, and the README's is thirty-two example sketches from every area of the framework. On the website both play. The markdown carries the still, which is what GitHub and a plain clone can show, and a reader who blocks video or has asked for less motion sees exactly that.
  • Every examples listing opens on a grid of its sketches. Each picture links to the sketch and carries its name, so a folder shows what it holds before it says it. On the website the grid is a gallery that reflows to the screen, and resting the pointer on a cell swaps its still for a short clip that plays there; nothing is fetched until then, and a screen with no pointer or a reader who has asked for less motion keeps the still. The examples front page and the 3D and recreations indexes show one picture per group instead, chosen per group and written down. The 3D folder became ten pages on the way, one per topic, since a hundred and thirty-seven sketches in one listing could not take a grid.
  • Thirteen figures stopped faking what they teach. The Guide's optical-flow, slit-scan, contour, saliency and detector figures used to author their subject in code: a dancer made of two hash-speckled discs, a clip made of sine stripes, a printed card rasterized from glyph outlines. They read the bundled film and the bundled photographs now, so what a reader sees is what the technique does to a real picture. Motion paints from the film's own frames, the arrows gather on the arms that swing and thin to nothing across the rest of him, the slit scan turns a swung arm into a comb of sleeves, the two detectors read a real desk and print the five lines they found, saliency lands on a face while the classifier puts marigold under its own floor, and the contour tracer comes back with both palms and their creases. The melt, the triplanar skin, the surface grain, the two live-feed environments, the design filters and the print separations took photographs too. Three swaps were tried, rendered, and put back: a spectrum, a dither and a seamless clone all teach better on the drawn source, and the reasons are written down.
  • VideoPlayer.snapshot() works offline. Under a headless export it reads the same virtual playhead the frame does, so seek(to:) and then snapshot() hands back exactly the moment asked for. A still-image detect(in:) over a clip now works where a live tracker reads nothing, which is what lets a figure measure optical flow between two frames of a film and render the same picture on any machine.
  • A sketch that reads the world still runs when the world is not plugged in. Camera.orStill(_:) gives back a running camera where the Mac has one and a still picture where it does not, and everything downstream takes either, since both are a feed. Fourteen of the vision examples now fall back to a bundled photograph instead of showing a notice, each to the one its own technique reads best: a body to find, hands to find, a face, a scene to name. --photo on launch takes the picture even where a camera would have worked, which is how a still of a live sketch is made.
  • StillFrames. The feed underneath: one picture published as both a VideoFeed and a FrameSource, so it stands wherever a camera or a video player would. It republishes a few times a second rather than once, because an analyzer drops frames it cannot keep up with and a single frame could be the one dropped.
  • A photograph in the color-vision check. Color/ColorVision and the Guide's figure now show a picture beside the palettes, seen the same four ways: the woven blankets, where red sits beside green oftener than in any other bundled photograph, so the two commonest kinds flatten the weave to one olive band while the blues hold. A palette is still checked color by color, a picture by filtering the layer. The example's preview parameter now draws every column in its true colors first, so the one filter over the finished canvas is the only thing simulating anything, which is what that mode always claimed to do.
  • A pair of hands worth tracking. SamplePhoto.hands joins the bundled set, two open palms against a dark ground with every finger separated, and Vision/HandTracking reads it instead of the breakdancer. Both hands come back with all twenty-one joints, so the sketch draws ten finger chains running out to ten separated fingertips; the breakdancer's hands are planted and half closed, which gives the tracker something to find and the drawing almost nothing to show.
  • A short film, for what a still cannot show. SampleClip.dance joins the bundled set: thirty seconds of a dancer on a plain studio ground, 960 square, silent, 1.5 MB, with the camera locked off so that nearly two thirds of the frame never changes. Optical flow, a contour tracer, a skeleton and a matte all want motion rather than a picture, and Vision/OpticalFlow and Vision/ContourTrace now read the film where there is no camera, having shown nothing at all before. It is vended as a URL rather than as a player, so opening it stays with OllinVideo.
  • The optical-flow overlay reads any source. Its arrows were scaled against a fixed number of pixels, which suits a hand waved at a webcam and drew almost nothing for a dancer moving a few pixels a frame. Length and color now read against that frame's own fastest motion, so the field is legible whatever it is pointed at.
  • A page under uneven light, and two surfaces to wrap a form in. SamplePhoto.page, .talavera and .stone join the bundled set: a book held open with shadow falling across it, a panel of Mexican talavera tilework, and a dry-stone wall. The page is what adaptive thresholding exists for, and the Guide's LocalAverages figure now shows it losing whole bands of text to one global cut and getting all of them back from a local one; Vision/TextScan reads it too, sixty lines where the desk gave five. The talavera is the one bundled picture that repeats seamlessly, cut to two whole periods of its motif, which is what 3D/Materials/Triplanar needs, since a triplanar projection tiles whatever it is given whatever the wrap says. The stone carries real relief, so 3D/Materials/Parallax reads one photograph as both its color and its height map. The two surfaces are 1024-pixel squares rather than 1600 on the long side, the size a GPU mips evenly.
  • Two landscapes at dusk, and the first wide pictures in the set. SamplePhoto.headland and .boats join the bundled photographs: a headland over the water at Loreto with cacti in silhouette, and small boats off Cozumel under a pink sky. Everything bundled before them was a square or an upright, so a sketch that wanted a landscape had to crop one and throw away the photographer's frame. These two are three to two and share one shape, so either drops into the same box. Effects/SeamlessClone now clones its slab onto the Cozumel picture instead of a painted wash, which is the honest demonstration of a compositing technique: it was picked because its brightness stays in a narrow band while its color runs from grey-violet to pink, which is the ground a clone needs.
  • Four streets, for the techniques that want a whole scene. SamplePhoto.alley, .street, .textiles, and .city join the bundled set: a Guanajuato alley of pastel walls under a wide sky, a San Miguel de Allende street strung with papel picado, woven blankets hung side by side at Teotitlán del Valle, and rooftops running back to the hills. Each is the largest square its original held, so two of the photographer's own edges are untouched. Pixel sorting and seam carving now run on the alley, where half the frame is sky for the carver to eat; the frequency domain runs on the weave, whose spectrum carries bands rather than a plain blob; and the picture-fit example and figure run on the city, whose round dome is the fastest way to see a stretch.
  • Image.cropped(x:y:width:height:) and Image.cropped(toAspect:). The companion to resized: that one changes how big a picture is, these change what is in it. The rectangle form is clamped to the picture, so a crop that runs off an edge comes back smaller rather than empty; the aspect form takes the largest centered piece of a given shape, which is what a square photograph needs before it can stand in for a w...
Read more

Ollin 0.2.0

Choose a tag to compare

@eaviles eaviles released this 08 Sep 15:17
0.2.0
73a478f

Added

  • Newton's basins. generate(.newton(roots:)) runs Newton's method from every pixel over the polynomial with the roots you place (up to eight) and colors each pixel by the root it lands on, shaded by how many steps it took, with the palette spread around the roots and phase turning it. relaxation scales the step so the basins swell, spiral, and shed islands, and a pixel the method never brings home is painted trapped. The Effects/NewtonBasins example, a Guide section in Chapter 18 with the NewtonBasins figure, and the newton-basins snapshot.
  • A homage after Frieder Nake. Examples/Recreations/FriederNake/HommageAPaulKlee draws the drawing usually called Hommage à Paul Klee (13/9/65 Nr. 2, 1965) from Nake's own list of its chance decisions: horizontal bands of rolled width whose edges buckle across the sheet without crossing, every quadrilateral left empty or filled with vertical lines or triangles, and circles thrown over the whole. A seed is a sheet, and --export-svg gives the plotter drawing back.
  • A board with no firmware of your own. FirmataBoard in OllinSerial speaks Firmata over a serial port, so a board running the StandardFirmata sketch every Arduino IDE ships answers pin by pin: analog(0) and digital(2, pullUp: true) read, write(13, true), write(9, 0.5), and write(10, angle: 90) drive a digital pin, a PWM duty, and a servo, bind(analog:to:) puts a pin on a @Param, and messages() drains every decoded message. Asking a pin is what turns it on, and a board that resets is told everything again. The Integration/Firmata example.
  • The public API, written down. API/ holds one generated listing per library product (every public type and member with its types, defaults, and deprecation), and Scripts/api-surface.sh fails preflight whenever the build and the listings disagree, so a change to the public surface is recorded on purpose and reads as a diff in review. This is the ground the 1.0 deprecation discipline stands on.
  • Deriving one easing curve from another. Easing.reversed() runs a curve from its other end, so an ease-in becomes its ease-out, and mirrored() puts the curve on the first half of a trip and its reverse on the second, the way the catalog builds its ease-in-outs. Where the catalog already holds the result the helper hands back the built-in (.easeInQuad.reversed() == .easeOutQuad), so it still reads by name on a menu; mirroring an ease-out gives the out-in curve the catalog lacks. Example Motion/DerivedCurves, a Guide chapter 3 figure, and the Animation page.
  • The performance host on a controller. OllinLiveCoding answers OSC at fixed addresses under /ollin/ (evaluate, evaluate fresh, hide the code, the code's backdrop and size, record) once a port is set in the inspector's new Controls card, and learns a MIDI pad, button, or fader for any of them: press Learn, move the control, done. A host preference, never the sketch's; the map holds across launches. Checked headless by --controltest. OllinInspector.Palette is public, so a host's own card can sit beside the shipped ones.
  • A room of your own: convolution reverb. Reverb(impulse, mix:, preDelay:) plays an instrument through an ImpulseResponse: a recording (.load, .resource), fading noise (.decay(seconds:damping:)), or a room drawn from a rule (ImpulseResponse(seconds:) { time, noise in }), and .reversed() runs any of them backward. It sits in the chain like the built-in rooms, synth.reverb still reaches it, it reaches an export exactly, and turning mix never cuts a tail that is sounding. Examples/Audio/Rooms draws five rooms from rules. Effect.Kind gains .convolution, the kind a reverb carrying a room reads as.
  • Timecode: a sketch that chases a timeline. TimecodeClock(from: midi) follows MIDI Time Code, the position a video deck, a show controller, a lighting desk, or a DAW locked to picture sends as hours, minutes, seconds, and frames, into timecode, seconds (gliding between frames), frameRate, isPlaying, and isReversed. The Timecode value does the drop-frame arithmetic and both wire forms, MIDIOutput.send(timecode:) and send(sysEx:) cover the other direction, and the Timecode example plays the deck itself. MIDI
  • Sharp features from a field: dual contouring. isosurface(at:in:resolution:method: .dualContouring) meshes a field with one vertex per cube placed where the field's own normals meet, so a block's corners land on the corners and a bored hole keeps its rim, at any resolution, where marching cubes (still the default, unchanged) rounds every edge off to the size of a cell. A cube on a crease carries one vertex per side, so the faces shade flat to the edge while a sphere still shades smooth. Isosurfaces
  • A homage after Josef Albers. Examples/Recreations/JosefAlbers/Homage puts the Homage to the Square format on the canvas, squares of ten, eight, six, and four units on a ten-unit grid, each set half a unit lower than the one around it, in the four arrangements he used, and rolls a new set of colors onto it every few seconds from five palette rules of its own (a ramp, a glow, near-value hues, grays around one color, warm against cool), built in OKLCH and crossfaded in OKLab, with contrast and drift scaling the sheet's steps live and --export-grid printing a wall of them. Recreations
  • Search on the website. Every page ollin site writes now carries a search: the button in the bar, /, or ⌘K opens it, and it looks through the Guide, the reference, and the examples one section at a time, a page's title counting most and the words of its prose least, with a prefix reaching a word (kuwa finds brushwork), a typo in a longer word forgiven, and the words that matched marked. The index is built with the site; the matching is MiniSearch (MIT), loaded from a content network on the first search and pinned to one release by its hash.
  • Embroidery, a frame as stitches. --export-embroidery leaf.dst writes the frame as the stitches an embroidery machine sews, in the Tajima .dst file nearly every machine reads: strokes as a running stitch whose every corner is a penetration, fills as connected rows (or as outlines), and each color as its own thread in draw order. Embroidery(width:) takes the hoop's real millimeters, and Embroidery.stitches(_:in:) plans your own contours, so a sketch can draw the stitches before anything sews.
  • Brushwork, the picture as paint. layer.filtered(.brushwork()) lays the layer on as paint patches that follow the picture's own flow: each pixel takes the average of the flattest of eight sectors of a brush around it, so detail flattens while edges stay crisp, and the brush stretches along whatever edge runs through the pixel, so the patches read as strokes along the contours rather than the square dabs of .oilPaint. radius is the brush size, stretch how far an edge can draw it out, and sharpness how decisively the flattest sector wins.
  • Lighter, darker, and the complement of a color. ink.lighter(), ink.darker(by: 0.25), and ink.complement derive a color from one you hold: the first two step its perceived lightness in OKLab units while its hue and chroma stay put, the third is the opposite hue at the same lightness and chroma. A vivid color pushed to the top ends at white, a gray is its own complement, and alpha rides along.
  • Gabor noise, a noise whose spectrum you design. generate(.gaborNoise()) fills a layer with sparse Gabor convolution: small Gaussian blobs each carrying a cosine wave, scattered and summed, so the field has one wavelength (in pixels), a band bandwidth wide around it, and one direction (angle, with spread 0) or every direction (spread π). Brushed metal, wood grain, silk, and rippled water at a turn of the parameters, with the long interference patterns a narrow band makes. impulses is the quality, phase slides the waves (periodic over 2π), seed picks the field, and the kernels are filtered for the pixel footprint so a wavelength near the pixel grid fades instead of aliasing. The same field on the CPU is gaborNoise(x, y, ...) and the GaborNoise value type, computing at a pixel what the generator paints there, and in a shader of your own it is the library's gaborNoise(p, ...). Examples/Effects/GaborNoise.
  • XDoG, the picture as pen and ink. layer.filtered(.xdog()) draws a line wherever the layer has an edge, solid ink where it is dark, and paper everywhere else, the lines following each edge's own direction so they run continuous rather than breaking into speckle. radius is the line scale, sharpening how far the edges are pushed over the tone, threshold and softness the cut and its ramp (0 is a two-tone print), flow how far along an edge the response is gathered, and foreground / background the ink and the paper. Empty space on a transparent layer reads as paper. Examples/Effects/InkDrawing.
  • Hobby's spline. drawCurve(points, spline: .hobby) threads a curve through points the way a practiced hand does: every gap becomes a Bézier whose control points are solved together, so the bend flows evenly instead of flattening between far-apart points and swelling between close ones. .hobby(tension:curl:) tunes it, Contour(curveThrough:spline:) and Shape(curveThrough:spline:) take the same choice, and HobbySpline(through:) is the typed form with the Béziers themselves. The default spline is unchanged.
  • The drag on the performance stage. Under OllinLiveCoding, hold Command over the stage and the shape under the pointer is outlined through the code. Dr...
Read more

Ollin 0.1.0

Choose a tag to compare

@eaviles eaviles released this 02 Sep 18:58
0.1.0
75090ad

The first tagged release. Ollin grew in one repository from May 2026 to this tag, and that history is not itemized here. This entry says what the release contains, area by area, with a link to the reference for each. The README carries the fuller catalog, and the Guide teaches it in order. Read this entry as a table of contents rather than a list of changes.

The framework

  • Drawing. A p5-style API over a Metal core: setup() and draw(), and bare calls like drawCircle and background. draw() runs at the display's refresh rate from the first line, so motion is the default. Some thirty analytic shapes, curved paths, concave and holed fills, variable-width strokes, marks that follow the hand, shape booleans and offsets, gradients, blend modes, clipping, accumulation, and retained batches. Every frame composites in linear light with HDR tone mapping and analytic anti-aliasing. Drawing.
  • Text and color. Bitmap, outline, and single-line plotter fonts through one drawText, in any script, in columns, justified, along a path, or as vector shapes. OKLab mixing, spectral paint mixing, palettes and colormaps, color vision simulation, and wide-gamut and HDR output. Text, Color.
  • Shapes that merge. SDF combinators in 2D and in raymarched 3D: smooth union, subtraction, morphing, domain mirror, tile, and radial repeat, and a clay-like sculpt { } block. Combinators.
  • Generative technique. Sixty-six classic techniques as first-class helpers, from Poisson-disk scatter and L-systems to Wave Function Collapse, strange attractors, marching cubes, paper marbling, and terrain erosion. Generators.
  • Layers, effects, and shaders. Off-screen layers with fifty-plus GPU filters, two-layer combines, feedback, simulation fields, and a compose DSL. Measured distance fields, light with shadows in a flat sketch, the frequency domain, and summed-area averages. Your own shade(uv, info) fragment function, inline or in a hot-reloading .metal file, with errors reported at your own line numbers. Chainable Visuals when you would rather skip the Metal. Effects, Shaders.
  • Motion and parameters. Frame-rate-independent easing and springs, keyframe timelines, seeded variations, and record and replay. @Param properties become typed inspector controls, persist across reloads, and can follow a curve or a formula. Sketch, Automation, Variations.
  • GPU compute and simulation. Compute kernels with a million particles a frame, and a neighbor-search primitive under artificial life and SPH fluids. Ping-pong texture simulations, steering crowds at GPU scale, populations that evolve, and chaotic systems. Compute, Simulation.
  • Physics. A stepped World with Verlet particles and springs, Box2D bodies and joints in 2D, and Jolt-backed 3D bodies, joints, characters, vehicles, ragdolls, cloth, ropes, and water. Physics, Physics in 3D.
  • 3D, opt-in. Cameras and imported scenes (OBJ, glTF, USD), instanced meshes and GPU-culled worlds, grass and an ocean, physically based materials with the full map set, glass with dispersion, area lights, image-based lighting and a procedural sky. Shadows, ray-traced reflections, caustics, global illumination, atmosphere, temporal anti-aliasing, motion blur, and lens flare. A 2D sketch never pays for any of it. 3D.
  • The world as input. Nineteen on-device Vision trackers over the camera or any video, depth from a video model, screen and window capture, and video playback. CSV, JSON, and SVG import, live and pushed data feeds, game controllers, and an iPhone over USB as a LiDAR, motion, body, face, and hand sensor. Vision, Data, The phone.
  • Sound. FFT analysis with beat detection, listening for words and named sounds, and a synthesizer the sketch plays. Voice presets, physical models, sampled instruments, wavetables, a patchable graph, effects chains, a place in the 3D scene, and algorithmic composition, all carried into an exported video. Audio, Synthesis.
  • Integration. MIDI, OSC, a network tempo session, serial, Bluetooth Low Energy, DMX and LED mapping, and show lasers. Syphon out and in, a virtual camera, haptics, a remote surface for a phone, and a room several machines join to draw one piece on one clock. Integration.
  • Output. Headless stills and deterministic sequences, MP4 and GIF with slow motion, SVG and PDF with hatched fills, G-code, and print separations with press proofing. STL, OBJ, and 3MF for printing, USDZ and spatial video, a path-traced export, live recording with sound, and an accessibility layer for color vision and motion. Export.
  • Running in the system. A finished piece as a signed Mac app, a screen saver, the desktop wallpaper, a menu-bar strip, or an installation that stays up for a week. A sketch as an app, Installation.

The tools

  • The ollin command. One loose .swift file is a whole sketch, run from any directory. ollin new writes a starter, ollin install puts the command on PATH, and ollin check compiles a shader on this machine's GPU. ollin docs and ollin examples read the reference and the examples in the terminal. Single-file sketches, The reference offline.
  • OllinLive. Save the file and the running window hot-swaps the sketch, with the clock carried across. @Param properties appear as typed inspector controls, a timeline records their automation, and Command-drag moves, resizes, or turns a shape by editing the source. ⌘] and ⌘[ put it in front of or behind its neighbor. The parameter timeline, Dragging a shape.
  • OllinLiveCoding. The performance host: the sketch fills the window, the code rides over it as translucent text, and Command-Return recompiles the buffer mid-motion. Live coding.
  • The project generator. ollin new and ollin generate write a ready-to-run project from ten templates or from any example. They also bring a GLSL fragment shader or a glTF or USD scene over as source, and scaffold an extension library other sketches import. Project generator.
  • The examples. 494 example sketches in their own package, browsable in one gallery window, including twelve recreations after eight artists. Examples.

The documentation

  • The reference under Docs/, about two hundred pages, with a short concept page for each idea the reference assumes.
  • The Guide, thirty-two chapters and four appendices: a practical introduction to creative coding taught through Ollin.
  • For contributors, CAPABILITIES.md inventories every capability's invariants, ARCHITECTURE.md explains the internals, and ATTRIBUTION.md credits the influences and the techniques.

Requirements

  • macOS 26 or later, a Metal-capable GPU, and a Swift 6 toolchain. Apple platforms only, by design: iOS and visionOS are future targets, and Linux, Windows, and the web are out of scope.

Versioning from here

  • Tags begin at 0.1.0. A breaking change or a new feature bumps the minor, a fix bumps the patch, and each release names its breaking renames in this file. Deprecation shims and a settled surface arrive at 1.0. Until then, pin .upToNextMinor(from: "0.1.0").