Skip to content

Commit ea71119

Browse files
authored
Merge branch 'main' into fix/azure-xregion-backup
2 parents 4c01a0d + 9c17ee4 commit ea71119

5 files changed

Lines changed: 111 additions & 20 deletions

File tree

src/components/ChangelogEntry.astro

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const isoDate = date;
6363
---
6464

6565
<article
66+
id={id}
6667
class="changelog-entry"
6768
data-date={isoDate}
6869
data-tags={tags.join(',')}
@@ -75,11 +76,15 @@ const isoDate = date;
7576
<div class="changelog-entry__tags">
7677
{tags.map((tag) => <ChangelogTag type={tag} />)}
7778
</div>
79+
</div>
80+
<div class="changelog-entry__heading">
81+
<slot name="heading" />
7882
<a href={`#${id}`} class="changelog-entry__anchor" aria-label={`Link to ${title}`}>
79-
#
83+
<span class="changelog-entry__anchor-icon" aria-hidden="true">
84+
<svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg>
85+
</span>
8086
</a>
8187
</div>
82-
<slot name="heading" />
8388
</header>
8489

8590
<div class="changelog-entry__content">
@@ -194,36 +199,64 @@ const isoDate = date;
194199
box-sizing: border-box !important;
195200
}
196201

202+
/* Title row: heading text followed by the hover-reveal chain-link anchor,
203+
matching the heading anchor affordance on every other docs page. */
204+
/* Starlight's heading-anchor technique: the title is inline with trailing
205+
padding that reserves room for the icon, and the anchor is pulled back into
206+
that space with a negative margin. So the icon sits right after the last
207+
word and wraps *with* it — never flush-right on a two-line title, never
208+
orphaned onto its own line. */
209+
.changelog-entry__heading {
210+
margin-bottom: 0.375rem;
211+
}
212+
213+
/* The title <h2> is slotted from the MDX, so it doesn't carry this
214+
component's scoped class — target it with :global or the rule won't apply. */
215+
.changelog-entry__heading > :global(.changelog-entry__title) {
216+
display: inline;
217+
padding-inline-end: 1.55rem;
218+
}
219+
197220
.changelog-entry__anchor {
198-
opacity: 0;
199-
font-size: 1rem;
200-
height: 1.75rem;
221+
/* Hidden until the title is hovered/focused, then the icon pops up. */
201222
display: inline-flex;
202223
align-items: center;
203-
justify-content: center;
204-
flex-shrink: 0;
205-
margin: 0;
206-
padding: 0;
224+
margin-inline-start: -1.25rem;
225+
opacity: 0;
207226
color: light-dark(#9ca3af, #6b7280);
208227
text-decoration: none;
209-
transition: opacity 0.15s ease, color 0.15s ease, outline-color 0.15s ease;
228+
vertical-align: middle;
229+
user-select: none;
230+
-webkit-user-select: none;
231+
transition: opacity 0.15s ease, color 0.15s ease;
210232
}
211233

212-
.changelog-entry:hover .changelog-entry__anchor {
213-
opacity: 1;
234+
.changelog-entry__anchor-icon {
235+
display: inline-flex;
236+
line-height: 0;
237+
}
238+
239+
.changelog-entry__anchor-icon > svg {
240+
display: block;
241+
width: 1.25rem;
242+
height: 1.25rem;
214243
}
215244

216-
.changelog-entry__anchor:hover,
217-
.changelog-entry__anchor:focus,
245+
/* Reveal AND turn accent purple whenever the whole title row is hovered
246+
(not just the icon) or the link is focused, matching other pages. */
247+
.changelog-entry__heading:hover .changelog-entry__anchor,
218248
.changelog-entry__anchor:focus-visible {
219249
opacity: 1;
220250
color: light-dark(#6446fb, #f5ff80);
251+
}
252+
253+
.changelog-entry__anchor:focus-visible {
221254
outline: 2px solid light-dark(#6446fb, #f5ff80);
222255
outline-offset: 2px;
223256
}
224257

225258
.changelog-entry__title {
226-
margin: 0 0 0.375rem 0;
259+
margin: 0;
227260
font-size: 1.375rem;
228261
font-weight: 600;
229262
line-height: 1.25;

src/components/ChangelogFilter.astro

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -766,10 +766,14 @@ const tagOptions = [
766766
params.set('tags', Array.from(activeFilters).join(','));
767767
}
768768

769-
const newURL = params.toString()
770-
? `${window.location.pathname}?${params.toString()}`
771-
: window.location.pathname;
772-
769+
// Preserve any deep-link hash (#entry-id) so pasting an entry link still
770+
// scrolls to it — updateURL runs on load via applyFilters and would
771+
// otherwise strip the fragment.
772+
const hash = window.location.hash;
773+
const newURL = params.toString()
774+
? `${window.location.pathname}?${params.toString()}${hash}`
775+
: `${window.location.pathname}${hash}`;
776+
773777
window.history.replaceState({}, '', newURL);
774778
};
775779

src/components/HeadScripts/TocScrollSpy.astro

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,36 @@
2525
return h + 24;
2626
}
2727

28+
/** The nearest scrollable ancestor of a TOC link (the rail column). */
29+
function scrollContainerOf(el) {
30+
var node = el.parentElement;
31+
while (node) {
32+
var oy = getComputedStyle(node).overflowY;
33+
if ((oy === "auto" || oy === "scroll") && node.scrollHeight > node.clientHeight + 1)
34+
return node;
35+
node = node.parentElement;
36+
}
37+
return null;
38+
}
39+
40+
/**
41+
* Keep the active TOC link visible by scrolling ONLY the rail's own scroll
42+
* container — never the window (so following the highlight can't move the
43+
* page). No-ops when the TOC fits without scrolling.
44+
*/
45+
function keepActiveVisible(link) {
46+
var c = scrollContainerOf(link);
47+
if (!c) return;
48+
var cr = c.getBoundingClientRect();
49+
var lr = link.getBoundingClientRect();
50+
var margin = 16;
51+
if (lr.top < cr.top + margin) {
52+
c.scrollTop -= cr.top + margin - lr.top;
53+
} else if (lr.bottom > cr.bottom - margin) {
54+
c.scrollTop += lr.bottom - (cr.bottom - margin);
55+
}
56+
}
57+
2858
/** Resolve the heading element a TOC hash points at. */
2959
function headingFor(hash) {
3060
var id = decodeURIComponent(hash.slice(1));
@@ -46,6 +76,7 @@
4676
return out;
4777
}
4878

79+
var lastActive = null;
4980
function update() {
5081
var entries = collect();
5182
if (!entries.length) return;
@@ -67,6 +98,12 @@
6798
link.removeAttribute("aria-current");
6899
}
69100
}
101+
// When the active section changes, scroll the rail so its highlight stays
102+
// visible even once it passes the bottom of the TOC's own scroll area.
103+
if (active !== lastActive) {
104+
lastActive = active;
105+
keepActiveVisible(active);
106+
}
70107
}
71108

72109
/**

src/components/PageSidebar.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ const hasLearnMore =
6464
width: calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x));
6565
/* Give the column enough height for LearnMore's position:sticky bottom
6666
to "float" above the page scroll. On pages without the card this
67-
just adds harmless empty space below the TOC. */
67+
just adds harmless empty space below the TOC.
68+
The matching max-height + internal scroll (so a long TOC stays
69+
reachable) is set in `src/styles/toc.css`. */
6870
display: flex;
6971
flex-direction: column;
7072
min-height: calc(100dvh - var(--sl-nav-height, 4rem) - 2rem);

src/styles/toc.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010
padding-inline-start: 1rem;
1111
}
1212

13+
/* Make a long "On this page" TOC scroll inside the rail.
14+
PageSidebar wraps the TOC in `.sl-container` (a flex column sized to the
15+
visible rail height via `min-height`, so LearnMore can float to the bottom).
16+
That wrapper sits between `.right-sidebar-panel` and `starlight-toc`, so
17+
Stainless's own `.right-sidebar-panel > starlight-toc { overflow-y: auto }`
18+
scroll rule no longer matches and a tall TOC overflows off-screen with its
19+
lower entries unreachable. Cap the wrapper at the same viewport-based height
20+
and let it scroll. `overflow: auto` shows no scrollbar until it's needed, so
21+
short pages are unaffected. */
22+
.right-sidebar-panel > .sl-container {
23+
max-height: calc(100dvh - var(--sl-nav-height, 4rem) - 2rem);
24+
overflow-y: auto;
25+
overflow-x: clip;
26+
}
27+
1328
/* Base typography */
1429
.right-sidebar-container .right-sidebar,
1530
.right-sidebar-panel,

0 commit comments

Comments
 (0)