# Free4AllHub CSV table workbench

A small browser-local text-table editor: choose a comma-separated UTF-8 CSV,
review it, explicitly open it, edit cells and headings, sort/filter a view, and
download protected CSV plus native editable JSON. It is not a full spreadsheet.
The original wrapper and fictional club-supplies example are MIT. PapaParse
5.5.3 is vendored unchanged, with full readable source, MIT notice, exact version,
registry integrity, hashes and audit notes in `vendor/` and `CREDITS.md`.

## Run and rebuild the downloadable source

1. Extract `csv-workbench-source.zip` and keep its directory together.
2. Optionally rebuild its standalone page with `python3 build-standalone.py`.
3. Serve the directory using an ordinary static server. With an existing Python:
   `python3 -m http.server 8000 --bind 127.0.0.1`.
4. Open `http://127.0.0.1:8000/` in a current JavaScript-enabled browser.

No package installation or build toolchain is needed. Direct `file://` launch
is not promised because browsers restrict ES modules. The standalone source has
a compact original wrapper, not the older Hub shell or shared stylesheet. It
rebuilds byte-identically from its included fragment and standalone builder.
The source ZIP contains the actual current app, parser/rights, fictional CSV,
core/UI-handler tests and build instructions, not a mock or static screenshot.

In the complete Hub checkout, run:

    python3 scripts/build-hub.py
    python3 scripts/build-csv.py
    node --test csv-workbench/tests/*.test.mjs
    node --test --test-concurrency=1 test/*.test.mjs tests/*.test.mjs

The CSV builder copies source into `dist/tools/csv/`, extracts the current shared
Hub header/footer, and emits a deterministic ZIP (sorted files, fixed timestamp,
fixed modes, no build/network/install). Only Home/Create/search gain a tool card;
other native tools and ordinary CSV parsing/export are unchanged. Shared Keep & reopen also accepts native CSV table JSON as described below.
Source tests use Node's built-in runner and a small explicit DOM test harness.
The harness exercises the real event handlers and real Blob download bytes, not
layout, browser permission behavior or an emulated visual browser.

## Try, save, reopen, revise

Choose **Try fictional club supplies**, inspect the incoming headers and first
three data rows, then explicitly open it. Label codes retain leading zeros.
The quantity `0` is not the blank quantity on the name-labels row. Change a cell,
find `paper`, or sort a column as text. Downloads still contain every stored row,
including filtered-out rows, in original stored order. Sort/filter/page settings
are deliberately view-only and are not saved in JSON.

Download editable JSON and protected CSV. A requested download does not prove
it was saved. Check the actual files. Open a fresh tab, choose JSON, review and
explicitly open it, make another edit, and download another JSON. Keep an earlier
file until the next one has been reopened and checked. This JSON remains native to
this CSV tool. Shared **Keep & reopen** can preserve it unchanged in a bundle:
choose **CSV table editable JSON**, add the already-downloaded JSON and save the
bundle. Reopen the actual bundle, explicitly extract its JSON, then choose it
here, review and explicitly open it to replace current work. The native preview
shows headers and only the first three rows; inspect all rows before sharing.
The bundle retains every stored row, including private filtered-out rows, exact
text and formula-like strings. Ordinary CSV is not accepted or converted by the
bundle. The bundle also rejects an initial BOM even though this native importer
accepts one. Links transfer no state; extraction is not native restoration.

Add row resets the view and opens the last page; add column appends a blank
heading and cell in every row. Remove row/column affects the whole dataset,
including hidden rows. The last removal can be undone until the next data edit,
removal or table replacement. Sort, filter, paging and downloads do not clear
that undo. There is no general edit history. A table always has at least one
column. Invalid edits remain visible and pause downloads/additions; correct them
before proceeding. Replacing invalid work still requires explicit review.

All edits stay in tab memory. Refreshing, closing or replacing loses current
work; leave-page warnings are best-effort. No browser storage, cookies, accounts,
autosave, upload, remote import, telemetry, background requests, sync or recovery.
The website host serves code normally. Ordinary external links navigate only
when chosen. Native JSON and CSV contain every row and are unencrypted, so check
before sharing and avoid sensitive data on shared computers.

## Bounds and CSV dialect

- Input CSV and native JSON: 1 MiB each; fatal UTF-8 decoding, no replacement of
  malformed bytes. Byte cap is checked both before and after reading.
- 1–30 columns, 0–1,000 data rows, 20 data rows rendered per editor page.
- Each heading/cell: 2,000 UTF-16 code units, including embedded newlines.
- All title/header/cell text combined: 200,000 UTF-16 units.
- Title: 1–120 units, nonblank and without C0/DEL controls or line breaks.
- Dense rectangular positional arrays only. No typed values, holes, extra
  properties, arbitrary cell objects or unpaired Unicode surrogates.
- One initial UTF-8 BOM is an encoding marker and is removed. Repeated leading
  BOM characters in CSV are rejected rather than silently losing header text.
  BOM characters inside quoted headers/other fields are text and preserved.
- CSV uses commas, double-quoted fields, doubled embedded quotes and consistent
  CRLF, LF or CR outer record separators. Separators inside quoted fields may
  differ and are preserved. Semicolon/tab dialects are not detected: their
  characters are ordinary cell text, not separators.
- The first record is the header. Blank/duplicate headings are preserved and
  identified by column number. Ragged records, bare quotes in unquoted fields,
  unclosed quotes, characters after closing quotes and mixed outer record
  separators reject before replacement. No automatic padding or dropping.
- One final record separator terminates the last record and is not an extra
  blank row. Additional blank records are retained if rectangular. An empty
  file rejects. A single empty heading can be represented as `""`.
- Every cell stays text. Leading zeros, long numeric-looking strings, dates,
  whitespace, empty strings, literal HTML and formula-like text are not inferred
  or executed. Render uses only DOM text/value APIs, never HTML injection.
- Textarea controls normalize an **edited** field's CR/CRLF line endings to LF.
  Untouched original strings remain exact in the model and native JSON, even
  after other cells are changed. No CSV byte-for-byte roundtrip is claimed.

The text/shape limits and a final native byte check ensure every accepted project
fits its own JSON reopen cap. An oversized/invalid import never replaces work.
Input is fully validated before its review renders. Pending review locks data
editing but allows downloading the prior valid table; choosing another file or
canceling invalidates earlier asynchronous selections. Stale reads cannot stage
or commit. Import is replacement, not merging. No filesystem write is automatic.

## Native JSON version 1

Exact top-level keys: `schema`, `version`, `title`, `headers`, `rows`.

    {"schema":"free4allhub-csv-table","version":1,"title":"Supplies",
     "headers":["Item","Code","Count"],
     "rows":[["Paper","0012","0"],["Labels","0003",""]]}

Unknown keys (including `__proto__`, `constructor` or `prototype` properties),
wrong schema, unknown/future version, nontext fields, oversize or nonrectangular
arrays reject. Those strings are allowed as literal header/cell values because
arrays are positional; they are never used as object property names. JSON's
standard parser accepts duplicate occurrences of an allowed property name using
the last value, then the resulting object must meet the exact schema. Do not
use duplicate keys to exchange data between applications.

The serialized file stores no view state, filename, audit trail, undo history,
network address, formula engine or external reference. It preserves literal
text and stored row order. A newline ends the compact UTF-8 JSON output.

## Protected CSV export

All fields are quoted; internal quotes are doubled. Export includes headings and
all stored rows, comma-separated with CRLF record separators and a final CRLF,
without a BOM. Embedded cell line endings remain their current model values.

Values whose first non-whitespace/control/format character is `=`, `+`, `-` or
`@` get an apostrophe **before the entire value**. Unicode White_Space, Cc and Cf
characters and U+FEFF are considered when checking that prefix. This applies to
headers too. Numeric-looking negatives remain text and receive protection.
The UI reports the count of changed fields. Native JSON preserves the original.

Protection intentionally changes CSV text. Reimporting protected CSV retains
the apostrophe; it is never silently removed. Other spreadsheet programs may
show it literally, infer types or apply their own import rules. This is not a
universal formula-injection security guarantee, spreadsheet-format export,
Excel/Calc interoperability test or arbitrary CSV byte-roundtrip promise.
Review imported results before relying on them.

## Scope and verification limits

Useful overlap with spreadsheet applications is small-table editing and CSV
exchange. No formulas, charts, XLSX, multiple sheets, macros, collaboration,
financial advice, account connection or full spreadsheet parity. LibreOffice
Calc offers broader free desktop workflows; Microsoft offers free Excel web as
well as paid products. This is a task choice, not a subscription-savings claim.
Official references are in `CREDITS.md`; CSV output has not been tested in those
applications.

Automated tests cover the actual core, UI handlers in an explicit small DOM
harness, serialized files, parser pinning, deterministic standalone/source ZIP
and isolated Hub discovery. No desktop-browser interaction, layout/screenshot,
real-phone, real-beginner or screen-reader testing has been completed for this
route. Browser infrastructure was unavailable; no replacement browser/runtime
was installed. No security or accessibility certification is claimed.
