Commit Graph
118 Commits
Author SHA1 Message Date
ajandClaude Opus 5.5 81ec6a8125 feat(server): list, reopen and keep server nest jobs once
A server Auto Nest result could only be reviewed in the session that sent it,
and nothing stopped two desktops from keeping the same result. Phase 3 adds
My Server Jobs, so results that finished while OpenNest was closed are easy
to find, and records each review on the server.

- Server: GET /api/nest-jobs lists jobs newest first with state, engine,
  submitter, times, queue position, review and the worker's summary (parts,
  sheets, utilization, unplaced). It filters to active, finished or
  unreviewed jobs and to a submitter (a bare user name matches that user on
  any workstation). An ETag turns unchanged polls into 304s. Schema v17 adds
  nestId, review and summary columns, only where missing.
- Keep once: POST .../keep and .../discard take If-Match on the job revision
  and change an unreviewed result only once. A second desktop gets 409 with
  who kept it. DELETE removes finished jobs. A succeeded job keeps its
  package until it is reviewed, so a later session can rebuild the job.
- Retention: OPENNEST_JOB_RETENTION_DAYS (default 7) deletes finished jobs
  with their results. It runs on claims and list reads. A malformed value
  fails startup instead of deleting results early.
- Worker: reports the result's summary with the transcript. A job cancelled
  while its result arrived keeps no summary (found in the Docker smoke run).
- Desktop: Nest > My Server Jobs polls the list every 3 s; polling rather
  than per-job SSE because the list must show other desktops' jobs and
  removals. Open rebuilds the job from job.json against the open nest's own
  drawings. New per-drawing geometry fingerprints make the freshness check
  work across sessions. Cancel Job and Remove act on the selected job. A
  status-bar item counts this user's results waiting for review; there is
  no popup. Auto Nest's review now records Keep on the server before it
  commits, and records Discard too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:18:11 -05:00
ajandClaude Opus 5.5 b0cdbb3e81 feat(server): stream server nest progress and notify when a job ends
A server Auto Nest showed only "Nesting on the nest server" for however long
the job took, and Stop could take a whole 20-second heartbeat to reach the
worker. Now the desktop shows the same Best plan so far strip a local run
does, and a job that ends while OpenNest is in the background says so.

- Worker: RecordingNestingEngine taps each solve's progress. NestJobReporter
  stores only the latest stage and plan, at most once every two seconds, so a
  chatty engine cannot flood SQLite. Each round also reads the cancel flag,
  so Stop reaches the worker within one interval (about 2 s in the Docker
  smoke test).
- Server: schema v16 adds per-job revision, progress, plan and planRevision
  columns, which the upgrade adds only where missing. PUT .../progress and
  .../plan renew the lease like a heartbeat. GET .../events is Server-Sent
  Events, woken by an in-process change signal: plan and job events with
  revision ids, keep-alives, Last-Event-ID resume, and the final state always
  sent before the stream ends. GET .../plan serves the polling fallback.
  Progress goes over HTTP because the worker never opens the database
  (another host, NFS).
- Desktop: NestJobWatch follows the stream. When the stream drops it polls
  once and reconnects, and it polls throughout when the server has no stream.
  RemoteNestPlanPreview draws worker plans from the client's own request,
  including cutout-trial frame proxies. ServerNestNotice shows a tray balloon
  (a toast on Windows 10/11) when the job ends while OpenNest is not in
  front. A real toast would need an AUMID shortcut that Velopack does not
  promise. Clicking the balloon brings the review forward; nothing is kept
  without Keep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:18:10 -05:00
ajandClaude Opus 5.5 54ad7ee84d feat(server): nest Auto Nest jobs on the server's worker
Large nests tie up the workstation that runs them. In Database mode, Auto Nest
can now queue the job on the nest server, where a worker container solves one
job at a time under its own CPU and memory limits. The job has no time limit,
so a throttled worker finds the same nest a fast one does, only later.

- Server: /api/nest-jobs queue (schema v15). The worker holds a job on a
  heartbeat lease. When the lease lapses, the job goes back to the queue,
  or fails after three attempts. Only one job runs at a time. Packages and
  results are opaque to the HTTP server, which stays off Engine and IO.
- Worker: OpenNest.Server.Worker in the same image (OPENNEST_ROLE=worker),
  reaching the server over HTTP only. It records each engine answer with
  RecordingNestingEngine and gives its job back on shutdown.
- Desktop: "Nest on the server" in the Auto Nest dialog, for built-in
  engines only. The client replays the recorded answers with
  RemoteNestSubmission, so validation, the review and Keep run locally
  against the client's own drawings, exactly as after a local run. A
  drawing edited after sending refuses the result. If the server cannot
  take the job, the user may nest locally instead.
- compose.server.yaml adds the throttled worker service. The Dockerfile,
  .dockerignore, CI paths and the deploy script now include the worker's
  projects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:18:09 -05:00
ajandClaude Opus 5.5 d29f919aa2 fix(io): save nests safely and reopen them with their cut-off settings
Reopening a nest regenerated its cut-offs with built-in defaults (no
overtravel, a different minimum segment) rather than the machine's
settings it was made with, so a nest saved, reopened and posted could
cut scrap that never separates. Plates now record the settings their
cut-offs were last generated with, and files carry them; older files
still load with the defaults.

Saving over a nest file truncated it before writing, so any failure
part way lost the only good copy, and the error reached the user as an
unhandled exception after the save path had already changed. Nest and
settings files are now written beside the target and swapped in, and a
failed save is reported with the old file left intact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 09:51:11 -05:00
ajandClaude Opus 5.5 082a50bbcd refactor: remove duplicated client, geometry and desktop code
Share the nest server client setup and page validation, entity endpoints,
post marker checks and the repeated desktop list, cut-time and busy-warning
code. Persistence tests use NestArchiveJson. No behavior change intended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 22:13:26 -04:00
ajandClaude Opus 5.5 01e2238258 feat(library): fill a blank nest from its first library drawing
A nest with no material, grade or thickness now takes them from the
library record instead of warning that the drawing differs and dropping
its cut pins. The machine's spacing and cut-off settings follow. Later
drawings still get the usual difference warning, and drawings from files
carry no thickness, so they leave the nest blank.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:45:39 -04:00
ajandClaude Opus 5.5 59d4c8d727 feat(machines): set spacing per thickness and save defaults from a nest
Plate > Set as Default and Nest > Edit's Defaults tab only changed the
open nest, so new nests never picked up the values. Set as Default now
also saves the new-nest defaults, and the Defaults tab gains an opt-in
"Use these defaults for new nests" check.

Machine thicknesses can now set part and edge spacing. A nest takes them
when its material, grade, thickness or machine changes and when a release
creates it; BOM import seeds its groups from them. Spacing edited in the
same dialog stays, and only an untouched trailing sheet follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 19:39:48 -04:00
ajandClaude Opus 5.5 4d55e11318 style: use one spelling across the codebase
The repo mixed variants such as center/centre and color/colour, so a
name could be spelled either way and searches missed half the hits.
Standardize on the US spelling, matching .NET's own APIs (Color, Center).

Wire values owned by other systems keep their spelling: the material
ledger's "Cancelled" hold status and GitHub's "cancelled" job status.

No behavior change beyond the spelling of a few status messages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:11:30 -04:00
ajandClaude Opus 5.5 b550640ef0 refactor: merge duplicated geometry, settings-file and form helpers
Roslyn Bridge flagged these as copy-paste clusters; each pair now shares
one implementation so a fix lands everywhere at once:

- Shape.OffsetInward/OffsetOutward differed only in the target winding.
- BestFitFinder kept its own copy of SpatialQuery's arc-extreme points.
- SplitDrawingForm rebuilt DrawingSplitter's clip-region grid for preview.
- GeometryOptimizer's collinear/coradial finders (one misspelled
  GetCoradialArs) become one generic FindRelated.
- Five settings files repeated the write-with-retry loop, and two also
  repeated the unit parser and unreadable-file filter (SettingsFile).
- ClampsForm and SupportGridForm repeated their row/box builders.
- Vertical/HorizontalRemnantComparer share a RemnantComparer base.

Behavior is unchanged except that Engine/NestStorage settings loads now
also tolerate NotSupported/Security exceptions, matching the other
settings files' never-throw contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:59:47 -04:00
ajandClaude Opus 5.5 1c2bf16b3e feat(data): give the Generic Laser real grades, leads and stock sizes
Mild steel splits into CR, HR and HR P&O, stainless into 304 and 316,
and aluminum into 5052 and 6061, so no thickness carries a grade it is
not sold in. Gauge thicknesses use MaterialLedger's decimals, which
also corrects stainless gauges that used carbon-steel values.

Every thickness gets a 60 degree line lead-in (0.15", longer from
1/4" up) and a 0.1" lead-out, with no lead-out on inside cuts, a 0.75
cut-off overtravel and every catalog plate size up to 72x144.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:09:46 -04:00
ajandClaude Opus 5.5 bb21ffdf35 feat(machines): split machine leads into outside and inside cuts
Machine Configuration held one lead-in and lead-out per thickness and
applied it to every contour. Each thickness now has Outside Cuts and
Inside Cuts tabs; inside cuts follow the outside leads until "Same as
outside cuts" is cleared, and configurations without inside leads keep
applying one pair to both.

The lead type lists, per-type fields and preview from Cutting Settings
are now a reusable LeadsEditor control that both dialogs use. The
machine tabs hide what the configuration cannot hold: the arc sweep,
the clean-hole kerf (taken from the thickness) and Line + Line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:51:42 -04:00
ajandClaude Opus 5.5 7ec11993de feat(data): ship a Generic Laser default machine in place of CL-980
New installs now start with a generic fiber laser: mild steel, 304
stainless and 5052 aluminum, gauge-named thicknesses, fiber-sized
kerfs and arc lead-ins, and no lead-out. It has its own id, so
uploading local machines never replaces an existing CL-980. A test
checks that the shipped values grow sensibly with thickness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:51:42 -04:00
ajandClaude Opus 5.5 16d4f706b2 feat(machines): name thickness gauges and keep the config tab
Thicknesses take an optional name or gauge shown beside the value in
the tree. The Machine Configuration form keeps the selected tab while
moving between thicknesses or machines, and opens fully expanded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:51:42 -04:00
ajandClaude Opus 5.5 0719476618 feat(clamps): give clamps their own clearance from parts
A clamp rail can now carry its own Clearance, the gap parts keep from
every clamp footprint, independent of part spacing. Unset, it keeps the
sheet's bottom edge spacing as before. Clamp blocks drawn under the
sheet can use close to zero so the laser only stays off them.

Plate.ClampClearance and NestPlateStock.ClampClearance resolve it once;
the engines, interactive fill/pack/push, the layout check, Expander walls
and automatic cut-off placement all read it. The interactive push now
holds clamps at their square clearance keep-out (the layout check's
shape) instead of the part spacing, so parts no longer tuck into its
corners; the clamped fill characterization digests are re-pinned after
checking the new layouts validate.

Persisted as an optional `clearance` on the .nest clamp rail and the
machine clamp settings; edited in Nest > Clamps and the machine's
Clamps tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:25:27 -04:00
ajandClaude Opus 5.5 41460481d6 fix(cutting): keep Plan Cutting's lead and tab edits
Plan Cutting saved its settings only on a successful Apply, so a plate
that would not plan lost them. Each open then laid the machine's leads
back over the last parameters, and the cutting panel rebuilt parameters
from scratch, dropping kerf and the machine/material/thickness stamp.

Save the settings when the dialog is accepted, keep the panel's
unexposed fields, and only apply the machine's leads when the last
parameters were not already taken from that machine, material, grade
and thickness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:58:14 -04:00
ajandClaude Opus 5.5 f1468e6f57 feat(library): let drawings without a material or thickness suit any nest
A drawing every nest needs, such as the operator's test square cut before
the job, had to be saved once per material and thickness or found with Show
all materials. A blank material or grade, or a zero thickness, now means
any: the server's library filter returns such drawings for every value,
and adding one to a nest is not a difference, so it keeps its cut pins and
takes the nest's material for the fields it left blank. Duplicate detection
still compares every key exactly on the client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 07:28:19 -04:00
ajandClaude Opus 5.5 8776eb4734 feat(ledger): keep MaterialLedger item codes from being double-booked
Auto Nest leaves out ledger item codes already on other open server nests,
and save and post re-check a nest's codes before going on.

- Server: GET /api/nests/stock-item-codes?exclude= lists the codes on
  non-deleted Draft, Quote and To Be Cut nests. Codes are stored in
  nest_stock_codes (schema v14) at every archive write; the upgrade reads
  each current archive once.
- Data: INestRepository.GetStockCodesInUseAsync, LedgerCodesInUse (5 s,
  failures become an unknown answer), the pure LedgerCodeFilter for ledger
  rows (reusable by release-time nesting) and LedgerCodeCheck for the
  re-check.
- Desktop: the Auto Nest ledger summary counts "(N on other nests)" or says
  other nests were not checked. Save, Save As and post open Ledger Stock
  Changed when codes are taken: proceed after a per-attempt
  acknowledgement, remove the codes (undoable), or cancel. A ledger that
  cannot be read skips the check with a status note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 22:43:08 -04:00
aj 242a0d2a1b fix(ui): match operator messages to the commands that exist
- archived library drawings: say they can't be edited or released; no
  front end can restore a library drawing yet
- library filter hint names the Show all materials checkbox
- undo labels Support Bed and Automatic Scrap Cutoffs match their menus
- display-outline posting hints name both Plate > Plan Cutting... and
  --plan-cutting, since the console prints them too
- MaterialLedger options are saved per Windows user, not per machine
2026-10-27 09:47:03 -04:00
ajandClaude Opus 5.5 3e1df45c7f feat(machines): keep the clamp rail on each machine
Clamps are physical fixtures that differ between machines, but one global
clamps.json served every machine, so a plasma table and a fiber laser could
not have different rails.

- MachineConfig.Clamps holds each machine's rail in the machine's units,
  edited on a new Clamps tab in Tools > Machine Configuration.
- New and BOM import give a nest the rail of the machine they assign;
  Nest > Clamps loads and saves the nest's machine's clamps. Each nest keeps
  its own copy, so machine edits never move existing nests' clamps.
- At startup, while no machine has clamps, the legacy clamps.json (or the
  server's shared copy) is copied once into the default or only machine;
  with several machines and no default the user picks one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:32:56 -04:00
ajandClaude Opus 5.5 81ec6cc738 feat(data): add a state-based undo history
The desktop app has no undo. Phase 0 showed whole-nest snapshots are
cheap enough to record after every edit, so the history keeps whole
states with a cursor instead of per-command inverses that each need
hand-written reversal code.

It drops the redo tail on commit, evicts the oldest states past 50 steps
or 128 MB without ever evicting the current one, tracks a save point for
a later dirty indicator, and merges matching commits within a second so
repeated nudges undo as one. The clock is a TimeProvider so tests control
the coalescing window. It is generic and free of IO and WinForms so it is
unit tested on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:11:40 -04:00
ajandClaude Opus 5.5 6e40c2e6ed feat(library): redesign Edit Drawing with drop-downs, color modes and a rotation dial
Typing customer, material, grade and thickness by hand let near-duplicate
spellings into the library, which then split drawings that should match.
The fields are now drop-downs of the values the library and the machine
material catalog already use, while still accepting new values; thicknesses
already used for the material are listed ahead of the standard gauges.

A library color could be set but never cleared back to the color scheme's,
so Automatic and Reset are added. The rotation start/end/step boxes gave no
sense of the range, so a dial shades it, ticks the angles the engines try
(via RotationPolicy) and lets either end be dragged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:43 -04:00
ajandClaude Opus 5.5 86b0370a8f fix(names): drop S from generated nest names
S reads too much like 5 on labels and screens, so the sequence alphabet is
now base-29. Already-issued names keep their numbers: the server's stored
counter stays authoritative and a base-29 name can never equal an earlier
base-30 one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:42 -04:00
ajandClaude Opus 5.5 90e2fd2e6e feat(server): share machines, post settings, clamps and nest defaults
Schema v13 adds shared_settings and shared_setting_revisions behind
/api/settings: one revision-guarded JSON document per kind and key, with
every revision (deletions included) kept with who saved it. Recreating a
deleted document continues its revision count.

OpenNest.Data gains the client (RemoteSharedSettings), a synchronous store
that keeps the last copy read for offline use and refuses writes while the
server is unreachable (SharedSettingsStore), a machine provider over it, and
JSON parse/serialize entry points for clamp settings and nest defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:02:08 -04:00
ajandClaude Opus 5.5 84e6adbcf2 fix(data): give the CL-980 default a 0.02 cut-off part clearance
Plates now take cut-off settings from the nest's machine, so the
default's 0.25-0.75 clearances replaced the 0.02 operators expect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:29:59 -04:00
ajandClaude Opus 5.5 1fbe57a841 fix(data): match machine thicknesses rounded to three decimals
PEP stores 3/16 as 0.188, which missed the machine's 0.1875 at the
0.0005 in tolerance; widen it to 0.001 in, still far below gauge steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:15:03 -04:00
ajandClaude Opus 5.5 2d8d877482 feat(server): keep a nest's units on its record
A record's thickness is in the nest's units, but nothing on the record said
which, so a list of nests could not tell 9.525 mm from 9.525 inches without
reading each archive. Records now carry units, read from the uploaded
archive like the counts (absent or unknown values are inches) and kept by a
metadata-only save. Schema v12 adds the column and reads every stored
archive, current and historical, so existing millimeter nests report it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:40:43 -04:00
ajandClaude Opus 5.5 610b9dafcb fix(autonest): mark ledger substitutes against the best rank matched
An acceptance rule may list sheet grades first, so at plate thickness its
best match can be rank 3 (A36 for HR). Auto Nest marked every row above
rank 1 a substitute; it now ranks rows against the best rule rank ML
matched at the nest's thickness, as ML labels them, so A36 is preferred
and only A572 is a substitute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:20:11 -04:00
ajandClaude Opus 5.5 079dfaf9ea fix(desktop): count only active MaterialLedger holds and link the nest page
MaterialLedger's Release lets a hold go, so a Released hold no longer keeps
a sheet for the nest; only Proposed, Approved and AwaitingCloseout holds,
the statuses ML counts against free stock, are open. The Has Been Cut
note's link pointed at /Nests/{id}, which ML does not serve; its nest page
is /Nests/Details/{id}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:20:11 -04:00
ajandClaude Opus 5.5 548522775f fix(server): report a nest's units with its sheet sizes
GET /api/nests/{id}/sheets returns sizes as stored, in the nest's units,
but did not say which. MaterialLedger compares them with inches, so a
millimeter nest's sheets never matched inventory. The response now carries
units (inches or millimeters), read the way NestReader reads them: absent
or unknown values are inches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:41:29 -04:00
ajandClaude Opus 5.5 be01124a0f feat(autonest): rank MaterialLedger stock and mark substitutes
MaterialLedger's /api/stock/opennest now resolves an OpenNest material
and grade through ranked acceptance rules. The client reads the new
spec rank, family, grade and finish and the item rank; an older ledger
that sends none of them deserializes as rank 1 with no label.

LedgerStockMapper groups drops only by identical size and spec, keeps
the ledger's order in rows and in each row's ItemCodes, and orders rows
by rank. Auto Nest's Source column shows the row's grade and finish
("A1011 P&O"); substitutes (rank above 1) read "A1011 P&O for HR" in
amber, name the substitution in their tooltip and still load ticked.
The engine still sees no rank.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:45 -04:00
ajandClaude Opus 5.5 f847b216f2 feat(desktop): show a server nest's MaterialLedger holds and closeout note
Server nests in Database mode get a read-only status bar label comparing their
open MaterialLedger holds with the sheets they cut, e.g. "Ledger: 2 of 4 held,
2 short". Nothing writes to the ledger and nothing waits on it.

- MaterialLedgerClient.GetHoldsAsync reads GET /api/stock/holds?openNestNestId=
  with the closed LedgerHoldsResult set; unknown status strings are kept. The
  request plumbing is shared with GetStockAsync.
- LedgerHoldSummary (OpenNest.Data) counts Proposed/Approved/Released/
  AwaitingCloseout holds against the plate quantities, skipping the trailing
  empty plate as /sheets does.
- NestHoldStatus refreshes on server open, after save, after storage or
  Options changes and on label click; it never polls. The tooltip lists holds
  in nest units. File-mode, unsaved and ledger-less nests show nothing.
- Setting Has Been Cut in Nest Info with holds still open shows a non-modal
  note linking to {ledgerUrl}/Nests/{id}. Posting is not gated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:45 -04:00
ajandClaude Opus 5.5 23f093fcc1 feat(nest): plates report the remnant they are expected to leave
Core PlateRemnant.Expected picks a full-span cut-off drop clear of the
parts (less kerf, nominal position for head-locked overcuts), else the
largest salvage edge offcut, else none below the nest's new persisted
MinimumRemnantDimension. NestJobCost's salvage offcut now calls the Core
rule unchanged. NestWriter writes a derived plate expectedRemnant that
readers skip, and /sheets reports it per plate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:44 -04:00
ajandClaude Opus 5.5 15750949dd feat(nest): plates name the ledger item each sheet is cut from
Auto Nest on MaterialLedger stock now records which inventory item each
physical sheet uses, so ML can hold exactly those items instead of matching
by size.

- Core: Plate.StockItemCodes (entry i = sheet i of Quantity; empty = uncoded).
  Core invalidates it: a resize clears it (turning the sheet keeps it), a lower
  Quantity truncates it, a nest material/grade/thickness change clears every
  plate's, ClearAll clears it and new plates never copy it.
- Desktop Auto Nest: after a kept proposal is committed, LedgerStockAssignment
  deals each ledger row's codes to its plates in Nest.Plates order (bundles
  repeat their code; clamp-removal variants use their base row). Rejected,
  discarded or cancelled proposals never reach it. NestPipelineCommit.PairWithStock
  pairs committed plates with their solved stock; the engine stays ledger-free.
- Plate list tooltip: "Stock: CODE (location)"; locations are display-only.
- nest.json: optional plate stockItemCodes, omitted when empty, read leniently.
- /api/nests/{id}/sheets: new plates[] { index, width, length, quantity,
  stockItemCodes }; sheets[] unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:44 -04:00
ajandClaude Opus 5.5 ea1decc097 feat(autonest): load sheet and drop stock from MaterialLedger
Auto Nest's Stock Options gets a Load from ledger button that reads the nest
material's free sheets and drops from MaterialLedger's API-key-protected
GET /api/stock/opennest. Ledger rows are request-local: the engine sees them as
ordinary NestPlateStock rows with ledger:{n} ids and finite quantities, and they
are never written to Nest.PlateOptions.

- OpenNest.Data/MaterialLedger: settings file, read-only client (X-Api-Key,
  5 s timeout, closed LedgerStockResult set) and LedgerStockMapper (bundles and
  sheets one row each, same-size drops grouped with ordered item codes, unknown
  shapes and too-small items skipped, inch/mm conversion).
- Options: MaterialLedger URL and API key (DPAPI, CurrentUser) with Test.
- Auto Nest grid: Use, Qty and Source columns for every row; PlateOption gains
  request-local Quantity and Id, which NestStockBuilder passes to the engine.
  Ledger rows score by area; hand-typed costs stay saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:44 -04:00
ajandClaude Opus 5.5 d4269c1f06 refactor(cutting): merge circle lead settings into internal leads
Full circles now take InternalLeadIn/InternalLeadOut like every other
cutout; the ArcCircle lead settings and the cutting panel's Arc / Circle
tab are removed. Circle-specific geometry handling (radius clamp, angle
rounding, pierce zone) is unchanged.

Older nest files and desktop settings still load: their arcCircleLeadIn/
arcCircleLeadOut keys are ignored and no longer written, so re-planned
circles take the internal leads. Saved part programs are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:20:09 -04:00
ajandClaude Opus 5.5 87672543cd feat(desktop): import a BOM into a drawing set
Drawing Sets > More > Import BOM reads a BOM and its drawing folder,
matches each drawing to the library by its D4 identity and geometry
(reuse, new revision, or new drawing) and opens the set editor with a
new set prefilled with the BOM quantities. Reading only queries the
library; Create Set lists the uploads and revisions and asks before
storing them, and the set is stored only by the editor's Save.

Parts with no material or thickness still import and are flagged for
Edit Drawing. DrawingPackage.GeometryKey compares geometry independent
of the fresh entity ids each DXF import assigns. In Database mode File >
Import BOM opens this flow instead of creating nests directly; File mode
is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:33:13 -04:00
ajandClaude Opus 5.5 b376d217dd feat(desktop): one Drawing Sets window with Sets and Releases tabs
The drawing sets window now has two tabs. Releases hosts the release list
(paging, nests, Undo Release, New Release) as a SetReleasesView control that
replaces SetReleasesForm. File > Releases stays as a shortcut that opens the
window on that tab. A set's Releases (Sets tab) and a library drawing's
Releases (Drawing Library) open the tab filtered to it, with the filter shown
above the list and Clear Filter to list every release. Releases re-reads its
page whenever the tab is shown again, so a release made on Sets appears.

The set editor gets History... for a saved set; the Sets tab keeps its
History button until the browser's buttons are rearranged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:33:13 -04:00
ajandClaude Opus 5.5 ee208b2766 feat(desktop): say "set" and "quantity" in the set and release dialogs
Wording only, no behavior change: "kit" becomes "set" or "Quantity",
the release button reads Make Nests... (Try Again after a failed
record), the releases list shows Material and Action, and the release
dialog's status lines are shorter. Help in docs/nest-storage.md follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:33:12 -04:00
ajandClaude Opus 5.5 0d1dade063 feat(desktop): open Edit Drawing from everywhere and warn before duplicates
Builds on the library's Edit Drawing (details as a new revision):
- warns before saving details that would duplicate another active library
  drawing (D4), naming them; a failed lookup also asks;
- File > Drawing Library, a nest-less mode of the library browser that lists
  every active drawing and edits on double-click;
- the set editor's member grid (double-click, right-click, button);
- the release dialog: Fix Drawings on a group refused for missing material or
  thickness or duplicate names, so those refusals are no longer a dead end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:33:12 -04:00
ajandClaude Opus 5.5 48863d51c8 feat(support): add slat support beds alongside the grid
Slats are strips of a set thickness running the full length or width of
the table, one every pitch. A part's contacts are the corners of where its
material crosses a slat, so the support check and support tabs work as on
the grid. Nest > Support Bed... picks grid or slats; nest files store
type "slats" and support-grid.json keeps both layouts' values. Draw
Support shows the slats cut off to the sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:33:12 -04:00
aj 414479fc4e feat(library): edit a library drawing's details as a new revision
Edit Settings becomes Edit Drawing: name, customer, material, grade,
thickness and cut type are editable beside the nesting settings and
notes. Units stay read-only. The save is a metadata revision with the
same package, so nests keep the revision they hold, and the release
refusal for drawings without material or thickness now has a fix.
2026-10-24 10:02:23 -04:00
ajandClaude Opus 5.5 6b63d41030 feat(cutting): start plates from the assigned machine's leads and cut-off settings
Machine configurations already held a lead-in, lead-out, kerf and cut-off per
material thickness, but nothing read them: Plan Cutting and Place Lead-in
started from whatever was last applied (often another machine's or another
thickness's leads), and cut-offs used fixed view defaults. Now that nests carry
a machine, those values can be the starting point.

- MachineCuttingDefaults maps a machine's thickness settings for the nest's
  material/grade/thickness onto CuttingParameters and CutOffSettings,
  converting units. Clean Hole stays on holes; Line + Line is not mapped
  because the configuration lacks the second segment.
- Plates without their own parameters start from the last-applied ones with
  the machine's leads and kerf laid over them; plates with parameters keep them.
- The plate view's cut-off clearance, overtravel, minimum segment and direction
  follow the machine on open, on Nest > Machine and after Nest > Edit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:27:58 -04:00
ajandClaude Opus 5.5 9c78b4ebec feat(machines): assign each nest a machine and keep the support grid per machine
The support grid was one shared support-grid.json, off by default, and a nest
had no machine at all, so a plasma table and a fiber laser could not have
different grids, and nothing could pick machine-specific behavior later
(lead-ins differ between laser and plasma; tabbing depends on the grid).

- Nest.Machine (id + name) persists as top-level `machine` in nest.json;
  malformed values load as none.
- MachineConfig.SupportGrid holds each machine's grid; a machine without one
  still falls back to the legacy support-grid.json.
- New and BOM import assign the Tools > Nest Defaults machine (first by name
  when none is chosen) and give the nest that machine's grid.
- Nest > Machine reassigns a nest and swaps in the new machine's grid;
  Nest > Support Grid saves machine defaults to the nest's machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:17:28 -04:00
ajandClaude Opus 5.5 7ce5e73a82 refactor: share duplicated paging, HTTP and helper code
Roslyn duplicate scans found the same code copied across layers:
- Data: the three browse sessions share BrowseSession<TPage, TSort>;
  the remote clients share NestServerHttp (JSON options, If-Match,
  error text).
- WinForms: five paged lists share BrowsePager for loading text,
  navigation, sort glyphs and failure messages. The Add Drawings to
  Release, Drawing Library and set browser dialogs now dispose their
  browse sessions.
- Server: NestQueryRequest reuses DrawingLibraryRequest's helpers;
  revision history queries share count and paging binds.
- Engine: RemnantPlateNester delegates to DefaultPlateNester;
  CuttingPlanService reuses JointCuttingPlanSearch.Map.
- Core: LineArc and CleanHole lead-ins share LineArcApproach with
  unchanged arithmetic order, so output is bit-identical.
- IO: Dxf.GetGeometry overloads share one reader; .nest JSON test
  helpers move to NestArchiveJson.

Also fixes the production compiler warnings (CS8632, CS8603, CS0108)
and moves a test off an obsolete forwarding API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:45:36 -04:00
ajandClaude Opus 5.5 39f28c7fa9 feat(server): report a nest's sheet sizes and part counts
GET /api/nests/{id}/sheets reads the stored archive's nest.json and returns
the sheet sizes it needs (grouped, width the shorter side, quantities summed)
and each drawing's required and nested counts, skipping the trailing empty
plate as plateCount does. MaterialLedger builds material holds from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 06:01:43 -04:00
ajandClaude Fable 5.1 dc4fc1d593 feat(desktop): list every release and start new ones from File > Releases
Releases could only be reached through one set or one library drawing, so
seeing what the shop released lately meant knowing which set to open first.
File > Releases shows every release on the server, newest first, naming what
each one had ("Kit A ×2, Kit B; Bracket ×4") in place of the per-set
revision and kits, with the same nest list and Undo Release. New Release
opens the release dialog empty, so a release can start from the list rather
than from a set; the plan builder now accepts no lines as an empty plan,
and the dialog's Release button stays disabled until a group is listed.
The list re-reads from the newest page once the dialog records a release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 13:27:20 -04:00
ajandClaude Opus 5.5 d4903a648c feat(desktop): pick sheet gauges and fractions from thickness dropdowns
Operators had to know that 16 ga is 0.0598 to enter a thickness. The Nest
Info and Save to Drawing Library thickness boxes now list steel gauges
(26-3 ga) and plate fractions (1/16"-2") with their decimals, and read a
typed gauge ("16ga", "#16"), any fraction ("3/32", "1-1/4") or a decimal.
Unreadable text blocks OK/Save instead of reading as zero.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:04:55 -04:00
ajandClaude Opus 5.5 54b15cff19 feat(releases): release library drawings on their own beside sets
Operators want to release a few loose drawings without first building a
set for them. The release record gains a `drawings` list (drawingId,
quantity, joined drawingName) and nest items with no `setId` carry those
drawings' quantities, so grouping, merging and undo stay one path.

Schema v11 adds release_drawings and rebuilds release_nest_items with a
nullable setId (SQLite cannot drop NOT NULL), keeping every row. The
query gains `drawingId=`; a server from before this refuses that unknown
parameter with 400, which the client turns into ServerTooOldException.
SetReleaseSession asks before building anything, because the release is
posted only after its nests are saved and an old server would refuse it
then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:54:30 -04:00
ajandClaude Opus 5.5 5ecdc98581 feat(server): add a company material list with list/add endpoints
Nests, drawings and releases store material and grade as free text, so
there was no canonical list for clients to suggest from or to fix
spelling against. Schema v10 adds materials and material_grades keyed by
SharedListNames.Key, with GET/POST /api/materials. Adding is idempotent
by key and keeps the first spelling.

The tables start empty: the materials already stored did not import
cleanly, so the list is filled through the API instead. Existing
free-text columns are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:32:41 -04:00
ajandClaude Opus 5.5 3ca7d6185f feat(desktop): nest released drawing sets with Auto Nest before saving
A release now ends in nests ready to cut rather than empty drafts, and a
merge no longer leaves the draft it cleared waiting for someone to run Auto
Nest. "Nest after release" (on by default) runs Auto Nest headless on every
released group, new or merged, before anything is uploaded.

NestRenest removes every plate and nests each drawing's required quantity
onto sheets from the nest's plate defaults and plate-size options. It keeps
a layout only when it is valid and keepable, so an invalid result never
reaches the server; the nest is saved without sheets and the operator is
told to run Auto Nest. Unplaced copies stay as remaining quantity, and the
status column lists sheets, utilization and what did not fit. A new Nesting
progress stage reports each group, and cancelling while nesting saves
nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:25:55 -04:00