tosijs-ui 1.12.7 → 1.13.0
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 +313 -0
- package/bin/make-icon-data.js +11 -0
- package/dist/diff.d.ts +47 -0
- package/dist/diff.js +262 -10
- package/dist/doc-browser.js +26 -53
- package/dist/doc-system/doc-system.js +17 -0
- package/dist/doc-system/site/dev-auth.d.ts +6 -0
- package/dist/doc-system/site/dev-auth.js +82 -0
- package/dist/doc-system/site/dev-server.d.ts +13 -0
- package/dist/doc-system/site/dev-server.js +184 -4
- package/dist/doc-system/site/epub.d.ts +1 -0
- package/dist/doc-system/site/epub.js +73 -1
- package/dist/doc-system/site/generate-site.d.ts +15 -0
- package/dist/doc-system/site/generate-site.js +20 -4
- package/dist/doc-system/site/orchestrator.d.ts +15 -0
- package/dist/doc-system/site/orchestrator.js +194 -28
- package/dist/doc-system/site/site-config.d.ts +9 -0
- package/dist/icon-data.js +6 -0
- package/dist/iife.js +88 -88
- package/dist/iife.js.map +11 -11
- package/dist/live-example/component.d.ts +5 -1
- package/dist/live-example/component.js +50 -7
- package/dist/live-example/styles.d.ts +3 -0
- package/dist/live-example/styles.js +14 -0
- package/dist/live-theme.d.ts +1 -1
- package/dist/localize.d.ts +8 -8
- package/dist/pocket-bar.d.ts +13 -0
- package/dist/pocket-bar.js +58 -6
- package/dist/router.d.ts +1 -1
- package/dist/theme.d.ts +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,318 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.13.0
|
|
4
|
+
|
|
5
|
+
### tosijs `^1.9.1` — and a console with nothing in it
|
|
6
|
+
|
|
7
|
+
The peer floor moves to `^1.9.1`. **The binding reason is a type-surface change, not the console
|
|
8
|
+
cleanup** — our emitted declarations now reference `TosiProps` and `TosiStyleSheet` (renamed
|
|
9
|
+
upstream from `XinProps`/`XinStyleSheet`), so `dist/*.d.ts` _hard-requires_ a tosijs that exports
|
|
10
|
+
them. An adopter on an older tosijs gets type errors, not warnings. The floor also carries
|
|
11
|
+
tosijs's **agent-surface security fix**, and the deprecation cleanup below.
|
|
12
|
+
|
|
13
|
+
**Migration note if you are on tosijs 1.7.x:** this floor takes you through **tosijs 1.8.0**,
|
|
14
|
+
which its own notes describe as deviating from semver — it removed `data-ref` and `<xin-slot>`.
|
|
15
|
+
Read tosijs's 1.8.0 entry before upgrading; that step is not automatic.
|
|
16
|
+
|
|
17
|
+
Before 1.9.1, every page of every doc site logged warnings a consumer could not act on:
|
|
18
|
+
|
|
19
|
+
- `xinValue, tosiValue, xinPath…` — tosijs reading its own deprecated proxy property
|
|
20
|
+
([tosijs#31](https://github.com/tonioloewald/tosijs/issues/31)). Nothing in any consumer's
|
|
21
|
+
code caused it.
|
|
22
|
+
- `bindText` / `bindList` — deprecated in 1.8, then **withdrawn as a category error**
|
|
23
|
+
([tosijs#33](https://github.com/tonioloewald/tosijs/issues/33)). They are a pair: `bindList`
|
|
24
|
+
supplies the per-item template and `bindText` fills it, and `{ textContent: proxy }` was
|
|
25
|
+
never a replacement for the path form (measured: `textContent: '^.name'` inside a template
|
|
26
|
+
renders the literal string, `bindText: '^.name'` binds). `bindText`'s runtime warning
|
|
27
|
+
outlived its typings by one release and is gone in 1.9.1.
|
|
28
|
+
|
|
29
|
+
Verified on a from-scratch build rather than assumed: **the three deprecation warnings above are
|
|
30
|
+
gone.** An earlier draft of these notes claimed _zero console warnings_, and that was false — one warning remains and it is ours, not tosijs's. Instantiating `<tosi-tabs>` still logs an
|
|
31
|
+
on-event collision from our deprecated `onCloseTab` alias, which the elements factory reads as
|
|
32
|
+
event-handler sugar.
|
|
33
|
+
`onCloseTab` is a deprecated alias we keep for compatibility, so removing it is a breaking change
|
|
34
|
+
and waits for the next major. Stating it rather than claiming a clean console we do not have.
|
|
35
|
+
|
|
36
|
+
### Live examples: pick a side by clicking, and see what actually changed
|
|
37
|
+
|
|
38
|
+
Both diff views — the resolvable one and the read-only one behind `<tosi-code>`'s review
|
|
39
|
+
overlay — now let you **click a coloured line** to take that side, and mark **the words that
|
|
40
|
+
actually differ** more strongly than the line containing them. Dragging to select text does not
|
|
41
|
+
change anything; those lines are also what you copy from.
|
|
42
|
+
|
|
43
|
+
`<tosi-example>`'s widget bar used to stay **open after every action** — you clicked "maximize"
|
|
44
|
+
and it sat there covering the corner of the thing you had just maximised, until you remembered
|
|
45
|
+
to click the handle again. It now closes once you have actually done something, its controls are ordered by reach (maximize nearest the handle), and the handle is the
|
|
46
|
+
tosi owl on a status-coloured badge — orange running, green passed, red failed.
|
|
47
|
+
|
|
48
|
+
**Tapping that handle on a touch device no longer flashes the menu open and shut.** A touch
|
|
49
|
+
device has no hover, but the browser still fires `pointerenter`/`pointerleave` around a tap, so
|
|
50
|
+
the hover _peek_ opened the bar and then undid itself before the click pinned it — you tapped
|
|
51
|
+
again believing it had failed, and that second tap closed it for real.
|
|
52
|
+
|
|
53
|
+
### Source moved into the app menu
|
|
54
|
+
|
|
55
|
+
The floating `<> Source` chip that sat over the content is gone; Source is a submenu of the app
|
|
56
|
+
menu beside Language and Color Theme, and it disappears entirely on a page with no source file.
|
|
57
|
+
|
|
58
|
+
### Security: cross-site requests can no longer reach the dev server's write endpoints
|
|
59
|
+
|
|
60
|
+
`POST /__docstore/source` and `POST /__build` were gated on the **peer address alone** — no
|
|
61
|
+
credential — and the handlers parse JSON regardless of `Content-Type`. That made a cross-site
|
|
62
|
+
`fetch(…, {mode:'no-cors'})` a _simple_ request: no preflight, peer is `127.0.0.1`, gate passes.
|
|
63
|
+
Any page you happened to visit while `bun start` was running could write any file under the repo
|
|
64
|
+
root, `.git/hooks/*` included — code execution at your next git operation
|
|
65
|
+
([#90](https://github.com/tonioloewald/tosijs-ui/issues/90)).
|
|
66
|
+
|
|
67
|
+
All three state-changing endpoints — `/__docstore/source`, `/__build` and `/report` — now
|
|
68
|
+
require the request to be same-origin, via one shared predicate rather than a term repeated per
|
|
69
|
+
call site. It checks **both** `Sec-Fetch-Site` **and** `Origin`: the first is unforgeable but
|
|
70
|
+
absent on Safari < 16.4, Firefox < 90 and older WKWebViews, and those browsers send the second.
|
|
71
|
+
An earlier pass checked only the first and left exactly that population unprotected. Applied
|
|
72
|
+
**on the non-tunnel path only** — the tunnel is legitimately cross-site and
|
|
73
|
+
is gated by a session cookie instead. Non-browser callers (the CLI, `curl`) send no fetch
|
|
74
|
+
metadata and are unaffected; the attack this closes is specifically a _browser_ being induced to
|
|
75
|
+
make the request, and a browser always tells us.
|
|
76
|
+
|
|
77
|
+
The prose in `dev-auth.ts` had claimed `SameSite=Lax` gave "free CSRF protection". That was true
|
|
78
|
+
only of the tunnel path, which needs a cookie — the default loopback path needed nothing, so the
|
|
79
|
+
stated defence never covered its own primary case.
|
|
80
|
+
|
|
81
|
+
### New dev-server endpoint: `POST /__build` (loopback only)
|
|
82
|
+
|
|
83
|
+
Called out under its own heading because it is a new HTTP surface on a tool people run all day.
|
|
84
|
+
|
|
85
|
+
It exists so `bun run build` can ask a running dev server to build instead of refusing (below).
|
|
86
|
+
It is gated exactly like `/__devlink`: **loopback and NOT via the tunnel**. Verified against a
|
|
87
|
+
real server rather than reasoned about — an anonymous tunnel request gets `401` from the session
|
|
88
|
+
gate, and an **authenticated** one gets `404` from this gate, which is the case that matters. A
|
|
89
|
+
remote visitor triggering builds would be a denial of service at best, and runs the project's own
|
|
90
|
+
build code at worst. `GET` does nothing.
|
|
91
|
+
|
|
92
|
+
Nothing else about the build lock changed: two builders racing `rm -rf` on one output tree is
|
|
93
|
+
still refused, and a stale lock still gets the old message.
|
|
94
|
+
|
|
95
|
+
### ePub dates, and an `epub.modified` option
|
|
96
|
+
|
|
97
|
+
`dcterms:modified` is the only date an ePub reader can surface, and it is baked into a
|
|
98
|
+
committed, redistributed file — so it has to be deterministic _and_ coherent. The
|
|
99
|
+
version-derived encoding introduced in this release **wrapped** — twice. First `1.13.24` and
|
|
100
|
+
`1.14.0` produced the same instant; the repack then made `1.13.1-beta.1` byte-identical to
|
|
101
|
+
`1.13.0`, collided `1.0.1000` with `1.1.0`, and sent a CalVer `2026.9.3` to the year 5872. Each
|
|
102
|
+
attempt shipped a docblock asserting the ordering that execution disproved.
|
|
103
|
+
|
|
104
|
+
So the promise is narrowed to what is actually needed and can actually be held: **the same
|
|
105
|
+
version always yields the same instant, and different versions yield different ones.** It is no
|
|
106
|
+
longer ordered, because nothing reads these dates as a sequence — a reader sees one date on one
|
|
107
|
+
book — and a claim nobody needs, made three times and false three times, is worse than no claim.
|
|
108
|
+
The test sweep is generated rather than hand-picked, which is what would have caught the
|
|
109
|
+
adjacent-version collision.
|
|
110
|
+
|
|
111
|
+
New: **`epub.modified`** in the site config. The derived date is synthetic — it looks like a
|
|
112
|
+
date without being one — and until now an adopter had no way to publish the real one.
|
|
113
|
+
|
|
114
|
+
That option shipped broken for a few hours and is worth recording: `modified` was written
|
|
115
|
+
**above** a `...epubOpts` spread, and `epubOpts` _is_ `config.epub` — so the adopter's raw value
|
|
116
|
+
overwrote the sanitised one. `'2026-09-03'` reached the OPF unnormalised and `'not a date'`
|
|
117
|
+
reached it verbatim, and EPUB 3 requires exactly `CCYY-MM-DDThh:mm:ssZ`, so the obvious spelling
|
|
118
|
+
of the new option produced a file EPUBCheck rejects. The option assembly is now an exported
|
|
119
|
+
**`epubOptionsFor()`** with tests that drive **the call site**, not just the date function beside it
|
|
120
|
+
— the third time in this release that a green test asserted a pure function whose only consumer
|
|
121
|
+
threw the result away.
|
|
122
|
+
|
|
123
|
+
### The book this project ships carries a real date again
|
|
124
|
+
|
|
125
|
+
`docs/tosijs-ui.epub` is redistributed, and its OPF has no `dc:date` — so `dcterms:modified` is
|
|
126
|
+
the only date a reader ever sees. The version-derived scheme hashed 1.13.0 to **2012**, fourteen
|
|
127
|
+
years adrift, where v1.12.8's book carried a correct contemporary date. This repo now sets
|
|
128
|
+
`epub.modified` explicitly, which is exactly what that new option is for; adopters who care
|
|
129
|
+
about the date should do the same, and adopters who don't still get a deterministic one.
|
|
130
|
+
|
|
131
|
+
A related tidy: a docblock asserting the monotonicity this function does not have had survived
|
|
132
|
+
three rewrites by sitting forty lines above it in a different file, and was shipping in `dist/`.
|
|
133
|
+
The behaviour is described in one place now.
|
|
134
|
+
|
|
135
|
+
### One place decides the ePub date
|
|
136
|
+
|
|
137
|
+
`buildEpub` — the public export, and what `bun book` calls — now defaults and validates
|
|
138
|
+
`dcterms:modified` itself. It previously used `new Date()` with no validation, so the
|
|
139
|
+
determinism fix and the `epub.modified` handling lived at a single `buildSite` call site that
|
|
140
|
+
`bun book` never executed: that path still stamped a wall clock, re-dirtying the committed ePub,
|
|
141
|
+
and still passed a date-only string into an OPF that EPUBCheck rejects.
|
|
142
|
+
|
|
143
|
+
That is the fourth instance in this release of one shape: **a green test asserting a function
|
|
144
|
+
whose only consumer discards the result.** The date function was correct and tested; the
|
|
145
|
+
assembly threw its return value away. The predicate was correct and tested; two of three
|
|
146
|
+
endpoints used it. The touch guard was correct; the test retyped it. And the fix for the
|
|
147
|
+
_first_ three introduced the fourth, by testing a helper that one of two shipped paths called.
|
|
148
|
+
|
|
149
|
+
The answer is not another helper — it is a single choke point every path already goes through,
|
|
150
|
+
tested from the public entry point by asserting the OPF text.
|
|
151
|
+
|
|
152
|
+
### Tests that can actually fail
|
|
153
|
+
|
|
154
|
+
Two "regression tests" added earlier in this release were **tautologies**: each re-declared the
|
|
155
|
+
production predicate inside the test file and asserted on the copy, so both passed forever and
|
|
156
|
+
stayed green when the real code was broken. A mutation test in the pre-release review killed
|
|
157
|
+
zero of them. They now mount the real `<tosi-pocket-bar>` and import the exported delegation
|
|
158
|
+
predicate, and were re-mutated to confirm the opposite — deleting the touch guards and the
|
|
159
|
+
pin-clear turns two tests red.
|
|
160
|
+
|
|
161
|
+
A re-review then found the same shape one layer down: the delegated-build queue —
|
|
162
|
+
`buildNow`/`buildWaiters` — had **zero** coverage, and an unconditional infinite loop passed the
|
|
163
|
+
whole green suite. The loop is fixed (it started a build on every pass instead of once, spinning
|
|
164
|
+
the dev server into ~30 builds and a `process.exit(1)` that blamed the user's watcher config),
|
|
165
|
+
and the decision is now a pure exported function, `nextBuildStep()`, with tests for the
|
|
166
|
+
contended orderings. `POST /report` was also found never to have received the CSRF gate — the
|
|
167
|
+
term had been spliced into two call sites by hand and the third missed, which is why all three
|
|
168
|
+
now share one named predicate.
|
|
169
|
+
|
|
170
|
+
Recorded here rather than buried because it is the same failure this project's notes already
|
|
171
|
+
describe from 1.9.0: work asserted in prose that the code did not do. A test that retypes the
|
|
172
|
+
condition it is testing is that failure wearing a lab coat.
|
|
173
|
+
|
|
174
|
+
### What delegation guarantees (and what it refuses)
|
|
175
|
+
|
|
176
|
+
The delegation path above was hardened after a pre-release review found several ways it could
|
|
177
|
+
report a success nobody got. Worth stating, because "my build tool said it built" is a claim you
|
|
178
|
+
rely on:
|
|
179
|
+
|
|
180
|
+
- **It proves the build was yours.** `holder.port` is validated before it reaches a URL, the
|
|
181
|
+
request carries `?root=`, and the reply carries `pid`/`root`/`outputDir` which must match the
|
|
182
|
+
lock and the tree you asked for. A planted lock plus any listener answering `{"ok":true}`
|
|
183
|
+
previously printed "rebuilt this tree" and exited 0 over an untouched tree — stale locks from
|
|
184
|
+
other projects are ordinary on a dev machine, one of them usually on the shared default port.
|
|
185
|
+
- **It waits for the tree to settle**, not for whichever build finishes next — starting one
|
|
186
|
+
build and then parking, with a cap so a watcher that keeps firing degrades to a message
|
|
187
|
+
instead of a build storm. (The first attempt called `rebuild()` on **every pass**, and
|
|
188
|
+
`rebuild()` sets `pending` when a build is in flight — so the settled check could never pass
|
|
189
|
+
under contention and it re-armed forever, spinning the dev server into ~30 builds and a
|
|
190
|
+
`process.exit(1)` blaming the user's watcher config. It was correct when idle, which is why
|
|
191
|
+
it looked fine. A re-review caught it; the decision is now a pure function with tests for
|
|
192
|
+
the contended orderings, which had no coverage at all.) A reply used to be
|
|
193
|
+
released by an already-running build one line before a queued rebuild began wiping the output
|
|
194
|
+
directory — success reported while the tree was being emptied, right before the documented
|
|
195
|
+
`git add`.
|
|
196
|
+
- **It does not believe a reply it does not recognise.** A dev server predating this endpoint
|
|
197
|
+
answers the POST with the SPA shell; that now reports "restart your dev server" instead of
|
|
198
|
+
accusing your build of failing. Dev servers here live for days running the code they loaded,
|
|
199
|
+
so that is the normal upgrade path.
|
|
200
|
+
- **Its timeout is the server's real one.** The client promised ten minutes while `Bun.serve`
|
|
201
|
+
idles connections at 120s, so a _successful_ long build was reported as a failure. "Did not
|
|
202
|
+
answer in time — the build may still be running" is now distinguished from "unreachable".
|
|
203
|
+
|
|
204
|
+
`<tosi-pocket-bar>` also gained a public `close()`. The live-example bar was setting `open =
|
|
205
|
+
false` directly, which left the component's internal pin set — so it re-opened on hover, stuck
|
|
206
|
+
open over the content, and the next handle tap did nothing. On touch, where this release removes
|
|
207
|
+
the hover path, there was no way back at all.
|
|
208
|
+
|
|
209
|
+
### Builds stopped fighting the dev server
|
|
210
|
+
|
|
211
|
+
`bun run build` with a dev server running now **asks it to build** instead of refusing, and
|
|
212
|
+
`bun run test-browser` uses its **own port (8798)** instead of reclaiming 8787 by killing
|
|
213
|
+
whatever holds it. Both produced the same workflow — kill the server, build, forget to restart
|
|
214
|
+
it — and the second twice took a live tunnel offline, where the symptom is a page reporting
|
|
215
|
+
"offline" with no hint which half died.
|
|
216
|
+
|
|
217
|
+
The generated ePub is **reproducible** now. It differed on every build, so it landed in every
|
|
218
|
+
diff and made a clean tree impossible after building — two independent causes:
|
|
219
|
+
`dcterms:modified` defaulted to `new Date()`, and the zip stored each file's mtime, and those
|
|
220
|
+
files had just been written. Entry mtimes are normalised to a constant before zipping (zip has
|
|
221
|
+
no flag for it; normalising the inputs is the standard fix), and the date is anchored to the
|
|
222
|
+
last commit that touched `package.json` rather than to a clock or to HEAD. After
|
|
223
|
+
commit-then-rebuild the tree carries one changed file (`docs/version.json`, which records the
|
|
224
|
+
commit by design) where it used to carry 68 plus a binary.
|
|
225
|
+
|
|
226
|
+
### Icons
|
|
227
|
+
|
|
228
|
+
`shift` (⇧) — the standard shift-key outline, asked for by tosijs-3d and general enough to live
|
|
229
|
+
here rather than in one consumer. `tab` (⇥) — an arrow pointing at a vertical bar, built on
|
|
230
|
+
`arrowRight`'s proportions. And `fatArrowUp`/`fatArrowRight`/`fatArrowDown`/`fatArrowLeft`: one
|
|
231
|
+
drawing and four rotations via the same redirect mechanism the arrow and chevron families use.
|
|
232
|
+
|
|
233
|
+
The shift glyph is centred on 12,12 (apex y=4, base y=20; it was 3..20, half a pixel high) —
|
|
234
|
+
invisible head-on, and what makes each rotation land square instead of drifting off-centre.
|
|
235
|
+
|
|
236
|
+
With `delete`, `cornerDownLeft` and `command` already present, an on-screen keyboard now has
|
|
237
|
+
everything except caps-lock.
|
|
238
|
+
|
|
239
|
+
### Smaller things
|
|
240
|
+
|
|
241
|
+
- **`<tosi-pocket-bar>` handles are brandable**: `--tosi-pocket-handle-bg`,
|
|
242
|
+
`--tosi-pocket-handle-radius` and `--tosi-pocket-handle-size`, all defaulting to exactly their
|
|
243
|
+
previous values, so nothing changes for an existing consumer. A circle needs all three —
|
|
244
|
+
`border-radius: 50%` on a content-sized flex button is an ellipse.
|
|
245
|
+
- The live-example handle uses them for the owl badge, and its **controls were reordered** so
|
|
246
|
+
maximize sits nearest the handle and tests furthest.
|
|
247
|
+
- The dead `.view-source` CSS went with the button it styled, rather than leaving rules for an
|
|
248
|
+
element nothing creates.
|
|
249
|
+
- **`bindText` in `<tosi-table>` briefly moved to `textContent`, then reverted back to
|
|
250
|
+
`bindText`.** `data-table.ts:2179` holds a proxy, which is the only shape the replacement
|
|
251
|
+
accepts — but with the deprecation withdrawn upstream, keeping it would have left the file
|
|
252
|
+
using two idioms for the same job with nothing gained. Net change: zero lines.
|
|
253
|
+
|
|
254
|
+
## 1.12.8
|
|
255
|
+
|
|
256
|
+
### Diffs: pick a side by clicking it, and see what actually changed
|
|
257
|
+
|
|
258
|
+
Two things, and they apply to **both** diff views — the resolvable one and the read-only one
|
|
259
|
+
used by `<tosi-code>`'s review overlay and the doc-browser's before-save screen.
|
|
260
|
+
|
|
261
|
+
**Clicking a coloured line picks that side**, equivalent to its button. The line is what a
|
|
262
|
+
reader is already looking at and pointing at; reaching for a small button to say "this one"
|
|
263
|
+
when the thing is under the cursor was friction with nothing behind it. Dragging to select
|
|
264
|
+
text does **not** change anything — those lines are also what you copy from, and without that
|
|
265
|
+
guard, selecting a line to copy it would silently flip the resolution and rewrite the value.
|
|
266
|
+
|
|
267
|
+
**Within a changed line, the words that actually differ are marked** more strongly than the
|
|
268
|
+
line around them. A whole-line wash says "something here changed", which for a one-word edit
|
|
269
|
+
throws away most of the signal — so the line colour tells you which side you are on, and the
|
|
270
|
+
run tells you what moved. `diffTokens(before, after)` and `tokenRunsForLines(lines)` are
|
|
271
|
+
exported if you want that data without the DOM.
|
|
272
|
+
|
|
273
|
+
Word runs, not characters: a character diff of `sat` → `sprawled` marks the shared `s` and
|
|
274
|
+
produces confetti. Whitespace and punctuation are tokens so the runs **reassemble each input
|
|
275
|
+
exactly** — a diff viewer that silently drops a space misrepresents the text it exists to show
|
|
276
|
+
— and whitespace between two changed words is absorbed into the change, or `sat on` renders as
|
|
277
|
+
two boxes with a hole punched between them. Lines over 400 tokens fall back to whole-line
|
|
278
|
+
marking rather than locking a tab on a minified bundle pasted into a diff.
|
|
279
|
+
|
|
280
|
+
The read-only view keeps its line order exactly. Pairing is computed per run of consecutive
|
|
281
|
+
changed lines, so an interleaved edit is marked correctly without the reordering that the
|
|
282
|
+
resolvable view's block grouping does.
|
|
283
|
+
|
|
284
|
+
`tokenRunsForLines` returns a **dense** array — one entry per input line, `undefined` where a
|
|
285
|
+
line has no runs. Worth stating because the obvious implementation (`new Array(n)`) is sparse,
|
|
286
|
+
and `map`/`forEach` silently skip holes: fine for the indexed read it was written for, a trap
|
|
287
|
+
for anyone iterating it.
|
|
288
|
+
|
|
289
|
+
### Generated asset URLs are build-stamped
|
|
290
|
+
|
|
291
|
+
`/doc-system.css` and the hydration bundle are now emitted with a `?v=` stamp taken from the
|
|
292
|
+
same build identity that writes `/version.json`, so a page cannot claim one build while
|
|
293
|
+
loading another's cached bundle. Stale filenames going stale is not hypothetical — it happened
|
|
294
|
+
on this project's own doc site and cost a round of "is this a layout regression?" before
|
|
295
|
+
turning out to be a cached bundle, with a hard reload as the fix nobody thinks to try.
|
|
296
|
+
|
|
297
|
+
A query rather than content-hashed filenames, deliberately: `docs/` is committed in this repo
|
|
298
|
+
and its siblings, so hashing would add and delete a file on every build and put churn in every
|
|
299
|
+
diff.
|
|
300
|
+
|
|
301
|
+
**The stamp is the project's own version**, so it moves once per release — the granularity at
|
|
302
|
+
which a published site actually changes — and a rebuild between releases is byte-identical. The
|
|
303
|
+
first attempt derived it from the commit, which was precise and unusable: it re-stamped all 68
|
|
304
|
+
generated pages on every commit, and because the test lanes build too, it did not terminate
|
|
305
|
+
(commit → build → 68 dirty → commit → build). The honest tradeoff of the version, stated rather
|
|
306
|
+
than left to be discovered: two builds of the same version with different code share a stamp, so
|
|
307
|
+
a mid-version redeploy can still serve a cached bundle. That is exactly the previous state — no
|
|
308
|
+
worse — and it closes the case that actually bit, a cached bundle surviving _across_ releases.
|
|
309
|
+
|
|
310
|
+
Cross-origin URLs are left alone — `scriptUrl` may point at a CDN, and busting someone else's
|
|
311
|
+
cache was never the point — as are URLs that already carry a query or fragment.
|
|
312
|
+
|
|
313
|
+
Consumers of `tosijs-ui/site` get this automatically; `generateSite` also takes an explicit
|
|
314
|
+
`assetStamp` if you are driving it directly. Leave it unset and the output is unchanged.
|
|
315
|
+
|
|
3
316
|
## 1.12.7
|
|
4
317
|
|
|
5
318
|
### Live-example and doc-test failures now tell you where
|
package/bin/make-icon-data.js
CHANGED
|
@@ -334,6 +334,17 @@ const manualRedirects = {
|
|
|
334
334
|
arrowDownRight: 'arrowUpRight90r',
|
|
335
335
|
arrowDownLeft: 'arrowUpRight180r',
|
|
336
336
|
arrowUpLeft: 'arrowUpRight270r',
|
|
337
|
+
|
|
338
|
+
// `shift` is the ⇧ key glyph and points UP, which is the wrong base for the
|
|
339
|
+
// `up-down-right-left/` folder convention (that expects a RIGHT-pointing base).
|
|
340
|
+
// Rather than redraw it sideways and make the keyboard glyph a rotation of an
|
|
341
|
+
// arrow nobody asked for, the four directions are declared here — the escape
|
|
342
|
+
// hatch this block exists for. The drawing is centred on 12,12 so each rotation
|
|
343
|
+
// lands square.
|
|
344
|
+
fatArrowUp: 'shift',
|
|
345
|
+
fatArrowRight: 'shift90r',
|
|
346
|
+
fatArrowDown: 'shift180r',
|
|
347
|
+
fatArrowLeft: 'shift270r',
|
|
337
348
|
}
|
|
338
349
|
Object.assign(iconRedirects, manualRedirects)
|
|
339
350
|
|
package/dist/diff.d.ts
CHANGED
|
@@ -39,6 +39,32 @@ export declare function resolveDiff(blocks: DiffBlock[], choices: DiffResolution
|
|
|
39
39
|
* in `after`), in display order.
|
|
40
40
|
*/
|
|
41
41
|
export declare function diffLines(before: string, after: string): DiffLine[];
|
|
42
|
+
/** A run of text within a changed line, flagged if it is part of what actually differs. */
|
|
43
|
+
export interface TokenRun {
|
|
44
|
+
text: string;
|
|
45
|
+
changed: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Word-level diff of two lines, as runs that reassemble each input exactly.
|
|
49
|
+
*
|
|
50
|
+
* Returns everything flagged `changed` when the lines are too long to diff cheaply, or when
|
|
51
|
+
* they share nothing — in both cases the containing line highlight already says it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function diffTokens(before: string, after: string): {
|
|
54
|
+
removed: TokenRun[];
|
|
55
|
+
added: TokenRun[];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Token runs for each line of a FLAT line diff, pairing within each run of changed lines
|
|
59
|
+
* without reordering anything.
|
|
60
|
+
*
|
|
61
|
+
* The resolvable view groups changes into blocks, which reorders an interleaved edit
|
|
62
|
+
* (`-a +A -b +B` becomes `-a -b +A +B`) — better to resolve, but a changed rendering. The
|
|
63
|
+
* read-only view must keep its order exactly, and it turns out it does not have to give
|
|
64
|
+
* anything up for that: pairing is computed per run of consecutive non-context lines, and the
|
|
65
|
+
* lines are still emitted where they already were. Same marking, same order.
|
|
66
|
+
*/
|
|
67
|
+
export declare function tokenRunsForLines(lines: DiffLine[]): Array<TokenRun[] | undefined>;
|
|
42
68
|
interface DiffParts extends PartsMap {
|
|
43
69
|
body: HTMLElement;
|
|
44
70
|
}
|
|
@@ -68,6 +94,11 @@ export declare class TosiDiff extends Component<DiffParts> {
|
|
|
68
94
|
whiteSpace: string;
|
|
69
95
|
wordBreak: string;
|
|
70
96
|
};
|
|
97
|
+
'.diff-line .line-text': {
|
|
98
|
+
minWidth: string;
|
|
99
|
+
whiteSpace: string;
|
|
100
|
+
wordBreak: string;
|
|
101
|
+
};
|
|
71
102
|
'.diff-line .marker': {
|
|
72
103
|
textAlign: string;
|
|
73
104
|
userSelect: string;
|
|
@@ -111,6 +142,22 @@ export declare class TosiDiff extends Component<DiffParts> {
|
|
|
111
142
|
background: string;
|
|
112
143
|
color: string;
|
|
113
144
|
};
|
|
145
|
+
'.diff-line .text.changed': {
|
|
146
|
+
borderRadius: string;
|
|
147
|
+
padding: string;
|
|
148
|
+
};
|
|
149
|
+
'.diff-add .text.changed': {
|
|
150
|
+
background: string;
|
|
151
|
+
};
|
|
152
|
+
'.diff-remove .text.changed': {
|
|
153
|
+
background: string;
|
|
154
|
+
};
|
|
155
|
+
'.diff-line[data-choice]': {
|
|
156
|
+
cursor: string;
|
|
157
|
+
};
|
|
158
|
+
'.diff-line[data-choice]:hover': {
|
|
159
|
+
filter: string;
|
|
160
|
+
};
|
|
114
161
|
'.diff-line.not-chosen': {
|
|
115
162
|
opacity: string;
|
|
116
163
|
textDecoration: string;
|