fix: scroll to tab header + add crowdin config

scarlett
NGPixel 3 weeks ago
parent aae079731b
commit b1261f4606
No known key found for this signature in database

@ -29,6 +29,7 @@ jobs:
run: | run: |
yq -iP '.version = strenv(REL_VERSION)' backend/package.json -o json yq -iP '.version = strenv(REL_VERSION)' backend/package.json -o json
yq -iP '.version = strenv(REL_VERSION)' frontend/package.json -o json yq -iP '.version = strenv(REL_VERSION)' frontend/package.json -o json
- name: Build Assets - name: Build Assets
working-directory: frontend working-directory: frontend
run: | run: |
@ -57,8 +58,8 @@ jobs:
context: . context: .
file: dev/build/Dockerfile file: dev/build/Dockerfile
push: true push: true
platforms: linux/amd64 # platforms: linux/amd64
# platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
tags: | tags: |
ghcr.io/requarks/wiki:3.0.0-alpha ghcr.io/requarks/wiki:3.0.0-alpha
ghcr.io/requarks/wiki:${{ env.REL_VERSION }} ghcr.io/requarks/wiki:${{ env.REL_VERSION }}
@ -82,3 +83,11 @@ jobs:
name: build name: build
path: wiki-js.tar.gz path: wiki-js.tar.gz
- name: Upload translations source file
uses: crowdin/github-action@v2
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
with:
command: 'upload'
command_args: '--config dev/crowdin.yml'

@ -45,6 +45,13 @@ from inside `backend/`. It boots in three phases: `preBoot()` (config → db →
scheduler → event emitters), `initHTTPServer()` (Fastify plugins, auth, routes), `postBoot()` scheduler → event emitters), `initHTTPServer()` (Fastify plugins, auth, routes), `postBoot()`
(refresh locales/strategies/sites from disk & db, start scheduler). (refresh locales/strategies/sites from disk & db, start scheduler).
Started with **`--no-experimental-webstorage`** — by the npm scripts and by the production image's
`CMD`, which is the only reason a bare `node backend` still opens with an experimental warning about
`localStorage`. Nothing here uses Web Storage; `lib0`, under yjs, probes for it as it loads the way a
library that runs in a browser too has to, and Node 26 answers that probe with a warning instead of a
value unless `--localstorage-file` is given. Off, the global is absent and the probe takes its node
path in silence.
- `api/` — REST route plugins, one file per resource (`sites.ts`, `users.ts`, `pages.ts`, - `api/` — REST route plugins, one file per resource (`sites.ts`, `users.ts`, `pages.ts`,
`system.ts`, `locales.ts`, `authentication.ts`), registered by `api/index.ts` under the `/_api` `system.ts`, `locales.ts`, `authentication.ts`), registered by `api/index.ts` under the `/_api`
prefix. prefix.

@ -25,8 +25,8 @@
"type": "module", "type": "module",
"main": "index.ts", "main": "index.ts",
"scripts": { "scripts": {
"start": "cd .. && node backend", "start": "cd .. && node --no-experimental-webstorage backend",
"dev": "cd .. && nodemon backend --watch backend --ext js,ts,json", "dev": "cd .. && nodemon --no-experimental-webstorage backend --watch backend --ext js,ts,json",
"typecheck": "tsc", "typecheck": "tsc",
"typecheck:watch": "tsc --watch", "typecheck:watch": "tsc --watch",
"ncu": "ncu -i", "ncu": "ncu -i",

@ -12,6 +12,16 @@ import { DarkMode } from '../shared/theme.js'
*/ */
const REVEAL_EVENT = 'block-reveal' const REVEAL_EVENT = 'block-reveal'
/**
* The room a heading in an article is given above it when it is scrolled to, in pixels.
*
* The article scrolls in its own column inside a fixed shell, so a heading brought into view stops
* clear of the column's top edge rather than flush against it. Written out because a block cannot read
* the app's stylesheet: this is the `scroll-margin-top: 1.25rem` that `_page-contents.scss` puts on
* every heading, and the two have to be kept in step.
*/
const HEADING_CLEARANCE = 20
/** /**
* Block Tabs * Block Tabs
*/ */
@ -230,27 +240,40 @@ Content of the second tab.
} }
/** /**
* Keep the strip on screen when something inside a panel is scrolled to. * Keep the whole block on screen when something inside a panel is scrolled to.
* *
* A heading carries a `scroll-margin-top` so it does not land flush against the top edge, but that * A heading carries a `scroll-margin-top` so it does not land flush against the top edge, but that
* margin knows nothing about the strip standing above it following a link to a heading in a tab * margin knows nothing about the strip standing above it following a link to a heading in a tab
* would scroll the tabs themselves out of view, leaving the reader in a panel with no way to see * would scroll the tabs themselves out of view, leaving the reader in a panel with no way to see
* which one they were in. Set on the elements because the content is slotted, and measured because * which one they were in. Set on the elements because the content is slotted, and measured because
* the strip is as tall as the labels wrapped onto however many rows. * a block cannot be told in CSS how tall its own strip is.
*
* Measured from the panel back up to the block, so that what a scroll clears is everything above
* the panel rather than the strip alone: the strip may have wrapped onto two rows, the frame draws
* a border, and the panel pads itself. Clearing only the strip's height put the tabs *just* on the
* edge of the column with nothing above them, and a tab acting as a page heading `header` on
* `block-tab`, anchored on the panel because the label is an attribute and no heading in the page
* carries it is aimed at from the contents list, so it landed with its own label against the
* edge. `HEADING_CLEARANCE` on top is what every heading in an article gets, so a tab arrived at
* from the contents list sits where a section heading would.
*/ */
_applyScrollMargin() { _applyScrollMargin() {
const strip = this.renderRoot.querySelector('.strip') const strip = this.renderRoot.querySelector('.strip')
if (!strip) { if (!strip) {
return return
} }
const margin = `${strip.offsetHeight + 20}px`
for (const { panel } of this._tabs) {
/* /*
The panel as well as what is in it. A tab whose label is a page heading `header` on From the open panel, which is the only one with a box to measure. They all sit in the same
`block-tab` is anchored on the panel element itself, since the label is an attribute and place, so its offset is every panel's and a block inside a panel that is not showing measures
there is no heading in the page to carry the anchor, so the panel is what a contents click nothing at all, which is why the strip's height stands in until there is something to read.
scrolls to and it needs the same margin as any heading in it.
*/ */
const open = this._tabs[this.active]?.panel
const above = open
? Math.round(open.getBoundingClientRect().top - this.getBoundingClientRect().top)
: strip.offsetHeight
const margin = `${above + HEADING_CLEARANCE}px`
for (const { panel } of this._tabs) {
// -> The panel as well as what is in it: either can be what a link or the contents list aims at
panel.style.setProperty('scroll-margin-top', margin) panel.style.setProperty('scroll-margin-top', margin)
for (const child of panel.children) { for (const child of panel.children) {
child.style.setProperty('scroll-margin-top', margin) child.style.setProperty('scroll-margin-top', margin)

@ -58,4 +58,11 @@ VOLUME ["/wiki/data/content"]
EXPOSE 3000 EXPOSE 3000
EXPOSE 3443 EXPOSE 3443
CMD ["node", "backend"] # Web Storage off, which is what it was before Node 26 turned it on by default.
#
# Nothing here uses `localStorage`, but `lib0` -- under yjs, which is what makes an editing session
# collaborative -- probes for it as it loads, the way a library that runs in both a browser and node
# has to. Without `--localstorage-file` that probe is answered with an experimental warning rather
# than a value, so the first line of every container's log was a warning about a feature the server
# does not use. Off, the global is absent and the probe takes its node path in silence.
CMD ["node", "--no-experimental-webstorage", "backend"]

@ -0,0 +1,6 @@
project_id: '921805'
preserve_hierarchy: false
files:
- source: 'backend/locales/en.json'
dest: 'app.json'
update_option: 'update_as_unapproved'
Loading…
Cancel
Save