Repository navigation
Releases: medinstech/perfboard-studio
Release list
Perfboard Studio v0.19.0
Added
-
A solderless breadboard is a board type. File ▸ Board Setup ▸ Type ▸ Breadboard, in
the three sizes they are sold in: full (830 points), half (400) and mini (170, no rails).
The pattern is the real one -- five holes to a clip up each half of a column, a groove down
the middle for a DIP to straddle, and on the larger two a + and a − rail along each long
edge -- and it is the board's shape rather than anything new in the file, so a.perffrom
an earlier release opens exactly as it did. Two parts of different nets in one clip are a
short, and LVS says so; a pin where the board has no hole is a DRC error. -
Every hole on a breadboard is named as the board prints it:
e12, the row letter and
then the column, andT+12orB-3for a hole on a rail. The guide, DRC, LVS, the
router's explanations, the window and the MCP server all say it that way, and an agent
addresses a breadboard's holes the same way. -
Place on the Board and Autoroute work on one. The placer keeps two nets out of one
clip, puts a chip across the groove because nowhere else separates its pins, and keeps
everything but ground off a − rail and everything but power off a + rail. Autoroute plans
jumpers into free holes -- one lead to a hole -- runs ground and power down the rails with a
short jumper from each clip that needs them, bridges a rail pair once at the end of the
board when both halves carry a net, and reports anything it cannot do. All six examples
place and wire on a full-size breadboard with LVS clean. -
It looks like one, in both views and in STEP: white plastic with square openings, the
groove, a blue line beside each ground rail and a red one beside each power rail, and the
column numbers and row letters it prints. Leads go into the plastic and stop there; nothing
is soldered underneath. -
Its build guide is a building guide. No iron, no solder and no flux on the bench --
unless a chip has to go onto its adapter first -- and solid-core wire, because a stranded
end frays in a clip. The jumpers go in first, flat on the plastic, cut to their length and
two 6 mm stripped ends; then the flat parts; then each IC across the groove, pin 1 named by
the hole; then everything else, shortest first. Each continuity check comes once the parts
it probes are in. In Turkish too. -
File ▸ New Board from This Design takes the circuit to another board as a new
document -- every part back in the design, the nets and the schematic carried, nothing of
the old board's -- and asks which board to put it on. A circuit tried on a breadboard goes
on to perfboard this way, and the breadboard's own file stays as it was.
Windows - PerfboardStudio_..._Setup.exe. It is unsigned, so SmartScreen will
warn on first run: More info -> Run anyway. A signed build is planned.
Linux - perfboard-studio-...-x86_64.AppImage. chmod +x and run it; it needs
glibc 2.35 or newer. If it will not start, the machine has no FUSE 2: install
libfuse2, or run the file with --appimage-extract-and-run, which unpacks it
instead of mounting it.
macOS - perfboard-studio-...-arm64.dmg, Apple silicon. Drag it to Applications,
then run this before opening it:
xattr -dr com.apple.quarantine /Applications/PerfboardStudio.app
It is signed ad-hoc but not notarized, so Gatekeeper blocks the first launch.
On macOS 15 and later the dialog it raises defaults to Move to Trash, which
deletes the install. Clearing the flag first avoids the dialog entirely. On an
Intel Mac, install from source.
Anywhere with Python 3.12+: pip install -U perfboard-studio, then perfboard-studio.
Same release, no installer and no SmartScreen warning; Qt and VTK are most of a
400 MB download the first time.
Checking your download - SHA256SUMS carries the SHA-256 of all three
files. sha256sum -c SHA256SUMS on Linux, shasum -a 256 -c SHA256SUMS on
macOS, Get-FileHash <file> in PowerShell. Perfboard Studio does this for you when it
fetches an update itself: Help - Check for Updates. It proves the file
arrived intact, not who built it - that second claim needs the code signing
this project has not bought yet.
This is pre-alpha software. Nobody has yet built a real board by following a
generated guide - check every measurement against the part in your hand.
Perfboard Studio v0.18.0
Added
-
A part on the panel is wired to the board, not placed on it. A pot, a jack, a
footswitch, a DC socket or a bezel LED that lives in the enclosure is taken off the board
with Mount on the Panel, on its right-click menu on the board or on the schematic. On
the board it becomes the row of pads its wires land in -- where its pins were when they
stood in a row, so the copper already laid still reaches them -- and it keeps everything its
package used to say: an LED its A and K, a pot its symbol on the sheet. One undo step takes
it back. Such a part can also be asked for directly aswire-<n>, with-p<step>for pads
further apart and-l<mm>for how long each wire is cut, or from Custom Part; KiCad's
SolderWireandSolderWirePadfootprints import as one, and a KiCad potentiometer
symbol on one stays a potentiometer. -
The build guide wires it in with the long wires (phase 7): which pad goes to which lug
of the part, named as the part names it, and over which edge of the board the wires leave.
Every wire is on the cut list, in its net's colour and gauge, cut to the length the part
gives or marked "cut to fit"; the bench list counts them with the hookup wire. A continuity
check on a net that reaches the part waits until its wires are in, rather than failing
before they are. -
Both views draw the wires leaving, and the same wires. Each runs from its pad over the
nearest edge in its net's colour, dressed like an insulated wire on the board, with its
tinned end showing at the pad; in 2D it fades out past the edge on its way to the panel, and
in 3D it rises out of the pad and drops away past the edge. A row of pads pointing at its
edge is laid in lanes side by side, rather than every wire on top of the one in front. -
Every such wire says where it goes. It carries a marker just past the edge, as a
harness wire does -- "RV1·2", or "J1·T" once the jack's leads are named -- in both views:
printed on the wire in 2D, on a white sleeve in 3D. The part's tooltip lists each wire with
its pad, the lead at the other end, its net and its colour, and the guide's step says the
same of each one: "H24 to RV1 pin 1 — GND, black wire". A part off the board no longer
prints its pin names beside its pads, where they lay on top of its own wires, and other
parts' names stop short of those wires. -
Two more symbols a part can declare: a potentiometer, and an LED once its leads are
named A and K. In the part's properties, and aspotentiometerandledfor an agent. -
A surface-mount chip goes on the board on a DIP adapter. A SOIC, SSOP, TSSOP or MSOP
is soldered onto a small adapter board that stands on two rows of header pins, numbered as
the DIP of the same pin count -- so the netlist drawn for the SOIC lands on the board
without a pin moving. Asked for asadp-soic-8,adp-tssop-20and so on, or from
Custom Part. The board is the size the makers sell (rows 0.3 inch apart for eight pins
and 0.6 above, half a pitch of board round the holes), and the part is as tall as the chip
on it or the header's tails, whichever stands higher. Both views draw the chip on its
adapter, legs and pin-1 dot included. -
A KiCad netlist's SOIC, SSOP, TSSOP and MSOP footprints import on an adapter instead
of being refused as surface-mount, and a part the catalog knows keeps its pin names in the
smaller package: an LM358 in a SOIC-8 is the catalog's LM358. A footprint with an exposed
pad, a SOT-23 or a QFN is still refused, because no DIP adapter brings it out pin for pin. -
Three more parts in the catalog: the DHT11 and DHT22 humidity and temperature
sensors, pinned from Aosong's datasheets, and Pololu's A4988 stepper motor driver carrier.
The carrier is drawn as what is on it in both views -- the chip, the sense resistors, the
capacitor and the current-limit trimmer, from Pololu's own photograph -- but only for a
part named Pololu: the StepStick boards sold as "A4988" share its pins and not its parts,
and keep the plain block. -
Pololu's DRV8825 carrier and the Seeed XIAO ESP32C3 in the catalog, each drawn as what
is on it: the DRV8825's chip across the lower half of its purple board, the XIAO's USB-C
socket, shield can, buttons and antenna socket -- placed from Seeed's own board file. On
these two, and on the A4988, the real parts stand where the names beside the pins would
go (their makers print the names on the back), so the names are printed over the parts on
the same dark tag the names on the board wear, and every part is where it really is. -
The build guide has the chips soldered onto their adapters first, in phase 0 while the
bench is clear and the legs can still be reached, and then fits each adapter as the DIP it
stands in for: a socket in phase 2 if there is one, the adapter with the other ICs at the
end. The bench list counts the header pins and asks for a magnifier.
Changed
- The phenolic board is a quieter orange. Still the hue of the boards in the photographs
it was measured from, at less saturation: a whole board of flat orange was the loudest thing
in the window, and an orange film capacitor all but disappeared into it. update_parttakesoff_board, which is Mount on the Panel for an agent, on the board
or in the design. Any other new footprint for a part already on the board is still refused.
Windows - PerfboardStudio_..._Setup.exe. It is unsigned, so SmartScreen will
warn on first run: More info -> Run anyway. A signed build is planned.
Linux - perfboard-studio-...-x86_64.AppImage. chmod +x and run it; it needs
glibc 2.35 or newer. If it will not start, the machine has no FUSE 2: install
libfuse2, or run the file with --appimage-extract-and-run, which unpacks it
instead of mounting it.
macOS - perfboard-studio-...-arm64.dmg, Apple silicon. Drag it to Applications,
then run this before opening it:
xattr -dr com.apple.quarantine /Applications/PerfboardStudio.app
It is signed ad-hoc but not notarized, so Gatekeeper blocks the first launch.
On macOS 15 and later the dialog it raises defaults to Move to Trash, which
deletes the install. Clearing the flag first avoids the dialog entirely. On an
Intel Mac, install from source.
Anywhere with Python 3.12+: pip install -U perfboard-studio, then perfboard-studio.
Same release, no installer and no SmartScreen warning; Qt and VTK are most of a
400 MB download the first time.
Checking your download - SHA256SUMS carries the SHA-256 of all three
files. sha256sum -c SHA256SUMS on Linux, shasum -a 256 -c SHA256SUMS on
macOS, Get-FileHash <file> in PowerShell. Perfboard Studio does this for you when it
fetches an update itself: Help - Check for Updates. It proves the file
arrived intact, not who built it - that second claim needs the code signing
this project has not bought yet.
This is pre-alpha software. Nobody has yet built a real board by following a
generated guide - check every measurement against the part in your hand.
Perfboard Studio v0.17.0
Added
-
The board view draws a module as what is on it too, from above, for every module the
3D view knows: the WROOM's can and antenna, the Nano's chip on its corner, the USB socket,
the buttons, the cans of a buck module. The dark block across the middle said nothing about
which way round a module was -- an ESP32 devkit's antenna end and its USB end looked the
same -- and the pin names stopped short of parts this view did not show; now they stop at
parts both views show, in the same colours. -
A module's board is the colour its maker makes it: a Raspberry Pi Pico is green and the
Espressif devkits black, in the board view, the 3D view and the STEP export alike. Every
other module keeps the blue most of them are made on. -
The build guide keeps your place. Each step in the Build Guide panel has a tick, and a
ticked step is remembered for that board: the phase headings and the summary count what
is done, and Next Step to Do picks the first step without a tick -- shown on the
board, and in 3D when the 3D view is open -- which is where somebody coming back to the
bench left off. The ticks live in the application's settings, not in the board file, so
ticking one off does not mark the board modified or join the undo stack; a board saved
under a new name takes them with it, and Clear Progress takes them all off, asking
first. -
A click in the 3D view selects what it lands on. A part clicked there is selected on
the board, which scrolls to it, and on the schematic, and the Build Guide goes to the step
that fits it; a wire or a solder run clicked lights its net everywhere, as clicking a wire
on the schematic does; a click on the bare board puts the selection down. What is selected
anywhere -- on the board, on the schematic, in the Nets panel -- is tinted in 3D, so the
three views side by side show one selection. Dragging still turns the camera: only a
press and release in the same place counts as a click. -
The 3D view steps through the build one step at a time, as well as playing it. Two
arrows either side of Play go back and forward a step -- and so do the left and right arrow
keys while the 3D view has the focus (only then, because on the board they nudge parts).
Playing is for watching the build go by; somebody following it at the bench wants the next
step when they have done this one, and dragging the slider a pixel at a time on a 40-step
board was the only way to ask for it. A step asked for stops the playing, and the Build
Guide beside the view moves to the same step, as the view already followed a step picked
in the guide.
Changed
- Board, Schematic and 3D stand side by side. The 3D view used to open split off to the
right of the board as a strip about 190 px wide -- it is shut on every start, so the
window's opening proportions never reached it -- with the build guide stacked behind it
as a tab, both their names cut short. Now a view opened takes its place beside the others
and the middle is shared out equally between them; one closed gives its room back. The
window opens on the board, the schematic and the 3D view come up beside it when they are
asked for, and the build guide stands down the right, given room to be read when it opens.
Because this is a new opening layout, a panel arrangement saved by an earlier version is
set aside once and the window opens on the new one. - The Board, Schematic, 3D and Guide buttons are at the start of the toolbar, as
switches. They sat at the far end of the row, where they read as one more tool rather
than as what is on screen; now each opens or closes its panel and is lit while it is
open. Ctrl+1 to Ctrl+4 still only ever bring a view forward. - The 3D view carries its own tools. Exploded View and Reset Camera are on a bar across
the top of the 3D panel, as the schematic panel carries its own; they lived in the View
menu, three menus away from the view they work on, and they are still there too. - The Turkish interface calls the 3D view one thing. The toolbar button, the workflow
bar and four tooltips said "3B" while every menu, dialog and the README said "3D"; it is
"3D" throughout now. - An LM2596 buck module and an SN65HVD230 CAN breakout are drawn as what is on them. A
module's id carries its board and the height of its tallest part and nothing else, so in
3D every module was its blue board with one dark block standing on it. A module whose value
names one of these two --LM2596,SN65HVD230,VP230,CJMCU-230-- now shows its
parts at their datasheet sizes: the buck its two cans, the D2PAK regulator with its legs,
the shielded inductor, the Schottky diode and the blue multi-turn trimmer with its brass
screw; the breakout its SO-8 chip with its legs and marking, its 0805 resistors and
capacitors and its silkscreen. The layout turns with the part and follows the pins' names
(an LM2596 described with IN on the right has its regulator on the right), stays on the
module's own board and clear of the pin names printed on it, and no part stands taller
than the id says -- the height DRC measures. A value nobody wrote a layout for, or a board
of another size, keeps the block, and the STEP export is still the envelope. Every module
also shows the tinned pad each of its pins comes up through. - The five modules in the catalog are drawn as what is on them too -- the ones most
boards will carry. The ESP32-DevKitC is its WROOM-32 with the shield can and the antenna
past the board, the regulator, the USB bridge, EN and Boot either side of the micro-USB
socket; the ESP32-C3-DevKitM-1 its C3-MINI-1, the RGB LED and Boot and RST; the Arduino
Nano its ATmega328P turned on its corner, the reset button, the four LEDs, the mini-USB
socket and the unfitted ICSP holes; the Raspberry Pi Pico its RP2040, the flash and
crystal, BOOTSEL and the green LED; the WEMOS D1 mini its ESP-12F and the reset button in
its side. Positions come from the makers' own drawings -- Espressif's dimension drawings,
Arduino's datasheet -- and from KiCad's models of the Pico, the D1 mini and the radio
modules, measured rather than eyeballed. A module described with its pins the other way
round is turned by their names, so the USB socket stays at the USB end, and a Nano Every,
a Pico W or an ESP32-S3 DevKitC on the same footprint keeps the block rather than being
drawn as a board it is not. The README's close-up ofnano-relayis redrawn, with the
Nano on it. - A module's pin names stop at the first part standing in their way, in both views. A
WROOM can is 18 mm wide between pin rows 25.4 mm apart, so no fixed strip beside the pins
could be kept clear on a real devkit; the names now run until they meet a part, which is
how the module's own silkscreen is printed, and every layout leaves each name room to be
read. Where a real board puts a small part closer to its pins than that, the part is drawn
a fraction of a millimetre in, and the layout says which. A module drawn as the block
prints its names exactly as before. - A vertical terminal is drawn as KiCad's header with a screw plug standing in it. It was
one green block with the wire openings and the screw heads side by side on its top, which
is no part anybody sells. The header is now KiCad's own Phoenix MSTBVA model -- the open
pocket, the locking lip and its notches, the ends past the pins -- cut into ways like the
side-entry block, so a header of any length is drawn. The plug is drawn from Phoenix's MSTB
2,5/..-ST-5,08 sizes and stands IN it: its foot down in the pocket, its body resting on
the rim with no gap between. On a vertical header the plug stands on end, so the wires go
in from above -- each entry a real hole with a funnelled mouth and the clamp's metal cage
down in it, straight over its pin as the 2D view draws it -- and the screws are in its
side, slotted heads in round pockets, on the face a side-entry terminal's mouth would be
on. Each way is its own cap with a groove between, the back carries grip ribs, and the
edges are broken. The nylon and the metal are the side-entry block's, so the two kinds
look like one family. The plug is narrower than Phoenix's 15 mm so it stays inside the
footprint's 12 mm, which DRC and the placer measure; neither moved.
Windows - PerfboardStudio_..._Setup.exe. It is unsigned, so SmartScreen will
warn on first run: More info -> Run anyway. A signed build is planned.
Linux - perfboard-studio-...-x86_64.AppImage. chmod +x and run it; it needs
glibc 2.35 or newer. If it will not start, the machine has no FUSE 2: install
libfuse2, or run the file with --appimage-extract-and-run, which unpacks it
instead of mounting it.
macOS - perfboard-studio-...-arm64.dmg, Apple silicon. Drag it to Applications,
then run this before opening it:
xattr -dr com.apple.quarantine /Applications/PerfboardStudio.app
It is signed ad-hoc but not notarized, so Gatekeeper blocks the first launch.
On macOS 15 and later the dialog it raises defaults to Move to Trash, which
deletes the install. Clearing the flag first avoids the dialog entirely. On an
Intel Mac, install from source.
Anywhere with Python 3.12+: pip install -U perfboard-studio, then perfboard-studio.
Same release, no installer and no SmartScreen warning; Qt and VTK are most of a
400 MB download the first time.
Checking your download - SHA256SUMS carries the SHA-256 of all three
files. sha256sum -c SHA256SUMS on Linux, shasum -a 256 -c SHA256SUMS on
macOS, Get-FileHash <file> in PowerShell. Perfboard Studio does this for you when it
fetches an update itself: Help - Check for Updates. It proves the file
arrived intact, not who built it - that second claim ...
Perfboard Studio v0.16.0
Added
- The board leaves as a STEP model, to draw its enclosure round. File ▸ Export 3D
Model (STEP)… writes the finished board as solids a mechanical CAD program can measure
and cut a box round: the board at its real size with every drilled hole and mounting bore
through it, every part as the room it takes -- the very outline and height DRC's
overhang and too-tall rules measure, a cylinder for a can, a barrel lying along its leads
for a resistor -- every lead down through its hole and a millimetre past the solder
side, and every wire along its path on the face it is laid on, as thick as the gauge the
cut list prints plus its sleeve, lifted over whatever it crosses as the 3D view lifts it.
Each part is its own named component (Board, R1, U1..., and Wires) in the colour the 3D
view gives it. Written by the application itself (ISO 10303-21, AP214), so it needs no
graphics driver and no new dependency; checked against OpenCASCADE, the kernel FreeCAD
and KiCad read STEP with. A project save writes-board.stepbeside the other outputs,
and the headless run writesboard.step. Copper and solder traces are left out, as
KiCad's own STEP export leaves copper out by default: an enclosure needs the room, not
the solder. export_step, the MCP server's 54th tool, writes the same STEP model for an agent:
asked to design the case, or only whether the board fits the one it has, it can now hand
the board to the program the case is drawn in. It takes a file path and is refused
without one, like every tool that writes, and needs neither Qt nor GL.
Windows - PerfboardStudio_..._Setup.exe. It is unsigned, so SmartScreen will
warn on first run: More info -> Run anyway. A signed build is planned.
Linux - perfboard-studio-...-x86_64.AppImage. chmod +x and run it; it needs
glibc 2.35 or newer. If it will not start, the machine has no FUSE 2: install
libfuse2, or run the file with --appimage-extract-and-run, which unpacks it
instead of mounting it.
macOS - perfboard-studio-...-arm64.dmg, Apple silicon. Drag it to Applications,
then run this before opening it:
xattr -dr com.apple.quarantine /Applications/PerfboardStudio.app
It is signed ad-hoc but not notarized, so Gatekeeper blocks the first launch.
On macOS 15 and later the dialog it raises defaults to Move to Trash, which
deletes the install. Clearing the flag first avoids the dialog entirely. On an
Intel Mac, install from source.
Anywhere with Python 3.12+: pip install -U perfboard-studio, then perfboard-studio.
Same release, no installer and no SmartScreen warning; Qt and VTK are most of a
400 MB download the first time.
Checking your download - SHA256SUMS carries the SHA-256 of all three
files. sha256sum -c SHA256SUMS on Linux, shasum -a 256 -c SHA256SUMS on
macOS, Get-FileHash <file> in PowerShell. Perfboard Studio does this for you when it
fetches an update itself: Help - Check for Updates. It proves the file
arrived intact, not who built it - that second claim needs the code signing
this project has not bought yet.
This is pre-alpha software. Nobody has yet built a real board by following a
generated guide - check every measurement against the part in your hand.
Perfboard Studio v0.15.0
Added
- Every wire in the build guide is drawn at 1:1, to cut and bend it on. Each wire step
shows the wire twice: bent to its shape over the board's holes -- a solid ring where it
goes down through the board, a dashed one at each corner, the copper-coloured ends to
strip -- and straight, with every mark's distance from the left-hand tip. Both are drawn
as seen from the face the wire lies on, because a bent wire turned over is its own mirror
image, and each tip is named by the hole it goes into. The guide ends with all of them at
real size beside a 50 mm ruler: print at 100 %, check the ruler, cut, mark and bend. Tip
to tip each drawing is exactly the cut list's length, because both come from the same
numbers. A point a hand-laid wire runs straight through is not marked as a bend, and a
shape too wide for an A4 page is turned a quarter -- a turn, never a flip. The guide's
JSON gains each conductor step'sside.
Changed
-
The build guide's pictures come in close on what each step is about. They were framed
on the whole board, so on a 9 × 15 cm board a resistor was a few pixels of highlight and
the picture said nothing a builder could act on. Each step is now framed on its own part
or conductor with at least 30 mm round it to count holes from -- centred even at the
edge, where the edge itself is the landmark -- and seen from the same direction as every
other step on that face, so the pages still read as one board being built. A subject
that needs the whole board gets the whole board. -
The board question builds your circuit on the boards before it suggests one. It used to
lay the circuit out quickly on every stock size and suggest the first it filled to a third
or less -- a rule set by looking at where the examples sat on the boards that same rule
had picked for them, and a board and a half too big: the 24-partatmega328-relaywas sent
to 9 × 15 cm, most of it bare, and it builds just as cleanly on 7 × 9. Now the circuit is
placed, wired and checked on that roomy board and on each size below it, and the suggestion
is the smallest that builds as well -- every part placed, every connection made, no DRC
error, no placement warning the roomy board does not have, and at most 20% dearer to wire.
Each tried board in the dialog shows the placement it was judged by and what was found
("builds as well, at 104% of its wiring cost", "warns: Too close to a mounting hole"); the
progress dialog names each board as it is tried, and Cancel keeps the answer so far. It
takes a few seconds on a small circuit and about half a minute on a 24-part one -- once per
design, since the answer is remembered until the design changes. -
The examples moved onto the boards the application recommends for them, placed exactly
as the window places them. The sizes used to be written into the build script by hand,
and each placement was the cheapest of a sweep of seeds -- a little better than anybody
pressing the same buttons would get. Now the script asks the Board step's own question and
commits the placement that board was judged by, at seed 0:was now atmega328-relay 9 × 15 cm 7 × 9 cm nano-relay 9 × 15 cm 6 × 8 cm lpb1-booster 7 × 9 cm 5 × 7 cm lm317-supply 6 × 8 cm 4 × 6 cm arduino-io-shield 5 × 7 cm 2 × 8 cm ne555-astable 4 × 6 cm 2 × 8 cm ne555-blinker (suggested) 6 × 8 cm 2 × 8 cm The README's pictures of nano-relay and the assembly animation are redrawn from them, and
the animation lays a board taller than it is wide across its frame rather than upright. -
Place on the Board after choosing a board puts down exactly what the dialog showed,
at once. The board was tried with the circuit placed on it, so that placement is committed
rather than worked out a second time, which took as long again and could come out
different from the picture just approved. An edit in between makes it place afresh. -
Auto-place keeps parts out from under the corner screws, and keeps a regulator or a
relay away from the electrolytics as a matter of course. DRC has always warned about
both, and the placer only ever priced the second, by millimetres, behind the cost of
routing -- so on a small board it would hand back a capacitor under a screw head or
beside the regulator whenever that routed a little cheaper. Both are now counted by the
same predicate DRC uses and ranked ahead of the routing cost, the way a part hanging off
the edge or standing in a terminal's wire entry already was, and the summary says when a
part was moved out from under a screw head or a hot pair moved apart. It matters most on
the smaller stock boards, where the corners are where the room runs out first: measured
on the examples, it is what separated a board one size down from the one they had room
on. None of the fifteen golden fixtures has a mounting hole or a part that runs hot, and
all of their placements came out byte for byte the same.
Fixed
- The board's tools left the toolbar when the 3D view or the build guide was opened.
The window kept ONE note of which panel was in front, for two groups of tabs -- the board
with the schematic, the 3D view with the guide -- so pressing 3D, Build Guide or the step
bar's "Open the Build Guide" made the board count as behind while it was showing: Delete,
Rotate, Autoroute and the drawing tools disappeared until its tab was clicked, and a
schematic in front stopped redrawing after edits the same way. Each group now keeps its
own, and opening a panel in one says nothing about the other. - The schematic could hang the window by fitting itself for ever. The sheet is drawn
with a small margin round it, so fitting the sheet left that margin overflowing; a scroll
bar came in, which made the panel smaller, which fitted the sheet again, which took the
bar away -- over 8000 fits in 50 ms in a small panel, and no frame drawn. A paste that
widened the sheet was enough to start it. A fitted sheet now shows no scroll bars at all;
zooming or panning brings them back. A sheet that grows while it is fitted -- a paste, a
new part parked past the edge -- is fitted again, rather than left with the new part
off the edge of a view nothing can scroll. - The welcome window's "Draw a New Circuit" button lost its style. Its stylesheet ended
in one brace too many, Qt refused the whole of it, and every start printed "Could not
parse stylesheet" three times. A test now reads the source for that mistake's shape --
a plain string glued onto an f-string, still holding a doubled brace -- and this was the
only one. - Opening the 3D view hung the window on macOS with Qt 6.10 or newer. Every frame the
panel drew asked Qt for another, so the window stopped answering the moment the panel
opened. On that combination the panel no longer paints on screen itself; everywhere else
it is drawn exactly as before. - The build guide gave bare wire its net's insulation colour. A ground run of bare wire
was "Cut 30 mm of black AWG 24", the cut list's colour column had red and orange in it
for wires with no insulation at all, and the bench list asked for hookup wire in colours
only the bare runs had been given. Bare wire is now "tinned copper", and the bench list
asks for hookup wire and bare wire on two lines, each with its own gauges and length. - A wire given its own colour was cut from the net's. A document can colour a wire
itself, and the board view drew that colour while the build guide's cut list, step and
bench list named the net's -- the screen said blue and the bench was told black. The
guide now names the colour the wire was given: a stocked name as it is, a hex as the
stocked colour it would be called ("#ffa500" is orange), any other word as written. The
3D view read only "#rrggbb" and drew a wire called "red" in its net's colour; it now
reads the colour exactly as the board view does, and a colour neither can read falls
back to the net's in both instead of the board view painting it black. - Place on the Board left parts off a board that had room for them. It laid the design
out in lanes with a hole between parts and a row between lanes, and whatever the lanes had
no room for stayed in the design with a count in the status bar -- a 24-part circuit on a
7 × 9 cm board came back five parts short. Those parts are now put down where they overlap
least and the placer works them clear; only a board that really is too small leaves any
behind, and it says which. - The board-size question counted the finger strips as room. Each stock board was laid
out as a bare grid, so the two edge strips a board is sold with -- holes nothing can be
soldered into -- and the pads its corner screws take were counted as space for parts, and
a board could be called a fit that placing on it would then fall short on. They are
reserved now, as they are when the design is placed for real. - DRC measured a screw head from the wrong point when the hole was moved off its grid
position. It took the nearest point of a part towards the hole's grid address and then
measured from where the screw actually is, which agree only while the offset is zero: a
head standing over the middle of a part could be measured to its far corner and pass.
Every stock board's corner holes sit on the grid, so no board shipped with the tool was
affected. - The Turkish README said the whole project was Apache-2.0. Since 0.12.0 the 3D package
meshes borrowed from KiCad are CC-BY-SA 4.0, and the English README's licence section
says so and answers the two questions people have about it; the Turkish one was never
given that paragraph. It has it now...
Perfboard Studio v0.14.0
Added
-
A step bar: Circuit, Board, Placement, Wiring, Check, Build — and Next. The window
had every tool and no order: nothing said the circuit comes first, that a board is
chosen before the parts go on it, that routing joins them, that DRC says it can be built,
or that the guide and the 3D view are what it is built from. A row under the toolbar now
shows the six steps, where this board stands on each ("8 parts · 7 nets", "5 x 7 cm",
"8/8 on the board", "3 connections left", "0 errors · 1 warning"), and a Next button that
does whichever is next — a DRC error or an open net first. Pressing a step goes where it
is done. Choosing the board is a step of its own. -
Boards chosen by looking at them. The question of which stock board a circuit goes
on is now a grid of pictures, each board with the circuit actually arranged on it, the
suggested one starred and the too-small ones greyed; and New Board and Board Setup draw
the chosen product — finger strips, corner holes, printed legend — beside the questions,
redrawn as the size changes. The pictures are drawn by the board view itself. -
A welcome that has somewhere to begin. Draw a New Circuit, the shipped examples by
name with what each one is ("11 parts on a 6 x 8 cm board", the drawn-but-not-built one
first), Import a KiCad Netlist, and the six steps named once. -
A wire on the sheet can be dragged from pin to pin, as well as clicked at both ends.
A drag used to take the first pin and do nothing when let go -- the gesture everybody
tries first. The pin under the pointer is ringed before the click, and a pin is a target
of at least eight pixels at any zoom (it was 1.2 mm: five pixels on a fitted sheet). -
Ground and power from a pin's right-click. A pin on nothing offers the sheet's rails
by name, and GND and +5V when there are none — where it used to take the Label tool and
typing the name. -
part.addMany: several parts into the design as one undo step, all or nothing. -
The build guide in Turkish. The guide is what this application is for — the thing a
person follows with an iron in their hand — and it was English in a Turkish window: every
step, every check, the cut list, the parts list and the page they are printed from. It is
now written in the window's language: "D1 1N4007 — I32 → E32, 4 delik ara", "Katot —
bantlı uç — I32 deliğine", "K32 ↔ L32 açık devre olmalı", down to the column headings of
the CSVs and the progress counter on the page. The panel, the exported files and a saved
project's outputs agree,--headless --lang trwrites it too, and an agent asks for it
withlanguageongenerate_guide. English is unchanged, word for word. The
translation lives in the engine (phrasebook.py) because the sentences are built from
the board rather than looked up, and a test builds every board in the repository in both
languages and searches the Turkish for English left in it. Part names are said the same
way in the Parts panel and the guide, from one table. -
Route ▸ Crossings: routing with no wire at all. The engine could always be told what
a solder trace does where it meets something it may not cross, and nothing let anybody
tell it. A short jumper over the crossing stays the default; "one wire for the whole
connection" and "never use wire" are the other two. The last uses solder traces only,
whatever connection is preferred, and names each connection a trace cannot make rather
than laying wire nobody asked for. It is remembered with the other routing choices, and
an agent asks for the same thing withcrossingsonautorouteandreroute. -
File ▸ Open Example. The examples ship with the application and nothing in the
window pointed at them. They open as new, untitled boards — the file lives inside the
application, so saving over it would change the example for everybody — and the empty
board's hint mentions them when there are any. -
Wires laid along the grid. Every wire the router laid was one straight run between
its two ends, so a routed board was a cat's cradle of diagonals crossing each other, and
a straight bare wire could pass 0.2 mm from another net's pad. Route ▸ Lay Wires Along
the Grid (on by default) runs them square to the rows and columns of holes with as few
bends as the board allows, keeps a bare wire off every pin, joint and run of copper, and
keeps wires from lying on top of one another. On the shipped examples, stripped and
routed again: no diagonal wire, nothing unrouted, no short, no DRC error, for about a
fifth more wire. The engine's default stays straight, so every golden route is
unchanged;RouterOptions.wire_pathchooses. The MCP server'sautorouteandreroute
take the same choice aswiresand default to the grid too, so an agent's board comes
out like a person's. -
DRC:
wire-over-joint. A bare wire or a bent lead lying across a pin of another net,
or across the soldered end of an insulated wire, is a short at the bench that neither the
netlist nor LVS can see, because a wire is joined only at its two ends. The router already
refused to lay one; a wire drawn by hand, or a part moved under an existing one, now gets
an error. Six of the fifteen golden fixtures carry one, which the original engine never
checked, so the rule joinsPYTHON_ONLY_RULESand is pinned there.
Changed
-
A netlist is imported into the design, not onto whatever board is open. Its parts
used to land on the board after a question, beside what they connect to — skipping the
choice of board and leaving a first-pass placement nobody would keep. They now arrive in
the design with their values and pin names, the sheet comes forward, and Choose a Board
and Place on the Board take them from there. -
Add a Part offers the catalog first, from the Parts panel's own tree: typing 555
finds the NE555 and gives it its value, its pin names and U as its letter, where it used
to list sixty-one packages with a crystal first and Y1 in the reference box. A value that
names a catalog part in the chosen package brings its pinout too. -
The board's tools are on the toolbar only while the board is in front. With the
sheet in front the bar offered solder-trace and jumper tools, and a Rotate and a Mirror
that acted on the board's selection beside the sheet's own. -
A net is classed by its name until somebody picks a class: GND typed in New Net, a
pin labelled GND, or N3 renamed GND is a ground net and drawn as ground symbols, where it
used to be a signal wired to every part. -
References on the board sit over their part, on a plate, instead of at the corner of
the courtyard across the next row's pads -- and over every part, so a crowded board no
longer prints "X12" as "X1" with the next part across the rest. -
The schematic keeps its lanes out of the text. Every row has room above for its
references and below for its values, so no trunk runs through "NE555" or "10nF"; net
names are placed clear of each other and of the parts' text; every power glyph says which
supply it is; and the panel draws text at the size the layout left room for, re-fitting
the sheet as the panel finds its size until somebody zooms (it used to fit once, while
the panel was still small, and stay there). -
What just happened is said in the window's language. A command describes itself in
English -- that sentence is the undo label, the journal line and what an agent reads -- so
a Turkish window's status bar said "Add J1 PWR to the schematic" and its Edit menu "Geri
Al Place and arrange 8 part(s)". Every sentence the commands, the planners and the
window's own labels produce is now recognised where it is shown and said in Turkish
("Şemaya J1 PWR ekle", "7/7 nette 14 bağlantı yönlendirildi"), a conductor in the guide's
own words. A test runs a session of nearly every command and fails on any sentence that
comes back untranslated. -
Qt's own buttons speak the window's language: OK, Cancel, Close, Yes and No are
Tamam, İptal, Kapat, Evet and Hayır in Turkish. -
The example boards are routed the way Ctrl+R routes, and the README shows the
application as it is.tools/build_examples.pyrouted with the engine's default, which
lays each wire as one straight run because that is what every golden route records — so
the first boards a stranger opened from File ▸ Open Example were the cat's cradle of
diagonal wire the window itself stopped producing when wires were laid along the grid.
All six are rebuilt with the window's settings and the current router: no diagonal wire,
every net closed, no DRC error, LVS clean. Three carry one R5' warning each, a trace laid
beside another net's pin, which the guide turns into a check. The README's pictures and
the assembly animation are regenerated from them, so they show wires along the grid and
solder drawn as solder. -
The potentiometer and the relay look like the parts in the bag in 3D. Neither has a
borrowed model, and the generated bodies were a blue disc with a peg on it and a plain
box. The potentiometer now has its steel cover crimped over the housing, a threaded
bushing and a knurled shaft with a screwdriver slot; the relay a case with rounded
corners standing on its feet. Both carry their value in print, as the real ones do —
across the relay's top, stamped into the potentiometer's cover. -
Solder and wire look like solder and wire in 3D. A run of solder was a round grey
tube with bulges, a wire's end a ball on its pad, an insulated wire a coloured pipe with a
mitre at every corner, and the leads came out of bare rings as if nothing had been
soldered. A run is now a low bright ridge swelling into a dome on each p...
Perfboard Studio v0.13.0
Added
-
An example that is a real part list:
examples/nano-relay. An Arduino Nano switching
a 12 V relay through a BC547, fed by a 7805, with a push button and an LED -- imported
from its KiCad netlist exactly as File ▸ Import KiCad Netlist does, with no footprint
named for it: the catalog's parts by their values, the rest by their KiCad footprints, the
Nano's and the LED's pins renumbered by name. It is on the README's new Real parts
pictures, andtools/build_examples.pybuilds it through the importer, so it is the
end-to-end check of that path too. The other examples were rebuilt with this release's
placer;tools/screenshots.pynow takes its pictures with a settings store of its own
rather than the user's, which it used to rearrange. -
A T on the sheet. A drawn wire went from a pin to a pin and nowhere else, so a net
reaching four pins was three wires from pin to pin -- nothing like the one run with
branches off it that a person draws. The wire tool now takes a drawn wire as either click:
a pin and then a wire, or a wire and then a pin, and the pin branches off it in a T, with
a dot where it lands and its last run across the wire rather than along it. A T can land
on a T. The circuit is stilldoc.nets: a T joins its pin to the net of the wire it lands
on, exactly as a wire to one of that wire's pins would. It is stored as the wire it is
plus the far end of the wire it lands on (SheetWire.tap, written only when set, so
every sheet drawn before is byte for byte what it was), and where it lands is laid back
onto that wire each time the sheet is drawn -- a symbol drags only the end runs of its
wires, so a T in the middle of one stays where it was put. Rubbing a wire out takes the
T's off it with it, in one undo step; a T whose wire is no longer drawn (its net lost an
end) goes to its pin instead. -
A KiCad netlist's parts arrive as what they are. The importer kept the nets and threw
away everything else a netlist says about a component, and guessed each part from its
reference letter and pin count: a 7805 in a TO-220 ("U1", three pins) became a DIP-8,
every electrolytic a disc capacitor, and every part came with no value. Now
parsers.kicad_partsreads, in order: the value against the catalog ("BC547B", "L7805CV",
"NE555P" -- with the part's pin names and symbol, when the KiCad footprint is the same
package); the KiCad footprint name, which KiCad writes as the package's measurements
(resistors and diodes by span and body, electrolytics and discs by can and lead spacing,
DIPs, headers, IDC, Phoenix and bornier terminals, LEDs, TO-92, TO-220, HC-49); and only
then the old guess. A surface-mount footprint, a relay, a push button and a module are
not mapped and say why. The parts are placed beside what they connect to
(placer.arrange) rather than in a grid in reference order, with their values, as one
undo step. Over MCP,import_netlistreturnssuggested_partsandnotes, and
place_missing=trueplaces them. -
Which leg is which, from the schematic's pin names. A netlist's pin numbers are the
symbol's, and they are not always the part's: KiCad's LED is pin 1 = cathode where this
library's is pin 1 = anode, and a genericQ_NPN_EBCgiven "BC547" as its value is pin 1 =
emitter where a BC547's pin 1 is its collector. Taken at its numbers, either netlist wires
a board for a part that does not exist, and no check can see it, because the netlist is
what every check is measured against. The netlist now carries each pin's name
(pinfunction, which the parser used to drop); where every named pin matches exactly one
pin of the real part, the import renumbers it to the part's own numbers and says so, and
a transistor or diode whose names cannot be settled is reported instead. An LED with no
names in the netlist is read by KiCad's own numbering of its footprint. A module comes
through the same way: KiCad numbers an Arduino Nano, a Pico or a D1 mini down one side
and up the other, the catalog's are numbered across their rows, and every pin is found by
its name -- KiCad's overbar markup off (~{RESET}is RESET), a Pico'sGPIO26_ADC0read
as its silkscreen's GP26, and a name on several pins (two GNDs) matched in order when
every one of them is in the same net, and never otherwise. A part that has no
catalog entry but three or more named pins -- a 2N5088, an IC -- gets the schematic's
names printed beside its pins. -
Labels written on the board. A perfboard has no silkscreen; what a builder writes on
one with a marker -- "MOTOR 24V" beside the terminal that takes it, "CAN ->" where the
chain goes -- is now part of the board: Edit ▸ Add Label…, or Add Label Here… on a
right-click, dragged into place, double-clicked to change, Delete to remove. A label is
on the top or the solder side, turns by 90°, is printed on the 1:1 sheet and in the
guide's pictures, and in 3D is drawn by Qt so a Turkish "ALT YÜZ" or an arrow comes out
whole (VTK's vector text knows ASCII only). Stored asboardNotes, addressed like a
mounting hole -- a hole and millimetres off it -- and omitted when there are none, so no
existing board changes by a byte. Nothing derives anything from a label: DRC, LVS and
the router ignore it. View ▸ Show Labels on the Board hides them; over MCP,
add_board_label(the 53rd tool) andremove_board_feature. A label stays on the board:
a drag stops at its edge, and one whose offset would carry it past the substrate -- on
adding, editing, or narrowing the border under it -- is refused (off-board,
would-strand-label). -
A push button as made:
sw-tactile-6x6andsw-tactile-12x12. A 6 x 6 mm button's
legs stand 6.5 x 4.5 mm apart -- three holes by two on a perfboard, across the gutter on a
breadboard -- and the registry'ssw-tactileis two holes by one, which no such button
is. Which legs are which matters more: the pair straight across is one strip of metal,
the pair down one side is what pressing it joins, andsw-tactilecalls the pair its
pins 1 and 2 stand on one node -- so a KiCad switch guessed onto it was a button pressed
for ever. The new ones are numbered as KiCad'sSW_PUSHfootprints are (pins 1 and 2 the
switched pair, 3 and 4 joined inside to them, named A B A B), have KiCad's models on
their holes, are in the catalog under Switches, and are what a netlist'sSW_PUSH_6mm
andSW_PUSH-12mm, and a guessedSW, now become.sw-tactileis unchanged, for the
boards that have it. -
Real parts by name: the catalog. The library is sixty-one packages, and a package is
not a part -- a TO-92 is a BC547 or a 2N7000 or a 78L05, and which leg is the base is the
fact the board depends on.perfboard_studio.cataloglists 60 parts a perfboard is
actually built from -- bipolar and MOSFET transistors, regulators and references, diodes
and zeners, a PTC, sensors, DIP ICs, push buttons and five dev boards -- each with its
package, what its datasheet calls every lead, its symbol, its value and its reference
letter, and
sourcesaying whose datasheet that is. They head the Parts panel, grouped by kind and
found by what they are ("p-channel", "esp32"); picking one places it with all of that
filled in, dragged or clicked, on the board or the sheet. Nothing in the document refers
back: what lands is an ordinary part, so a board opens the same anywhere. Over MCP,
list_cataloglists them andplace_component/add_parttakepart="bc547". In
Turkish, what each part is and what to check on it are in Turkish, and the filter finds
a part in either language ("regülatör" or "regulator"); the datasheet citations stay as
written. -
A module on header pins:
mod-<cols>x<rows>-p<c>-r<r>-<W>x<D>x<T>-s<seat>[-o<X>x<Y>].
A devkit or a breakout is a board of its own standing on its pins, andbox-described
it as a solid block from the board up. The newmodule-boardarchetype knows its pins,
its own board, how high that sits -- 8.5 mm in a female header, 2.5 mm soldered on its
own pins -- and the height of what is on it. 3D draws the board on its header strips
with its pins through it; the guide says to solder the headers and plug the module in
last. The catalog's ESP32-DevKitC V4 is measured from Espressif's own DXF; the
ESP32-C3-DevKitM-1, Arduino Nano, Raspberry Pi Pico and WEMOS D1 mini from KiCad's
footprints. Custom Part… has a module family that takes the pin names too. -
Pin names on the board, and a switch for them. A part's pin names are printed beside
its pins in 2D and 3D: on a module's own board as its silkscreen is, and on this board
just outside anything else -- a DIP's down either side, a terminal's behind it rather
than across its mouth -- on a dark tag, since white ink across white pad rings cannot
be read. Only names a part declares are printed off a module. View ▸ Show Pin Names
turns them off for a crowded board being placed or routed.
The names are laid out for the whole board, not part by part: on the first real board two
terminals stood side by side and each one's names ran under the other's body. A name now
stops at whatever is in its way -- a body, a terminal's mouth, a screw head, a label, the
board's edge, another name. A part with its pins in one row prints them on its other side
when that is clear; a name short of room is narrowed, down to 60 % of its width; one with
none is left off, and the part's tooltip lists it. Both views ask
bodies.lay_out_pin_names, so they print and leave off the same names, and 3D draws each
one with Qt, as it does a board label, so "GİRİŞ" keeps its dots. -
A screw terminal whose wires go in from above:
screw-terminal-<n>-v. The pluggable
bloc...
Perfboard Studio v0.12.0
Added
-
The schematic is an editor now. Symbols are dragged where you want them, turned a
quarter at a time and flipped; wires are drawn from pin to pin by hand; a pin can be
joined to a net by NAME instead, with a label at the pin, which is how every schematic
ever drawn handles a reset line reaching six parts; and text, boxes, lines and circles can
be put on the drawing. Parts are dragged onto the sheet out of the Parts panel, and
Deletetakes what is selected out of the design. The sheet's own toolbar carries the
tools;Escapealways comes back to the pointer; the middle button pans in every tool.PLAN.md D3 is reversed on purpose, and only half of it. D3 declined to write a
geometric schematic editor because that is a year of work whose output this tool already
accepts from KiCad. What it did not weigh is that a tool which can capture a circuit and
cannot DRAW one sends people back to KiCad anyway. So there are two kinds of sheet and the
document decides which: withdoc.sheetempty the whole drawing is derived exactly as
before — symbols in cells, wires in the channels between them, no wire able to cross a
symbol — and that is still what every imported netlist, every fresh document and every
press ofArrangeproduces. The moment anything is moved, turned or wired, every symbol
gets a position of its own in one command, and from then on the sheet is the drawing
somebody made.The netlist is still the one answer to what is connected. A drawn wire carries no net
id: which net it belongs to is whichever net holds both of its ends, looked up every time.
That is what makes a wire left over from a connection somebody has since removed stop
being drawn instead of quietly asserting a join that no longer exists — and it is what
keeps LVS, the router, the placer, the build guide and the board from having to learn
anything about geometry.Arrow keys nudge the selection one grid square, through the same command a drag ends in,
so it undoes as one thing. A selection survives the redraw every command causes — turning
a symbol twice takes one click on it.Right-clicking a wire offers Rub Out This Wire, which takes the line off the sheet
and leaves the pins connected — the net says so by name instead. It is a separate entry
from Delete Net on purpose: deleting the picture of a join is not the same decision as
taking a pin off one..perffiles are untouched by all of this unless somebody draws on the sheet:sheet,
sheetWiresandsheetNotesare omitted when empty, so all fifteen golden fixtures
serialize to exactly the bytes they did before andDOCUMENT_FORMAT_VERSIONhas still
never moved. -
Parts are dragged out of the Parts panel onto the board. Picking a part and then
clicking where it goes is still the way to put down a run of them — five 10k resistors,
one after another, without going back to the list — and it was the ONLY way, which is the
problem: dragging is what everybody tries first, and there was nothing to discover that it
was not supported. The row simply did nothing. The drop goes through the same arming the
list has always used, so the next free reference, the typed value and the "this hole is
already taken" warning are all the ones that were already there, and the part stays armed
afterwards. -
The board is a panel now, and so is the schematic. Everything else in the window
could be moved, floated, stacked or closed; the two views this application exists for
were the one pair nailed down — a central widget with a tab beside it — so "board on the
left, sheet on the right" was not something anybody could ask for, and wanting both at
once meant a second window built by a button. They are dock widgets like the 3D view and
the build guide: side by side is a drag, a window of its own is the title bar's float
button, stacked is a drag onto the other one, and Qt remembers whatever it was left as.
The toolbar carries a button per panel beside 3D, andView ▸ Reset the Panel Layout
puts everything back where a new installation opens it.The panel shortcuts are renumbered around the four views of the design:
Ctrl+1Board,
Ctrl+2Schematic,Ctrl+33D,Ctrl+4Build Guide, thenCtrl+5Parts,Ctrl+6Nets
andCtrl+7DRC / LVS. Saved window layouts from before this are ignored once, on
purpose:restoreStateputs back the docks it knows and leaves the ones it has never
heard of wherever the constructor put them, which landed the two new panels off the side
of the window. -
A tactile switch is drawn as a pushbutton and a relay as a coil and a contact block,
instead of both being boxes with numbered pins. Both shapes come from what the PACKAGE
says about itself — a 6 mm switch's four legs are two bonded pairs, and a relay's winding
is the pair of pins along one side — read off the footprint's own geometry rather than
assumed. The relay deliberately stops there: which contact is common is a fact about the
part and not the package, so the contacts stay numbered leads out of a block. A TO-92 and
a TO-220 stay boxes for the same reason, and there is now a test saying so for each. -
Right-click the sheet to edit the circuit. A symbol offers its properties, a rename
that carries every net it is wired into, a duplicate, and eitherRemove from the Design
orTake off the Boarddepending on which of the two it is in; a pin under the pointer
offers to come off its net, named in full —Disconnect U1.3 from VOUT; a wire offers its
net's name, its class, the full net dialog and delete. All of it existed already, in a
tree of twenty net names or behind a dialog with three fields — except taking one pin off
a net, which had no door on the sheet at all. A symbol somebody has dragged also offers to
go back to the layout on its own, without undoing an afternoon of other drags. -
The Part Properties dialog no longer apologises for a bug that was fixed. It said
renaming a part takes it off the nets that name it and to rename before importing a
netlist rather than after;rename_in_netshas carried the wiring for some time. -
A symbol can be dragged to another cell on the sheet, and it stays there. The
drawing is still derived: what the document carries is a CELL per part, not millimetres,
so the layout keeps every position and keeps its guarantee that no wire crosses a symbol —
which is what lets this exist without reversing PLAN.md D3. Positioning one symbol leaves
every other one exactly where it was,Arrange the Sheethands the whole lot back to the
layout in one step, and a document nobody has rearranged serializes to exactly the bytes
it did before, so all fifteen golden fixtures are untouched and the format version has not
moved. -
Drag a symbol onto the board to place that one part. "Place on the Board" moves the
whole design; until now there was no way to put down a single part from the sheet at all
— double-clicking a symbol opened its properties. Dropping one whose part is already on
the board moves it instead, because that is the only thing the gesture can mean. Dragging
onto the Board tab switches to it and lets the drag carry on, so it works whether the
sheet is a tab or a window of its own. -
A fifth, much larger example:
atmega328-relay. An ATmega328 with a 7805, a
crystal, a relay and four connectors — 24 parts, 24 nets, 52 conductors and a 76-step
guide with 104 checkpoints, on a 9 × 15 cm board. The four existing examples are between
8 and 12 parts, which is the size at which everything works; this is the size at which
the interesting rules all fire at once, and it is what the placer, the router and the
build guide now have to keep working against. -
geometry.unusable_holes— every hole nothing can be soldered into, which is a
mounting bore that has taken the pad or an edge-connector finger that is solid copper
with no bore. One fact with three consumers that were not allowed to disagree and did:
DRC reported a pin or a run on one as an error while the placer and the router happily
produced them. -
A project is a folder, and the board inside it is still an ordinary
.perf.
File ▸ New Project…names the board, asks what it is built on, makes the directory and
saves it immediately — so it has somewhere to autosave to, somewhere to put a build guide
and something to put in the recent list from the first minute rather than from whenever
it was first saved.File ▸ Save Pro&ject(Ctrl+Alt+S) writes the board and rewrites
everything generated from it — the 1:1 sheets, the schematic as SVG/PDF/PNG, the build
guide, the bill of materials, the cut list — intooutputs/beside it.File ▸ Open Project…opens the board inside one, and refuses a folder with two boards in it rather
than guessing which was meant.Not an archive, not a manifest, not a new format. The byte-for-byte
.perfdoes not
move, the document still opens in a text editor and an agent's file tools, and throwing
the folder away loses nothing but files the tool can write again.project.pydecides
what a project contains without a filesystem andui/project.pyhas the disk, the same
split asrecovery.py/ui/autosave.py. -
A welcome dialog offers the way back to last night's board, over a window that is
already up rather than instead of one. Everything on it was already reachable; none of
it was reachable in the two seconds after a launch, which is when it is wanted. Skipped
entirely on a document that has anything in it — one opened from the command line, or
handed back by crash recovery — and it has a checkbox that means what it says. -
**The schematic panel opens by itself on a doc...
Perfboard Studio v0.11.0
Changed
-
The application is called Perfboard Studio; its package and command are
perfboard-studioand its Python module isperfboard_studio. It was PerfStudio
through ten releases. Two things were wrong with that name, and the second is the one
that decided it. The first is how it reads: one letter separates "perf" from "perv",
and not every eye lands on the right one. The second is what the abbreviation already
means in software — "perf" is performance. It is the name of Linux's profiler, and
AMD shipped a graphics profiler called GPU PerfStudio for years. PLAN.md §12 picked
"perf" for open-source discoverability, and discoverability is exactly what it cost:
a search for "perfstudio" lands among profiling tools, not among perfboard.The identifier is
perfboard-studiorather than the bareperfboard, and that is
a second decision rather than a longer spelling of the first.perfboardis the name
of the board, not of this program; taking the generic word as a package name confuses
the product with the material it works on. It is also a word this repository writes in
prose on nearly every page, which would have made the name impossible to change again
without reading every line by hand.What it means for an existing install:
uv tool install perfboard-studio, orpipx install perfboard-studio. The old
perfstudiodistribution is not renamed in place — PyPI has no such operation — so
it stays where it is and stops at v0.10.0.- The command is
perfboard-studioand the MCP server isperfboard-studio-mcp. A
client configured withpython -m perfstudio.mcphas to be pointed at
python -m perfboard_studio.mcp. - The environment variables are
PERFBOARD_STUDIO_LANG,PERFBOARD_STUDIO_DOCUMENT,
PERFBOARD_STUDIO_LOGand the threePERFBOARD_STUDIO_BLESS_*. - The window's stored settings are keyed on the application name, so the first start
after upgrading comes up with the default layout, the default board colour and an
empty recent-files list. Nothing on disk is lost; the old keys are simply not read.
The document format did not move.
DOCUMENT_FORMAT_VERSIONis still 1, the
extension is still.perf— it was always short for perfboard, never for the
application — and every golden fixture intools/diffcheck/golden/is byte-identical
across the rename. A board written by v0.10.0 opens in this build unchanged, and the
differential proof against the TypeScript engine is untouched. -
The board is the board people actually buy, checked against a photograph of one.
Somebody held a 5 × 7 cm double-sided board up against the screen and asked whether it
matched. The geometry did — 70 × 50 mm, the grid, the four corner screw holes in the
border, the finger strips down both short edges — and two things did not:- The pads are TINNED, not gold.
_GOLDclaimed to be "what the green, blue and
black prototyping boards are finished with", and on the photograph they are plainly
not: the pads there are neutral and light, at a blue/red ratio of about 1.0 where
gold's is 0.4. It is HASL — hot-air levelled solder over copper._TINNEDreplaces it
for every masked scheme, kept warmer and lighter than the grey a solder run is drawn
in, because those two are nearly the same metal on a real board and telling them apart
is what this application is for. The 2D render signatures were re-blessed after
reading the diff: ink coverage identical to three decimals, every cell moved about ten
levels — a colour change and not a geometry one, which is exactly what it should be. - The legend was taken off the green board in the same pass and put straight back:
the 400-pixel product shot resolved no printing, a photograph of the same product at a
usable size shows the column letters plainly along the border under the bottom row,
beside the part number and "5X7CM". A thumbnail too small to resolve a print is
evidence that the print cannot be SEEN, not that the board has none — which is now
written atboard_from_presetand in the test, because the same mistake is available
to anyone reading the same photograph.
- The pads are TINNED, not gold.
-
The corner screw hole is AT the corner position, centred, and the pad it replaces is
gone. It used to be pushed as far diagonally into the printed border as it would fit,
to spare that pad — and one offset was used for both axes, so on a 5 x 7, whose two
borders are 3.41 and 5.79 mm, the bore ended up 0.20 mm from one edge of the board and
floating 2.6 mm from the other, with the corner pad jammed against it. Nothing was
centred in anything, and it is the corner somebody looking at the board looks at first.- The board is manufactured with a screw hole at that position INSTEAD of a pad, so the
pad is consumed — throughconsumed_holes, which DRC, both renderers and the guide
already read, with no special case for corners. - A board whose border is too thin to hold a screw steps the hole one position inward
rather than drilling a notch out of its own edge:CORNER_HOLE_WEB_MMis 1 mm of board
and the corner position leaves 0.31 mm of it on the 20 x 30 and 0.88 mm on the 7 x 9.
Both are now measured by a test rather than left to the eye. It also means every
double-sided preset gets its four screw holes; the six largest boards used to get none
at all, because the old arithmetic gave up and returned zero. - The finger strip already trimmed itself clear of the screws, but by taking the run
from the first clear finger to the last — which assumed the obstruction is always at an
END. An inset screw obstructs one in the middle, and first-to-last quietly put it back;
it takes the longest contiguous clear run now.
- The board is manufactured with a screw hole at that position INSTEAD of a pad, so the
-
A mounting bore takes the copper it is drilled through, including a finger. A hole
put one position in from the corner — which is what Board Features offers by default —
reaches the edge-connector strip, and the finger it went through was still drawn intact
in both views: a 3.2 mm hole sitting on top of a contact the board no longer has. A bore
does not distinguish between the two shapes of copper it destroys, so
geometry.surviving_finger_holesanswers that for both renderers the way
consumed_holesalready answered it for a round pad. -
The printed legend is not printed into a hole. Ink goes ON the substrate and a bore
takes the substrate away, so a letter under one is not faint, it is absent — which is
what a real board shows.geometry.printed_label_is_clearis asked by the 2D legend and
the 3D one, in the same units, because a legend that agreed with the board in one view
and not the other is the disagreement this pair keeps producing. -
The board in 3D has holes in it. Which sounds like a statement of the obvious and
was not true: the substrate was one solid cube and every hole on it was faked by laying
a dark cylinder over the top. A dark disc on green reads as a mark printed on the board,
not as something you can push a lead through — and it was worst on a mounting bore,
which has no copper ring around it to explain the darkness, so a screw hole read as a
sticker. The plate is punched now: both faces, every hole, and the wall of the bore
visible inside it.- Affordably, which is why the fake existed. A boolean subtraction per hole is
nearly two thousand of them on a 945-hole board, and that argument still holds. This
is neither: a face is ONE TILE — a pitch square with its hole taken out — glyphed at
every hole, so both faces of any board cost one source and two actors. The tiles are
exactly a pitch across, so neighbours share an edge and the surface is watertight;
what is left between the outermost tiles and the board's edge is the printed border,
drawn as rectangles, and a bore in it is cut out of them. - A mounting bore takes whole tiles and lays one patch over them, and the tiles it
takes are exactly the holesgeometry.consumed_holesreports the copper gone from —
one bore, one answer, in the renderer and in DRC. It was also being drawn at its hole
ADDRESS rather than atmounting_hole_centre_mm, so every corner hole appeared back
on the grid in the middle of four pads that are perfectly intact. CLAUDE.md has said
not to do that since the feature was written. - An edge-connector finger keeps a solid tile: it is a contact with no bore, and
drilling through it is what made a finger look like a long pad.
- Affordably, which is why the fake existed. A boolean subtraction per hole is
-
The parts look like the parts. Every model was looked at on its own, close up, which
is how most of these were found: at the whole-board zoom a wrong shape reads as a small
coloured blob and nothing more.- A resistor was a flat-ended tube; it has the moulded shoulder at each end now,
with the barrel shortened by what the domes add back so the part still measures the
length its footprint says it does. - A film capacitor was a plain orange brick. The dipped case is a slab with
half-round ends, so it is one now. - A disc ceramic was a coin with a sharp rim; the dipped case is a lens, thicker in
the middle, and that is the same one solid. - A TO-92 was a squashed cylinder — which is an ellipse, so it has two flats and
marks nothing. It is the D-shaped case now, with the flat facing its row of pins, and
getting a transistor round the wrong way is the classic way to spend an evening. - A TO-220's tab was a slab the width of the package sitting on top of it. The tab
is sheet metal the plastic is moulded around: thin, flush with the back face, and with
the bolt hole through it — the side a heatsink goes on. - An HC-49 crystal was a flat-topped tube. It ...
- A resistor was a flat-ended tube; it has the moulded shoulder at each end now,
PerfStudio v0.10.0
A polish release: nothing new to learn, and a long list of things that were almost right.
Four audits — the window, the three views, the engine's own prose and the loader's
tolerance for bad files — each came back with what they found, and what follows is what
survived being checked.
Added
- The three panels that had no way back. Parts, Nets and DRC / LVS can be closed from
their title bars, and the only route back was a right-click on the menu bar, which nobody
finds. View ▸ Show Parts (Ctrl+1), Show Nets (Ctrl+2) and Show DRC / LVS (Ctrl+6)
join the three toggles the other panels already had. - Right-clicking a conductor. The menu over a trace was the bare-board menu — Paste,
Connect, Fit — with no way to delete the trace under the pointer. It now selects the
conductor and offers Copy, Duplicate and Delete, the way a right-click on a part already
selects the part. Ctrl+0,Ctrl++andCtrl+-follow the keyboard focus. Over the schematic panel
they fit and zoom the sheet, which had a Fit button and no key; over the board they do
what they always did. The sheet's zoom also gained the board's limits — a few trackpad
flicks used to leave a blank field or one pin, with nothing but the button to recover.- Undo and Redo say what they will do. The Edit menu read "Undo" and "Redo" and the
answer was one hover away; it now reads "Undo Move R4 to C7", and Redo — which never
named anything, because the bus had no way to ask — reads the same.CommandBusgained
redo_history()for it. - Load warnings are shown, not counted. Opening a file that was read differently from
how it was written — a diagonal solder-trace step, an unknown property, a duplicated
reference — put "(2 warning(s))" in the status bar and nothing else anywhere. They are
listed in a dialog now. And a duplicated reference IS a warning now: every net node
names a part by reference, so two R1s is a netlist that cannot say which one it wired. - Two DRC rules for the file only a hand can write.
conductor-off-board(a conductor
with a hole outside the grid, the twin ofcomponent-off-board) andunknown-footprint
(a part whose footprint neither the library nor the id grammar can produce). No command
can create either state, so both only ever fire on a document edited by hand or written
by another tool — which is exactly the file that must still open and be told what is
wrong with it. Both are Python-only (PYTHON_ONLY_RULES); the second turned out to fire
on eight of the fifteen golden fixtures, all on one id,c-disc-1, which exists in
neither engine. The fixtures are dumps of the original and are left as they are; the
rule is excluded from the differential comparison and pinned by its own test. - The interface is Turkish where it was still English. The status bar's DRC, LVS and
ratsnest fields, the Nets panel's "done", the DRC panel's summaries, the schematic
panel's summary, the three file dialogs, the progress dialog's five labels, the
confirmations' bodies, the "select a part first" line and its five verbs, the Board Setup
material list, and the board's own prose while measuring, picking pins and joining them.
tests/test_i18n.pynow also checks the View menu for accelerator clashes, which found
one in each language.
Changed
- A group moved together is one undo step. Five parts dragged or nudged at once used
to be fivecomponent.movecommands, so fiveCtrl+Zpresses to put them back — and a
refusal part-way through left the group torn apart. The scene dispatches one
component.moveMany, which is all-or-nothing: a locked part in the selection refuses the
whole move and says which part. - Every destructive question puts Cancel under Enter. Delete parts, delete conductors,
delete a net and reload from disk all usedQMessageBox.questionwith no buttons named,
which puts Yes under Enter — so a confirmation answered by reflex was a deletion. They
share one box now,MainWindow._confirm, whose button carries the verb ("Delete") and
whose default is Cancel. Tests that used to stubQMessageBox.questionstub_confirm. - Deleting a mixed selection deletes all of it. A rubber band that took a part and the
two traces on it used to delete the part and keep the traces without a word; the
question now says the conductors go too, and they do. - The loader refuses what
board.setrefuses. A.perfwith"pitch": 0, no columns,
or a drill wider than its pad used to load without a word, and then every repaint divided
by it — in the pure render path, a segfault. Two components sharing an id (a part no edit
could ever reach, and one the placer refused the whole board over) and aformatVersion
below 1 (no such version has existed) are refused too, with the same codes and paths the
other load errors carry.Missing required field "cols"now says"board.cols", the
path every neighbouring message already quoted. CommandBus.dispatchnever raises, as its contract says. A payload of the wrong
shape — a dict where a dataclass was expected, a tuple where aHoleCoordwas — used to
surface as anAttributeErrorout of the command; it is{ok: false, code: "invalid-payload"}now, which is what an agent on the far side of the MCP server needs.
The undo stack is bounded at a thousand entries, where it had no bound at all.- Save is enabled only when there is something to save.
Ctrl+Son an unmodified
board rewrote the file — a new modified stamp, a new mtime — for nothing. An untitled
board can always be saved somewhere, so Save stays live there. - The Downloaded strip has a Close button. Its only button was Hide, which skips that
version for good — so closing the strip after a successful download told the application
never to mention the release the user had just fetched. Hide stays on the announcement,
where it means what it says. board.setnames every stranded part, and refuses to strand a track cut. Shrinking a
board with three parts outside the new size used to name the first, so the refusal
repeated once per part, each discovered only after the previous one had been moved. A
cut off the board was kept silently — and if the board was grown again, it broke a strip
nobody remembered cutting. A component also needs a non-empty reference now; the wiring
cannot name one without.- MCP results mean one thing by
ok.run_lvsreturnedok: falsewhen the board
did not match the schematic, while everywhere elseok: falsemeans the call was
refused; it returnsok: truewithmatches_schematicnow, andrun_drccarries
ok: truetoo.autorouteon stripboard returnedcuts: 0for an empty plan and
cuts: [...]for a committed one; it is always a list, withcut_countand
link_countbeside it.export_pdfwith nodirectorywrote two PDFs into the
server's working directory, against the instructions' own promise that nothing writes to
disk unless a path is named — it refuses withno-directory. Paths accept~, and an
empty path is refused rather than blamed on permissions. The server's instructions now
list all six connection kinds (not four), give the design-first working order
docs/MCP.mdgives, say whatplainis as a board type, and send an agent toreroute
afteroptimize_placementrather than toautoroute, which only adds. - The KiCad importer says what it was handed. A
.kicad_schgiven to it — the
schematic instead of the netlist exported from it, which is the first-time mistake — got
"no export form found"; it now says it is a schematic and where the netlist comes from.
Two nets sharing acode(a broken export, but the user's export) used to refuse the
whole import as a duplicate id; they are imported with a suffix and a warning. A
component with no footprint is warned about, since it is what forces the host to guess
one.
Fixed
- Measuring, then flipping the board, broke every tool. The measurement marker is an
item like the others and a rebuild destroys it, but the mode kept a wrapper for it — so
the next click, or the next arming of any tool (all of which disarm measuring first),
raised fromremoveItemand left every board mode unreachable until the window was
reopened.test_measuring_survives_a_flipis the reproduction. - Three more things a rebuild lost. A half-drawn trace kept its path and lost its
picture, so after a flip or an undo the tool was armed with nothing on the board and
Enter committed an invisible chain. The placement ghost came back at A1 after every
placement — at exactly the moment the next part was being lined up. And a conductor
selected to be deleted was deselected by whatever unrelated command came first, while
parts had always been re-selected by id. - Picking a part mid-trace armed both. Clicking the Parts panel while a trace was being
drawn left the ghost following the pointer while every click still extended the trace,
so the part could never be placed. The board modes are mutually exclusive and placement
now disarms drawing, as drawing already disarmed placement. - The solder side and the keyboard disagreed. The arrow keys moved a part one column in
the document, which on the mirrored solder side is a step to the LEFT underRight;
Rotate Clockwise turned the other way there for the same reason; and the placement ghost
painted a DIP's pins running one way while the placement put them the other. A drag was
right all along, because it round-trips throughscreen_to_hole. All three now correct
for the side, andtest_arrow_keys_follow_the_screen_on_the_solder_sideholds it. - Flipping the board flips the view and the camera with it. The scene is ...