mirror of
https://github.com/hectorqin/reader.git
synced 2026-11-13 06:20:54 +08:00
Merge remote-tracking branch 'origin/main' into auto/library-browse-files-split-2e69
把 #48 的朗读条与快捷列改动合进本分支,两处冲突都在 `web/src/ui/reader-chrome.tsx`,都取 main 那一侧: - 快捷列:main 删掉了 目录 / 行距 / 页边距 / 设置 四个入口(#48 的报告), 本分支新增的 `menu` / `line-height` / `indent` / `sliders` 随之作废。 留下 主题 / 字号 / 听书 三项,字号改用本分支新词汇表的 `text-size`, 听书改用 `volume`。 - 朗读条:main 拆成两行(控制行 + 进度行)并加了 `tts-stop`。本分支的单行 版本丢弃。图标名按本分支的词汇表回落:`backward-step`/`forward-step` → `step-backward`/`step-forward`,`xmark` → `close`。 为让两边都能编译,`icon-names.ts` 增加一层别名表 `ICON_ALIASES` (`font`/`volume-high`/`bars`/`gear`/`xmark`/`magnifying-glass`/`books` → 已画的码点),另导出 `ICON_CODEPOINT_TABLE`。别名是**拼写**不是字形: `icons.test.ts` 的借用名单断言只管 `paths.mjs`,新增两条断言钉住别名既 落在被否名单里、又必须指向字体真画了的码点;码点唯一性改按去重后的集合计。
This commit is contained in:
20 files changed
+1350
-273
No files matched your search
+147
-31
@@ -178,6 +178,19 @@
|
||||
*/
|
||||
--reader-band-inset: 0.75rem;
|
||||
|
||||
/*
|
||||
* The surface a floating band is drawn on.
|
||||
*
|
||||
* Translucent, with a blur, and that is what makes a bar over the text usable
|
||||
* now that the page is not inset for it: the reader can see the line the bar
|
||||
* covers *through* the bar, so the toolbar stops being a thing that hides a
|
||||
* paragraph. `color-mix` against the theme's own surface rather than a fixed
|
||||
* rgba, so the three themes get their own tint from one value; the fallback is
|
||||
* the opaque surface, which is what a host without `color-mix` draws and which
|
||||
* is exactly the old behaviour.
|
||||
*/
|
||||
--reader-surface-float: color-mix(in srgb, var(--reader-surface) 88%, transparent);
|
||||
|
||||
/*
|
||||
* The reader's own paint order, weakest first.
|
||||
*
|
||||
@@ -288,26 +301,29 @@ body {
|
||||
* and that is the whole of the "内容会上下滑动" fix: with the stage in flow, the
|
||||
* two bars took height out of it, so showing or hiding the chrome resized the page
|
||||
* and moved every line on it. Here the page is the screen, always, in both states.
|
||||
*
|
||||
* ## No block inset: the page *is* the screen
|
||||
* —
|
||||
* This rule used to reserve the bars' own heights as `padding-block`, on the
|
||||
* reasoning that a floating bar must not cover a line the reader is reading. The
|
||||
* reasoning was sound and the result was the report: "内容要铺满,不要预留 stage".
|
||||
* Reserving the bars' height means the reading column is permanently 9rem shorter
|
||||
* than the phone, in *both* chrome states — so a reader who has hidden the toolbar
|
||||
* and wants the whole screen for the book still loses the top and bottom bands to
|
||||
* empty paper, and the last line of every page sits 5.6rem above the bottom edge
|
||||
* for no reason at all.
|
||||
*
|
||||
* The column keeps its own `padding-block` (see `FLOW_STYLESHEET`), which is the
|
||||
* reader's *page* padding and is symmetric — the text is not flush with the glass
|
||||
* even when the bars are gone. What it is not is a variable reserve for chrome
|
||||
* that may not be there: the page occupies the whole slot and the bars are drawn
|
||||
* over it, which is what "浮动" means and what makes the text stand still when
|
||||
* they come and go.
|
||||
*/
|
||||
.stage-host {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
/*
|
||||
* The space the two floating bars occupy, kept clear of the text.
|
||||
*
|
||||
* Constant, and that is the entire point. The bars are a layer now, so they no
|
||||
* longer take height out of the page — but they do cover it, and a bar drawn over
|
||||
* the first line of a chapter is a bar that has hidden something the reader was
|
||||
* reading. So the page is inset by exactly the bars' own heights: the text is
|
||||
* never underneath them, and the inset does not change when they come or go, so
|
||||
* the text never moves either.
|
||||
*
|
||||
* `--reader-chrome-top` is the top bar's height and `--reader-chrome-bottom` the
|
||||
* footer's, both declared on `.reader-screen`: one number per bar, read by the bar
|
||||
* itself and by this rule, so the clearance and the bar cannot drift apart.
|
||||
*/
|
||||
padding-block: var(--reader-chrome-top) var(--reader-chrome-bottom);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -513,7 +529,11 @@ book-content {
|
||||
/* The band inset on both sides, so the outermost glyphs are the same distance
|
||||
from the device edge as every control in the footer below them. */
|
||||
padding-inline: var(--reader-band-inset);
|
||||
background: var(--reader-surface);
|
||||
/* A floating band over the page, not a solid shelf above it: the reader has to be
|
||||
able to read the line it covers. See `--reader-surface-float`. */
|
||||
background: var(--reader-surface-float);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
/* Same hairline treatment as the footer, so the two edges of the reading area
|
||||
are the same weight. */
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--reader-line) 80%, transparent);
|
||||
@@ -2156,7 +2176,10 @@ book-content {
|
||||
inset-inline: 0;
|
||||
bottom: 0;
|
||||
z-index: var(--reader-layer-bar);
|
||||
background: var(--reader-surface);
|
||||
/* The same floating surface as the top bar — over the page, see above. */
|
||||
background: var(--reader-surface-float);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
/* A hairline that survives a dark theme and a bright page equally: one solid
|
||||
colour has to fight whichever side it borders, and this is the one place the
|
||||
footer meets the paper. */
|
||||
@@ -2325,8 +2348,11 @@ book-content {
|
||||
* them. It used to be drawn only while the chrome was hidden, which made it the one
|
||||
* piece of chrome that survived immersion: a reader who tapped to get "just the
|
||||
* page" still had a floating column over the right edge of the text. The rail holds
|
||||
* the same destinations the top bar names (目录 / 主题 / 字号 / 设置), so it belongs to
|
||||
* the same state — the reader has asked for the controls — and it goes with them.
|
||||
* the two adjustments a reader makes from the page itself (主题 / 字号) plus 听书, so it
|
||||
* belongs to the same state the bars do — the reader has asked for the controls — and
|
||||
* it goes with them. The five destinations that used to be here (目录 / 字体 / 行距 /
|
||||
* 页边距 / 设置) are gone: all five open or reach a sheet, and a second, worse copy of
|
||||
* the settings panel floating over the text is not a shortcut.
|
||||
*
|
||||
* Positioned in the *page margin* rather than over the text, which is why it can be
|
||||
* on screen while a reader is reading: the reading column is capped by
|
||||
@@ -2522,17 +2548,40 @@ book-content {
|
||||
/* ---- read-aloud bar ---- */
|
||||
|
||||
/*
|
||||
* The bar sits above the footer and, unlike the footer, is *not* hidden with the
|
||||
* chrome: a reader listening while walking needs the play/pause without the
|
||||
* header covering the sentence being read.
|
||||
* The bar floats over the bottom of the page, and that is a *fix*, not a style
|
||||
* choice.
|
||||
*
|
||||
* It used to be an ordinary flex child of `.reader-screen`. The screen is a flex
|
||||
* column whose first child is `.stage-host`, and the stage host is
|
||||
* `position: absolute; inset: 0` — it covers the whole screen so the page does not
|
||||
* resize when the bars come and go. A static sibling in that column is therefore
|
||||
* laid out *behind* the reading surface, which paints at `--reader-layer-page`.
|
||||
* The bar was drawn, its buttons were focusable, and the stage was painted over
|
||||
* all of it: the reader could see the sentence being read and could not press
|
||||
* anything. That is the whole of "没法停止" — the stop button existed and was
|
||||
* unreachable, which reads as a control that does nothing.
|
||||
*
|
||||
* So it is a layer with the bars' own z-index (`--reader-layer-bar`), pinned above
|
||||
* the bottom edge and above the footer's own height, which is where its controls
|
||||
* have to be to be hit by a thumb. Unlike the footer it is *not* hidden with the
|
||||
* chrome: a reader listening while walking needs play/pause without the header
|
||||
* covering the sentence being read.
|
||||
*
|
||||
* `--reader-chrome-bottom` is the footer's own height, read here rather than
|
||||
* guessed, so the two bands cannot overlap however the footer's rows change.
|
||||
*/
|
||||
.tts-bar {
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
bottom: var(--reader-chrome-bottom);
|
||||
z-index: var(--reader-layer-bar);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
padding-bottom: calc(0.35rem + env(safe-area-inset-bottom));
|
||||
background: var(--reader-surface);
|
||||
background: var(--reader-surface-float);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-top: 1px solid var(--reader-line);
|
||||
}
|
||||
|
||||
@@ -2540,24 +2589,91 @@ book-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tts-bar .tts-main {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
/*
|
||||
* Two rows, stacked: the transport controls, then the sentence with its scrubber.
|
||||
*
|
||||
* A column rather than a single flex row because the single row could not fit —
|
||||
* see the comment on `.tts-bar`. The two rows take the full width of the bar, so
|
||||
* the five controls split it evenly and none of them is ever clipped.
|
||||
*/
|
||||
.tts-bar .tts-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tts-bar .tts-controls {
|
||||
/* Every control the same width, including the chip: a row whose cells are
|
||||
sized by their contents leaves the widest one (the ✕, whose label is a
|
||||
glyph) flush to the edge and the others bunched. Equal cells read as one
|
||||
transport, which is what the row is. */
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.tts-bar .tts-controls > * {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* The chip is a text control in a row of icons, so its own padding is dropped:
|
||||
the cell's width is the equal share, not the label's. */
|
||||
.tts-bar .tts-chip {
|
||||
appearance: none;
|
||||
border: 1px solid color-mix(in srgb, var(--reader-line) 90%, transparent);
|
||||
background: var(--reader-surface);
|
||||
color: var(--reader-muted);
|
||||
border-radius: 999px;
|
||||
height: 2.75rem;
|
||||
min-width: 0;
|
||||
padding: 0 0.4rem;
|
||||
font: inherit;
|
||||
font-size: var(--text-meta);
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.tts-bar .tts-chip:active {
|
||||
background: color-mix(in srgb, var(--reader-ink) 8%, transparent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop is the one destructive control on the bar and it is drawn as such.
|
||||
*
|
||||
* It is not a warning colour: this is a reading app, and a red button in the
|
||||
* middle of a page is a louder statement than "stop the voice". The accent fill
|
||||
* is enough to separate it from the three transport buttons beside it, which is
|
||||
* the whole job — the reader who wants the voice to stop has to be able to pick
|
||||
* this control out of a row of five at a glance.
|
||||
*/
|
||||
.tts-bar .tts-stop {
|
||||
background: color-mix(in srgb, var(--reader-accent) 16%, transparent);
|
||||
border-color: color-mix(in srgb, var(--reader-accent) 45%, transparent);
|
||||
color: var(--reader-ink);
|
||||
}
|
||||
|
||||
.tts-bar .tts-progress {
|
||||
gap: var(--space-2);
|
||||
padding-inline: 0.15rem;
|
||||
}
|
||||
|
||||
.tts-bar .tts-text {
|
||||
flex: 0 1 auto;
|
||||
max-width: 40%;
|
||||
font-size: 0.78rem;
|
||||
color: var(--reader-ink);
|
||||
color: var(--reader-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.tts-range {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
accent-color: var(--reader-accent);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user