retypeset-odyssey 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -47,7 +47,8 @@ const { journals, lang } = Astro.props
|
|
|
47
47
|
|
|
48
48
|
{/* journal description */}
|
|
49
49
|
<div class="heti hidden lg:block">
|
|
50
|
-
|
|
50
|
+
{/* Pre-`<!-- more -->` excerpt rendered as raw markdown HTML. */}
|
|
51
|
+
<Fragment set:html={getJournalDescription(journal, 'list')} />
|
|
51
52
|
</div>
|
|
52
53
|
</li>
|
|
53
54
|
)
|
|
@@ -47,7 +47,8 @@ const { notes, lang } = Astro.props
|
|
|
47
47
|
|
|
48
48
|
{/* note description */}
|
|
49
49
|
<div class="heti hidden lg:block">
|
|
50
|
-
|
|
50
|
+
{/* Pre-`<!-- more -->` excerpt rendered as raw markdown HTML. */}
|
|
51
|
+
<Fragment set:html={getNoteDescription(note, 'list')} />
|
|
51
52
|
</div>
|
|
52
53
|
</li>
|
|
53
54
|
)
|
|
@@ -78,7 +78,10 @@ const isHome = isHomePage(Astro.url.pathname)
|
|
|
78
78
|
class="heti hidden"
|
|
79
79
|
lg="mt-2.25 block"
|
|
80
80
|
>
|
|
81
|
-
|
|
81
|
+
{/* Pre-`<!-- more -->` excerpt rendered as raw markdown HTML
|
|
82
|
+
so blockquotes / lists / paragraphs keep their structure.
|
|
83
|
+
The `heti` parent provides Chinese typographic styling. */}
|
|
84
|
+
<Fragment set:html={getPostDescription(post, 'list')} />
|
|
82
85
|
</div>
|
|
83
86
|
)}
|
|
84
87
|
</li>
|
|
@@ -4,19 +4,41 @@ import { getPageInfo } from '@/utils/page'
|
|
|
4
4
|
|
|
5
5
|
const { currentLang } = getPageInfo(Astro.url.pathname)
|
|
6
6
|
const currentUI = ui[currentLang as keyof typeof ui] ?? {}
|
|
7
|
+
|
|
8
|
+
// Input placeholder — librarian's note tone, doubles as empty-state hint.
|
|
9
|
+
const placeholderText: Record<string, string> = {
|
|
10
|
+
'zh': '试着搜「agent」或「translate」…',
|
|
11
|
+
'en': 'Try “agent” or “translate”…',
|
|
12
|
+
'ja': '「agent」や「translate」で試してみてください…',
|
|
13
|
+
}
|
|
14
|
+
const inputPlaceholder = placeholderText[currentLang] ?? placeholderText.en
|
|
15
|
+
|
|
16
|
+
// Close button aria-label by language — kept as an inline lookup so we
|
|
17
|
+
// don't have to widen the theme's Translation interface in ui.ts for a
|
|
18
|
+
// single component.
|
|
19
|
+
const closeLabels: Record<string, string> = {
|
|
20
|
+
'zh': '关闭',
|
|
21
|
+
'en': 'Close',
|
|
22
|
+
'ja': '閉じる',
|
|
23
|
+
}
|
|
24
|
+
const closeLabel = closeLabels[currentLang] ?? closeLabels.en
|
|
7
25
|
---
|
|
8
26
|
|
|
9
|
-
<dialog id="search-modal" class="search-modal">
|
|
10
|
-
<div class="modal-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
<dialog id="search-modal" class="search-modal" aria-label={currentUI.search || 'Search'}>
|
|
28
|
+
<div class="modal-card">
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
id="close-search-modal"
|
|
32
|
+
class="modal-close"
|
|
33
|
+
aria-label={closeLabel}
|
|
34
|
+
>×</button>
|
|
35
|
+
<div
|
|
36
|
+
id="search-container"
|
|
37
|
+
class="modal-results"
|
|
38
|
+
data-placeholder={inputPlaceholder}
|
|
39
|
+
data-no-results={currentUI.searchNoResults}
|
|
40
|
+
data-results-found={currentUI.searchResultsFound}
|
|
41
|
+
></div>
|
|
20
42
|
</div>
|
|
21
43
|
</dialog>
|
|
22
44
|
|
|
@@ -24,317 +46,431 @@ const currentUI = ui[currentLang as keyof typeof ui] ?? {}
|
|
|
24
46
|
<script is:inline src="/pagefind/pagefind-ui.js" type="text/javascript"></script>
|
|
25
47
|
|
|
26
48
|
<script>
|
|
27
|
-
let pagefindInitialized = false
|
|
49
|
+
let pagefindInitialized = false
|
|
28
50
|
|
|
29
51
|
function initPagefind() {
|
|
30
|
-
if (pagefindInitialized) return
|
|
31
|
-
if (typeof PagefindUI === 'undefined') return
|
|
52
|
+
if (pagefindInitialized) return
|
|
53
|
+
if (typeof (window as any).PagefindUI === 'undefined') return
|
|
32
54
|
|
|
33
|
-
const container = document.getElementById('search-container')
|
|
34
|
-
|
|
55
|
+
const container = document.getElementById('search-container')
|
|
56
|
+
if (!container) return
|
|
57
|
+
const placeholder = container.dataset.placeholder || 'Type to search...'
|
|
35
58
|
|
|
36
|
-
new PagefindUI({
|
|
59
|
+
new (window as any).PagefindUI({
|
|
37
60
|
element: '#search-container',
|
|
38
61
|
showImages: false,
|
|
39
|
-
showSubResults: true
|
|
40
|
-
})
|
|
62
|
+
showSubResults: true,
|
|
63
|
+
})
|
|
41
64
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
// Pagefind UI mounts its own input next tick. Adopt our placeholder
|
|
66
|
+
// (the librarian-note we want shown while empty). No separate empty-state
|
|
67
|
+
// block — the placeholder itself carries that copy.
|
|
68
|
+
requestAnimationFrame(() => {
|
|
69
|
+
const input = document.querySelector('.pagefind-ui__search-input') as HTMLInputElement | null
|
|
70
|
+
if (!input) return
|
|
71
|
+
input.placeholder = placeholder
|
|
72
|
+
})
|
|
47
73
|
|
|
48
|
-
pagefindInitialized = true
|
|
74
|
+
pagefindInitialized = true
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
function openSearchModal() {
|
|
52
|
-
const modal = document.getElementById('search-modal') as HTMLDialogElement
|
|
53
|
-
if (modal)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
78
|
+
const modal = document.getElementById('search-modal') as HTMLDialogElement | null
|
|
79
|
+
if (!modal) return
|
|
80
|
+
modal.showModal()
|
|
81
|
+
initPagefind()
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
const input = modal.querySelector('.pagefind-ui__search-input') as HTMLInputElement | null
|
|
84
|
+
input?.focus()
|
|
85
|
+
}, 80)
|
|
61
86
|
}
|
|
62
87
|
|
|
63
88
|
function closeSearchModal() {
|
|
64
|
-
const modal = document.getElementById('search-modal') as HTMLDialogElement
|
|
65
|
-
modal?.close()
|
|
89
|
+
const modal = document.getElementById('search-modal') as HTMLDialogElement | null
|
|
90
|
+
modal?.close()
|
|
66
91
|
}
|
|
67
92
|
|
|
93
|
+
// Trigger button on the chrome + close button if any consumer renders one.
|
|
68
94
|
document.addEventListener('click', (e) => {
|
|
69
|
-
const target = e.target as Element
|
|
95
|
+
const target = e.target as Element
|
|
70
96
|
if (target.closest('#search-button')) {
|
|
71
|
-
e.preventDefault()
|
|
72
|
-
openSearchModal()
|
|
97
|
+
e.preventDefault()
|
|
98
|
+
openSearchModal()
|
|
73
99
|
}
|
|
74
100
|
if (target.closest('#close-search-modal')) {
|
|
75
|
-
closeSearchModal()
|
|
101
|
+
closeSearchModal()
|
|
76
102
|
}
|
|
77
|
-
})
|
|
78
|
-
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Cmd+K / Ctrl+K to toggle. Skip when the user is typing in some other
|
|
106
|
+
// editable field (so the OS / app shortcut still works there), unless
|
|
107
|
+
// that field is the search input itself (closing should work from inside).
|
|
108
|
+
document.addEventListener('keydown', (e) => {
|
|
109
|
+
if (!(e.metaKey || e.ctrlKey) || e.key.toLowerCase() !== 'k') return
|
|
110
|
+
const active = document.activeElement as HTMLElement | null
|
|
111
|
+
const inSearch = active?.classList.contains('pagefind-ui__search-input') ?? false
|
|
112
|
+
const inEditable = !!active && (
|
|
113
|
+
active.tagName === 'INPUT'
|
|
114
|
+
|| active.tagName === 'TEXTAREA'
|
|
115
|
+
|| active.isContentEditable
|
|
116
|
+
)
|
|
117
|
+
if (inEditable && !inSearch) return
|
|
118
|
+
e.preventDefault()
|
|
119
|
+
const modal = document.getElementById('search-modal') as HTMLDialogElement | null
|
|
120
|
+
if (modal?.open) closeSearchModal()
|
|
121
|
+
else openSearchModal()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// Backdrop click closes. Standard <dialog> idiom: clicks on modal content
|
|
125
|
+
// fire on inner elements (e.target = inner), clicks on the backdrop fire on
|
|
126
|
+
// the dialog itself (e.target = dialog). Coordinate-based checks like the
|
|
127
|
+
// previous bounding-rect math are unreliable because some browsers report
|
|
128
|
+
// negative/oversized rects for full-viewport dialogs.
|
|
79
129
|
document.getElementById('search-modal')?.addEventListener('click', (e) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
e.clientX < rect.left ||
|
|
84
|
-
e.clientX > rect.right ||
|
|
85
|
-
e.clientY < rect.top ||
|
|
86
|
-
e.clientY > rect.bottom
|
|
87
|
-
) {
|
|
88
|
-
closeSearchModal();
|
|
130
|
+
if (e.target === e.currentTarget) {
|
|
131
|
+
closeSearchModal()
|
|
89
132
|
}
|
|
90
|
-
})
|
|
133
|
+
})
|
|
91
134
|
|
|
92
|
-
(window as any).openSearchModal = openSearchModal
|
|
135
|
+
;(window as any).openSearchModal = openSearchModal
|
|
93
136
|
</script>
|
|
94
137
|
|
|
95
138
|
<style is:global>
|
|
96
|
-
/*
|
|
139
|
+
/* ============================================================
|
|
140
|
+
Search Modal — "white-paper" (variant 2C)
|
|
141
|
+
------------------------------------------------------------
|
|
142
|
+
- Pure paper aesthetic: hairline frame, sharp 90° corners,
|
|
143
|
+
no shadow. Reads like a slip of paper pinned over the page,
|
|
144
|
+
not a SaaS popup.
|
|
145
|
+
- Typographic input: transparent, bottom-border only, serif.
|
|
146
|
+
The form bar isn't a "field" so much as a writing line.
|
|
147
|
+
- Yellow <mark> highlight is the theme's only accent token,
|
|
148
|
+
same as inline marks in body type.
|
|
149
|
+
- 0 `!important`. The `.search-modal .pagefind-ui …` chain
|
|
150
|
+
(specificity 0,3,1) outweighs Pagefind's defaults.
|
|
151
|
+
============================================================ */
|
|
152
|
+
|
|
97
153
|
.search-modal {
|
|
154
|
+
--sm-fg: oklch(var(--un-preset-theme-colors-primary));
|
|
155
|
+
--sm-fg-soft: oklch(var(--un-preset-theme-colors-secondary));
|
|
156
|
+
--sm-bg: oklch(var(--un-preset-theme-colors-background));
|
|
157
|
+
--sm-highlight: oklch(
|
|
158
|
+
var(--un-preset-theme-colors-highlight) /
|
|
159
|
+
var(--un-preset-theme-colors-highlight--alpha)
|
|
160
|
+
);
|
|
161
|
+
/* Hairline surfaces derived from foreground at low alpha — keeps
|
|
162
|
+
rules in the same hue family as text. */
|
|
163
|
+
--sm-rule: color-mix(in oklch, var(--sm-fg) 16%, transparent);
|
|
164
|
+
--sm-rule-soft: color-mix(in oklch, var(--sm-fg) 8%, transparent);
|
|
165
|
+
--sm-row-hover: color-mix(in oklch, var(--sm-fg) 5%, transparent);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ------------- dialog shell ---------------------------------- */
|
|
169
|
+
|
|
170
|
+
.search-modal {
|
|
171
|
+
/* Drawer pulled down from the top edge, not centered popup. */
|
|
98
172
|
padding: 0;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
173
|
+
margin: 0;
|
|
174
|
+
margin-inline: auto;
|
|
175
|
+
margin-top: max(8vh, 3rem);
|
|
176
|
+
border: 1px solid var(--sm-rule);
|
|
177
|
+
border-radius: 0; /* sharp paper corners */
|
|
178
|
+
width: min(560px, calc(100vw - 2rem));
|
|
179
|
+
max-width: 560px;
|
|
180
|
+
max-height: min(calc(100dvh - 16vh - 2rem), 560px);
|
|
181
|
+
background: var(--sm-bg);
|
|
182
|
+
color: var(--sm-fg);
|
|
183
|
+
/* No shadow — the paper aesthetic prefers a hairline frame over a lift. */
|
|
184
|
+
box-shadow: none;
|
|
185
|
+
overflow: visible;
|
|
105
186
|
}
|
|
106
187
|
|
|
107
188
|
.search-modal::backdrop {
|
|
108
|
-
|
|
109
|
-
|
|
189
|
+
/* Quiet overlay; light enough to keep the page legible behind. */
|
|
190
|
+
background: color-mix(in oklch, black 18%, transparent);
|
|
191
|
+
backdrop-filter: blur(2px);
|
|
192
|
+
-webkit-backdrop-filter: blur(2px);
|
|
110
193
|
}
|
|
111
194
|
|
|
112
|
-
.modal-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
overflow: hidden;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/* Header */
|
|
119
|
-
.modal-header {
|
|
195
|
+
.search-modal .modal-card {
|
|
196
|
+
position: relative;
|
|
197
|
+
background: var(--sm-bg);
|
|
120
198
|
display: flex;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
border-bottom: 1px solid var(--c-divider, rgba(128, 128, 128, 0.2));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.modal-title {
|
|
128
|
-
font-size: 1rem;
|
|
129
|
-
font-weight: 600;
|
|
130
|
-
color: var(--c-text-1);
|
|
131
|
-
margin: 0;
|
|
199
|
+
flex-direction: column;
|
|
200
|
+
max-height: inherit;
|
|
201
|
+
overflow: hidden;
|
|
132
202
|
}
|
|
133
203
|
|
|
134
|
-
|
|
135
|
-
|
|
204
|
+
/* Visible close affordance for mouse users — kept tiny so it doesn't break
|
|
205
|
+
the bone-minimal paper aesthetic. Positioned vertically aligned with the
|
|
206
|
+
input row's center, on the right edge — same slot the Pagefind clear
|
|
207
|
+
button used to occupy (now hidden), so it reads as "the input row's
|
|
208
|
+
right-hand control". */
|
|
209
|
+
.search-modal .modal-close {
|
|
210
|
+
position: absolute;
|
|
211
|
+
/* Input-row paddings: 1rem top + ~2.15rem input height + 0.875rem bottom.
|
|
212
|
+
Center of input ≈ 1rem + 2.15/2 ≈ 2.075rem from modal-card top.
|
|
213
|
+
Half the button height (1.5/2 = 0.75rem) backed off → ~1.325rem. */
|
|
214
|
+
top: 1.325rem;
|
|
215
|
+
right: 1rem;
|
|
216
|
+
z-index: 1;
|
|
217
|
+
width: 1.5rem;
|
|
218
|
+
height: 1.5rem;
|
|
219
|
+
padding: 0;
|
|
220
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
221
|
+
font-size: 1.125rem;
|
|
222
|
+
line-height: 1;
|
|
223
|
+
color: var(--sm-fg-soft);
|
|
136
224
|
background: transparent;
|
|
137
|
-
border:
|
|
138
|
-
|
|
225
|
+
border: 0;
|
|
226
|
+
border-radius: 2px;
|
|
139
227
|
cursor: pointer;
|
|
140
|
-
|
|
141
|
-
display: flex;
|
|
142
|
-
align-items: center;
|
|
143
|
-
justify-content: center;
|
|
144
|
-
transition: color 0.2s, background 0.2s;
|
|
228
|
+
transition: color 120ms, background-color 120ms;
|
|
145
229
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
background: rgba(128, 128, 128, 0.1);
|
|
230
|
+
.search-modal .modal-close:hover {
|
|
231
|
+
color: var(--sm-fg);
|
|
232
|
+
background: color-mix(in oklch, var(--sm-fg) 6%, transparent);
|
|
150
233
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
padding: 0;
|
|
234
|
+
.search-modal .modal-close:focus-visible {
|
|
235
|
+
outline: 1px solid var(--sm-fg);
|
|
236
|
+
outline-offset: 1px;
|
|
155
237
|
}
|
|
156
238
|
|
|
157
|
-
/*
|
|
239
|
+
/* ============================================================
|
|
240
|
+
Pagefind UI overrides — scoped under .search-modal .pagefind-ui
|
|
241
|
+
(specificity 0,3,1) so we win without any !important.
|
|
242
|
+
============================================================ */
|
|
243
|
+
|
|
158
244
|
.search-modal .pagefind-ui {
|
|
159
|
-
--pagefind-ui-
|
|
160
|
-
--pagefind-ui-
|
|
161
|
-
--pagefind-ui-
|
|
162
|
-
--pagefind-ui-
|
|
163
|
-
--pagefind-ui-
|
|
245
|
+
--pagefind-ui-primary: var(--sm-fg);
|
|
246
|
+
--pagefind-ui-text: var(--sm-fg);
|
|
247
|
+
--pagefind-ui-background: var(--sm-bg);
|
|
248
|
+
--pagefind-ui-border: var(--sm-rule);
|
|
249
|
+
--pagefind-ui-tag: var(--sm-bg);
|
|
164
250
|
--pagefind-ui-border-width: 1px;
|
|
165
|
-
--pagefind-ui-border-radius:
|
|
251
|
+
--pagefind-ui-border-radius: 0;
|
|
166
252
|
--pagefind-ui-font: inherit;
|
|
167
253
|
}
|
|
168
254
|
|
|
169
|
-
|
|
170
|
-
padding: 1rem 1.25rem;
|
|
171
|
-
border-bottom: 1px solid var(--c-divider, rgba(128, 128, 128, 0.2));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.search-modal .pagefind-ui__search-input {
|
|
175
|
-
font-size: 0.95rem !important;
|
|
176
|
-
padding: 0.625rem 1rem !important;
|
|
177
|
-
height: auto !important;
|
|
178
|
-
background: var(--c-bg-soft, rgba(128, 128, 128, 0.05)) !important;
|
|
179
|
-
border: 1px solid var(--c-divider, rgba(128, 128, 128, 0.2)) !important;
|
|
180
|
-
border-radius: 8px !important;
|
|
181
|
-
color: var(--c-text-1) !important;
|
|
182
|
-
transition: border-color 0.2s, box-shadow 0.2s !important;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.search-modal .pagefind-ui__search-input:focus {
|
|
186
|
-
outline: none !important;
|
|
187
|
-
border-color: var(--c-primary, #3b82f6) !important;
|
|
188
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.search-modal .pagefind-ui__search-input::placeholder {
|
|
192
|
-
color: var(--c-text-3, rgba(128, 128, 128, 0.6)) !important;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.search-modal .pagefind-ui__search-clear {
|
|
196
|
-
padding: 0.5rem !important;
|
|
197
|
-
right: 0.5rem !important;
|
|
198
|
-
color: var(--c-text-3) !important;
|
|
199
|
-
background: transparent !important;
|
|
200
|
-
}
|
|
255
|
+
/* ------------- input row ------------------------------------- */
|
|
201
256
|
|
|
202
|
-
|
|
203
|
-
|
|
257
|
+
/* `position: relative` re-declared so the icon (Pagefind's `::before`)
|
|
258
|
+
anchors here. */
|
|
259
|
+
.search-modal .pagefind-ui .pagefind-ui__form {
|
|
260
|
+
position: relative;
|
|
261
|
+
padding: 1rem 1.25rem 0.875rem;
|
|
204
262
|
}
|
|
205
263
|
|
|
206
|
-
/*
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
264
|
+
/* Re-place Pagefind's magnifying-glass icon. Extra `.pagefind-ui` is
|
|
265
|
+
not a no-op: Pagefind's own rule is
|
|
266
|
+
`.pagefind-ui__form.svelte-xxxxx::before` (0,2,1); ours is 0,3,1. */
|
|
267
|
+
.search-modal .pagefind-ui .pagefind-ui__form::before {
|
|
268
|
+
top: 50%;
|
|
269
|
+
left: 1.25rem;
|
|
270
|
+
transform: translateY(-50%);
|
|
271
|
+
width: 14px;
|
|
272
|
+
height: 14px;
|
|
273
|
+
opacity: 0.5;
|
|
210
274
|
}
|
|
211
275
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
276
|
+
/* Typographic input — no fill, no rounded chrome, just a writing line. */
|
|
277
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input {
|
|
278
|
+
width: 100%;
|
|
279
|
+
height: auto;
|
|
280
|
+
padding: 0.375rem 2.25rem 0.375rem 1.625rem;
|
|
281
|
+
background: transparent;
|
|
282
|
+
border: 0;
|
|
283
|
+
border-bottom: 1px solid var(--sm-fg);
|
|
284
|
+
border-radius: 0;
|
|
285
|
+
box-shadow: none;
|
|
286
|
+
font-family: var(--at-font-serif, ui-serif, Georgia, "Times New Roman", serif);
|
|
287
|
+
font-size: 1rem;
|
|
288
|
+
line-height: 1.4;
|
|
289
|
+
color: var(--sm-fg);
|
|
290
|
+
transition: border-color 140ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
217
291
|
}
|
|
218
292
|
|
|
219
|
-
.search-modal .pagefind-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
293
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input:focus,
|
|
294
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input:focus-visible {
|
|
295
|
+
outline: none;
|
|
296
|
+
border-bottom-color: var(--sm-fg);
|
|
223
297
|
}
|
|
224
298
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
border-bottom: 1px solid var(--c-divider, rgba(128, 128, 128, 0.1)) !important;
|
|
229
|
-
transition: background 0.15s !important;
|
|
299
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input::placeholder {
|
|
300
|
+
color: color-mix(in oklch, var(--sm-fg-soft) 65%, transparent);
|
|
301
|
+
font-style: italic;
|
|
230
302
|
}
|
|
231
303
|
|
|
232
|
-
|
|
233
|
-
|
|
304
|
+
/* Defang Chrome autofill — its yellow/blue would shatter the paper feel. */
|
|
305
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input:-webkit-autofill,
|
|
306
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input:-webkit-autofill:focus {
|
|
307
|
+
-webkit-text-fill-color: var(--sm-fg);
|
|
308
|
+
box-shadow: 0 0 0 100px var(--sm-bg) inset;
|
|
309
|
+
transition: background-color 9999s ease-in-out 0s;
|
|
234
310
|
}
|
|
235
311
|
|
|
236
|
-
|
|
237
|
-
|
|
312
|
+
/* Hide Pagefind's own clear-input × — we already render a close button
|
|
313
|
+
for the entire modal in the top-right corner (.modal-close), and a
|
|
314
|
+
second × pinned to the input itself would create a confusing double-
|
|
315
|
+
icon situation right when the user starts typing. Backspace / closing
|
|
316
|
+
the modal achieves the same thing. */
|
|
317
|
+
.search-modal .pagefind-ui .pagefind-ui__search-clear {
|
|
318
|
+
display: none;
|
|
238
319
|
}
|
|
239
320
|
|
|
240
|
-
|
|
241
|
-
padding: 0 !important;
|
|
242
|
-
}
|
|
321
|
+
/* ------------- results --------------------------------------- */
|
|
243
322
|
|
|
244
|
-
.search-modal .pagefind-
|
|
245
|
-
|
|
323
|
+
.search-modal .pagefind-ui .pagefind-ui__results-area {
|
|
324
|
+
padding: 0;
|
|
325
|
+
margin: 0;
|
|
246
326
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
text-decoration: none !important;
|
|
253
|
-
transition: color 0.15s !important;
|
|
327
|
+
.search-modal .pagefind-ui .pagefind-ui__results {
|
|
328
|
+
max-height: min(60vh, 420px);
|
|
329
|
+
overflow-y: auto;
|
|
330
|
+
padding: 0;
|
|
331
|
+
margin: 0;
|
|
254
332
|
}
|
|
255
|
-
|
|
256
|
-
.search-modal .pagefind-
|
|
257
|
-
|
|
333
|
+
.search-modal .pagefind-ui .pagefind-ui__results::-webkit-scrollbar { width: 5px; }
|
|
334
|
+
.search-modal .pagefind-ui .pagefind-ui__results::-webkit-scrollbar-track { background: transparent; }
|
|
335
|
+
.search-modal .pagefind-ui .pagefind-ui__results::-webkit-scrollbar-thumb {
|
|
336
|
+
background: var(--sm-rule);
|
|
337
|
+
border-radius: 0;
|
|
338
|
+
}
|
|
339
|
+
.search-modal .pagefind-ui .pagefind-ui__results::-webkit-scrollbar-thumb:hover {
|
|
340
|
+
background: color-mix(in oklch, var(--sm-fg) 28%, transparent);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* Result row — soft hairline rules between, no card chrome.
|
|
344
|
+
Focus / hover is a background tint only (kept under BAN 1). */
|
|
345
|
+
.search-modal .pagefind-ui .pagefind-ui__result {
|
|
346
|
+
padding: 0.75rem 1.25rem;
|
|
347
|
+
border-top: 1px solid var(--sm-rule-soft);
|
|
348
|
+
transition: background-color 120ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
349
|
+
}
|
|
350
|
+
.search-modal .pagefind-ui .pagefind-ui__result:first-child {
|
|
351
|
+
border-top: 1px solid var(--sm-rule);
|
|
352
|
+
}
|
|
353
|
+
.search-modal .pagefind-ui .pagefind-ui__result:hover,
|
|
354
|
+
.search-modal .pagefind-ui .pagefind-ui__result:focus-within {
|
|
355
|
+
background: var(--sm-row-hover);
|
|
356
|
+
}
|
|
357
|
+
.search-modal .pagefind-ui .pagefind-ui__result-inner { padding: 0; }
|
|
358
|
+
.search-modal .pagefind-ui .pagefind-ui__result-title { margin: 0 0 0.1875rem; }
|
|
359
|
+
|
|
360
|
+
/* Title link — serif, typographic; :link/:visited covered so the
|
|
361
|
+
browser's default purple-blue for visited links cannot leak through. */
|
|
362
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link,
|
|
363
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link:link,
|
|
364
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link:visited,
|
|
365
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link:any-link {
|
|
366
|
+
display: block;
|
|
367
|
+
font-family: var(--at-font-serif, ui-serif, Georgia, serif);
|
|
368
|
+
font-size: 0.9375rem;
|
|
369
|
+
font-weight: 500;
|
|
370
|
+
line-height: 1.4;
|
|
371
|
+
color: var(--sm-fg);
|
|
372
|
+
text-decoration: none;
|
|
373
|
+
transition: color 120ms;
|
|
374
|
+
}
|
|
375
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link:hover,
|
|
376
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link:focus-visible {
|
|
377
|
+
color: var(--sm-fg);
|
|
378
|
+
text-decoration: underline;
|
|
379
|
+
text-decoration-color: var(--sm-fg);
|
|
380
|
+
text-underline-offset: 3px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.search-modal .pagefind-ui .pagefind-ui__result-excerpt {
|
|
384
|
+
margin: 0;
|
|
385
|
+
font-size: 0.8125rem;
|
|
386
|
+
line-height: 1.6;
|
|
387
|
+
color: var(--sm-fg-soft);
|
|
258
388
|
}
|
|
259
389
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
390
|
+
/* Sub-results (Pagefind's grouped sections) — kept slightly indented.
|
|
391
|
+
1px left rule is allowed (BAN 1 only prohibits >1px decorative stripes). */
|
|
392
|
+
.search-modal .pagefind-ui .pagefind-ui__result-nested {
|
|
393
|
+
margin: 0.5rem 0 0;
|
|
394
|
+
padding-left: 0.75rem;
|
|
395
|
+
border-left: 1px solid var(--sm-rule);
|
|
263
396
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
line-height: 1.5 !important;
|
|
268
|
-
color: var(--c-text-2) !important;
|
|
269
|
-
margin-top: 0.25rem !important;
|
|
397
|
+
.search-modal .pagefind-ui .pagefind-ui__result-nested .pagefind-ui__result-link::before {
|
|
398
|
+
content: '↳ ';
|
|
399
|
+
color: var(--sm-fg-soft);
|
|
270
400
|
}
|
|
271
401
|
|
|
272
|
-
/*
|
|
273
|
-
.search-modal .pagefind-ui__result-excerpt mark,
|
|
402
|
+
/* Highlighted match — yellow, theme highlight token; same as body <mark>. */
|
|
403
|
+
.search-modal .pagefind-ui .pagefind-ui__result-excerpt mark,
|
|
274
404
|
.search-modal mark {
|
|
275
|
-
background:
|
|
276
|
-
color: inherit
|
|
277
|
-
padding: 0.
|
|
278
|
-
border-radius:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
padding
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
margin:
|
|
296
|
-
padding: 0.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
color: var(--c-text-2) !important;
|
|
301
|
-
font-size: 0.875rem !important;
|
|
302
|
-
cursor: pointer !important;
|
|
303
|
-
transition: all 0.15s !important;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
.search-modal .pagefind-ui__button:hover {
|
|
307
|
-
background: var(--c-bg-mute, rgba(128, 128, 128, 0.1)) !important;
|
|
308
|
-
color: var(--c-text-1) !important;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/* Hide default drawer toggle */
|
|
312
|
-
.search-modal .pagefind-ui__drawer {
|
|
313
|
-
display: none !important;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/* Loading state */
|
|
317
|
-
.search-modal .pagefind-ui__loading {
|
|
318
|
-
padding: 2rem 1.25rem !important;
|
|
319
|
-
text-align: center !important;
|
|
320
|
-
color: var(--c-text-3) !important;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/* Scrollbar styling */
|
|
324
|
-
.search-modal .pagefind-ui__results::-webkit-scrollbar {
|
|
325
|
-
width: 6px;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
.search-modal .pagefind-ui__results::-webkit-scrollbar-track {
|
|
405
|
+
background: var(--sm-highlight);
|
|
406
|
+
color: inherit;
|
|
407
|
+
padding: 0.05em 0.15em;
|
|
408
|
+
border-radius: 0;
|
|
409
|
+
font-weight: 500;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* Pagefind status / message line */
|
|
413
|
+
.search-modal .pagefind-ui .pagefind-ui__message {
|
|
414
|
+
padding: 0.75rem 1.25rem;
|
|
415
|
+
font-size: 0.8125rem;
|
|
416
|
+
color: var(--sm-fg-soft);
|
|
417
|
+
font-style: italic;
|
|
418
|
+
border-top: 1px solid var(--sm-rule);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* "Load more" button */
|
|
422
|
+
.search-modal .pagefind-ui .pagefind-ui__button {
|
|
423
|
+
display: block;
|
|
424
|
+
width: calc(100% - 2.5rem);
|
|
425
|
+
margin: 0.75rem 1.25rem;
|
|
426
|
+
padding: 0.5rem 0.875rem;
|
|
427
|
+
font: inherit;
|
|
428
|
+
font-size: 0.8125rem;
|
|
429
|
+
color: var(--sm-fg-soft);
|
|
329
430
|
background: transparent;
|
|
431
|
+
border: 1px solid var(--sm-rule);
|
|
432
|
+
border-radius: 0;
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
transition: background-color 120ms, color 120ms, border-color 120ms;
|
|
435
|
+
}
|
|
436
|
+
.search-modal .pagefind-ui .pagefind-ui__button:hover {
|
|
437
|
+
color: var(--sm-fg);
|
|
438
|
+
background: var(--sm-row-hover);
|
|
439
|
+
border-color: color-mix(in oklch, var(--sm-fg) 28%, transparent);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* `.pagefind-ui__drawer` is the OUTER wrapper around results-area, NOT the
|
|
443
|
+
small "show filters" toggle (despite the name). Inherited from a legacy
|
|
444
|
+
override that set this to `display: none` — which silently collapsed all
|
|
445
|
+
results to 0×0px even though the DOM was populated. Force block layout
|
|
446
|
+
so it stacks below the form like prose. */
|
|
447
|
+
.search-modal .pagefind-ui .pagefind-ui__drawer { display: block; }
|
|
448
|
+
|
|
449
|
+
.search-modal .pagefind-ui .pagefind-ui__loading {
|
|
450
|
+
padding: 1.25rem 1.25rem;
|
|
451
|
+
text-align: center;
|
|
452
|
+
color: var(--sm-fg-soft);
|
|
453
|
+
font-style: italic;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* Responsive — narrow screens get a bigger modal so excerpts have room. */
|
|
457
|
+
@media (max-width: 480px) {
|
|
458
|
+
.search-modal {
|
|
459
|
+
margin-top: max(5vh, 1.5rem);
|
|
460
|
+
width: calc(100vw - 1rem);
|
|
461
|
+
max-height: calc(100dvh - 10vh - 1rem);
|
|
462
|
+
}
|
|
463
|
+
.search-modal .pagefind-ui .pagefind-ui__results { max-height: 55vh; }
|
|
330
464
|
}
|
|
331
465
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
.search-modal .pagefind-
|
|
338
|
-
|
|
466
|
+
@media (prefers-reduced-motion: reduce) {
|
|
467
|
+
.search-modal .pagefind-ui .pagefind-ui__search-input,
|
|
468
|
+
.search-modal .pagefind-ui .pagefind-ui__result,
|
|
469
|
+
.search-modal .pagefind-ui .pagefind-ui__result-link,
|
|
470
|
+
.search-modal .pagefind-ui .pagefind-ui__button,
|
|
471
|
+
.search-modal .pagefind-ui .pagefind-ui__search-clear,
|
|
472
|
+
.search-modal .pagefind-ui .pagefind-ui__search-clear::before {
|
|
473
|
+
transition: none;
|
|
474
|
+
}
|
|
339
475
|
}
|
|
340
476
|
</style>
|
package/src/utils/description.ts
CHANGED
|
@@ -125,9 +125,16 @@ function getEntryDescription(
|
|
|
125
125
|
|
|
126
126
|
const renderedContent = markdownParser.render(cleanContent)
|
|
127
127
|
|
|
128
|
-
// For 'list' scene with <!-- more --> marker, return
|
|
128
|
+
// For 'list' scene with an explicit <!-- more --> marker, return the
|
|
129
|
+
// *rendered HTML* (not stripped text) so blockquotes, lists, and
|
|
130
|
+
// paragraphs keep their structure on the homepage post list. The
|
|
131
|
+
// consumer (PostList / NoteList / JournalList) injects this via
|
|
132
|
+
// `<Fragment set:html={…} />` inside a `heti` typography wrapper.
|
|
133
|
+
// Other scenes (og, meta, feed) still receive plain text because they
|
|
134
|
+
// feed single-line metadata / RSS where HTML would leak through as raw
|
|
135
|
+
// tags.
|
|
129
136
|
if (scene === 'list' && hasMoreMarker) {
|
|
130
|
-
return
|
|
137
|
+
return renderedContent
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
// Otherwise, apply truncation
|