tosijs-ui 1.9.8 → 1.10.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 +125 -6
- package/bin/caddy-install.ts +204 -0
- package/bin/deploy-preview.ts +8 -1
- package/bin/tunnel.ts +9 -2
- package/dist/data-table.d.ts +27 -1
- package/dist/data-table.js +282 -20
- package/dist/doc-system/site/build-lock.d.ts +50 -0
- package/dist/doc-system/site/build-lock.js +140 -0
- package/dist/doc-system/site/check-examples.js +12 -1
- package/dist/doc-system/site/dev-auth.d.ts +44 -7
- package/dist/doc-system/site/dev-auth.js +28 -9
- package/dist/doc-system/site/dev-server.d.ts +17 -0
- package/dist/doc-system/site/dev-server.js +110 -16
- package/dist/doc-system/site/docs.js +14 -1
- package/dist/doc-system/site/make-llms-txt.d.ts +7 -0
- package/dist/doc-system/site/make-llms-txt.js +17 -0
- package/dist/doc-system/site/orchestrator.d.ts +1 -0
- package/dist/doc-system/site/orchestrator.js +710 -654
- package/dist/doc-system/site/output-guard.d.ts +15 -0
- package/dist/doc-system/site/output-guard.js +15 -0
- package/dist/doc-system/site/site-config.d.ts +69 -5
- package/dist/doc-system/site/truncated-doc.d.ts +27 -0
- package/dist/doc-system/site/truncated-doc.js +55 -0
- package/dist/icons.js +31 -11
- package/dist/iife.js +41 -41
- package/dist/iife.js.map +6 -6
- package/dist/live-example/component.js +34 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +8 -1
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,115 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.10.0
|
|
4
|
+
|
|
5
|
+
Mostly a **silent-failure** release: several things that were quietly not working, or quietly
|
|
6
|
+
not being checked, now say so. If you use `tosijs-ui/site`, three of these have probably cost
|
|
7
|
+
you time already.
|
|
8
|
+
|
|
9
|
+
### `<tosi-table>`
|
|
10
|
+
|
|
11
|
+
**Scroll position survives a re-render.** Sorting, filtering, or toggling
|
|
12
|
+
`visibleGroupedRowIds` no longer throws you back to the top. The table anchors on the
|
|
13
|
+
**topmost visible row**, not on `scrollTop`, and restores that row to the same offset — the
|
|
14
|
+
distinction matters as soon as the row count changes, because the same pixel offset then
|
|
15
|
+
shows entirely different rows. Set `preserveScroll = false` for the case where a render means
|
|
16
|
+
"here is a different dataset" and starting at the top is right.
|
|
17
|
+
|
|
18
|
+
### The tunnel: links now work on a second device
|
|
19
|
+
|
|
20
|
+
A magic link is redeemable **repeatedly for 15 minutes** instead of dying on first use. The
|
|
21
|
+
old behaviour collided with the feature's own purpose: glance at a link and close the tab and
|
|
22
|
+
you need a new one; open it on a laptop then reach for your phone and it is dead — in a
|
|
23
|
+
workspace built for reading your uncommitted tree _on a phone_. One project had already
|
|
24
|
+
replaced it with a never-expiring link of their own, which is the tell.
|
|
25
|
+
|
|
26
|
+
The bound moved from _uses_ to _time_, not away. Set the level per project:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
tunnel: { linkPolicy: 'single-use', linkTtlMinutes: 2 } // ratchet up
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
An expired link is refused under either policy — widening reuse never widens lifetime.
|
|
33
|
+
|
|
34
|
+
Every user-facing string — including the line the CLI prints at the moment you share a link
|
|
35
|
+
— is derived from the policy in force, rather than asserting "single-use" regardless.
|
|
36
|
+
|
|
37
|
+
### Silent failures, now loud
|
|
38
|
+
|
|
39
|
+
- **`docPaths` are watched.** A root-level doc was served and rendered but never watched:
|
|
40
|
+
edit, save, refresh, stale page, no rebuild, no message — indistinguishable from a browser
|
|
41
|
+
cache. (#49)
|
|
42
|
+
- **Two builders on one output tree are refused,** naming the holder's pid and port. Both
|
|
43
|
+
wipe-then-repopulate `docs/`, so running a standalone build against a live dev server meant
|
|
44
|
+
each deleted what the other was writing; it killed a dev server with no error anywhere and
|
|
45
|
+
left `docs/` a fraction populated. A standalone `buildSite()` refuses; a second **dev
|
|
46
|
+
server** only warns and continues, because `bun playwright test` and `bun run test-browser`
|
|
47
|
+
each start their own and a hard failure there would break the lanes this protects. The lock
|
|
48
|
+
is stale-safe (a dead holder's lock is debris), re-entrant, released on Ctrl-C, and fails
|
|
49
|
+
open. (#51)
|
|
50
|
+
- **A truncated `/*#` doc block warns, by name.** Block comments cannot nest, so a `*/`
|
|
51
|
+
inside a doc demo ends the doc there — previously surfacing as parse errors pointing at
|
|
52
|
+
_prose_, never at the delimiter responsible. (#70)
|
|
53
|
+
- **Inline doc tests no longer under-report.** A page could report "done" before its examples
|
|
54
|
+
had run, so their tests were omitted rather than failed — silently, with the suite green.
|
|
55
|
+
**If you use `tosijs-ui/site` with async examples, your counts may have been wrong;** a
|
|
56
|
+
number that goes UP after upgrading is this fix.
|
|
57
|
+
|
|
58
|
+
### Build output
|
|
59
|
+
|
|
60
|
+
**The hydration bundle now builds into your site output, not `dist/`.** It was written into
|
|
61
|
+
the library tree with only the `.js` copied out, stranding the sourcemap in a directory you
|
|
62
|
+
publish and commit but never serve — 65 MiB across 216 packed blobs in one adopter, ~35% of
|
|
63
|
+
that repo's packed blob store, for a file nothing could load. Set `bundleOutDir` if your
|
|
64
|
+
bundle is itself a published artifact. If you have that history:
|
|
65
|
+
`git rm --cached dist/iife.js dist/iife.js.map` and gitignore them. (#69)
|
|
66
|
+
|
|
67
|
+
### Also in this release
|
|
68
|
+
|
|
69
|
+
- **`checkExamples` accepts `{ contextKeys: [...] }`** as well as a boolean, so a library
|
|
70
|
+
that documents ITSELF — whose examples import its own package name — keeps the guard on
|
|
71
|
+
instead of disabling it. The keys are **added to** the `tosijs` / `tosijs-ui` defaults, not
|
|
72
|
+
substituted for them. (#71)
|
|
73
|
+
- **`preview.tunnel.linkPolicy`** (`'window'` | `'single-use'`) and
|
|
74
|
+
**`preview.tunnel.linkTtlMinutes`** (default 15) set the link security level.
|
|
75
|
+
- **`SiteConfig.preview.host` is optional.** The documented practice supplies it from
|
|
76
|
+
`PREVIEW_HOST`, so a correct config used to fail typecheck. (#72)
|
|
77
|
+
- **`marked` peer is `^16.4.2 || ^17.0.0 || ^18.0.0`** — it was two majors behind `latest`, so
|
|
78
|
+
a consumer on a current marked got a peer violation on a fresh install. (#60)
|
|
79
|
+
- **The live-example docs now state that `test()` bodies run concurrently** within a fence,
|
|
80
|
+
and give the rule that follows: steps that depend on each other belong in one `test()`.
|
|
81
|
+
(#43)
|
|
82
|
+
- **`llms.txt` carries the browser-control affordance** when a project sets `haltijaDev`, so
|
|
83
|
+
an agent learns it can drive the running page instead of inferring behaviour from source.
|
|
84
|
+
(#18)
|
|
85
|
+
- **Host bootstrap in `doc-site-system.md` now gives the actual commands** rather than
|
|
86
|
+
describing the danger and leaving the dangerous step as an exercise.
|
|
87
|
+
- **`svg2DataUrl`: two real fixes, and one that is NOT done.** It wrote `strokeWidth` /
|
|
88
|
+
`strokeLinecap` / `strokeLinejoin` — names SVG ignores — on any element carrying inline
|
|
89
|
+
style, and explicit `fill` / `stroke` / `strokeWidth` arguments were clobbered by the style
|
|
90
|
+
pass. Both fixed.
|
|
91
|
+
|
|
92
|
+
**[#68](https://github.com/tonioloewald/tosijs-ui/issues/68) is NOT closed by this.** Most
|
|
93
|
+
icons carry their styling on the **root** `<svg>`, which this does not touch, so a
|
|
94
|
+
`stroked` icon — about 280 of ~287 — still serializes with no stroke at all and renders as
|
|
95
|
+
a solid black wedge. Verified after the fix: `svg2DataUrl(icons.chevronRight())` emits no
|
|
96
|
+
`stroke` and no `stroke-width`. Prefer inline `<svg>` over a data URL for stroked icons
|
|
97
|
+
until this lands.
|
|
98
|
+
|
|
99
|
+
- **`acquireBuildLock` / `lockDecision`** in `build-lock.ts` — the
|
|
100
|
+
output-tree lock behind the concurrent-build refusal. **Internal**: not exported from
|
|
101
|
+
`tosijs-ui/site`, so do not build on it yet. Say so if you want it public.
|
|
102
|
+
- New **`tosijs-caddy-install`** bin — installs the Caddy snippets the deploy/tunnel bins
|
|
103
|
+
need, **refuses** to install a template with placeholders left in it, keeps the outgoing
|
|
104
|
+
config as `Caddyfile.bak`, and is dry-run by default. That refusal was the reason this
|
|
105
|
+
existed as a private script; shipping the template without it was the wrong half to keep.
|
|
106
|
+
- The unit lane now covers `bin/` — `bunfig.toml`'s test root was `./src`, so tests anywhere
|
|
107
|
+
else were silently never collected.
|
|
108
|
+
|
|
109
|
+
> **Versioning note.** This is a patch that adds public API. Minors are for breaking changes
|
|
110
|
+
> and feature rollouts; additive, non-breaking extensions ship as patches. The contract you
|
|
111
|
+
> rely on is unchanged: **a patch never breaks you.**
|
|
112
|
+
|
|
3
113
|
## 1.9.8
|
|
4
114
|
|
|
5
115
|
**Grouped tables can now say how much of a group is showing.** The piece missing from 1.9.7's
|
|
@@ -54,14 +164,23 @@ know anything about grouping. It is additive to the filter's own result, so a fi
|
|
|
54
164
|
ranks as well as selects keeps its ranking.
|
|
55
165
|
|
|
56
166
|
Two details worth knowing. **Clusters keep your sort**: groups appear in first-appearance
|
|
57
|
-
order, so a group lands wherever its best-sorted row landed. Sorting the clusters by their
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
167
|
+
order, so a group lands wherever its best-sorted row landed. Sorting the clusters by their id
|
|
168
|
+
instead would have quietly thrown away the sort the user just clicked.
|
|
169
|
+
|
|
170
|
+
And repeated cells are **hidden, not emptied** — transparent text plus `display: none` on
|
|
171
|
+
element children, via `.tr:not(.table-cluster-first) .cluster-repeat`. Deliberately not
|
|
172
|
+
`visibility: hidden`, which also stops the cell painting its **background**: a pinned
|
|
173
|
+
column's background is the only thing masking the columns scrolling underneath it, so
|
|
174
|
+
repeated cells there became windows onto the scrolled content. And never `display: none` on
|
|
175
|
+
the cell itself — every cell is an item of the row's grid, so removing one pulls each later
|
|
176
|
+
cell a column left and the row renders under the wrong headers.
|
|
63
177
|
`table.isFirstInGroup(row)` answers the same question in JavaScript.
|
|
64
178
|
|
|
179
|
+
Group ids are **memoized per render** in a `WeakMap` — the id is computed several times for
|
|
180
|
+
one row in a single pass (clustering, parity, first-of-group, counts, then again per stamped
|
|
181
|
+
row), and the inferred form does a `JSON.stringify` each time. The map is replaced on every
|
|
182
|
+
render rather than kept, so a cached id can never outlive the values it came from.
|
|
183
|
+
|
|
65
184
|
Ungrouped tables are unaffected — all three properties default to off.
|
|
66
185
|
|
|
67
186
|
**The inline doc-test lane was under-reporting, and staying green about it.** If you use
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/*
|
|
3
|
+
Install the Caddy snippets `tosijs-deploy` / `tosijs-tunnel` depend on.
|
|
4
|
+
|
|
5
|
+
tosijs-caddy-install # DRY RUN — shows the diff, changes nothing
|
|
6
|
+
tosijs-caddy-install --go # substitute, validate, install, reload
|
|
7
|
+
tosijs-caddy-install --host=me@1.2.3.4 --go
|
|
8
|
+
tosijs-caddy-install --template=./my-Caddyfile --go
|
|
9
|
+
|
|
10
|
+
Both deploy bins write a fragment ending in `import preview_site` / `import tunnel_site`,
|
|
11
|
+
and those snippets have to exist on the box first — otherwise `caddy validate` fails on
|
|
12
|
+
every deploy forever, per project, and nothing routes.
|
|
13
|
+
|
|
14
|
+
This existed only as an inline `package.json` script in the tosijs-ui repo, so adopters got
|
|
15
|
+
the template and the warning but not the tool. That is the wrong half to keep private: the
|
|
16
|
+
template is inert text, while the VALUE here is the refusal to install it with placeholders
|
|
17
|
+
intact. Installing `{{ACME_EMAIL}}` and `__PREVIEW_TOKEN__` verbatim gives you a preview host
|
|
18
|
+
whose invite gate is a literal string published in a public repo, issuing certificates under
|
|
19
|
+
someone else's Let's Encrypt account. A hand-rolled `sed` pipeline has no such guard.
|
|
20
|
+
|
|
21
|
+
Three deliberate properties:
|
|
22
|
+
|
|
23
|
+
DRY RUN BY DEFAULT. This replaces `/etc/caddy/Caddyfile` wholesale. If the box serves
|
|
24
|
+
anything else through Caddy, that is destructive — so the dry run prints a diff against
|
|
25
|
+
the live file and you get to look at it before `--go`.
|
|
26
|
+
|
|
27
|
+
THE SECRET NEVER LEAVES THE BOX. Substitution happens remotely, reading
|
|
28
|
+
`/etc/caddy/preview.env`. `PREVIEW_TOKEN` is never transmitted, never appears in argv on
|
|
29
|
+
this machine, and is redacted out of the diff before it is printed.
|
|
30
|
+
|
|
31
|
+
VALIDATE BEFORE SWAP. `caddy fmt` then `caddy validate` run against a scratch file; the
|
|
32
|
+
live config is replaced by an atomic `mv` only once it is known to parse. A rejected
|
|
33
|
+
config leaves the running site untouched.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { existsSync, readFileSync } from 'fs'
|
|
37
|
+
import { resolveSiteConfig } from './resolve-site-config'
|
|
38
|
+
|
|
39
|
+
const siteConfig = await resolveSiteConfig()
|
|
40
|
+
|
|
41
|
+
const args = process.argv.slice(2)
|
|
42
|
+
const flag = (name: string): string | undefined => {
|
|
43
|
+
const hit = args.find((a) => a.startsWith(`--${name}=`))
|
|
44
|
+
return hit ? hit.slice(name.length + 3) : undefined
|
|
45
|
+
}
|
|
46
|
+
const has = (name: string): boolean => args.includes(`--${name}`)
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
Reject anything we do not understand, BEFORE reaching the network.
|
|
50
|
+
|
|
51
|
+
An unrecognised flag used to be ignored, so `--status` — which this bin has never had — read
|
|
52
|
+
as "no flags at all" and the bin went straight on to ssh into the configured host. A tool
|
|
53
|
+
that contacts someone's server must not treat an unknown instruction as consent to proceed.
|
|
54
|
+
*/
|
|
55
|
+
const KNOWN = ['--go', '--help']
|
|
56
|
+
const unknown = args.filter(
|
|
57
|
+
(a) => !KNOWN.includes(a) && !/^--(host|template)=/.test(a)
|
|
58
|
+
)
|
|
59
|
+
if (unknown.length || has('help')) {
|
|
60
|
+
const bad = unknown.length
|
|
61
|
+
console.error(
|
|
62
|
+
(bad ? `\nUnknown option(s): ${unknown.join(' ')}\n` : '') +
|
|
63
|
+
`\ntosijs-caddy-install — install the Caddy snippets the deploy/tunnel bins need\n\n` +
|
|
64
|
+
` tosijs-caddy-install DRY RUN — shows the diff, changes nothing\n` +
|
|
65
|
+
` tosijs-caddy-install --go substitute, validate, install, reload\n` +
|
|
66
|
+
` --host=user@box override the configured preview host\n` +
|
|
67
|
+
` --template=./path/to/Caddyfile use your own template\n`
|
|
68
|
+
)
|
|
69
|
+
process.exit(bad ? 1 : 0)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const host =
|
|
73
|
+
flag('host') ??
|
|
74
|
+
process.env.PREVIEW_HOST ??
|
|
75
|
+
// Legacy alias. This repo's own `deploy:index` script still asks for PREVIEW_SSH,
|
|
76
|
+
// so someone who exported the only variable it ever mentioned would otherwise be
|
|
77
|
+
// told "No preview host" with nothing pointing at why.
|
|
78
|
+
process.env.PREVIEW_SSH ??
|
|
79
|
+
siteConfig.preview?.host
|
|
80
|
+
const go = has('go')
|
|
81
|
+
|
|
82
|
+
if (!host) {
|
|
83
|
+
console.error(
|
|
84
|
+
`\nNo preview host.\n\n` +
|
|
85
|
+
` Set one once in your site config:\n` +
|
|
86
|
+
` preview: { host: 'user@example.com' }\n\n` +
|
|
87
|
+
` ...or pass it per-run:\n` +
|
|
88
|
+
` tosijs-caddy-install --host=user@example.com --go\n` +
|
|
89
|
+
` PREVIEW_HOST=user@example.com tosijs-caddy-install\n`
|
|
90
|
+
)
|
|
91
|
+
process.exit(1)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/*
|
|
95
|
+
The template ships inside the package, so the default is resolved relative to THIS file
|
|
96
|
+
rather than to the cwd — an adopter runs the bin from their own project, where `deploy/` is
|
|
97
|
+
their business and not ours.
|
|
98
|
+
*/
|
|
99
|
+
const templatePath =
|
|
100
|
+
flag('template') ?? new URL('../deploy/Caddyfile', import.meta.url).pathname
|
|
101
|
+
|
|
102
|
+
if (!existsSync(templatePath)) {
|
|
103
|
+
console.error(
|
|
104
|
+
`\nNo Caddyfile template at ${templatePath}\n\n` +
|
|
105
|
+
` The package ships one at node_modules/tosijs-ui/deploy/Caddyfile.\n` +
|
|
106
|
+
` Pass your own with --template=./path/to/Caddyfile\n`
|
|
107
|
+
)
|
|
108
|
+
process.exit(1)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const template = readFileSync(templatePath, 'utf8')
|
|
112
|
+
|
|
113
|
+
// The template is delivered inside a quoted heredoc, so it is copied verbatim with no shell
|
|
114
|
+
// expansion. A template containing the delimiter would truncate the file silently — refuse
|
|
115
|
+
// rather than install something that is not what was read.
|
|
116
|
+
const EOF_MARKER = 'TOSIJS_CADDY_TEMPLATE_EOF'
|
|
117
|
+
if (template.split('\n').some((line) => line.trim() === EOF_MARKER)) {
|
|
118
|
+
console.error(
|
|
119
|
+
`\nTemplate contains the line \`${EOF_MARKER}\`, which is used as the transfer\n` +
|
|
120
|
+
`delimiter. Rename that line in ${templatePath}.\n`
|
|
121
|
+
)
|
|
122
|
+
process.exit(1)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const remoteScript = `set -euo pipefail
|
|
126
|
+
umask 077
|
|
127
|
+
cat > /etc/caddy/Caddyfile.tpl <<'${EOF_MARKER}'
|
|
128
|
+
${template}
|
|
129
|
+
${EOF_MARKER}
|
|
130
|
+
|
|
131
|
+
if [ ! -f /etc/caddy/preview.env ]; then
|
|
132
|
+
echo "No /etc/caddy/preview.env on this box." >&2
|
|
133
|
+
echo "Create it with PREVIEW_TOKEN=, ACME_EMAIL= and PREVIEW_DOMAIN=." >&2
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
set -a; . /etc/caddy/preview.env; set +a
|
|
137
|
+
: "\${PREVIEW_TOKEN:?set PREVIEW_TOKEN in /etc/caddy/preview.env}"
|
|
138
|
+
: "\${ACME_EMAIL:?set ACME_EMAIL in /etc/caddy/preview.env}"
|
|
139
|
+
: "\${PREVIEW_DOMAIN:?set PREVIEW_DOMAIN in /etc/caddy/preview.env}"
|
|
140
|
+
|
|
141
|
+
sed -e "s|__PREVIEW_TOKEN__|\$PREVIEW_TOKEN|g" \\
|
|
142
|
+
-e "s|{{ACME_EMAIL}}|\$ACME_EMAIL|g" \\
|
|
143
|
+
-e "s|{{PREVIEW_DOMAIN}}|\$PREVIEW_DOMAIN|g" \\
|
|
144
|
+
/etc/caddy/Caddyfile.tpl > /etc/caddy/Caddyfile.new
|
|
145
|
+
|
|
146
|
+
if grep -qE '\\{\\{|__PREVIEW_TOKEN__' /etc/caddy/Caddyfile.new; then
|
|
147
|
+
echo "Unsubstituted placeholders remain — refusing to install:" >&2
|
|
148
|
+
grep -nE '\\{\\{|__PREVIEW_TOKEN__' /etc/caddy/Caddyfile.new >&2
|
|
149
|
+
rm -f /etc/caddy/Caddyfile.new
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
caddy fmt --overwrite /etc/caddy/Caddyfile.new
|
|
154
|
+
caddy validate --config /etc/caddy/Caddyfile.new
|
|
155
|
+
|
|
156
|
+
if [ "${go ? '1' : '0'}" = "1" ]; then
|
|
157
|
+
# Keep the outgoing config. This replaces a file that may be serving OTHER sites, and
|
|
158
|
+
# "validated" only means it parses — it does not mean it is the config you wanted.
|
|
159
|
+
if [ -f /etc/caddy/Caddyfile ]; then
|
|
160
|
+
cp -p /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak
|
|
161
|
+
fi
|
|
162
|
+
mv /etc/caddy/Caddyfile.new /etc/caddy/Caddyfile
|
|
163
|
+
systemctl reload caddy
|
|
164
|
+
echo "Installed /etc/caddy/Caddyfile and reloaded caddy."
|
|
165
|
+
echo "Previous config saved as /etc/caddy/Caddyfile.bak"
|
|
166
|
+
else
|
|
167
|
+
echo "--- would change /etc/caddy/Caddyfile (token redacted) ---"
|
|
168
|
+
if [ -f /etc/caddy/Caddyfile ]; then
|
|
169
|
+
diff -u /etc/caddy/Caddyfile /etc/caddy/Caddyfile.new \\
|
|
170
|
+
| sed "s|\$PREVIEW_TOKEN|***REDACTED***|g" || true
|
|
171
|
+
else
|
|
172
|
+
echo "(no existing /etc/caddy/Caddyfile — this would create it)"
|
|
173
|
+
fi
|
|
174
|
+
rm -f /etc/caddy/Caddyfile.new
|
|
175
|
+
echo "--- dry run: nothing changed. Re-run with --go to install. ---"
|
|
176
|
+
fi`
|
|
177
|
+
|
|
178
|
+
console.log(
|
|
179
|
+
`${
|
|
180
|
+
go ? 'Installing' : 'Checking'
|
|
181
|
+
} Caddy config on ${host} from ${templatePath}\n`
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
// The script goes over stdin rather than as an argv string: it contains the template
|
|
185
|
+
// verbatim, and quoting an arbitrary file through a shell argument is a bug waiting to
|
|
186
|
+
// happen.
|
|
187
|
+
const proc = Bun.spawn(['ssh', host, 'bash', '-s'], {
|
|
188
|
+
stdin: new TextEncoder().encode(remoteScript),
|
|
189
|
+
stdout: 'inherit',
|
|
190
|
+
stderr: 'inherit',
|
|
191
|
+
})
|
|
192
|
+
const exitCode = await proc.exited
|
|
193
|
+
|
|
194
|
+
if (exitCode !== 0) {
|
|
195
|
+
console.error(`\nFailed (exit ${exitCode}). Nothing was installed.\n`)
|
|
196
|
+
process.exit(exitCode)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!go) {
|
|
200
|
+
console.log(
|
|
201
|
+
`\nNext: re-run with --go once the diff above looks right.\n` +
|
|
202
|
+
`Then \`tosijs-deploy --go\` and \`tosijs-tunnel\` will validate.\n`
|
|
203
|
+
)
|
|
204
|
+
}
|
package/bin/deploy-preview.ts
CHANGED
|
@@ -51,7 +51,14 @@ const preview = siteConfig.preview
|
|
|
51
51
|
// Precedence: explicit flag > env > site config. The config is the "just works"
|
|
52
52
|
// default; the flag exists so you can push a one-off somewhere else without editing
|
|
53
53
|
// (and committing) a config file.
|
|
54
|
-
const host =
|
|
54
|
+
const host =
|
|
55
|
+
flag('host') ??
|
|
56
|
+
process.env.PREVIEW_HOST ??
|
|
57
|
+
// Legacy alias. This repo's own `deploy:index` script still asks for PREVIEW_SSH,
|
|
58
|
+
// so someone who exported the only variable it ever mentioned would otherwise be
|
|
59
|
+
// told "No preview host" with nothing pointing at why.
|
|
60
|
+
process.env.PREVIEW_SSH ??
|
|
61
|
+
preview?.host
|
|
55
62
|
const remotePath =
|
|
56
63
|
flag('path') ??
|
|
57
64
|
process.env.PREVIEW_PATH ??
|
package/bin/tunnel.ts
CHANGED
|
@@ -5,7 +5,7 @@ Expose THIS machine's dev server at an authenticated public URL.
|
|
|
5
5
|
tosijs-tunnel # open the tunnel (foreground; Ctrl-C closes it)
|
|
6
6
|
tosijs-tunnel --status # is one already up?
|
|
7
7
|
tosijs-tunnel --close # close any tunnel this project opened
|
|
8
|
-
tosijs-tunnel --link # ask the dev server to print a fresh
|
|
8
|
+
tosijs-tunnel --link # ask the dev server to print a fresh edit link
|
|
9
9
|
|
|
10
10
|
(In this repo `bun run tunnel` is a shortcut for the same thing.)
|
|
11
11
|
|
|
@@ -49,7 +49,14 @@ const flag = (n: string) => {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const preview = siteConfig.preview
|
|
52
|
-
const host =
|
|
52
|
+
const host =
|
|
53
|
+
flag('host') ??
|
|
54
|
+
process.env.PREVIEW_HOST ??
|
|
55
|
+
// Legacy alias. This repo's own `deploy:index` script still asks for PREVIEW_SSH,
|
|
56
|
+
// so someone who exported the only variable it ever mentioned would otherwise be
|
|
57
|
+
// told "No preview host" with nothing pointing at why.
|
|
58
|
+
process.env.PREVIEW_SSH ??
|
|
59
|
+
preview?.host
|
|
53
60
|
const localPort = Number(
|
|
54
61
|
flag('port') ?? resolveDevPort(siteConfig, process.env)
|
|
55
62
|
)
|
package/dist/data-table.d.ts
CHANGED
|
@@ -96,7 +96,11 @@ export declare class TosiTable extends WebComponent {
|
|
|
96
96
|
background: string;
|
|
97
97
|
};
|
|
98
98
|
':host .tr:not(.table-cluster-first) .cluster-repeat': {
|
|
99
|
-
|
|
99
|
+
color: string;
|
|
100
|
+
userSelect: string;
|
|
101
|
+
};
|
|
102
|
+
':host .tr:not(.table-cluster-first) .cluster-repeat > *': {
|
|
103
|
+
display: string;
|
|
100
104
|
};
|
|
101
105
|
':host .tr.table-cluster-odd': {
|
|
102
106
|
_tosiTableBg: string;
|
|
@@ -155,6 +159,7 @@ export declare class TosiTable extends WebComponent {
|
|
|
155
159
|
nohide: boolean;
|
|
156
160
|
noreorder: boolean;
|
|
157
161
|
localized: boolean;
|
|
162
|
+
nopreservescroll: boolean;
|
|
158
163
|
};
|
|
159
164
|
selectionChanged: SelectCallback;
|
|
160
165
|
rowRendered: ((item: any, cells: HTMLElement[]) => void) | null;
|
|
@@ -200,6 +205,7 @@ export declare class TosiTable extends WebComponent {
|
|
|
200
205
|
get nonRepeatingGroupedRowCells(): string[] | null;
|
|
201
206
|
set nonRepeatingGroupedRowCells(props: string[] | null);
|
|
202
207
|
/** The grouping function in force, or null when the table is ungrouped. */
|
|
208
|
+
private _groupIdMemo;
|
|
203
209
|
private get groupIdFn();
|
|
204
210
|
/**
|
|
205
211
|
* This row's group id, or null when the table is ungrouped.
|
|
@@ -223,6 +229,24 @@ export declare class TosiTable extends WebComponent {
|
|
|
223
229
|
get rowGroupCounts(): Map<string, GroupCount>;
|
|
224
230
|
private _grouping;
|
|
225
231
|
private _rowGroupCounts;
|
|
232
|
+
/**
|
|
233
|
+
* Keep the reader in place across a re-render (default `true`).
|
|
234
|
+
*
|
|
235
|
+
* Set `false` when a render means "here is a different dataset" rather than "here is the
|
|
236
|
+
* same data, re-viewed" — then starting at the top is the correct answer.
|
|
237
|
+
*
|
|
238
|
+
* Backed by the `nopreservescroll` ATTRIBUTE so it is settable from markup like every
|
|
239
|
+
* other boolean here (`nosort`, `nohide`, `noreorder`). The inverted name is not
|
|
240
|
+
* gratuitous: this defaults to `true`, and a presence-only attribute can only ever turn
|
|
241
|
+
* something ON — so a `preservescroll` attribute could never express "off", which is the
|
|
242
|
+
* only thing anyone needs to say. Getting this wrong is unfixable after release without a
|
|
243
|
+
* rename, so it is settled here rather than later.
|
|
244
|
+
*/
|
|
245
|
+
get preserveScroll(): boolean;
|
|
246
|
+
set preserveScroll(value: boolean);
|
|
247
|
+
/** In-flight scroll restore, so a newer one supersedes it and gestures can cancel it. */
|
|
248
|
+
private _scrollRestore;
|
|
249
|
+
private _scrollAnchor;
|
|
226
250
|
get sort(): SortCallback | undefined;
|
|
227
251
|
set sort(sortFunc: SortCallback | undefined);
|
|
228
252
|
get columns(): ColumnOptions[];
|
|
@@ -269,6 +293,8 @@ export declare class TosiTable extends WebComponent {
|
|
|
269
293
|
private updateSelection;
|
|
270
294
|
private findCell;
|
|
271
295
|
private _pendingFocus;
|
|
296
|
+
private captureScrollAnchor;
|
|
297
|
+
private restoreScrollAnchor;
|
|
272
298
|
private handleScrollEnd;
|
|
273
299
|
private focusCell;
|
|
274
300
|
private handleKeyNav;
|