Codync
A free, open-source 1:1 alternative to Grok Bot and Muse. Message Claude Code, Codex, Cursor and 40+ coding agents as persistent bots from your iPhone, Mac or Linux desktop. Group chats, threads, appr
Correct-by-construction passive two-terminal chip library generator. A JD Brinton Consulting, Inc. tool.
Translation pending
Developer toolsGitHub @jdbconsultingOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
Correct-by-construction passive two-terminal chip library
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Are you tired of messing around with passive footprints? Want to get an Altium footprint library that does it correctly? This library is for you!
This repo is two things in one:
house.PcbLib), schematic library (house.SchLib), and per-vendor .xls / .DbLib databases for chip resistors and chip capacitors between 01005 (metric 0402) and 1210 (metric 3225). Source lives under hlcl/.The SvelteKit project lives at the repo root (so src/, svelte.config.js, package.json, etc. are all top-level), and the Python build sits next to it under hlcl/ so a host-side user can cd to the project root and run python hlcl/build.py directly. For the in-browser path, a Vite plugin (see Web configurator below) packs the build-relevant subset of hlcl/ into a single gzipped tarball under static/hlcl-build-inputs.tgz, which adapter-static copies verbatim into the deployed site; Pyodide fetches that one file, unpacks it into MEMFS at /hlcl/, and runs the same build.py unchanged.
.
├── src/ SvelteKit app (Configurator UI)
│ ├── app.html HTML shell + Inter / JetBrains Mono fonts
│ ├── lib/
│ │ ├── components/ Header, Console, Toggle, NumberField, …
│ │ │ └── tabs/ One component per workflow tab
│ │ ├── console.svelte.ts Reactive log buffer (Pyodide stdout/stderr sink)
│ │ ├── hlcl-runner.ts Pyodide driver (mounts hlcl/ tarball, runs build)
│ │ ├── libraries.ts Vendor family catalog (drives the Libraries tab)
│ │ ├── pyodide.ts Lazy CDN loader for Pyodide
│ │ ├── schema.ts Zod definition of `BuildConfig` (canonical JSON)
│ │ └── state.svelte.ts Persisted app state (localStorage)
│ └── routes/ +layout.svelte, +page.svelte, configure/
├── hlcl/ Python build pipeline (CLI + Pyodide source)
│ ├── build.py Top-level Python build orchestrator (no `make`,
│ │ no `subprocess`; runs in-process so Pyodide
│ │ drives the same code unchanged)
│ ├── coupon/ Reference Altium project (test board)
│ ├── house/ Merger, STEP gen, .PcbLib writer
│ ├── references/ Standards documents + vendor reference PDFs
│ └── vendors/ Per-vendor / per-family generators
├── docs/ Project-root documentation assets
│ ├── coupon-board-screenshot.png README image
│ ├── database-example.png README image
│ └── standards/ HLCL-001 LaTeX + Markdown source. The Vite
│ plugin packs both into the build-inputs
│ tarball so the in-process `standards`
│ target can render them with the active
│ BuildConfig values substituted in.
├── static/
│ ├── hlcl-build-inputs.tgz Auto-generated by the Vite plugin (gzipped tar
│ │ of the build-relevant subset of `hlcl/`).
│ │ SvelteKit's adapter-static copies this verbatim
│ │ into the deployed site; Pyodide fetches it on
│ │ the first "Run build" click and unpacks it into
│ │ MEMFS at /hlcl/.
│ └── scripts/ Altium DelphiScript helpers (e.g. relink_*.pas)
├── vite/plugins/ Custom Vite plugins (hlcl-build-assets.ts)
├── package.json / svelte.config.js / vite.config.ts / tsconfig.json
├── eslint.config.js / .prettierrc / .prettierignore / .npmrc
├── requirements.txt Python deps (xlwt only)
└── .gitignore Combined node + Python + Altium + LaTeX rules
After running the build, two build/ directories appear (both gitignored):
build/ at the repo root — SvelteKit's static-adapter output (the deployable site bundle).hlcl/build/ — the Python pipeline's output (vendor .xlss, house.PcbLib, etc).These never collide because each lives next to the tool that generated it.
This library is limited to two-terminal passive chip components between 01005 (metric 0402) and 1210 (metric 3225). To calculate 3D model and landing pad dimensions, certain dimensions are needed from the chip, such as terminal width, height, and tolerances. So it isn't possible to make a truly generic footprint library. No highly optimized footprint library can really be generic. So, we prioritize certain brands over others for creating a generic footprint library that is as reliable as possible for the largest number of brands. This prioritization can be customized in the configuration.
IPC-SM-782 was one of the original standards documents to define Printed Circuit Board (PCB) land patterns for Surface Mount Devices (SMD). Originally published in 1996, it was later superseded by IPC-7351 in 2005. IPC-7351 was then revised in 2007 and 2010. This library is based on the IPC-7351 2010 guidelines.
However, the guidelines do not fully define land pads. Rather, they specify three classes of PCB fabrication and assembly tolerances and corresponding solder fillets given component tolerances. These component tolerances must come from the designer.
So, what component tolerances do we use? The JEDEC standardization body defines a list of standardized two-terminal chips.
....
When mechanical engineers are taking a 3D model of a printed circuit board for enclosure or heatsink design, they'll often ask if the component dimensions are nominal or max. If they're designing a compression thermal pad, they need to know the minimum and maximum component dimensions. Having 3D bodies that represent the maximum dimensions as provided by the manufacturer can be useful to ensure clearances to adjacent components are guaranteed, but this is not the only use case. Designing a thermal pad is an example where having maximum dimensions can lead to a lack of thermal contact. Therefore, this library uses the nominal component dimensions in its 3D models.
Different organizations make different layer, drafting, solder mask, paste mask, courtyard, and 3D-body choices. This library intentionally uses a minimal footprint artwork set:
Pads a. Land dimensions calculated from IPC-7351B (2010) solder-joint goals for the selected density level. b. Paste mask expansion is rule-based and defined at the PCB document level. c. Solder mask expansion is manually set to 0.05 mm (1.97 mil). d. SMD pads use rounded-rectangle geometry with a 25% corner radius. e. Component body length, body width, and height tolerance inputs are set to ±0% unless otherwise stated. f. Terminal/band length assumptions are family-specific: RESC uses Panasonic ERJ-family dimensions; CAPC uses Samsung CL-family dimensions.
Component outline on Mechanical 15.
Component centroid/origin mark on Mechanical 15.
3D model on Mechanical 1.
The IPC-calculated pad dimensions are used except for CAPC0402*, CAPC0603*, and RESC0402*, where the pads are adjusted to preserve at least 0.10 mm solder-mask web between adjacent pads.
Footprints are named using the IPC-7351B land-pattern naming convention for chip resistors and chip capacitors:
RESC{LL}{WW}X{H}{D}
CAPC{LL}{WW}X{H}{D}
where:
RESC / CAPC: IPC package-family prefix for chip resistors / chip capacitors.LL: nominal component body length in 0.1 mm units, encoded with one digit on each side of the decimal point.WW: nominal component body width in 0.1 mm units, encoded with one digit on each side of the decimal point.H: nominal component height in 0.01 mm units. The value is encoded by formatting the height with two decimal places, removing the decimal point, and removing leading zeros before the decimal point only. Therefore 0.05 mm -> X05, 0.65 mm -> X65, 1.00 mm -> X100, and 2.50 mm -> X250.D: IPC density-level suffix: L for least, N for nominal, or M for most.By default each family uses vendor-specific names, which add a family tag before the density letter (RESC0603X26_CRCWL, RESC1005X35_ERJN). Then no two families ever share a footprint, so each part gets exactly the geometry its own datasheet implies. A family can instead use generic IPC-7351B names (RESC0603X26L), set under Footprint names on its card in the configurator's Libraries tab. Generic names can be shared with other families; when two families emit the same name, the house library keeps the geometry from the family ranked higher on the Priorities tab, which lists only the families whose names actually collide in the current build.
Every vendor .xls workbook in this repository follows the same column schema, captured canonically in resistor_template.xlsx. Columns are split into five groups; the first two groups carry the human-facing identity of the part, the third carries electrical parameters used for filtering / BOM, and the last two tell Altium where to fetch the schematic symbol and PCB footprints when the part is placed. Example values below show one row for ERJ-XGNF10R0:
Notes:
Comment is the row's key field — it's what the *.DbLib binds as its Single key lookup database field (see Regenerating the Databases for the gotcha that tripped us up here). Keeping Comment = MPN is the simplest convention and what every script in this repo does.Description follows the form <CONSTRUCTION> <VALUE> <TOLERANCE> <CASE> (e.g. RESISTOR THICK FILM 10 OHM 1% 01005) so it's human-scannable at BOM time without a schematic..xls before downloading it.Footprint Path / Footprint Ref pairs hold the three IPC-7351 density variants for the same physical part. This library follows IPC-7351B's L/N/M suffix convention (Least / Nominal / Most), which is the same concept the older IPC-SM-782 "A/B/C" labels referred to. Which variant the PCB actually uses is a board-level choice made via Altium PCB rules, not encoded here.Library Path and Footprint Path are resolved by Altium relative to the *.DbLib file's location (put a .\ prefix in front of a bare filename to force Altium into "relative path" resolution mode, otherwise it falls through to LibrarySearchPath lookup). Current vendor scripts write .\house.SchLib / .\house.PcbLib — the panasonic.SchLib / panasonic.PcbLib values shown in the template are legacy illustrative values from before the shared house.* libraries landed.Qual).The build has a single host-side prerequisite:
Python 3.11+ — the per-vendor generator scripts, the house-footprints merge, the parametric STEP generator, and the .PcbLib writer are all Python. The only third-party dependency pinned in requirements.txt is xlwt (writes the database .xls files Altium's DbLib reads). Everything else — the per-vendor footprints JSONs, the merge, the STEP geometry engine, the MS-CFB v3 container writer, the AltiumSharp v1.0.2-compatible record writer, and the hlcl/_config.py module that loads the JSON BuildConfig — is pure stdlib.
Earlier versions of this repo used a small C# project (under
house/HouseLibGenerator/, since deleted) that wrapped theOriginalCircuit.AltiumSharpNuGet package to emit the.PcbLib. That dependency, plus its transitive .NET 8 SDK requirement, has been replaced by the pure-Python writer inhlcl/house/altium_pcblib/. Output is byte-stable across runs (same MD5 every build) and structurally identical to what the C# tool produced (verified end-to-end against AltiumSharp's reader on a 168-footprint reference library).
(The configurator also needs Node 22+ if you want to run the static site locally — see Web configurator below. Pure CLI users can skip Node entirely.)
Linux / macOS / WSL — replace python3.12 below with whatever ≥3.11 interpreter you have on PATH (python3.11, python3.13, …):
python3.12 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt # or: npm run py:install
On Debian/Ubuntu (WSL included) the
venvmodule ships in a separate apt package per minor version. Ifpython3.X -m venverrors withNo module named ensurepip, install the matching package:sudo apt install python3.12-venv(orpython3.11-venv, etc.).
Windows (PowerShell or cmd)
py -3.12 -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
Once the venv is activated, plain python resolves to the venv's interpreter, so python hlcl/build.py Just Works.
After the Python venv is activated:
python hlcl/build.py
# or, via npm:
npm run py:build
(Bare python hlcl/build.py is the same as python hlcl/build.py all.) If you'd rather not activate the venv, point at it explicitly: .venv/bin/python hlcl/build.py on Linux/macOS/WSL, or .venv\Scripts\python static\hlcl\build.py on Windows.
To build against a custom BuildConfig JSON:
python hlcl/build.py --config my-config.json
# or, via npm:
npm run py:build:config -- my-config.json
WSL note —
Permission deniedonpython hlcl/build.py cleanWhen the project lives on a Windows drive (e.g.
/mnt/d/...) and any of thehlcl/build/output/*.xlsdatabase workbooks orhlcl/build/output/house.PcbLibare open on the Windows side (Excel on the workbooks, Altium on the .PcbLib), thecleanstep can't unlink them and aborts. Close the offending app and re-run.
Everything generated by the Python pipeline lives under hlcl/build/ (.gitignored). The tree is split into two top-level subdirectories with mutually-exclusive purposes:
| Directory | Contents | Audience |
|---|---|---|
hlcl/build/output/ | .xls databases, matching .DbLib files, house.PcbLib, house.SchLib, standards/standards.{tex,md} | You. This is what you point Altium at, ship to colleagues, or commit to a downstream library repo. |
hlcl/build/intermediate/ | per-vendor footprints JSONs, the merged house-footprints.json, parametric .step 3D models | The build chain itself. Nothing user-facing reads these. Safe to ignore unless you're debugging the generator. |
Every Python generator writes its user-facing artifacts directly into build/output/; there is no copy or stage step, and no parallel copy of any user-facing file lives in build/intermediate/. So if a file shows up in build/output/, it's the deliverable; no need to wonder which directory the "real" copy is in.
The per-vendor .DbLib files use LibrarySearchPath=. (their own directory) for house.SchLib / house.PcbLib lookups, so a build/output/ copied / committed elsewhere as a unit keeps resolving correctly.
build.pyThe repo ships a single Python build orchestrator at hlcl/build.py. It runs every generator in-process (no subprocess, no make) so the same code drives the build from a host shell today and from Pyodide in the browser tomorrow. It auto-discovers per-family generators by globbing every hlcl/vendors/*/*/_build.py (and hlcl/house/*/_build.py); a new family just needs to drop in its own _build.py stub.
python hlcl/build.py or … all — build everything (vendor .xlss in build/output/ + per-vendor footprint JSONs in build/intermediate/footprints/ + merged JSON + 3D STEP models in build/intermediate/step/ + build/output/house.PcbLib + build/output/house.SchLib)python hlcl/build.py panasonic-erj — build build/output/panasonic-erj.{xls,DbLib} + matching build/intermediate/footprints/panasonic-erj-footprints.json (Panasonic ERJ thick-film, 01005-0805)python hlcl/build.py panasonic-era-a — build build/output/panasonic-era-a.{xls,DbLib} + matching footprints JSON (ERA-A thin-film, 0201 only)python hlcl/build.py panasonic-era-v — build build/output/panasonic-era-v.{xls,DbLib} + matching footprints JSON (ERA-V/K thin-film high-stability, 0402-0805)python hlcl/build.py panasonic-era-p — build build/output/panasonic-era-p.{xls,DbLib} + matching footprints JSON (ERA-P 500V thin-film, 1206)python hlcl/build.py panasonic — convenience aggregate that builds all four panasonic-* family targets above (auto-derived from any 2+ family targets that share a manufacturer prefix)python hlcl/build.py tdk-capacitors — build build/output/tdk-capacitors.{xls,DbLib} + matching footprints JSONpython hlcl/build.py samsung-capacitors — build build/output/samsung-capacitors.{xls,DbLib} + matching footprints JSONpython hlcl/build.py murata-gcm — build build/output/murata-gcm.{xls,DbLib} (Murata GCM, automotive-qualified MLCC) + matching footprints JSONpython hlcl/build.py murata-grm — build build/output/murata-grm.{xls,DbLib} (Murata GRM, commercial / general-purpose MLCC) + matching footprints JSONpython hlcl/build.py murata-ferrites — run the Murata BLM-series ferrite-bead generator (build/output/murata-ferrite.{xls,DbLib})python hlcl/build.py murata — aggregate that builds all three murata-* family targets abovepython hlcl/build.py ohmite-kdv — build build/output/ohmite-kdv.{xls,DbLib} (Ohmite KDV, metal-film current-sense resistors) + matching footprints JSONpython hlcl/build.py house-footprints — merge per-vendor footprints JSONs into build/intermediate/footprints/house-footprints.json (transitively builds every vendor target first)(Paths relative to the build orchestrator are written without the hlcl/ prefix in the bullet list above; the orchestrator resolves every output relative to its own location.)
build.py is fully driven by the active BuildConfig. The same JSON the web app emits also drives the CLI:
families.<id>.enabled is true AND (b) any of artifacts.{xls, dblib, footprints_json} is on.house-pcblib ↔ artifacts.pcblib, etc.).enabled_densities, sizes, override_pad_radius, plus catalog-specific knobs like Murata GCM's min_voltage) gate per-row emission inside each vendor script. The shared helpers in hlcl/vendors/_common.py (density_codes, enabled_sizes, xls_footprint_columns, expand_footprint_rows) read from hlcl/_config.py's singleton, so vendor scripts have one line of config plumbing instead of N.Adding a vendor / family. Each vendor folder under
hlcl/vendors/<mfg>/<family>/(and eachhlcl/house/<component>/subfolder) has its own_build.pystub thatbuild.pypicks up via wildcard glob. To add a new family:mkdir hlcl/vendors/<mfg>/<family>, drop in your generator script + a 10-line_build.pythat callsregister(...), plus acatalog.jsonthat the SvelteKit configurator picks up (see Vendor catalog discovery), then add the vendor key to the defaultsettings.house_footprints.prioritylist insrc/lib/schema.ts(regenerated intohlcl/factory-defaults.jsonon the nextnpm run dev). No edits tobuild.py,src/lib/libraries.ts, or any other source file are required. See any existinghlcl/vendors/*/*/{_build.py,catalog.json}for templates.
The configurator at the repo root is a static SvelteKit + Tailwind 4 app that lets engineers point-and-click a custom HLCL build (subset of vendor families, project-settings overrides, build-output toggles), then runs the same Python pipeline above inside the browser via Pyodide — no server runtime, no installer.
hlcl/presets/<id>.json file: a small envelope of
card metadata (title, tagline, description, bullets,
sort_order) wrapped around a fully-formed BuildConfig that's
applied wholesale on click. The Vite plugin discovers, validates,
and merges the files into src/lib/_generated/presets.ts
on every dev save / prod build, so adding a preset is a pure-data
drop — no edits to the SvelteKit source required. Crosslinks are
intentionally preserved across applyPreset() — they're user data,
not a configuration knob — and edits in any later tab automatically
flip the badge to Custom configuration.catalog.json files (see Vendor catalog discovery
below) — no edits to the SvelteKit source are required to add a new
family.settings branch of BuildConfig. The Python build consumes these via
hlcl/_config.py; there is no longer a settings.toml
file (deprecated in favour of the JSON config)..xls / .DbLib, footprints JSON, STEP, house.PcbLib,
house.SchLib, populated standards.tex / , zip).A persistent Python console dock lives at the bottom of the configure page; collapse it via the chevron in its header.
State is persisted to localStorage under hlcl-configurator-v2 (a v1 envelope from before BuildConfig existed migrates automatically on first load). Visiting the site again restores the last configuration.
Everything the configurator captures, plus everything the in-browser build needs to know to reproduce a run, lives in a single canonical JSON document — BuildConfig, defined once in src/lib/schema.ts using Zod. The same JSON drives both the in-browser Pyodide build and the host-side CLI (python hlcl/build.py --config foo.json); there is no other data exchange between the Python pipeline and any caller.
| Top-level field | Shape | Notes |
|---|---|---|
schema_version | string literal "1.0.0" | Bump minor for additive / backwards-compatible changes (a v1.0.0 reader still parses a v1.1.0 file because Zod ignores unknown keys and missing fields fall back to defaults); bump major for breaking changes. |
families | Record<string, FamilyConfig> | Keyed by stable family id (panasonic-erj, murata-gcm, …). Each entry carries enabled: boolean, options: Record<string, …> (per-family knobs from the catalog), and overrides: Record<string, boolean> for the "use global ↔ override" flags. |
settings | project-wide knobs | house_footprints.priority, ipc.{F,P}, the HLCL drawing-standard knobs, STEP geometry tunables, and the per-family RGB colour palette. Loaded into Python by hlcl/_config.py; legacy callers reach it via the hlcl/house/_house_settings.SETTINGS shim. |
artifacts | ten booleans | xls, dblib, footprints_json, house_footprints_merge, step_models, pcblib, schlib, standards_tex, standards_md, zip_output. build.py consults these to skip whole targets — disabling pcblib, for instance, completely skips house-pcblib, even when invoked directly. |
crosslinks | Array<{ primary_mpn, primary_mfg, substitute_mpn, substitute_mfg, notes }> | Captured for the future BOM substitution generator. |
The Vite plugin re-derives two JSON sidecars at every dev start and prod build:
hlcl/build-config.schema.json ← auto-generated JSON Schema 2020-12, gitignored
hlcl/factory-defaults.json ← auto-generated default BuildConfig, gitignored
They sit next to build.py so the CLI (and any external jsonschema consumer who's cloned the repo) can validate against the same canonical contract the web app uses, and python hlcl/build.py with no --config arg falls back to factory-defaults.json.
localStorage envelope is { storage_version, config: BuildConfig, ui: { active_preset_id, config_name } } — UI-only state lives outside BuildConfig so it can evolve independently of the Python contract. The Generate tab's Download configuration.json button writes just the bare BuildConfig so a recipient can feed it straight into the CLI via python hlcl/build.py --config <path>.
_config.py: the Python side of the contracthlcl/_config.py is the single source of truth on the Python side. It exports:
bootstrap(config_path: str | Path | None) -> BuildConfig — called once at the top of build.py (and from the Pyodide runner) to load + validate the JSON, then install it as the process-wide singleton.current() -> BuildConfig — every vendor / house script imports this and uses it to look up enabled families, sizes, densities, options, and project settings. There is no module-level state to mutate; the dataclass tree is immutable.densities_for(family_id), sizes_for(family_id), pad_corner_radius_percent_for(family_id), family(family_id), is_artifact_enabled(name), is_target_enabled(target_name, target_kind). These are what the build helpers in hlcl/vendors/_common.py call internally, so vendor scripts rarely touch _config directly — they just call _vendor_common.enabled_sizes(FAMILY_ID) / density_codes(FAMILY_ID) / etc.The shim at hlcl/house/_house_settings.py re-exports SETTINGS = _config.current().settings so legacy from house._house_settings import SETTINGS imports keep working without modification.
The Libraries-tab catalog is not hardcoded in the SvelteKit source. Each vendor family ships its own catalog.json next to its build script:
hlcl/vendors/<manufacturer>/<family>/
├── catalog.json ← Libraries-tab metadata (this file)
├── _build.py ← `build.py` registration stub
└── <family>.py ← the generator itself
vite/plugins/hlcl-build-assets.ts globs hlcl/vendors/*/*/catalog.json on every dev save and production build, validates each entry against familyCatalogEntrySchema (defined alongside BuildConfig in src/lib/schema.ts), and emits a typed TypeScript module at src/lib/_generated/catalog.ts. src/lib/libraries.ts imports that module and groups the entries into the kind → subtype → family tree the Libraries tab renders.
A catalog.json is a single object with these fields:
| Field | Type | Notes |
|---|---|---|
id | string | Stable id, must match the build.py target name (e.g. panasonic-erj). |
name | string | Display name shown in the family card. |
manufacturer | string | Free-form; appears as small caps under the family name. |
summary | string | One-line blurb under the name. |
kind | "resistor" | "capacitor" | "ferrite" | "inductor" | Closed taxonomy; tab in the Libraries-tab nav. |
subtype_id | string | Subtype id within the kind (e.g. thick-film). Multiple families may share one. |
subtype_label | string | Human label for the subtype heading. |
subtype_order | int | Lower renders first; merger takes the minimum across all entries that share a subtype_id. Defaults to 100. |
sort_order | int | Family order within a subtype, ascending. Ties break alphabetically by id. Defaults to 100. |
available_sizes | string[] | EIA case sizes this family ships with (["01005", "0201", …]). Restricts the per-family "Case sizes" multiselect. |
options | FamilyOption[] | Family-specific extras BEYOND the common (densities / sizes / pad-radius) options every family already gets. Most are an empty array. |
Adding a new vendor family is therefore a pure-data drop:
mkdir hlcl/vendors/<mfg>/<family> and write your generator + _build.py (see the existing scripts for templates).catalog.json next to them with the fields above.npm run dev — the Vite plugin regenerates src/lib/_generated/catalog.ts, HMR reloads the page, and the new family appears in the Libraries tab.settings.house_footprints.priority list in src/lib/schema.ts so the merger has a tie-break ordering. Re-running npm run dev regenerates hlcl/factory-defaults.json with the new entry baked in.The standalone npm run regen (or the prepare / check scripts) regenerates the same artefact without booting Vite — useful for fresh-clone CI.
npm install
npm run dev # http://localhost:5173
Other npm scripts: npm run check (svelte-check), npm run lint / npm run format (prettier + eslint), npm run build (production static build into ./build/), npm run preview (serve ./build/ locally), npm run py:install (pip install -r requirements.txt), npm run py:build (run hlcl/build.py with the default config), npm run py:build:config -- <path> (run hlcl/build.py --config <path> with a custom BuildConfig JSON).
svelte.config.js reads BASE_PATH from the environment so dev runs at / and CI builds at whatever prefix the deploy URL uses. The default is '' (empty) because production is served at the apex of the custom domain hlcl.jdbrinton.consulting — configure that custom domain in the repository’s GitHub Pages settings. Forks landing on https://<owner>.github.io/<repo>/ need to set BASE_PATH=/<repo> to bake that prefix into the emitted asset URLs.
NODE_ENV=production npm run build # apex / custom domain
NODE_ENV=production BASE_PATH=/my-fork npm run build # https://<owner>.github.io/my-fork/
The build/ directory at the repo root is what GitHub Pages serves. It contains index.html and configure/index.html (prerendered), 404.html (SPA fallback), _app/ (JS / CSS bundle), .nojekyll (opt out of Jekyll's _app/-eating prefix munging), and hlcl-build-inputs.tgz (gzipped tar of the build-relevant subset of hlcl/; Pyodide fetches and unpacks it on the first "Run build" click).
The original website is 0.1.0, and version tracking starts with the 0.2.0 development cycle. Development versions use a -dev suffix and are marked unreleased in the website's revision history. Every page links to that history from its version footer.
package.json supplies the build version; src/lib/release-history.json supplies release notes. npm run build validates both before building. Keep development on dev; production website deployment requires a matching stable vX.Y.Z tag on a commit already merged into main.
See Website versions and releases for the commit, pull request, tag, release, redeployment, and next-development-cycle instructions, including a worked example for promoting 0.2.0.
Two workflows live under .github/workflows/. Website checks run for pushes and pull requests to dev and main. Version tags trigger website production deployment after release validation. Manual website runs deploy only when an existing version tag is selected. The library workflow runs only on pushes to main and requires stable release metadata before updating its rolling latest release.
| Workflow | What it does | Output |
|---|---|---|
pages.yml | Validate release metadata, run release tests and Svelte checks, and build the site. Stable version tags on main history deploy to Pages and publish a versioned website release. | Website build artifact on each run; tagged releases deploy to https://hlcl.jdbrinton.consulting/ and attach website-vX.Y.Z.zip to the corresponding GitHub Release. |
hlcl-build.yml | pip install -r requirements.txt && python hlcl/build.py (default all target — pure Python, no native deps), then publishes the result as the rolling latest GitHub Release. | (1) Rolling latest GitHub Release with hlcl-build-output.zip attached — visible in the repo's Releases sidebar with a stable, permanent download URL (see below). (2) Workflow artifact hlcl-build-output on each run page (90-day retention) for downstream actions/download-artifact@v4 consumers and run history. |
Visitors who just want the prebuilt Altium library don't need to log in to GitHub or open the Actions tab. The URL below always resolves to the most recent successful build of main:
https://github.com/<owner>/<repo>/releases/latest/download/hlcl-build-output.zip
The archive contains the full hlcl/build/output/ tree (vendor .xls + .DbLib workbooks, merged footprints JSON, parametric STEP models, house.PcbLib, house.SchLib). Only .zip is published — Altium is the sole supported EDA tool today and ships Windows-only, so the audience for this archive is on Windows where .zip is the native unpack format. The latest git tag is moved on every push to main, so the URL itself never changes. The Releases page also lists every prior latest snapshot's commit SHA in its title (e.g. Latest HLCL build (abc1234)) for traceability.
The release publishing step requires the workflow's
GITHUB_TOKENto havecontents: write, which is granted inhlcl-build.ymlitself. If you fork the repo into an org with a default-restricted token policy, also flip Settings → Actions → General → Workflow permissions to Read and write permissions (or pin the samepermissions:block in your fork's workflow).
For a fork hosted at a different Pages path, set BASE_PATH (or unset it for a user / org page) in pages.yml:
- run: npm run build
env:
NODE_ENV: production
BASE_PATH: /my-fork # or BASE_PATH: '' for user/org pages
To consume the prebuilt HLCL artifacts in another workflow (e.g. a release job that publishes them to a downstream library repo), use actions/download-artifact@v4 against the hlcl-build-output name when running in the same repo, or download the release asset directly with gh release download latest --repo <owner>/<repo> -p 'hlcl-build-output.zip' from any external workflow.
Each vendor family has a Python script under hlcl/vendors/<mfg>/<family>/ (e.g. hlcl/vendors/panasonic/erj/panasonic-erj.py, hlcl/vendors/tdk/cga/tdk-capacitors.py, hlcl/vendors/murata/gcm/murata-gcm.py) that emits two artifacts per script:
hlcl/build/output/<vendor>.xls — the database workbook the vendor's *.DbLib binds to (one row per MPN). Excel 97-2003 BIFF8 written via xlwt. User-facing.hlcl/build/intermediate/footprints/<vendor>-footprints.json — the per-vendor footprint specification: one entry per unique CAPC / RESC footprint × density variant (L, N, M) the database above references. Each entry carries body geometry (L × W × H, terminal length T), a kind field (C / R / I / FB), and a drawingNote source attribution (e.g. "Dimensions from Panasonic ERJ-XGN (01005 thick film)"). The schema is defined and validated in hlcl/vendors/_common.py. Intermediate; consumed by the merge / STEP / .PcbLib steps.Once every per-vendor script has run, hlcl/house/build_house_footprints.py (wired into python hlcl/build.py all via the house-footprints target, whose @vendors pseudo-dep transitively depends on every registered vendor target) merges every hlcl/build/intermediate/footprints/*-footprints.json into a single hlcl/build/intermediate/footprints/house-footprints.json — the canonical input for both the STEP 3D model generator and the .PcbLib autogenerator. When two vendor JSONs define a row with the same name (e.g. Samsung CL and TDK CGA both define CAPC1005X50N), the merge breaks ties using the settings.house_footprints.priority list from the active BuildConfig (default values in hlcl/factory-defaults.json) — the vendor that appears first wins. The merge logs every conflict it resolves on stderr.
Adding a new vendor. Drop a script under
hlcl/vendors/<mfg>/<family>/that calls_common.write_footprints_jsonwith the unique CAPC/RESC/INDC footprints it requires (the existing scripts are good templates), and a 10-line_build.pystub next to it that callsbuild.register(...).build.pypicks up everyhlcl/vendors/*/*/_build.pyvia wildcard glob, so no top-level edit is needed. Add the vendor key to the defaultsettings.house_footprints.prioritylist insrc/lib/schema.tsto control tie-break behaviour (regenerated intohlcl/factory-defaults.jsonautomatically).
hlcl/build/intermediate/step/*.step)python hlcl/build.py house-step-models (also pulled in by … all) runs hlcl/house/build_step_models.py which delegates to the geometry engine in hlcl/house/stepgen/. The IPC-7351B density variants (L / N / M) only change the pad geometry — the component body itself is identical — so the generator deduplicates by footprint root (FootprintName minus its trailing density letter) and emits one STEP per unique chip body:
hlcl/build/intermediate/step/CAPC0402X20.step ← shared by CAPC0402X20{L,N,M}
hlcl/build/intermediate/step/RESC0402X13.step ← shared by RESC0402X13{L,N,M}
...
The .pcblib autogenerator strips the same density suffix when looking up each footprint's STEP file, so all three density variants in hlcl/build/output/house.PcbLib reference (and zlib-embed) the same STEP body. After python hlcl/build.py all, no external STEP files are required for the .PcbLib to render in Altium — the STEP files in hlcl/build/intermediate/step/ are kept around solely so a human can pop one open in any STEP viewer to spot-check the geometry.
The generator is intentionally pure-stdlib Python — no CadQuery, no FreeCAD, no OpenCASCADE bindings — for two reasons:
requirements.txt.Per-family layering today:
| Family | Pieces | Edges | Body colour | Terminal colour |
|---|---|---|---|---|
| CAPC | dielectric body + 2 terminals | filleted (r=0.05 mm) | MLCC tan #B7860B | Sn silver #CCCCCC |
| INDC, FB | ferrite body + 2 terminals | filleted (r=0.05 mm) | ferrite blue #264D94 | Sn silver #CCCCCC |
| RESC | alumina substrate + passivation cover + 2 C-shaped terminals (3 sub-pieces each) | sharp | substrate #9E9E9E / passivation #171717 | darker grey #808080 |
CAPC and INDC bodies use a 26-face filleted-box B-rep (6 main planes + 12 cylindrical edge fillets + 8 spherical corner octants), matching the Altium IPC LP Wizard output style. The fillet radius is clamped to min(L, W, H) / 4 per box so it never eats more than 1/4 of any dimension — on tiny CAPC0402X20 footprints (W=0.2 mm) the fillet shrinks gracefully to 0.05 mm and remains visible.
RESC stays sharp-edged (no fillets) and instead uses a layered geometry that mirrors a real chip resistor: each "C"-shaped terminal is built from three flush sub-boxes (end-cap + top wrap + bottom wrap) so the terminal wraps around the substrate; the alumina substrate sits between the two C-terminals raised off the floor by the metallisation thickness; the passivation cover fills the top sandwich slot, flush with the terminal tops.
hlcl/build/output/house.PcbLibpython hlcl/build.py house-pcblib (also pulled in by … all) runs hlcl/house/build_pcblib.py, the driver script for the pure-Python writer in hlcl/house/altium_pcblib/. It turns the JSON sidecar plus the parametric STEP files into hlcl/build/output/house.PcbLib directly — no Altium IPC Batch Generator round-trip, no manual per-footprint tweaks, no .NET dependency. The writer applies:
J_T, heel J_H, side J_S, and round-off granularity per density level (L/N/M); fab tolerance F = 0.10 mm and placement tolerance P = 0.05 mm per IPC-7351B §3.1.3. The component-tolerance term C is zero by repo policy (the JSON sidecar enforces Lmin == Lmax etc. before the writer ever runs). See hlcl/house/altium_pcblib/ipc.py.L × W × H; no silkscreen, no courtyard. See docs/standards/standards.tex and hlcl/house/altium_pcblib/hlcl.py.G - 2E falls below 0.1 mm, the writer overlays a single Top Solder region across both apertures (instead of shrinking copper) and prints a "mask-bridge" diagnostic to stderr so the deviation is auditable. The families that hit this in the current dataset are CAPC0402, CAPC0603, RESC0402, and RESC0603.The writer is structured into clean layers:
| Module | Role |
|---|---|
altium_pcblib/cfb.py | Pure-Python MS-CFB v3 container writer (sectors, FAT, mini-FAT, directory tree). |
altium_pcblib/binary.py | Length-prefixed binary block framing (mirrors AltiumSharp's BinaryFormatWriter). |
altium_pcblib/encoding / parameters_to_string | Windows-1252 encoding + |KEY=VAL|... parameter list emitter. |
altium_pcblib/primitives.py | Coord (1/10000 mil), CoordPoint, Layer enum, OLE color packing. |
altium_pcblib/records.py | Dataclasses for PcbPad, PcbTrack, PcbRegion, PcbComponentBody, PcbComponent, PcbModel, PcbLibrary. |
altium_pcblib/writer.py | Per-record binary serialisers; ties everything into the CFB container. |
altium_pcblib/ipc.py | IPC-7351B chip-component land pattern math. |
The writer's wire format target is AltiumSharp v1.0.2-compatible: the same record byte layout, parameter-list ordering, and field-default sentinels that the v1 NuGet writer produced. We replicate v1's specific quirks (e.g. EMBED=T instead of EMBED=TRUE in model metadata; IDENTIFIER=67,104,... codepoint encoding; Coord.FromInt32(1) sentinels for otherwise-zero fields) so the output round-trips through v1's reader cleanly. AltiumSharp's reader on a 168-footprint reference library yields 0 errors, 0 warnings, 0 DTO field diffs vs. the older C#-emitted output, and 1180 of 1181 internal CFB streams are byte-identical (the lone exception is Library/Data, where we deliberately use a fixed sentinel DATE/TIME for reproducibility instead of the wall-clock).
To regenerate just the .PcbLib (after a vendor JSON change has already propagated):
python hlcl/build.py house-pcblib
Or invoke the writer directly:
python hlcl/house/build_pcblib.py \
--input hlcl/build/intermediate/footprints/house-footprints.json \
--output hlcl/build/output/house.PcbLib \
--step-dir hlcl/build/intermediate/step
Historical note —
xlwt's malformedWRITEACCESSrecord.Older revisions of this repo required every freshly-generated
hlcl/build/output/<vendor>.xlsto be opened (and closed — no save needed) in Microsoft Excel before Altium's DbLib OLE DB reader could load it. The symptom was that the DbLib would open but its tables would appear empty, or tables would load but placed components would fail to resolve their symbols/footprints even with the field mappings correct.Root cause:
xlwt-1.3emits a malformedWRITEACCESSrecord (BIFF type0x005C). Per [MS-XLS] §2.4.351 the 112-byte payload is supposed to be anXLUnicodeString(uint16 cch,uint8 fHighByte, then chars, then space padding);xlwtskips the framing bytes entirely and packs the owner name as raw ASCII followed by space padding. Excel's reader silently repairs the record on open; the ACE/Jet OLE DB driver Altium uses does not, and silently fails to load the table.Fix:
hlcl/vendors/_common.pymonkey-patchesxlwt.BIFFRecords.WriteAccessRecordto emit a spec-compliantXLUnicodeStringpayload. Every vendor script imports_commonbefore saving its workbook, so the patch is applied in one place and every generated.xlsopens directly in Altium. No Excel round-trip required.
hlcl/vendors/panasonic/README.md. Per-family script details:
hlcl/vendors/panasonic/erj/hlcl/vendors/panasonic/era-a/hlcl/vendors/panasonic/era-v/hlcl/vendors/panasonic/era-p/hlcl/vendors/tdk/README.md + hlcl/vendors/tdk/cga/hlcl/vendors/samsung/README.md + hlcl/vendors/samsung/cl/hlcl/vendors/murata/README.md. Per-family scripts:
See docs/standards.
| Imperial Inch (EIA) | Metric (JEITA) <- used by this library |
|---|---|
| 01005 | 0402 |
| 0201 | 0603 |
| 0402 | 1005 |
| 0603 | 1608 |
| 0805 | 2012 |
| 1206 | 3216 |
python hlcl/build.py house-step-models — generate parametric 3D STEP models in build/intermediate/step/*.step via hlcl/house/stepgen/python hlcl/build.py house-pcblib — autogenerate build/output/house.PcbLib from the merged JSON + STEP models (calls into hlcl/house/altium_pcblib/, the pure-Python writer)python hlcl/build.py house-schlib — copy the hand-maintained house.SchLib into build/output/python hlcl/build.py standards — render docs/standards/standards.tex and / or docs/standards/standards.md with the active drawing-standard values substituted in (e.g. @@PAD_CORNER_RADIUS_PERCENT@@ becomes 25) and stage the populated copies under hlcl/build/output/standards/. Pure Python, no LaTeX toolchain needed; the same code path runs under host CPython and Pyodide. Gated by artifacts.standards_tex / artifacts.standards_md -- both off by default.python hlcl/build.py clean — remove the entire hlcl/build/ tree (both intermediate/ and output/) plus any __pycache__/ directories under hlcl/vendors/ and hlcl/house/python hlcl/build.py --list — print every registered target with its config-derived gate state ([ON] / [off])python hlcl/build.py --config <path> — load the BuildConfig from <path> (JSON, validated against hlcl/build-config.schema.json). Defaults to hlcl/factory-defaults.json (auto-generated by the Vite plugin from the canonical Zod schema) when omitted. Errors out if neither is available — run npm run dev once to generate the defaults file. The flag composes with every target above: python hlcl/build.py --config my-config.json murata-gcm builds GCM with your overrides applied.standards.mdstatic/hlcl-build-inputs.tgz (~370 KB gzipped, regen'd
at site-build time by vite/plugins/hlcl-build-assets.ts);
(b) installs xlwt via micropip; (c) extracts the tarball into Pyodide's
in-memory FS at /hlcl/; (d) writes the active BuildConfig to
/hlcl/build-config.json in MEMFS and runs
build.build_all(root='/hlcl', config_path='/hlcl/build-config.json'),
yielding to the JS event loop between targets so stdout / stderr stream
live to the console; (e) lists every artifact under /hlcl/build/output/
with per-file download links plus a Download <config-name>.zip
button that bundles every artifact alongside the canonical
build-config.json that produced them.altium_pcblib/hlcl.py | HLCL-001 drawing-standards constants. |
altium_pcblib/footprint.py | High-level chip-footprint factory (combines the above). |
hlcl/vendors/murata/gcm/hlcl/vendors/murata/grm/README.mdhlcl/vendors/murata/blm/hlcl/vendors/ohmite/kdv/README.md