Skip to content

Repository files navigation

CoatCart

CI Pages Release License: MIT

Buy the right paint cans across a whole project — after openings, coats, waste, and compatible leftovers are accounted for.

CoatCart is a zero-dependency, offline command-line planner. It does more than divide area by coverage: for each finish, it searches the available can sizes and prices and returns the exact lowest-cost purchase, then breaks ties by least leftover paint and fewest cans.

The result is real, reusable data: a machine-readable report, a printable HTML plan, and a shopping-list CSV. View the generated demo report or read this page in 简体中文.

Why this exists

Paint calculators usually stop at “you need 8.2 litres.” The store sells discrete cans, different finishes cannot be mixed, and you may already own usable leftovers. Rounding each room independently or greedily buying the largest can can cost more than necessary.

CoatCart plans all finishes in one input while keeping their inventories separate:

surfaces - openings -> coats + waste -> compatible stock -> exact can optimizer
                                                           |
                                                           v
                                            JSON + HTML + shopping CSV

It does not recommend colors, estimate labor, call a manufacturer API, or upload project data.

Quick start

Requires Node.js 20 or newer.

git clone https://github.com/KanadeK/coatcart.git
cd coatcart
npm ci
npm run demo

Open docs/index.html, or inspect the three files in docs/demo/.

To install the released package directly from GitHub:

npm install --global https://github.com/KanadeK/coatcart/releases/download/v0.1.0/coatcart-0.1.0.tgz
coatcart demo --out coatcart-output

Plan your own project

Copy examples/whole-home.json, then edit the finishes, areas, stock, and cans:

{
  "project": "Upstairs refresh",
  "measurement": "metric",
  "currency": "USD",
  "finishes": [
    {
      "id": "bedroom-walls",
      "label": "Bedroom matte walls",
      "coverage_per_volume": 10,
      "coats": 2,
      "waste_percent": 8,
      "surfaces": [
        { "label": "Long walls", "width": 4.8, "height": 2.6, "count": 2 }
      ],
      "openings": [
        { "label": "Door", "width": 0.9, "height": 2.1 }
      ],
      "stock": [
        { "label": "Sealed leftover", "amount": 0.75 }
      ],
      "packages": [
        { "label": "1 L can", "amount": 1, "price": 24 },
        { "label": "2.5 L can", "amount": 2.5, "price": 52 },
        { "label": "5 L can", "amount": 5, "price": 94 }
      ]
    }
  ]
}

Validate before planning:

coatcart validate upstairs.json
coatcart plan upstairs.json --out upstairs-plan

The bundled whole-home example produces this purchase:

Finish Existing stock Buy Cost Left after project
Sage matte walls 0.75 L 1 × 1 L, 1 × 2.5 L, 1 × 5 L USD 170.00 0.275 L
Flat white ceiling 0.4 L 1 × 0.75 L, 1 × 2.5 L USD 67.00 0.58 L
Cream satin trim 1.75 L Nothing USD 0.00 0.21 L

Total purchase cost: USD 237.00.

Commands and exit codes

Command Purpose
coatcart validate <input.json> Validate measurements and purchase data without writing reports.
coatcart plan <input.json> --out <directory> Optimize every finish and write all report formats.
coatcart demo --out <directory> Run the packaged whole-home example.
coatcart --help Show the command contract.
coatcart --version Show the package version.
Exit Meaning
0 The command succeeded and the plan is feasible.
2 The command or JSON input is invalid; the error names the field or file to repair.
3 The input is valid, but at least one finish cannot be fulfilled. Reports are still written.

A failure you can reproduce and repair

node bin/coatcart.js plan examples/stock-only-impossible.json --out coatcart-output/incomplete

This intentionally exits 3 and reports a 5.05 L shortfall because purchasing is disabled. Repair the source by doing one truthful thing: add enough compatible stock, set allow_purchase to true and supply real package options, or correct an inaccurate measurement. Then rerun the same command. CoatCart never silently changes coats, waste, or finish compatibility to force a green result.

See Troubleshooting for validation, permissions, install, CI, and release failures.

Acceptance commands

These are the same gates used by CI:

npm ci
npm test
npm run check
npm run accept:package

npm run check performs syntax checks, runs all tests, regenerates the demo, proves the intentional exit-3 path, and inspects the package contents. npm run accept:package packs CoatCart, installs that tarball into a clean temporary prefix, and runs the installed demo.

How the optimizer is exact

Volume is converted to thousandths and money to cents. An unbounded dynamic program explores every reachable purchase volume through one less than the largest can beyond the deficit. Any solution farther away is dominated: removing one can still covers the deficit and cannot increase cost. Candidate plans are compared in the documented order: cost, leftover, can count.

Read Architecture, Input format, the design decision, and the documented differentiation search.

Limits in 0.1.0

  • One measurement system per project: metric (m, , L) or imperial (ft, ft², gal).
  • Stock is compatible only within its finish object.
  • Volume precision is 0.001 unit and price precision is 0.01 currency unit.
  • Prices are user-supplied whole-can prices; taxes, promotions, and labor are out of scope.
  • No interactive editor or product catalog. The HTML is a generated evidence report, not a shell around future functionality.

Contributing and security

Read CONTRIBUTING.md before changing the input or output contract. Please report vulnerabilities through the private route in SECURITY.md, not a public issue.

CoatCart is available under the MIT License.

About

Exact offline paint can optimizer for whole-project takeoff, leftover stock, package sizes, and prices

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages