Skip to content

Commit d636f36

Browse files
committed
Polish, Add CI
1 parent f014776 commit d636f36

10 files changed

Lines changed: 315 additions & 101 deletions

File tree

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, v2]
6+
pull_request:
7+
8+
concurrency:
9+
group: ci-${{ github.workflow }}-${{ github.ref }}
10+
cancel-in-progress: true
11+
12+
jobs:
13+
test:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Node
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: 22
23+
cache: npm
24+
25+
- name: Install dependencies
26+
run: npm ci
27+
28+
- name: Typecheck
29+
run: npm run typecheck
30+
31+
- name: Test
32+
run: npm test

src/content/App.tsx

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import {
77
} from 'react';
88
import { writeSidebarCollapsed } from '../lib/storage';
99
import type { ThemePreference, TocItem } from '../lib/types';
10-
import {
11-
getActiveTocIndex,
12-
identifyPrintBlock,
13-
} from '../host/toc';
10+
import { getActiveTocIndex, identifyPrintBlock } from '../host/toc';
1411
import { Sidebar } from './sidebar/Sidebar';
1512

1613
type AppProps = {
@@ -19,14 +16,9 @@ type AppProps = {
1916
initialTheme: ThemePreference;
2017
};
2118

22-
type SidebarPhase =
23-
| 'closed'
24-
| 'opening'
25-
| 'open'
26-
| 'closing-ready'
27-
| 'closing';
19+
type SidebarPhase = 'closed' | 'opening' | 'open' | 'closing-ready' | 'closing';
2820

29-
const SIDEBAR_TRANSITION_MS = 150;
21+
const SIDEBAR_TRANSITION_MS = 200;
3022
const SIDEBAR_TRANSITION_FALLBACK_MS = SIDEBAR_TRANSITION_MS + 100;
3123

3224
function syncSidebarPhase(phase: SidebarPhase): void {
@@ -38,18 +30,14 @@ function prefersReducedMotion(): boolean {
3830
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
3931
}
4032

41-
export function App({
42-
items,
43-
initialCollapsed,
44-
initialTheme,
45-
}: AppProps) {
33+
export function App({ items, initialCollapsed, initialTheme }: AppProps) {
4634
const [sidebarPhase, setSidebarPhase] = useState<SidebarPhase>(
47-
initialCollapsed ? 'closed' : 'open'
35+
initialCollapsed ? 'closed' : 'open',
4836
);
4937
const [paletteOpen, setPaletteOpen] = useState(false);
5038
const [activeIndex, setActiveIndex] = useState(0);
5139
const [dark, setDark] = useState(() =>
52-
document.body.classList.contains('dark')
40+
document.body.classList.contains('dark'),
5341
);
5442
const prepareFrameRef = useRef<number | null>(null);
5543
const startFrameRef = useRef<number | null>(null);
@@ -67,7 +55,7 @@ export function App({
6755
window.cancelAnimationFrame(startFrameRef.current);
6856
}
6957
},
70-
[]
58+
[],
7159
);
7260

7361
useEffect(() => {
@@ -94,10 +82,7 @@ export function App({
9482
}
9583

9684
container?.addEventListener('transitionend', onTransitionEnd);
97-
const fallback = window.setTimeout(
98-
finish,
99-
SIDEBAR_TRANSITION_FALLBACK_MS
100-
);
85+
const fallback = window.setTimeout(finish, SIDEBAR_TRANSITION_FALLBACK_MS);
10186
return () => {
10287
container?.removeEventListener('transitionend', onTransitionEnd);
10388
window.clearTimeout(fallback);
@@ -203,7 +188,11 @@ export function App({
203188
}, [paletteOpen]);
204189

205190
return (
206-
<div className={['sep-plus-app', dark ? 'is-dark' : ''].filter(Boolean).join(' ')}>
191+
<div
192+
className={['sep-plus-app', dark ? 'is-dark' : '']
193+
.filter(Boolean)
194+
.join(' ')}
195+
>
207196
<Sidebar
208197
items={items}
209198
activeIndex={activeIndex}

src/content/palette/Palette.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export function Palette({ open, onClose, anchorRef, dark }: PaletteProps) {
8989
const [loading, setLoading] = useState(false);
9090
const [loadError, setLoadError] = useState<string | null>(null);
9191
const [activeIndex, setActiveIndex] = useState(0);
92+
const prevActiveIndexRef = useRef(0);
9293
const [mounted, setMounted] = useState(open);
9394
const [expanded, setExpanded] = useState(false);
9495
const [anchor, setAnchor] = useState<AnchorRect | null>(null);
@@ -116,6 +117,7 @@ export function Palette({ open, onClose, anchorRef, dark }: PaletteProps) {
116117
setMounted(false);
117118
setQuery('');
118119
setActiveIndex(0);
120+
prevActiveIndexRef.current = 0;
119121
setLoadError(null);
120122
setAnchor(null);
121123
}, delay);
@@ -217,8 +219,14 @@ export function Palette({ open, onClose, anchorRef, dark }: PaletteProps) {
217219
}, [query, rows.length]);
218220

219221
useEffect(() => {
220-
// Skip index 0 / mount — scrollIntoView during the open morph nudges the list.
221-
if (!open || closing || !expanded || activeIndex === 0 || !listRef.current) {
222+
if (!open || closing || !expanded || !listRef.current) {
223+
return;
224+
}
225+
const previousIndex = prevActiveIndexRef.current;
226+
prevActiveIndexRef.current = activeIndex;
227+
// Skip idle/initial index 0 — scrollIntoView during the open morph nudges the
228+
// list. Still scroll when wrapping from the last result back to the first.
229+
if (activeIndex === 0 && previousIndex === 0) {
222230
return;
223231
}
224232
const active = listRef.current.querySelector<HTMLElement>(

src/content/sidebar/Sidebar.tsx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ type SidebarProps = {
2020

2121
type TogglePhase = 'idle' | 'area' | 'target' | 'pressed';
2222

23-
const SIDEBAR_TOGGLE_FALLBACK_MS = 250;
23+
const SIDEBAR_TOGGLE_TRANSITION_MS = 200;
24+
const SIDEBAR_TOGGLE_FALLBACK_MS = SIDEBAR_TOGGLE_TRANSITION_MS + 100;
2425

2526
function IconAnnotations() {
2627
return (
@@ -52,13 +53,7 @@ function IconAnnotations() {
5253
function IconSettings() {
5354
return (
5455
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
55-
<circle
56-
cx="6.5"
57-
cy="5"
58-
r="1.5"
59-
stroke="currentColor"
60-
strokeWidth="1.2"
61-
/>
56+
<circle cx="6.5" cy="5" r="1.5" stroke="currentColor" strokeWidth="1.2" />
6257
<circle
6358
cx="10.5"
6459
cy="11"
@@ -132,7 +127,7 @@ export function Sidebar({
132127
const toggleResetTimerRef = useRef<number | null>(null);
133128
const searchSlotRef = useRef<HTMLDivElement>(null);
134129
const logoSrc = chrome.runtime.getURL(
135-
dark ? 'sep-logo-white.png' : 'sep-logo.png'
130+
dark ? 'sep-logo-white.png' : 'sep-logo.png',
136131
);
137132

138133
useEffect(() => {
@@ -150,7 +145,7 @@ export function Sidebar({
150145
window.clearTimeout(toggleResetTimerRef.current);
151146
}
152147
},
153-
[]
148+
[],
154149
);
155150

156151
function finishToggleTransition(): void {
@@ -169,7 +164,7 @@ export function Sidebar({
169164
setTogglePhase('pressed');
170165
toggleResetTimerRef.current = window.setTimeout(
171166
finishToggleTransition,
172-
SIDEBAR_TOGGLE_FALLBACK_MS
167+
SIDEBAR_TOGGLE_FALLBACK_MS,
173168
);
174169
onToggleCollapsed();
175170
}
@@ -240,7 +235,7 @@ export function Sidebar({
240235
aria-current={surface === 'annotations' ? 'page' : undefined}
241236
onClick={() =>
242237
setSurface((current) =>
243-
current === 'annotations' ? 'toc' : 'annotations'
238+
current === 'annotations' ? 'toc' : 'annotations',
244239
)
245240
}
246241
>
@@ -260,7 +255,7 @@ export function Sidebar({
260255
aria-current={surface === 'settings' ? 'page' : undefined}
261256
onClick={() =>
262257
setSurface((current) =>
263-
current === 'settings' ? 'toc' : 'settings'
258+
current === 'settings' ? 'toc' : 'settings',
264259
)
265260
}
266261
>
@@ -314,10 +309,7 @@ export function Sidebar({
314309

315310
{createPortal(
316311
<div
317-
className={[
318-
'sep-sidebar-toggle',
319-
collapsed ? 'is-collapsed' : '',
320-
]
312+
className={['sep-sidebar-toggle', collapsed ? 'is-collapsed' : '']
321313
.filter(Boolean)
322314
.join(' ')}
323315
data-phase={togglePhase}
@@ -372,7 +364,7 @@ export function Sidebar({
372364
<span className="sep-expander bottom" />
373365
</button>
374366
</div>,
375-
document.getElementById('sep-plus-edge-toggle') ?? document.body
367+
document.getElementById('sep-plus-edge-toggle') ?? document.body,
376368
)}
377369
</>
378370
);

src/content/styles.css

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
--sep-duration-fast: 100ms;
3333
--sep-duration-ui: 150ms;
3434
--sep-duration-drawer: 200ms;
35-
--sep-font: 'Public Sans', system-ui, -apple-system, BlinkMacSystemFont,
36-
'Segoe UI', sans-serif;
35+
--sep-font:
36+
'Public Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
37+
sans-serif;
3738

3839
font-family: var(--sep-font);
3940
color: var(--sep-text);
@@ -100,7 +101,7 @@
100101
border-right: none;
101102
transform: translateX(0);
102103
/* Match article #container timing so the sheet edge and sidebar move as one. */
103-
transition: transform var(--sep-duration-ui) var(--sep-ease-in-out);
104+
transition: transform var(--sep-duration-drawer) var(--sep-ease-out);
104105
box-sizing: border-box;
105106
}
106107

@@ -595,7 +596,7 @@
595596
background: transparent;
596597
color: var(--sep-muted-soft);
597598
font-family: var(--sep-font);
598-
font-size: 11px;
599+
font-size: 12px;
599600
line-height: 16px;
600601
font-weight: 500;
601602
pointer-events: none;

src/host/footnotes.css

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
inset: 0;
55
z-index: 1000;
66
pointer-events: none;
7-
font-family: 'Public Sans', system-ui, -apple-system, BlinkMacSystemFont,
8-
'Segoe UI', sans-serif;
7+
font-family: var(--sep-font);
98
color: var(--sep-text);
9+
-webkit-font-smoothing: antialiased;
10+
-moz-osx-font-smoothing: grayscale;
1011
}
1112

1213
.sep-footnote-preview {
@@ -19,8 +20,8 @@
1920
background: var(--sep-panel-bg);
2021
box-shadow: 0 10px 30px
2122
color-mix(in srgb, var(--sand-12) 14%, transparent);
22-
color: var(--sep-muted);
23-
font-size: 13px;
23+
color: var(--sep-text);
24+
font-size: 14px;
2425
font-weight: 400;
2526
line-height: 20px;
2627
opacity: 0;
@@ -61,7 +62,7 @@
6162
max-height: min(420px, calc(100vh - 24px));
6263
overflow: auto;
6364
overscroll-behavior: contain;
64-
padding: 16px 20px;
65+
padding: 12px 20px;
6566
}
6667

6768
.sep-footnote-preview p,

0 commit comments

Comments
 (0)