mirror of
https://github.com/requarks/wiki.git
synced 2026-08-26 05:07:30 -05:00
fix: scroll to tab header + add crowdin config
This commit is contained in:
@@ -29,6 +29,7 @@ jobs:
|
||||
run: |
|
||||
yq -iP '.version = strenv(REL_VERSION)' backend/package.json -o json
|
||||
yq -iP '.version = strenv(REL_VERSION)' frontend/package.json -o json
|
||||
|
||||
- name: Build Assets
|
||||
working-directory: frontend
|
||||
run: |
|
||||
@@ -57,8 +58,8 @@ jobs:
|
||||
context: .
|
||||
file: dev/build/Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
# platforms: linux/amd64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
ghcr.io/requarks/wiki:3.0.0-alpha
|
||||
ghcr.io/requarks/wiki:${{ env.REL_VERSION }}
|
||||
@@ -82,3 +83,11 @@ jobs:
|
||||
name: build
|
||||
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()`
|
||||
(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`,
|
||||
`system.ts`, `locales.ts`, `authentication.ts`), registered by `api/index.ts` under the `/_api`
|
||||
prefix.
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
"type": "module",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"start": "cd .. && node backend",
|
||||
"dev": "cd .. && nodemon backend --watch backend --ext js,ts,json",
|
||||
"start": "cd .. && node --no-experimental-webstorage backend",
|
||||
"dev": "cd .. && nodemon --no-experimental-webstorage backend --watch backend --ext js,ts,json",
|
||||
"typecheck": "tsc",
|
||||
"typecheck:watch": "tsc --watch",
|
||||
"ncu": "ncu -i",
|
||||
|
||||
@@ -12,6 +12,16 @@ import { DarkMode } from '../shared/theme.js'
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -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
|
||||
* 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
|
||||
* 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() {
|
||||
const strip = this.renderRoot.querySelector('.strip')
|
||||
if (!strip) {
|
||||
return
|
||||
}
|
||||
const margin = `${strip.offsetHeight + 20}px`
|
||||
/*
|
||||
From the open panel, which is the only one with a box to measure. They all sit in the same
|
||||
place, so its offset is every panel's — and a block inside a panel that is not showing measures
|
||||
nothing at all, which is why the strip's height stands in until there is something to read.
|
||||
*/
|
||||
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. A tab whose label is a page heading — `header` on
|
||||
`block-tab` — is anchored on the panel element itself, since the label is an attribute and
|
||||
there is no heading in the page to carry the anchor, so the panel is what a contents click
|
||||
scrolls to and it needs the same margin as any heading in it.
|
||||
*/
|
||||
// -> 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)
|
||||
for (const child of panel.children) {
|
||||
child.style.setProperty('scroll-margin-top', margin)
|
||||
|
||||
@@ -58,4 +58,11 @@ VOLUME ["/wiki/data/content"]
|
||||
EXPOSE 3000
|
||||
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'
|
||||
Reference in New Issue
Block a user