e5ed1d3c9f
- Add tauri/ subfolder: a Tauri-based desktop shell that loads the same index.html as the Electron app, producing a ~2MB installer (vs ~71MB for Electron) by using the OS WebView2 instead of bundled Chromium. Includes sync-frontend.js to keep tauri/dist/ in sync with the canonical index.html and vendor/ at build time (no UI duplication/drift). - Vendor lucide, jsPDF, and jspdf-autotable locally under vendor/ instead of loading from CDNs, so the app works fully offline in both shells. - Fix server.js path traversal vulnerability (sanitize/contain request paths). - Fix exportPDF() using stale/edited piece data that could produce Infinity/NaN in generated PDFs; add a recalculate guard. - Fix race conditions: overlapping copy/save-defaults button revert timers, and FileReader callbacks for logo/CSV import that could apply out of order. - Minor cleanups: explicit array index parsing, hasResult() helper, consistent event binding for NMFC search results, cached DOM lookups. - Document the dual-shell architecture and vendoring conventions in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
63 lines
6.3 KiB
Markdown
63 lines
6.3 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Two desktop shells — same frontend
|
|
|
|
This repo ships **two interchangeable desktop shells** that both load the exact same `index.html`:
|
|
|
|
1. **Electron** (original, root of repo) — heavier (~70MB installer) but mature/familiar
|
|
2. **Tauri** (`tauri/` subfolder) — much lighter (~2MB installer, ~8MB exe) using the OS's built-in WebView2 instead of bundled Chromium
|
|
|
|
`index.html` (plus `vendor/`) is the **single source of truth** for the UI — never duplicate or fork it. The Tauri shell's `tauri/dist/` is a *generated* copy (gitignored, rebuilt by `tauri/sync-frontend.js` before each `dev`/`build`run) — edit `index.html` at the repo root, never inside `tauri/dist/`.
|
|
|
|
### Electron commands (run from repo root)
|
|
- `npm start` — run the app as an Electron desktop window (loads `index.html` via `main.js`)
|
|
- `npm run serve` — run a plain Node HTTP server (`server.js`) at http://localhost:3010, serving the same `index.html` for browser-based testing
|
|
- `npm run build` — package the Windows installer via `electron-builder` (NSIS, output goes to `dist/`)
|
|
|
|
### Tauri commands (run from `tauri/`)
|
|
- `npm run dev` — sync the frontend into `tauri/dist/` and launch the Tauri dev shell
|
|
- `npm run build` — sync the frontend and produce MSI + NSIS installers (output in `tauri/src-tauri/target/release/bundle/`)
|
|
- `npm run serve` — same static server as the Electron side, for browser-based testing
|
|
- Requires the Rust toolchain (`rustup`/`cargo`) plus MSVC Build Tools and WebView2 (Tauri prerequisites) — see https://tauri.app/start/prerequisites/
|
|
|
|
There is no test suite, linter, or build step for the app code itself — `index.html` is loaded directly (as a file in Electron/Tauri, or statically served). To verify a change, just reload the app/page in whichever shell you're using.
|
|
|
|
## Architecture
|
|
|
|
This is a single-page Electron desktop app (also runnable as a static page) for calculating NMFC freight class from shipment dimensions and weight. Almost all logic lives in one file:
|
|
|
|
- **`index.html`** — contains everything: markup, CSS (`<style>` block), and all application JavaScript (`<script>` block starting around line 370). There are no separate JS/CSS files or build/bundling steps; what you see in `index.html` is what runs.
|
|
- **`main.js`** — minimal Electron bootstrap (creates the `BrowserWindow`, loads `index.html`, with `nodeIntegration: true` / `contextIsolation: false`)
|
|
- **`server.js`** — minimal static file server used only for `npm run serve` (browser-based dev/testing instead of Electron)
|
|
- **`vendor/`** — local copies of third-party JS libraries (`lucide.js`, `jspdf.umd.min.js`, `jspdf.plugin.autotable.min.js`), referenced by `index.html` via relative `<script src="vendor/...">` tags so the app runs **fully offline** with no CDN dependency. Not gitignored — these files are committed and get bundled into the installer by `electron-builder` automatically. **Do not switch these back to CDN URLs** — if a library needs upgrading, download the new version into `vendor/` and update the filename/reference together.
|
|
|
|
### Structure inside `index.html`'s script
|
|
|
|
The script is organized into clearly marked sections (look for the `// ─── Section ───` banner comments) in this order:
|
|
|
|
1. **Data** — `densityRanges` (density → freight class lookup table with descriptions) and `nmfcData` (searchable list of NMFC codes/descriptions/classes)
|
|
2. **State** — `appState`, persisted to `localStorage` under the key `freightCalcState` via `saveState()`. Defaults are merged with `Object.assign` so new fields can be added without breaking existing saved state. Shape: `{ theme, branding: {name, color, logo}, history[], units: {dim, wt}, defaults: {L, W, H, Weight} }`. The working `pieces` array (current shipment line items being entered) is ephemeral and NOT persisted.
|
|
3. **Init** — `init()`, wires up the page on load, applies branding, populates settings/defaults forms, renders the density reference table
|
|
4. **Theme** — light/dark toggle
|
|
5. **Branding** — company name/color/logo customization, applied to the page header and PDF export
|
|
6. **Defaults tab** — user-configurable default piece dimensions/weight pre-filled on new entries
|
|
7. **Modal** — settings modal open/close/revert logic (`pendingBranding` holds unsaved edits so Cancel can discard them)
|
|
8. **Tabs** — settings modal tab switching (Branding / Defaults / History)
|
|
9. **History** — calculation history stored in `appState.history` (capped at 50 entries), CSV export/import, restore-from-history (re-applies the exact units the entry was saved in)
|
|
10. **Unit toggles** — switching between in/cm and lbs/kg; conversions are applied at calculation time via `dimFactor`/`wtFactor`, not by mutating stored values
|
|
11. **Pieces** — managing the dynamic list of shipment pieces (`renderPieces`, `addPiece`)
|
|
12. **Validation & Clear** — input validation/error display, form reset
|
|
13. **Density gauge** — visual gauge showing where the calculated density falls
|
|
14. **Calculate** — `calculateFreightClass()`: the core calculation. Converts all dimensions/weights to inches/lbs, computes total cubic feet and total weight, derives density, looks up the class from `densityRanges` (first range where `density >= min` wins), saves a history entry, and updates the UI/gauge
|
|
15. **Copy to clipboard** — copies the result summary as text
|
|
16. **PDF Export** — `exportPDF()` builds a branded PDF report using `jsPDF` + `jspdf-autotable` (loaded from local `vendor/` copies via `<script>` tags in the `<head>` — see `vendor/` note above), including logo, company branding, pieces table, and footer
|
|
17. **NMFC Search** — live search over `nmfcData` by code or description; selecting a result highlights the matching row in the on-page density reference table
|
|
|
|
### Key conventions
|
|
|
|
- All persisted user data lives in one `appState` object in `localStorage`; always go through `saveState()` after mutating it.
|
|
- Unit conversions (`in`↔`cm`, `lbs`↔`kg`) happen only at calculation/display time — stored values keep their original units alongside a `units` field so they can be restored exactly.
|
|
- External libraries (`lucide` icons, `jsPDF`, `jspdf-autotable`) are vendored locally in `vendor/` and loaded via relative `<script>` tags in the `<head>` — **not** from CDNs — so the app works completely offline.
|