@aiquants/spreadsheet 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,37 @@
2
2
 
3
3
  All notable changes to `@aiquants/spreadsheet` are documented here.
4
4
 
5
- ## 0.16.0 (unreleased)
5
+ ## 0.16.1 (2026-10-27)
6
+
7
+ ### Added
8
+
9
+ - **`iterateTsvPrefixRows(text)`** (`/tsv` and the barrel) reads a text that may be only the beginning of a TSV — a file
10
+ read partway, a stream not yet ended — and yields, unpadded, only the rows the rest cannot change: every row the text
11
+ ends with a line terminator, up to the first field that opens a quote the text does not close. Whether such a quote is
12
+ one quoted field or a literal split at its line breaks is the rest's to decide, so the rows of any beginning are the
13
+ first rows of the whole (`iterateTsvRows` reads the same quote of a whole text as a literal, which a reader of a cut
14
+ text would otherwise show as many rows). The one grammar walk serves both readings.
15
+
16
+ ### Changed
17
+
18
+ - A quoted field's content is read a run between quotes at a time instead of a character at a time: the same fields,
19
+ without a string grown once per character (a quoted cell of 15 MB with 3 million line breaks reads in about 1 ms
20
+ where it took over a second).
21
+
22
+ ### Documentation
23
+
24
+ - **The freeze divider's default reads `#858e9d` everywhere in the README.** Two places still named `#9ca3af`, the default
25
+ 0.16.0 replaced to reach 3:1 against the surface and the header band, and the non-text contrast paragraph listed the
26
+ values the defaults replaced; it now lists the current ones only.
27
+
28
+ ### Verification
29
+
30
+ - **公開面**: 公開面は `index.ts` の値 44 + 型 98 = 142 export (`iterateTsvPrefixRows` を追加)。
31
+ - **検証 — 走行台帳 (census の錨は本未リリース節)**: 単体 / 統合 **97 ファイル 4,208 テスト** 全緑 —
32
+ 0.16.0 出荷時の 97 ファイル 4,202 に v0.16.1 進行分 +6 (`tsv` の `iterateTsvPrefixRows` +5、0.16.0 節の出荷時凍結ピンで
33
+ `hxCensus` +1)。`tsc --noEmit` / biome 0。
34
+
35
+ ## 0.16.0 (2026-10-26)
6
36
 
7
37
  TSV の往復を文字列のどの矩形行列でも厳密にし、拡大率の操作を等比の段に揃え、`@aiquants/spreadsheet/view` にホストが最初の画面を
8
38
  グリッドと同じ数で描くための既定値を加える。
package/README.md CHANGED
@@ -21,7 +21,7 @@ deep-reach proof route.
21
21
  - 🧊 **Frozen columns & rows** (v0.5 / v0.6): `frozenColumns` pins the first N columns as a static left band and `frozenRows` pins the first N rows as a static top band — two-band headers, a Cartesian-product 4-quadrant clipped selection overlay, partition-aware hit-testing and ensure-visible on both axes, all riding the engine's anchor-free frozen bands
22
22
  - 📌 **Trailing freeze & strong freeze dividers** (v0.9): `frozenTrailingColumns` / `frozenTrailingRows` pin the **last** N columns / rows (a fixed totals row is the canonical use) as static right / bottom bands over the **same data** (count-based, Glide-style — no extra-row API), with three-band headers,
23
23
  a {main, frozen, scroll, trailing}² clipped selection overlay, three-way partitioned hit-testing / ensure-visible, near-edge resize strips with pinned-right semantics, and band-side unhide markers; **every** freeze boundary (leading and trailing, both axes) draws a strong Excel / Sheets-style
24
- divider over the scroll band's edge pixels — 2px `#9ca3af` by default, consumer-tunable through the CSS variables `--aqsp-divider-color` / `--aqsp-divider-width` (no prop), `pointer-events: none` — see "Trailing freeze and the freeze divider"
24
+ divider over the scroll band's edge pixels — 2px `#858e9d` by default (3:1 against the surface and the header band), consumer-tunable through the CSS variables `--aqsp-divider-color` / `--aqsp-divider-width` (no prop), `pointer-events: none` — see "Trailing freeze and the freeze divider"
25
25
  - 🔒 **Read-only cells as a capability** (v0.10 — ADR-27..31): one optional adapter predicate `isCellReadOnly(row, col)` locks cells — no edit session can open on them (all six entries, the touch on-screen-keyboard invitation included), an open session's commit is live re-checked,
26
26
  and every write whose target set touches a read-only cell (Delete / paste / cut both walls / fill with diagonal L-legs decomposed / format / self-calc / annotations) refuses the **whole operation** before the adapter is reached (Excel-style, no per-cell skip); painted cells carry
27
27
  `aria-readonly="true"` + `data-aqsp-readonly` (style it yourself — no package CSS), refusals speak through the single sealed `onReadOnlyRejected` (+ opt-in `formatReadOnlyAnnouncement`), and copying or viewing notes / comments / links stays legal (content-aware chords,
@@ -417,7 +417,7 @@ two CSS custom properties — the only consumer surface (there is no prop):
417
417
 
418
418
  | Variable | Default | Meaning |
419
419
  | :--- | :--- | :--- |
420
- | `--aqsp-divider-color` | `#9ca3af` (gray-400 — one step darker than every gridline) | Divider colour |
420
+ | `--aqsp-divider-color` | `#858e9d` (3:1 against the surface and the header band: the divider is a mark, not decoration) | Divider colour |
421
421
  | `--aqsp-divider-width` | `2px` (Sheets strength) | Divider thickness |
422
422
 
423
423
  ```css
@@ -604,11 +604,10 @@ name itself.
604
604
 
605
605
  **Non-text contrast (WCAG 1.4.11, 3:1).** The marks that carry meaning reach 3:1 against the default surface and the
606
606
  header-row surface: accent 5.17 / 4.7, the selection outline 3.97 (over the surface),
607
- the note marker 7.56 / 6.87, the comment marker `#eb6406` 3.3 / 3.0 (was `#f97316`, 2.80),
608
- danger 4.83 / 4.39, the reference and fill-preview green `#16a24a` 3.33 / 3.03 (was `#16a34a`, 3.30 / 2.99),
609
- the freeze divider `#858e9d` 3.31 / 3.0 (was `#9ca3af`, 2.54), the unhide markers at full opacity 4.7 on the
610
- header band (they were 70 % opaque, 2.86), and the popover input border `#8b95a4` 3.03 (new; inputs used the decorative
611
- `#d1d5db`, 1.47). Grid lines, band edges, popover frames and the note tip's frame are decorative (the tip is identified by its
607
+ the note marker 7.56 / 6.87, the comment marker `#eb6406` 3.3 / 3.0,
608
+ danger 4.83 / 4.39, the reference and fill-preview green `#16a24a` 3.33 / 3.03,
609
+ the freeze divider `#858e9d` 3.31 / 3.0, the unhide markers at full opacity 4.7 on the header band, and the popover
610
+ input border `#8b95a4` 3.03. Grid lines, band edges, popover frames and the note tip's frame are decorative (the tip is identified by its
612
611
  surface and shadow).
613
612
 
614
613
  **Contrast (WCAG 1.4.6, 7:1).** Every text / background pair of the light defaults is at least 7:1, measured with the WCAG 2
@@ -1017,11 +1016,12 @@ const zoomIn = () => setZoom((zoom) => stepSpreadsheetZoom(zoom, 1))
1017
1016
 
1018
1017
  ## TSV codec (`@aiquants/spreadsheet/tsv`)
1019
1018
 
1020
- `@aiquants/spreadsheet/tsv` publishes `parseTsv`, `iterateTsvRows`, `serializeTsv`, `serializeClipboardTsv` and
1021
- `measureTsv` — the codec the grid copies and pastes with — without loading the grid (no React, no `@aiquants/virtualscroll`; about 1 KB). A host that lazily imports the grid can
1019
+ `@aiquants/spreadsheet/tsv` publishes `parseTsv`, `iterateTsvRows`, `iterateTsvPrefixRows`, `serializeTsv`,
1020
+ `serializeClipboardTsv` and `measureTsv` — the codec the grid copies and pastes with — without loading the grid (no React, no `@aiquants/virtualscroll`; about 1 KB). A host that lazily imports the grid can
1022
1021
  still read the grid's text during the first render, for example to server-render a placeholder table of the rows the grid
1023
- will show (the same markup on the client, so hydration matches). The barrel exports the same five functions (one
1024
- definition), so code that already loads the grid keeps importing them from `@aiquants/spreadsheet`.
1022
+ will show (the same markup on the client, so hydration matches), or preview a TSV file it reads as a stream. The barrel
1023
+ exports the same six functions (one definition), so code that already loads the grid keeps importing them from
1024
+ `@aiquants/spreadsheet`.
1025
1025
 
1026
1026
  The round trip is exact: `parseTsv(serializeTsv(m))` deep-equals `m` for every rectangular matrix of strings of at least
1027
1027
  1 × 1, whatever the cells hold (tabs, CR, LF, CRLF, quotes, a cell that starts with `"`). Store tables with `serializeTsv`.
@@ -1039,6 +1039,7 @@ other sources.
1039
1039
  | `parseTsv(text)` | A rectangular `string[][]`: one trailing line terminator dropped, empty text read as one empty cell (`[[""]]`), ragged rows padded with `""`; a field that starts with `"` is read as quoted only when the quote closes properly (the closing quote is followed by a tab, a line break or the end) and the unescaped content holds a tab, CR, LF or `"` — anything else is literal text, so Excel's raw output reads as Excel wrote it (`"hi"` stays `"hi"`). The one raw shape that reads unquoted is a value that looks exactly like a quoted field holding a doubled quote (`"a""b"` reads `a"b`) |
1040
1040
  | `serializeTsv(matrix)` | Rows joined with CRLF and a trailing CRLF; a field is quoted (its quotes doubled) when it holds a tab, CR or LF — the cells Excel quotes — or starts with `"`, the one other cell a reader could take for a quoted field; every other field, inner quotes included, is written verbatim |
1041
1041
  | `iterateTsvRows(text)` | A `Generator<string[]>` that yields the rows one at a time in exactly `parseTsv`'s grammar (same quoting rule, line terminators and final-terminator handling), **unpadded**; a consumer that stops early materialises nothing past the last row it took (preview a large file under a cell budget). `parseTsv` is these rows, right-padded |
1042
+ | `iterateTsvPrefixRows(text)` | The rows of a text that may be only the **beginning** of a TSV (a file read partway, a stream not yet ended), in the same grammar, unpadded, but only those the rest of the text cannot change: every row the text ends with a line terminator, up to the first field that opens a quote the text does not close (whether it is one quoted field or a literal one split at its line breaks is the rest's to decide; `iterateTsvRows` reads such a quote of a whole text as a literal). So the rows of any beginning of a TSV are the first rows of the whole, and a stream's reader can show them as final; empty text yields none |
1042
1043
  | `serializeClipboardTsv(matrix)` | The clipboard's `text/plain` (Excel's form): rows joined with CRLF and a trailing CRLF; a field is quoted (its quotes doubled) only when it holds a tab, CR or LF, so `"Important" note` is written as typed. Not an exact round trip for cells that start with `"`; the grid's own copy uses it, and a host's copy control should too |
1043
1044
  | `measureTsv(text)` | `{ rows, cols }` — the dimensions `parseTsv(text)` would build, from the same grammar walk, without building any row array (the paste limit check runs on it before anything is materialised) |
1044
1045
 
@@ -1057,7 +1058,7 @@ importers with the exact same Excel-compatible semantics the grid uses. `enumera
1057
1058
  `onOperationRejected` op), and `EditRejection` (the `onEditRejected` context — see "Editing, Undo, and rejection") are exported for
1058
1059
  consumer-side annotations.
1059
1060
 
1060
- v0.8 adds the rich-cell engines as public pure functions (all React-free; `src/index.ts` exports 43 values and 98 types in total — every named type an exported declaration reaches is exported too, so nothing has to be extracted with `Awaited<ReturnType<>>` / `Extract<>`; `src/index.spec.ts` pins it):
1061
+ v0.8 adds the rich-cell engines as public pure functions (all React-free; `src/index.ts` exports 44 values and 98 types in total — every named type an exported declaration reaches is exported too, so nothing has to be extracted with `Awaited<ReturnType<>>` / `Extract<>`; `src/index.spec.ts` pins it):
1061
1062
 
1062
1063
  - **Clip payload** — `serializeClipPayload(payload)` / `parseClipPayload(json)` (the lossless wire format `{ v: 1, rows, cols, clipId, formats, validations, cells, colWidths?, rowHeights?, wholeRows, wholeCols, conditionalFormats }`; the parser is total and returns `null` on ANY malformed input, the serializer validates the same way and throws a `RangeError`),
1063
1064
  `detectClipSource(flavours, channel)` (`"aqsp" | "sheets" | "excel" | "html" | "tsv" | "none"` — the ADR-14 detection over `{ types, getData }`), and the types `ClipPayload` / `ClipCell` / `ClipComment` / `ClipSource` / `ClipInfo` / `ClipFlavours` / `ClipChannel` (the `detectClipSource` parameter types).