tosijs-ui 1.17.0-beta.5 → 1.17.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +222 -123
  2. package/README.md +1 -1
  3. package/bin/make-icon-data.js +39 -4
  4. package/dist/data-table.d.ts +27 -0
  5. package/dist/data-table.js +163 -13
  6. package/dist/doc-browser.js +26 -8
  7. package/dist/doc-system/doc-title.d.ts +5 -0
  8. package/dist/doc-system/doc-title.js +10 -0
  9. package/dist/doc-system/render.js +19 -0
  10. package/dist/doc-system/site/a11y-audit-cli.js +5 -8
  11. package/dist/doc-system/site/a11y-audit.d.ts +8 -0
  12. package/dist/doc-system/site/a11y-audit.js +50 -13
  13. package/dist/doc-system/site/bounded-child.d.ts +10 -0
  14. package/dist/doc-system/site/bounded-child.js +44 -0
  15. package/dist/doc-system/site/config-fingerprint.d.ts +22 -0
  16. package/dist/doc-system/site/config-fingerprint.js +76 -0
  17. package/dist/doc-system/site/dev-server.d.ts +1 -0
  18. package/dist/doc-system/site/dev-server.js +23 -9
  19. package/dist/doc-system/site/epub-a11y.d.ts +13 -4
  20. package/dist/doc-system/site/epub-a11y.js +67 -21
  21. package/dist/doc-system/site/generate-site.js +1 -8
  22. package/dist/doc-system/site/make-llms-txt.js +3 -1
  23. package/dist/doc-system/site/orchestrator.js +69 -12
  24. package/dist/doc-system/site/page-globals.d.ts +9 -0
  25. package/dist/doc-system/site/page-globals.js +14 -0
  26. package/dist/icon-data.js +11 -11
  27. package/dist/icons.js +38 -1
  28. package/dist/iife.js +106 -106
  29. package/dist/iife.js.map +13 -13
  30. package/dist/live-example/example-hooks.d.ts +7 -1
  31. package/dist/live-example/example-hooks.js +11 -3
  32. package/dist/live-example/execution.d.ts +2 -1
  33. package/dist/live-example/execution.js +8 -2
  34. package/dist/palette.d.ts +4 -2
  35. package/dist/palette.js +26 -12
  36. package/dist/select.js +6 -1
  37. package/dist/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/llms.txt +32 -32
  40. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,15 +1,95 @@
1
1
  # Changelog
2
2
 
3
- ## 1.17.0-beta.5
3
+ ## 1.17.1
4
4
 
5
- > Prerelease, published under the `beta` dist-tag: install with `tosijs-ui@beta`. `latest` stays
6
- > on 1.16.41. Accessibility fixes across the components, the doc site's SEO surface, and two
7
- > ways to run live examples under a strict Content Security Policy.
5
+ > Fixes from the 1.17.0 review, plus elastic table columns that can be dragged narrower. Nothing
6
+ > to change on upgrade. **`dist/iife.js` is unchanged in size** (506.2 kB gzip).
8
7
 
9
- **Visible changes.** An editable `<tosi-select>` now looks like a text field rather than a
10
- button, because it is one. `<tosi-month>` uses mediumvioletred where it used hotpink, which
11
- failed contrast; its CSS variables restore the old colour. `<tosi-carousel>`'s dots are further
12
- apart. A section page's `<!-- toc -->` list is no longer written into its source file.
8
+ ### Security
9
+
10
+ Both affect sites that turn on the accessibility mode in **1.17.0 and its betas**; no advisory is
11
+ filed, because each needs the build running and the reach is local. Upgrade if you build with
12
+ the accessibility mode on a shared machine or network.
13
+
14
+ - The accessibility audit's temporary server bound every interface, and its root check was a bare
15
+ prefix, so for the length of the audit a request like `/..%2fdocs-private/key.txt` was served a
16
+ sibling directory whose name starts with the output directory's. It now listens on localhost
17
+ only and contains paths with root + separator.
18
+ - The audit's payload and the altText corpus, which can hold hidden pages, were written to
19
+ predictable names in the shared temp directory and never removed. They now go in private
20
+ temporary directories removed when the build ends.
21
+
22
+ ### Added
23
+
24
+ - With a `dist-sizes.json` in the project root (`bun run sizes` writes one in this repo), the site
25
+ build prints the bundle's gzip size change since that baseline and the version it was recorded
26
+ at.
27
+
28
+ ### Fixed
29
+
30
+ - `<tosi-table>`: an elastic column can now be dragged narrower than the width it is drawn at.
31
+ The last elastic column's edge is the table's edge, so dragging it moved nothing and quietly
32
+ shrank its `width` to the minimum, and a middle one fought the columns beside it. A drag now
33
+ fixes the dragged column, and the elastic columns to its left, at the width they are drawn at
34
+ (it sets `width` and `elastic: false`), so the edge follows the pointer exactly. Elastic
35
+ columns to its right absorb the difference; with none, the space it gives up is left beside
36
+ the columns, painted with `--tosi-table-fill`.
37
+ - Under path or query routing, clicking a `/slug/#heading` link in a doc puts the hash in the URL
38
+ and scrolls to the heading. It pushed the page's URL without the hash and landed at the top.
39
+ - The accessibility mode's axe audit and DAISY Ace run with a time limit (the audit's worst case
40
+ plus a minute; Ace ten minutes). One that hangs is stopped and reported as not having run,
41
+ instead of hanging the build, and a build stopped with Ctrl-C or SIGTERM stops them too,
42
+ instead of leaving them and their browser running.
43
+ - The ePub accessibility check accepted any program named `ace` on PATH as DAISY Ace, so an
44
+ unrelated tool could decide whether a book claims WCAG conformance. It now accepts only an
45
+ `ace` inside a `@daisy/ace` package; another is skipped and named in the reason the check did
46
+ not run, and a real Ace later on PATH is still found.
47
+ - `llms.txt` link titles no longer carry the project suffix ("Menus — tosijs-ui" on every line).
48
+ - When a delegated build is refused because the dev server has a different site config, the
49
+ message now says that a refusal on every build means the config computes a value per process
50
+ (`Date.now()`, a random id, an environment variable one process lacks), and how to fix that.
51
+
52
+ ## 1.17.0
53
+
54
+ > Accessibility and SEO for doc sites, and the components themselves passing WCAG 2.2 AA. A doc
55
+ > site can now be built to a stated accessibility level, with an accessible ePub, and every page
56
+ > carries share images, structured data and an llms.txt worth reading. Live examples can run
57
+ > under a Content Security Policy with no `'unsafe-eval'`. `<tosi-table>` gains elastic columns,
58
+ > and the icon generator keeps open paths open. Released as five betas; this section folds them
59
+ > together.
60
+ >
61
+ > **`dist/iife.js` grew 505.2 kB → 509.5 kB gzip (+4.3 kB, +0.9%)**, measured with `gzip -9`
62
+ > against 1.16.41. The ESM entry points pay only for what they import.
63
+
64
+ **Visible changes.** Nothing an adopter must change, but these look or behave differently:
65
+
66
+ - An editable `<tosi-select>` looks like a text field rather than a button, because it is one.
67
+ - `<tosi-month>` uses mediumvioletred where it used hotpink, which failed contrast;
68
+ `--month-day-color` and `--month-date-checked-background` restore the old colour.
69
+ - `<tosi-carousel>`'s dots are 24px apart rather than 16px.
70
+ - An icon whose classes are both `stroked filled` now draws its outline, over a knockout fill
71
+ (`--tosi-icon-knockout`, default `--tosi-bg`). The `filled` class used to win and remove every
72
+ stroke.
73
+ - The doc site header's site name is a `<span class="site-name">`, not an `<h2>`: site CSS that
74
+ styled `header h2` should target `header .site-name`.
75
+ - A section page's `<!-- toc -->` list is no longer written into its source file.
76
+
77
+ ### Security
78
+
79
+ - **Image alt and title text are escaped in rendered pages.** marked wrote alt text into the
80
+ attribute as it was, so a `"` ended it. An ordinary caption (`A sign reading "Open"`) broke the
81
+ tag in every release that renders markdown images. In the 1.17 betas, alt text from an
82
+ `altText` hook, which is machine-generated and so untrusted, could inject markup such as a live
83
+ `<img onerror>`. **Affected:** 1.17.0-beta.1 through beta.5 with an `altText` hook (author
84
+ markdown with a `"` in its alt text rendered a broken tag in every earlier version, but that
85
+ text is the site author's own).
86
+ - **What a `setExampleHooks` `sanitize` hook returns is what the preview gets.** The betas
87
+ serialized the hook's fragment and parsed it again, and HTML does not survive that round trip
88
+ through MathML or SVG: markup the hook had judged harmless could come back as a live
89
+ `<img onerror>` (shown in Chromium, Firefox and WebKit). The fragment is now inserted as it is.
90
+ **Affected:** 1.17.0-beta.5, on pages that register a `sanitize` hook.
91
+
92
+ The 1.17.0 betas are deprecated on npm with a message pointing here; upgrade to 1.17.0.
13
93
 
14
94
  ### Accessibility: the components pass axe's WCAG 2.2 AA rules
15
95
 
@@ -47,6 +127,97 @@ included, at zero.
47
127
  strip takes focus, with ← and → paging it (#3573). `<tosi-rich-text>`'s toolbar buttons are
48
128
  at least 24px, and a live example's scrolling test results take focus.
49
129
  - The doc examples name their controls and use colours that pass AA (#3574).
130
+ - **Enter opens a `<tosi-select>` you pick from**, like Space and ↓. (beta.5 cancelled it.)
131
+ - **`<tosi-table>` gives `role="row"` only to its own rows**: a list inside a cell keeps its list
132
+ items.
133
+
134
+ ### Accessibility mode for doc sites
135
+
136
+ - **An opt-in accessibility mode for doc sites (#3521).** `accessibility: 'wcag22-aa'` in the
137
+ site config makes the build do what it can decide on its own and report what it cannot, on the
138
+ site, in print and in the ePub:
139
+ - an image alone in its paragraph, followed by an italic-only paragraph, renders as a
140
+ `<figure>` with that text as its `<figcaption>`;
141
+ - every page has exactly one `<h1>`, its title: a page's first heading is promoted when its
142
+ title came from it, a later `#` becomes `##`, and the rest shift to start at `##`;
143
+ - the build lists, by file and line, each image with no alt text and no caption, each heading
144
+ that skips a level, and each link named "here" or the like, and fails on them under `strict`.
145
+
146
+ Off by default; a site that does not set it renders exactly as before.
147
+ - **The mode audits the built site with axe.** A full build opens every page in Chromium and runs
148
+ axe-core's WCAG 2.2 AA rules, listing each failure by page, rule and element and failing under
149
+ `strict`. It needs `playwright` and `axe-core` in your project; without them the build says the
150
+ audit did not run rather than reporting a pass. Never under a dev server.
151
+ - **Accessible ePubs (#3522).** In the mode, the book's package document declares its access
152
+ modes, features, hazards and a summary (EPUB Accessibility 1.1), derived from the build: it
153
+ claims alternative text only when every image has alt text or a caption. Conformance to WCAG
154
+ 2.2 AA is claimed only when DAISY Ace, installed in your project, has checked the finished book
155
+ and passed it. `epub: { accessibility: { summary, hazards, certifiedBy } }` overrides the
156
+ defaults. Checked against real Ace: this site's own book passes.
157
+ - **Every ePub, mode or not: the right language, readable links, landmarks.** Each chapter, the
158
+ nav, the contents and the cover said `xml:lang="en"` whatever the book's language, and the
159
+ package said none, so a reading system pronounced a French book as English. Links were
160
+ `#0088cc` (3.9:1 on white) and marked by colour alone; they are now 6.6:1 and underlined.
161
+ The nav gains a landmarks list (cover, contents, start of the text) and its table of contents
162
+ `role="doc-toc"`.
163
+ - **`altText`: a hook that supplies alt text for undescribed images.** With the mode on, it is
164
+ called for each image the build would report, and its answer becomes that image's alt text in
165
+ the built site, `docs.json` and the ePub. It never writes a source file. It runs on every
166
+ build, so cache anything slow by `src`.
167
+ - **The doc-site search trigger's name contains its visible text (#3569).** Its `aria-label`
168
+ was "Search (⌘K)" beside visible text "Search ⌘K", which fails WCAG 2.5.3 (a voice-control user
169
+ says what they see) on every page of every doc site. A `<tosi-palette>` field trigger is now
170
+ named by its text, an icon trigger keeps an `aria-label`, and the shortcut is announced through
171
+ `aria-keyshortcuts`. The frame's axe test now runs every WCAG 2.2 A and AA rule, not four.
172
+ - **The header's site name is no longer a heading.** It was an `<h2>` above every page's own
173
+ `<h1>`, so it was each page's first heading. It is now `<span class="site-name">` and looks the
174
+ same; site CSS that styled `header h2` should target `header .site-name`.
175
+ - **Accessibility mode: headings in a blockquote or list shift with the rest of the page
176
+ (#3576).** A sidebar's `> ####` under `###` sections stayed an `<h4>` under the new `<h2>`s, so
177
+ the build reported a skipped level and DAISY Ace failed the ePub on heading order. A heading
178
+ inside a blockquote still never becomes the page's title.
179
+ - **DAISY Ace is found on `PATH` (#3577).** The docs said `bun add -d @daisy/ace`, which brings
180
+ Electron and Puppeteer into the project, and their advisories then failed the build's own
181
+ dependency audit. Install it globally (`npm install -g @daisy/ace`) instead; the build looks in
182
+ the project first, then on `PATH`.
183
+ - **The axe audit is about twice as fast, and audits the page a reader gets (#3585).** Pages are
184
+ audited four at a time (this site: 80s to 43s). Each is audited once it stops adding elements,
185
+ rather than after a fixed 300ms, which on a busy machine caught pages half-built. And the
186
+ audit no longer starts the doc browser's background test runner, which runs on localhost and
187
+ never on a deployed site. `TOSIJS_A11Y_AUDIT=off` skips the audit and the Ace check for one
188
+ build while the mode stays on; the ePub then claims no conformance.
189
+ - **Section stubs are not written where they cannot be read, or mid-edit (#3584).** A page whose
190
+ `parent` names a missing doc gets a stub section doc in `sectionsDir`. When `sectionsDir` is
191
+ outside every `docPaths` entry, the build now warns and writes nothing, since the stub would
192
+ never be read back. Stubs are created only on the first build in a process, so a parent that
193
+ disappears for a second during a rename no longer leaves files behind. Each stub written is
194
+ named in the build output.
195
+ - **`preview.host` is optional, and the preview docs no longer say `host` is required (#3586).**
196
+ The type has allowed a config with only `preview.tunnel` since 1.10.0, but the doc comment and
197
+ the reference table still said so. `as any` casts written for it can go.
198
+ - **The axe audit waits for a page marked `aria-busy`, and one slow page no longer ends it.** A
199
+ page that is still loading (a 3D scene fetching large models, say) marks an element
200
+ `aria-busy="true"`, the standard ARIA signal, and is waited for up to 30 seconds; other pages
201
+ are audited once they have stopped adding elements, or after five seconds. A page that never
202
+ loads, or is still busy at 30 seconds, is listed as not fully checked, and the build makes no
203
+ claim about it. Before, each page load waited for the network to go idle, so a page that kept
204
+ downloading for 30 seconds, or that polled, threw and ended the audit of every page. The doc
205
+ site reference shows a component setting and clearing `aria-busy`.
206
+ - **Every doc page can be scrolled by keyboard, and has a main landmark (#3575).** A page of
207
+ plain prose had nothing to tab to inside its scrolling content, so a keyboard user could not
208
+ scroll it, which axe reports as `scrollable-region-focusable` (Learn 3D: 71 of 126 pages). The
209
+ content scroller is now focusable, and it is the page's main landmark, which the doc site did not
210
+ have at all: a screen reader's "jump to main content" now works. A doc browser embedded in
211
+ another page stays focusable without claiming that page's main landmark.
212
+ - **`buildSite` says it needs `happy-dom` before it starts (#3600).** Without it, the build died
213
+ partway through with "Cannot find package 'happy-dom'" from a file inside tosijs-ui. It now
214
+ stops before writing anything and prints the install command. `happy-dom` stays an optional
215
+ peer: a project that only uses the components never needs it.
216
+ - **TJS's own doc comments, `/# … #/`, become documentation (#3601).** In `.tjs` and `.ajs`
217
+ files they are read in source order with any `/*#` blocks. tjs-lang finds them, so its grammar
218
+ applies exactly; that needs a tjs-lang that exports `findDocCommentSpans` (requested). With an
219
+ older one, the build names each file whose `/#` comments it could not read, instead of
220
+ dropping the page silently. `.ajs` files are now scraped for `/*#` blocks too.
50
221
 
51
222
  ### Live examples under a strict CSP
52
223
 
@@ -106,123 +277,51 @@ included, at zero.
106
277
  A description derived from a page's first paragraph no longer keeps `_emphasis_`,
107
278
  `~~strikethrough~~`, wikilinks or footnote markers, and is cut at 155 characters, where
108
279
  search results cut it.
280
+ - **`bun run og` no longer stops the dev server it captures from.** It wrote each card into the
281
+ watched `demo/static/og/` as it went, and the server's rebuild-loop guard shut the server down
282
+ part way through; cards are now written elsewhere and copied in at the end. It also removes
283
+ haltija's dev widget before capturing, which sat in the corner of every card.
109
284
 
110
- ### Build
111
-
112
- - **`buildSite` no longer rewrites the `<!-- toc -->` block in a section's source `.md` on
113
- every build (#3733),** which left every adopter with section pages a dirty tree after
114
- building. The list of child pages is filled in the built page (and its book variant) only; a
115
- section with no `<!-- toc -->` marker still gets the list at the end. A committed block's old
116
- list is harmless (the build replaces it), but can be emptied to the bare markers.
117
-
118
- ### 1.17.0-beta.4 (prerelease detail)
119
-
120
- > Prerelease, published under the `beta` dist-tag: install with `tosijs-ui@beta`. `latest` stays
121
- > on 1.16.41. Fixes from Learn 3D and from tosijs-native's first site build.
122
-
123
- - **Every doc page can be scrolled by keyboard, and has a main landmark (#3575).** A page of
124
- plain prose had nothing to tab to inside its scrolling content, so a keyboard user could not
125
- scroll it, which axe reports as `scrollable-region-focusable` (Learn 3D: 71 of 126 pages). The
126
- content scroller is now focusable, and it is the page's main landmark, which the doc site did not
127
- have at all: a screen reader's "jump to main content" now works. A doc browser embedded in
128
- another page stays focusable without claiming that page's main landmark.
129
- - **`buildSite` says it needs `happy-dom` before it starts (#3600).** Without it, the build died
130
- partway through with "Cannot find package 'happy-dom'" from a file inside tosijs-ui. It now
131
- stops before writing anything and prints the install command. `happy-dom` stays an optional
132
- peer: a project that only uses the components never needs it.
133
- - **TJS's own doc comments, `/# … #/`, become documentation (#3601).** In `.tjs` and `.ajs`
134
- files they are read in source order with any `/*#` blocks. tjs-lang finds them, so its grammar
135
- applies exactly; that needs a tjs-lang that exports `findDocCommentSpans` (requested). With an
136
- older one, the build names each file whose `/#` comments it could not read, instead of
137
- dropping the page silently. `.ajs` files are now scraped for `/*#` blocks too.
138
-
139
- ### 1.17.0-beta.3 (prerelease detail)
140
-
141
- > Prerelease, published under the `beta` dist-tag: install with `tosijs-ui@beta`. `latest` stays
142
- > on 1.16.41. beta.2 was staged and never published; its notes follow.
143
-
144
- - **The axe audit waits for a page marked `aria-busy`, and one slow page no longer ends it.** A
145
- page that is still loading (a 3D scene fetching large models, say) marks an element
146
- `aria-busy="true"`, the standard ARIA signal, and is waited for up to 30 seconds; other pages
147
- are audited once they have stopped adding elements, or after five seconds. A page that never
148
- loads, or is still busy at 30 seconds, is listed as not fully checked, and the build makes no
149
- claim about it. Before, each page load waited for the network to go idle, so a page that kept
150
- downloading for 30 seconds, or that polled, threw and ended the audit of every page. The doc
151
- site reference shows a component setting and clearing `aria-busy`.
152
-
153
- ### 1.17.0-beta.2 (prerelease detail)
154
-
155
- > Prerelease, published under the `beta` dist-tag: install with `tosijs-ui@beta`. `latest` stays
156
- > on 1.16.41. Fixes from Learn 3D's first run of beta.1.
157
-
158
- - **Accessibility mode: headings in a blockquote or list shift with the rest of the page
159
- (#3576).** A sidebar's `> ####` under `###` sections stayed an `<h4>` under the new `<h2>`s, so
160
- the build reported a skipped level and DAISY Ace failed the ePub on heading order. A heading
161
- inside a blockquote still never becomes the page's title.
162
- - **DAISY Ace is found on `PATH` (#3577).** The docs said `bun add -d @daisy/ace`, which brings
163
- Electron and Puppeteer into the project, and their advisories then failed the build's own
164
- dependency audit. Install it globally (`npm install -g @daisy/ace`) instead; the build looks in
165
- the project first, then on `PATH`.
166
- - **The axe audit is about twice as fast, and audits the page a reader gets (#3585).** Pages are
167
- audited four at a time (this site: 80s to 43s). Each is audited once it stops adding elements,
168
- rather than after a fixed 300ms, which on a busy machine caught pages half-built. And the
169
- audit no longer starts the doc browser's background test runner, which runs on localhost and
170
- never on a deployed site. `TOSIJS_A11Y_AUDIT=off` skips the audit and the Ace check for one
171
- build while the mode stays on; the ePub then claims no conformance.
172
- - **Section stubs are not written where they cannot be read, or mid-edit (#3584).** A page whose
173
- `parent` names a missing doc gets a stub section doc in `sectionsDir`. When `sectionsDir` is
174
- outside every `docPaths` entry, the build now warns and writes nothing, since the stub would
175
- never be read back. Stubs are created only on the first build in a process, so a parent that
176
- disappears for a second during a rename no longer leaves files behind. Each stub written is
177
- named in the build output.
178
- - **`preview.host` is optional, and the preview docs no longer say `host` is required (#3586).**
179
- The type has allowed a config with only `preview.tunnel` since 1.10.0, but the doc comment and
180
- the reference table still said so. `as any` casts written for it can go.
181
-
182
- ### 1.17.0-beta.1 (prerelease detail)
183
-
184
- > Prerelease, published under the `beta` dist-tag: install with `tosijs-ui@beta`. `latest` stays
185
- > on 1.16.41. It carries the accessibility half of 1.17.0; the SEO work and #245/#246 follow.
186
-
187
- - **An opt-in accessibility mode for doc sites (#3521).** `accessibility: 'wcag22-aa'` in the
188
- site config makes the build do what it can decide on its own and report what it cannot, on the
189
- site, in print and in the ePub:
190
- - an image alone in its paragraph, followed by an italic-only paragraph, renders as a
191
- `<figure>` with that text as its `<figcaption>`;
192
- - every page has exactly one `<h1>`, its title: a page's first heading is promoted when its
193
- title came from it, a later `#` becomes `##`, and the rest shift to start at `##`;
194
- - the build lists, by file and line, each image with no alt text and no caption, each heading
195
- that skips a level, and each link named "here" or the like, and fails on them under `strict`.
285
+ ### Components
196
286
 
197
- Off by default; a site that does not set it renders exactly as before.
198
- - **The mode audits the built site with axe.** A full build opens every page in Chromium and runs
199
- axe-core's WCAG 2.2 AA rules, listing each failure by page, rule and element and failing under
200
- `strict`. It needs `playwright` and `axe-core` in your project; without them the build says the
201
- audit did not run rather than reporting a pass. Never under a dev server.
202
- - **Accessible ePubs (#3522).** In the mode, the book's package document declares its access
203
- modes, features, hazards and a summary (EPUB Accessibility 1.1), derived from the build: it
204
- claims alternative text only when every image has alt text or a caption. Conformance to WCAG
205
- 2.2 AA is claimed only when DAISY Ace, installed in your project, has checked the finished book
206
- and passed it. `epub: { accessibility: { summary, hazards, certifiedBy } }` overrides the
207
- defaults. Checked against real Ace: this site's own book passes.
208
- - **Every ePub, mode or not: the right language, readable links, landmarks.** Each chapter, the
209
- nav, the contents and the cover said `xml:lang="en"` whatever the book's language, and the
210
- package said none, so a reading system pronounced a French book as English. Links were
211
- `#0088cc` (3.9:1 on white) and marked by colour alone; they are now 6.6:1 and underlined.
212
- The nav gains a landmarks list (cover, contents, start of the text) and its table of contents
213
- `role="doc-toc"`.
214
- - **`altText`: a hook that supplies alt text for undescribed images.** With the mode on, it is
215
- called for each image the build would report, and its answer becomes that image's alt text in
216
- the built site, `docs.json` and the ePub. It never writes a source file. It runs on every
217
- build, so cache anything slow by `src`.
218
- - **The doc-site search trigger's name contains its visible text (#3569).** Its `aria-label`
219
- was "Search (⌘K)" beside visible text "Search ⌘K", which fails WCAG 2.5.3 (a voice-control user
220
- says what they see) on every page of every doc site. A `<tosi-palette>` field trigger is now
221
- named by its text, an icon trigger keeps an `aria-label`, and the shortcut is announced through
222
- `aria-keyshortcuts`. The frame's axe test now runs every WCAG 2.2 A and AA rule, not four.
223
- - **The header's site name is no longer a heading.** It was an `<h2>` above every page's own
224
- `<h1>`, so it was each page's first heading. It is now `<span class="site-name">` and looks the
225
- same; site CSS that styled `header h2` should target `header .site-name`.
287
+ - **Elastic table columns (snowfox, #3858).** A `<tosi-table>` column with `elastic: true` takes
288
+ a share of the space left over when the columns add up to less than the table's width, in
289
+ proportion to its `width`, and never shrinks below it, so a narrow table still scrolls. The
290
+ table's `elastic` attribute makes every unpinned column elastic unless it says
291
+ `elastic: false`, which covers columns inferred from the data. Pinned columns are never
292
+ elastic, and elastic columns take precedence over `full-width-header`. Dragging an elastic
293
+ column's edge keeps the edge under the pointer, and a stretched column's edge can be grabbed
294
+ where it is drawn.
295
+ - **`--tosi-table-fill`** paints the space to the right of a table's columns when they don't
296
+ fill it. Transparent by default.
297
+ - **`<tosi-palette>`'s `localizer` localizes every caption the palette owns (tosijs-ui#247):** the
298
+ footer's Navigate, Select and Close, the dialog's Search and the clear button's Clear names,
299
+ `emptyCaption` and `quickLinksCaption`, not only the placeholder.
300
+ - **`tosijs-make-icons` keeps open paths open (tosijs-ui#249).** In a folder that is both stroked
301
+ and filled (`stroked/filled/`), an element's own `fill="none"`, or `fill:none` in its style, is
302
+ kept: those icons' fill is a knockout, and `fill="none"` is what keeps an arc or arrowhead
303
+ open. It also no longer swallows the rest of an icon after a `style` whose last fill or stroke
304
+ had no trailing `;`, and drops a style left empty. Checked against tosijs-3d's icons: the
305
+ output matches their post-pass, which can now go.
306
+
307
+ ### Doc system and build
308
+
309
+ - **A memory-routed doc browser follows `/slug/` links in memory (tosijs-ui#243).** It resolved
310
+ only `?filename`, yet rewrote every internal link to `/slug/` itself, so every doc-to-doc link
311
+ in an embedded browser was a full page load. A `/slug/#heading` link scrolls to the heading, and
312
+ canonicalising a content link keeps its `#heading`; it used to drop it.
313
+ - **A build handed to a running dev server is refused when the server has a different site
314
+ config (#3772, tosijs-ui#241).** The server builds the config it was started with, so editing
315
+ the config used to produce a build of the old one that reported success. Now `bun run build`
316
+ says to restart the server. The dev server also watches the `bundleEntry` file, so editing it
317
+ rebuilds the hydration bundle.
318
+ - **`buildSite` no longer rewrites the `<!-- toc -->` block in a section's source `.md` on every
319
+ build (#3733),** which left every adopter with section pages a dirty tree after building. The
320
+ list of child pages is filled in the built page (and its book variant) only; a section with no
321
+ `<!-- toc -->` marker still gets the list at the end. A committed block's old list is harmless,
322
+ but can be emptied to the bare markers.
323
+ - **A `liveExamples` or `kind` value the build does not know fails the build**, naming the allowed
324
+ values. `'nocode'` silently ran every example, on a site meant to run none.
226
325
 
227
326
  ## 1.16.41
228
327
 
package/README.md CHANGED
@@ -53,7 +53,7 @@ The `tosijs-ui` iife build bundles `tosijs`, `tosijs-ui`, `marked` and — as of
53
53
  **CodeMirror** into a single minified javascript source file. You can access `xinjs` and
54
54
  `xinjsui` as globals which contain all the things exported by `tosijs` and `tosijs-ui`.
55
55
 
56
- > **Size (1.16.21):** ~484KB gzip, up from ~120KB in 1.6. Two things dominate it, and
56
+ > **Size (1.17.0):** ~506KB gzip, up from ~120KB in 1.6. Two things dominate it, and
57
57
  > `bun build --format iife` cannot
58
58
  > code-split, so `<tosi-code>`'s lazy CodeMirror import is flattened into this bundle
59
59
  > whether or not the page uses an editor. **If you don't need `<tosi-code>`, use the ESM
@@ -32,6 +32,9 @@ Folder names control CSS classes on the generated SVGs:
32
32
  - `color/` — icons with baked-in colors (class `color`, fill/stroke preserved)
33
33
  - `stroked/` — stroke-only icons (class `stroked`, fill styles removed)
34
34
  - `filled/` — fill-only icons (class `filled`, stroke styles removed)
35
+ - a folder that is both (`stroked/filled/`) — classes `stroked filled`; fill colours are
36
+ removed (the renderer supplies the fill), but an element's own `fill="none"` (or
37
+ `fill:none` in its style) is kept, because that is what keeps an arc or arrowhead open
35
38
 
36
39
  ### Directional folders
37
40
 
@@ -195,6 +198,9 @@ function getDirectionConfig(dir) {
195
198
  return DIRECTION_FOLDERS[dirName] || null
196
199
  }
197
200
 
201
+ // Stands in for an element's `fill="none"` while the strips run (see keepsOpenPaths).
202
+ const OPEN_PATH_MARK = 'data-tosi-open-path'
203
+
198
204
  function generateDirectionalRedirects(name, dirConfig) {
199
205
  // Find the base direction word in the camelCase name
200
206
  const baseDir = dirConfig.base
@@ -239,7 +245,28 @@ function findIcons(dirs, ignore = []) {
239
245
  .replace(/-([a-z0-9])/g, (_, char) => char.toLocaleUpperCase())
240
246
  )
241
247
  const name = names[0]
242
- let svgSource = content
248
+ /*
249
+ In a folder that is BOTH stroked and filled, a fill is a knockout (the renderer sets
250
+ it on the root) and an element's own `fill="none"` is what keeps a path OPEN: an arc,
251
+ an arrowhead. Every strip below removes fills, so such an element filled with the
252
+ knockout colour, and an open arc painted a lens over whatever it crossed
253
+ (tosijs-ui#249). Mark those elements first; the mark survives the strips and becomes
254
+ `fill="none"` again at the end. The root <svg> is not an element here: its fill is
255
+ the folder's to decide.
256
+ */
257
+ const keepsOpenPaths = dir.includes('stroked') && dir.includes('filled')
258
+ let svgSource = (
259
+ keepsOpenPaths
260
+ ? content.replace(/<(?!svg\b)(\w+)\b[^>]*>/g, (tag) =>
261
+ /\bfill="none"|\bfill\s*:\s*none/.test(tag)
262
+ ? tag
263
+ .replace(/\s?\bfill="none"/, '')
264
+ .replace(/\bfill\s*:\s*none\s*;?/, '')
265
+ .replace(/^<\w+/, (open) => `${open} ${OPEN_PATH_MARK}`)
266
+ : tag
267
+ )
268
+ : content
269
+ )
243
270
  .replace(/(<\?xml.*?>|<!DOCTYPE.*?>)\s?/g, '')
244
271
  .replace(/<svg.*?>/, (a) =>
245
272
  a.replace(
@@ -280,7 +307,7 @@ function findIcons(dirs, ignore = []) {
280
307
  out of the tinting path for an unrelated reason (tosijs-ui#30).
281
308
  */
282
309
  svgSource = svgSource.replace(
283
- /(fill|stroke)(?!-rule)(-\w+)?:[^;]+;?/g,
310
+ /(fill|stroke)(?!-rule)(-\w+)?:[^;"]+;?/g,
284
311
  ''
285
312
  )
286
313
  }
@@ -289,12 +316,20 @@ function findIcons(dirs, ignore = []) {
289
316
  // survives every strip. `clip-rule` likewise.
290
317
  if (dir.includes('stroked')) {
291
318
  classes.push('stroked')
292
- svgSource = svgSource.replace(/(fill)(?!-rule)(-\w+)?:[^;]+;?/g, '')
319
+ svgSource = svgSource.replace(/(fill)(?!-rule)(-\w+)?:[^;"]+;?/g, '')
293
320
  }
294
321
  if (dir.includes('filled')) {
295
322
  classes.push('filled')
296
- svgSource = svgSource.replace(/(stroke)(?!-rule)(-\w+)?:[^;]+;?/g, '')
323
+ svgSource = svgSource.replace(
324
+ /(stroke)(?!-rule)(-\w+)?:[^;"]+;?/g,
325
+ ''
326
+ )
327
+ }
328
+ if (keepsOpenPaths) {
329
+ svgSource = svgSource.replaceAll(OPEN_PATH_MARK, 'fill="none"')
297
330
  }
331
+ // A style that held only colours is empty now.
332
+ svgSource = svgSource.replace(/\sstyle=""/g, '')
298
333
  if (classes.length) {
299
334
  svgSource = svgSource.replace(
300
335
  /^<svg/,
@@ -11,6 +11,14 @@ export interface ColumnOptions {
11
11
  align?: string;
12
12
  type?: ValueRendererType;
13
13
  pinned?: 'left' | 'right';
14
+ /**
15
+ * Whether this column takes a share of the space left over when the columns add up to
16
+ * less than the table's width. Elastic columns grow in proportion to their `width`, and
17
+ * never shrink below it. Defaults to the table's `elastic`. A pinned column is never
18
+ * elastic: its sticky offset is its width. Resizing a column by dragging sets this to
19
+ * `false` on it and on the elastic columns to its left, at the widths they were drawn at.
20
+ */
21
+ elastic?: boolean;
14
22
  sort?: false | 'ascending' | 'descending';
15
23
  /**
16
24
  * What this column SORTS by, when that differs from what it stores.
@@ -49,6 +57,19 @@ export interface ColumnOptions {
49
57
  }
50
58
  /** Builds the element shown under an expanded row. `null` means the row has no detail. */
51
59
  export type RowDetailFn = (row: any) => HTMLElement | null;
60
+ /**
61
+ * How a table's columns divide its width: the grid tracks, and the width each column is
62
+ * actually drawn at. Pure, so the grid (CSS) and the resize hit-test (JS) cannot disagree.
63
+ *
64
+ * When the columns add up to less than `available`, the leftover goes to the elastic
65
+ * columns in proportion to their widths, or, if there are none, to `stretchAt` (the
66
+ * `fullWidthHeader` column). Otherwise every column is drawn at its width.
67
+ */
68
+ export declare function columnLayout(widths: number[], elastic: boolean[], stretchAt: number, available: number): {
69
+ tracks: string[];
70
+ drawn: number[];
71
+ stretches: boolean;
72
+ };
52
73
  export interface TableData {
53
74
  columns?: ColumnOptions[] | null;
54
75
  array: any[];
@@ -81,6 +102,7 @@ declare const TosiTable_base: import("tosijs").WithAttributes<{
81
102
  nopreservescroll: boolean;
82
103
  editable: boolean;
83
104
  fullWidthHeader: boolean;
105
+ elastic: boolean;
84
106
  }>;
85
107
  export declare class TosiTable extends TosiTable_base {
86
108
  static preferredTagName: string;
@@ -100,6 +122,8 @@ export declare class TosiTable extends TosiTable_base {
100
122
  height: string;
101
123
  overflow: string;
102
124
  overscrollBehavior: string;
125
+ backgroundImage: string;
126
+ backgroundAttachment: string;
103
127
  };
104
128
  ':host .thead, :host .tbody': {
105
129
  display: string;
@@ -479,6 +503,9 @@ export declare class TosiTable extends TosiTable_base {
479
503
  private focusCell;
480
504
  private handleKeyNav;
481
505
  connectedCallback(): void;
506
+ private pinElasticThrough;
507
+ private isElastic;
508
+ private layoutColumns;
482
509
  setColumnWidths(): void;
483
510
  sortByColumn: (columnOptions: ColumnOptions, direction?: "ascending" | "descending" | "auto") => void;
484
511
  popColumnMenu: (target: HTMLElement, options: ColumnOptions) => void;