Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ categories = ["network-programming", "web-programming"]
exclude.workspace = true

[workspace.package]
version = "0.28.19"
version = "0.28.20"
readme = "README.md"
edition = "2024"
authors = ["root@ltpp.vip"]
Expand All @@ -38,13 +38,13 @@ resolver = "3"
crate-type = ["rlib"]

[workspace.dependencies]
euv = { path = ".", version = "0.28.19" }
euv-ui = { path = "ui", version = "0.28.19" }
euv-cli = { path = "cli", version = "0.28.19" }
euv-core = { path = "core", version = "0.28.19" }
euv-engine = { path = "engine", version = "0.28.19" }
euv-macros = { path = "macros", version = "0.28.19" }
euv-example = { path = "example", version = "0.28.19" }
euv = { path = ".", version = "0.28.20" }
euv-ui = { path = "ui", version = "0.28.20" }
euv-cli = { path = "cli", version = "0.28.20" }
euv-core = { path = "core", version = "0.28.20" }
euv-engine = { path = "engine", version = "0.28.20" }
euv-macros = { path = "macros", version = "0.28.20" }
euv-example = { path = "example", version = "0.28.20" }

log = "0.4.34"
toml = "0.9.12"
Expand Down
12 changes: 7 additions & 5 deletions docs/src/app/const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,24 @@ pub(crate) const APP_GLOBAL_CSS: &str = "html, body { height: 100% !important; o
.c_euv_sidebar_group_title_root:hover { background: var(--muted, #f4f4f5) !important; color: var(--foreground, #000) !important; } \
.c_euv_sidebar_group_title_root_active, .c_euv_sidebar_group_title_root_active:hover { background: var(--accent) !important; color: var(--text-on-accent) !important; } \
.c_theme_dark .c_euv_sidebar_group_title_root:hover { background: var(--muted, #27272a) !important; color: var(--foreground, #fff) !important; } \
.c_euv_sidebar_link_active_flush, .c_euv_sidebar_group_title_active { position: relative !important; margin-left: calc(-1 * (8px + 8px + 1px)) !important; width: calc(100% + 8px + 8px + 1px) !important; } \
.c_euv_sidebar_link_active_flush, .c_euv_sidebar_group_title_active { position: relative !important; margin-left: -9px !important; width: calc(100% + 9px) !important; padding-left: calc(12px + 9px) !important; } \
.c_euv_sidebar_link_active_flush::before, .c_euv_sidebar_group_title_active::before { left: 0 !important; } \
.c_euv_sidebar_link_active_flush:hover::before { background: var(--accent, #000) !important; } \
\
.c_euv_doc_layout { max-width: 1160px !important; display: flex !important; flex-direction: row !important; width: 100% !important; } \
.c_euv_doc_layout { max-width: 1160px !important; display: flex !important; flex-direction: row !important; width: 100% !important; flex-shrink: 0 !important; } \
/* `min-height: 100%` fills the visible area of `c_app_main` so the tail can be pushed to the bottom; a viewport unit overshoots by the header. `space-between` puts the tail at the bottom when the article is short and lets it follow the article when the article is long. Not sticky, not fixed: the tail scrolls with the page. */ \
.c_euv_doc_content { display: flex !important; flex-direction: column !important; flex: 1 !important; justify-content: space-between !important; } \
.c_euv_doc_content article.md-body { display: block !important; flex: 0 0 auto !important; min-height: 0 !important; overflow: visible !important; } \
.c_euv_doc_content article.md-body > div { display: block !important; min-height: 0 !important; } \
.c_euv_doc_tail { display: block !important; flex: 0 0 auto !important; } \
.c_euv_doc_toc { width: 280px !important; flex-shrink: 0 !important; position: sticky !important; top: 0 !important; align-self: flex-start !important; max-height: 100% !important; overflow-y: auto !important; } \
.c_euv_doc_toc { width: 280px !important; flex-shrink: 0 !important; position: sticky !important; top: var(--padding-main-top, 24px) !important; align-self: flex-start !important; max-height: 100% !important; overflow-y: auto !important; } \
.c_euv_toc_link_nested { padding-left: 0.75rem !important; font-size: var(--font-sm, 0.875rem) !important; color: var(--muted-foreground, #555) !important; line-height: 1.5 !important; } \
.c_euv_toc_link, .c_euv_toc_link_nested { font-weight: 400 !important; } \
.c_euv_toc_link:hover, .c_euv_toc_link_nested:hover { color: var(--accent, #000) !important; font-weight: 700 !important; } \
.c_euv_toc_link:hover, .c_euv_toc_link_nested:hover { color: var(--accent, #000) !important; font-weight: 400 !important; } \
.c_euv_toc_link_active, .c_euv_toc_link_nested_active { color: var(--accent, #000) !important; font-weight: 700 !important; } \
.c_theme_dark .c_euv_toc_link:hover, .c_theme_dark .c_euv_toc_link_nested:hover, .c_theme_dark .c_euv_toc_link_active, .c_theme_dark .c_euv_toc_link_nested_active { color: var(--accent, #fff) !important; font-weight: 700 !important; } \
.c_euv_toc_link_active:hover, .c_euv_toc_link_nested_active:hover { color: var(--accent, #000) !important; font-weight: 700 !important; } \
.c_theme_dark .c_euv_toc_link:hover, .c_theme_dark .c_euv_toc_link_nested:hover { color: var(--accent, #fff) !important; font-weight: 400 !important; } \
.c_theme_dark .c_euv_toc_link_active, .c_theme_dark .c_euv_toc_link_nested_active, .c_theme_dark .c_euv_toc_link_active:hover, .c_theme_dark .c_euv_toc_link_nested_active:hover { color: var(--accent, #fff) !important; font-weight: 700 !important; } \
\
.c_euv_pagination { padding-bottom: var(--space-xl, 1.25rem) !important; gap: var(--gap-component, 1rem) !important; flex-wrap: nowrap !important; align-items: stretch !important; width: 100% !important; } \
.c_euv_pagination_link { padding: var(--space-md, 0.75rem) !important; gap: var(--space-2xs, 0.25rem) !important; min-width: 0 !important; max-width: none !important; } \
Expand Down
22 changes: 14 additions & 8 deletions ui/src/style/class/display/fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,14 @@ class! {
// the intermediate auto-height flex row makes `100%` degenerate to
// `auto`.
min-height: "100%";
// As a flex item of the scroll container's column, the default
// `flex-shrink: 1` lets this row be compressed to the `min-height`
// floor — 824px here — even though its content is 12780px tall. A
// sticky descendant can only travel inside its containing block, so
// the clamped row made `c_euv_doc_toc` unpin after roughly one
// viewport of scroll. Never shrink: the row must carry the article's
// full height so the sticky range spans the whole page.
flex-shrink: "0";
}

pub c_euv_doc_content {
Expand Down Expand Up @@ -1078,15 +1086,13 @@ class! {
flex-shrink: "0";
// Pin the TOC column to the viewport while the main content scrolls.
// The sticky scroll container is `c_app_main` (the overflow:auto
// wrapper), so `top: 0` keeps the TOC aligned with the top of the
// scrollable area for the whole article. The column is left at its
// natural flex stretch height (= the article's content height) so
// the sticky range covers the whole main column — without the stretch
// the column would shrink to its content height and the sticky would
// disengage as soon as the TOC's bottom exits the viewport.
// wrapper), so this offset is measured from that container's top edge.
// It tracks `padding-main-top` rather than a bare `0` so the pinned
// column keeps the same breathing room below the container edge as the
// article has below the same edge — the two columns stay on one
// vertical rhythm instead of the TOC riding flush against the top.
position: "sticky";
top: "0px";
padding-top: var!(padding-main-top);
top: var!(padding-main-top);
@media ((max-width: 1100px)) {
display: "none";
}
Expand Down
57 changes: 44 additions & 13 deletions ui/src/style/class/shell/fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ class! {
flex: "1";
height: "100%";
overflow: "auto";
padding: format!("{} {} {} {}", var!(padding-main-top), var!(edge-gutter), var!(padding-main-bottom), var!(edge-gutter));
padding: format!("{} {} 0 {}", var!(padding-main-top), var!(edge-gutter), var!(edge-gutter));
scrollbar-color: format!("{} {}", var!(scrollbar-thumb), var!(scrollbar-track));
::-webkit-scrollbar {
width: "6px";
Expand All @@ -601,7 +601,7 @@ class! {
background: var!(scrollbar-thumb-active);
}
@media ((max-width: 767px)) {
padding: format!("{} {} {} {}", var!(padding-main-top-mobile), var!(edge-gutter-mobile), var!(padding-main-bottom), var!(edge-gutter-mobile));
padding: format!("{} {} 0 {}", var!(padding-main-top-mobile), var!(edge-gutter-mobile), var!(edge-gutter-mobile));
scrollbar-width: "none";
::-webkit-scrollbar {
width: "0px";
Expand Down Expand Up @@ -994,11 +994,12 @@ class! {
font-weight: "600";
// Same flush fill as `c_euv_sidebar_link_active_flush`: a nested
// active group title should read as one block with the sidebar, not
// as a chip sitting 17px in from the edge. Keep the left padding so
// the label does not jump out from under the cursor, and pay for the
// shift with a matching negative margin.
// as a chip sitting in from the edge. The negative margin below pulls
// the fill out to this level's dashed guide; the left padding pays it
// straight back so the label keeps the exact x it has while inactive.
margin-left: format!("-{}px", var!(side-indent-num));
width: format!("calc(100% + {}px)", var!(side-indent-num));
padding-left: format!("calc({} + {}px)", var!(space-md), var!(side-indent-num));
:hover {
box-shadow: "none";
}
Expand Down Expand Up @@ -1077,17 +1078,25 @@ class! {
// An active child needs the accent fill to read as one continuous block
// against the sidebar, not as a chip floating inside its parent's gutter.
// `c_euv_sidebar_children` insets every nesting level by
// `margin-left + padding-left` and paints a dashed guide on the way in, so
// a nested active link that keeps its own background starts 17px to the
// right of the row above it. This variant pulls the active fill back out
// to the sidebar's own left edge while leaving non-active rows indented,
// so the highlight lines up with the header and the section label.
// The negative margin is safe because the row is the full width of its
// container: the fill grows leftward into the parent's padding and
// nothing to its left moves.
// `margin-left + padding-left` and paints a dashed guide on the way in.
// The active row must pull its fill back out only as far as the guide it
// hangs from — `margin-left + border-left-width`, i.e. `side-indent`
// (9px) — so the fill starts exactly at the dashed rail and nothing
// crosses it. Compensating by the *full* inset (margin + padding +
// border = 17px) is wrong: it also cancels the row's own `padding-left`,
// pushing the fill 8px past the guide and past the sidebar's left edge on
// shallower levels. The negative margin is otherwise safe because the row
// is the full width of its container: the fill grows leftward into the
// parent's padding and nothing to its left moves.
pub c_euv_sidebar_link_active_flush {
display: "block";
// Text must not move between the inactive and active states, so the
// negative margin that pulls the fill out to the dashed guide is paid
// back here as extra left padding. The two cancel for the label while
// the background still reaches the guide. Only `padding-left` is
// touched — the shorthand would drop the other three sides.
padding: format!("{} {}", var!(space-md), var!(space-md));
padding-left: format!("calc({} + {}px)", var!(space-md), var!(side-indent-num));
// `var!()` expands to a bare `var(--token)`, so the unit has to live
// inside the token — writing `-{}px` here would emit
// `calc(-var(--side-indent-num)px)`, which is not a length and gets
Expand Down Expand Up @@ -1135,13 +1144,35 @@ class! {
color: var!(muted-foreground);
cursor: "pointer";
line-height: "1.5";
// Regular weight by default: only the *active* entry carries weight,
// so hovering an inactive entry must not bold it or the weight would
// read as a false "you are here" cue on mouse-over alone.
font-weight: "400";
:hover {
color: var!(accent);
}
}

pub c_euv_toc_link_active {
c_euv_toc_link();
color: var!(accent);
font-weight: "700";
:hover {
font-weight: "700";
}
}

pub c_euv_toc_link_nested {
c_euv_toc_link();
padding-left: var!(space-lg);
}

pub c_euv_toc_link_nested_active {
c_euv_toc_link_nested();
color: var!(accent);
font-weight: "700";
:hover {
font-weight: "700";
}
}
}
34 changes: 20 additions & 14 deletions ui/src/style/css/const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ pub(crate) const EUV_MD_CSS: &str = r#"
position: relative;
font-weight: 700;
letter-spacing: -0.01em;
margin-top: 1.8em;
margin-bottom: 0.6em;
/* Vertical rhythm is token-driven so article copy sits on the same
scale as every other surface in the framework (`space-sm` between
text, `gap-component` before a block). em-based margins drift as
heading font sizes change, which is what made the docs article
spacing read wider than the rest of the UI. */
margin-top: var(--gap-component);
margin-bottom: var(--space-sm);
scroll-margin-top: 72px;
line-height: 1.3;
}
Expand All @@ -41,10 +46,9 @@ pub(crate) const EUV_MD_CSS: &str = r#"
font-weight: 400;
transition: opacity 0.15s ease-out;
user-select: none;
/* The `.md-body a` rule below applies `text-decoration: underline
dashed` to every anchor. The header-anchor is a typographic
icon, not a navigable link in the visual sense, so drop the
underline. */
/* The `.md-body a` rule below applies `text-decoration: underline` to
every anchor. The header-anchor is a typographic icon, not a
navigable link in the visual sense, so drop the underline. */
text-decoration: none;
}
.md-body h1:hover .header-anchor,
Expand Down Expand Up @@ -129,32 +133,34 @@ pub(crate) const EUV_MD_CSS: &str = r#"
}
}
.md-body p, .md-body ul, .md-body ol, .md-body blockquote, .md-body pre, .md-body table {
margin: 1em 0;
/* Token rhythm: `space-sm` between blocks of copy, matching
`c_page_title` / `c_page_subtitle` in the example app. */
margin: var(--space-sm) 0;
}
.md-body ul, .md-body ol {
padding-left: 1.4em;
}
.md-body ul { list-style: disc; }
.md-body ol { list-style: decimal; }
.md-body ul ul, .md-body ul ol, .md-body ol ul, .md-body ol ol {
margin: 0.25em 0;
margin: var(--space-xs) 0;
}
.md-body li { margin: 0.25em 0; }
.md-body li { margin: var(--space-xs) 0; }
.md-body li input[type="checkbox"] {
margin-right: 0.4em;
accent-color: var(--accent);
}
.md-body a {
color: var(--accent);
font-weight: 500;
/* Static underline, no hover variant. A link that restyles itself on
hover reads as a state change rather than a link, and the dashed ->
solid swap made every hovered link shimmer. Keep one treatment for
both states; the accent colour alone carries the affordance. */
text-decoration: underline;
text-underline-offset: 3px;
text-decoration-style: dashed;
text-decoration-color: var(--border);
}
.md-body a:hover {
text-decoration-style: solid;
text-decoration-color: var(--accent);
text-decoration-color: var(--border);
}
.md-body strong { font-weight: 700; }
.md-body em { font-style: italic; }
Expand Down
Loading
Loading