@hanology/cham-browser 0.4.64 → 0.4.65
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 +1 -1
- package/template/index.html +1 -1
- package/template/src/components/AnnotationTooltip.vue +21 -21
- package/template/src/components/BackToTop.vue +2 -2
- package/template/src/components/HorizontalDisplay.vue +18 -5
- package/template/src/components/PoemCard.vue +2 -2
- package/template/src/components/PronunciationGroup.vue +12 -2
- package/template/src/components/ReadingToolbar.vue +3 -2
- package/template/src/components/SectionBlock.vue +10 -1
- package/template/src/components/SideNav.vue +4 -4
- package/template/src/components/VerticalScroll.vue +19 -6
- package/template/src/composables/useI18n.ts +7 -2
- package/template/src/styles/annotation-targets.css +20 -20
- package/template/src/styles/main.css +2 -2
- package/template/src/views/PieceView.vue +1 -1
package/package.json
CHANGED
package/template/index.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="zh-Hant">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<title>CHAM</title>
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
@@ -364,7 +364,7 @@ onBeforeUnmount(() => {
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
.ann-sheet-scroll {
|
|
367
|
-
padding: 4px 16px 24px;
|
|
367
|
+
padding: 4px 16px max(24px, env(safe-area-inset-bottom, 0px));
|
|
368
368
|
overflow-y: auto;
|
|
369
369
|
overscroll-behavior: contain;
|
|
370
370
|
flex: 1;
|
|
@@ -384,45 +384,45 @@ onBeforeUnmount(() => {
|
|
|
384
384
|
|
|
385
385
|
/* ─── Active annotation on page ─── */
|
|
386
386
|
:global(.ann-target.ann-active) {
|
|
387
|
-
background:
|
|
388
|
-
box-shadow: 0 0 0 2px
|
|
387
|
+
background: color-mix(in srgb, var(--vermillion) 12%, transparent) !important;
|
|
388
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--vermillion) 15%, transparent);
|
|
389
389
|
border-radius: 2px;
|
|
390
390
|
}
|
|
391
391
|
:global(.ann-target.ann-active.pronunciation) {
|
|
392
|
-
background:
|
|
393
|
-
box-shadow: 0 0 0 2px
|
|
392
|
+
background: color-mix(in srgb, var(--jade) 12%, transparent) !important;
|
|
393
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--jade) 15%, transparent);
|
|
394
394
|
}
|
|
395
395
|
:global(.ann-target.ann-active.person) {
|
|
396
|
-
background:
|
|
397
|
-
box-shadow: 0 0 0 2px
|
|
396
|
+
background: color-mix(in srgb, var(--ann-person) 12%, transparent) !important;
|
|
397
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-person) 15%, transparent);
|
|
398
398
|
}
|
|
399
399
|
:global(.ann-target.ann-active.place) {
|
|
400
|
-
background:
|
|
401
|
-
box-shadow: 0 0 0 2px
|
|
400
|
+
background: color-mix(in srgb, var(--ann-place) 12%, transparent) !important;
|
|
401
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-place) 15%, transparent);
|
|
402
402
|
}
|
|
403
403
|
:global(.ann-target.ann-active.event) {
|
|
404
|
-
background:
|
|
405
|
-
box-shadow: 0 0 0 2px
|
|
404
|
+
background: color-mix(in srgb, var(--ann-event) 12%, transparent) !important;
|
|
405
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-event) 15%, transparent);
|
|
406
406
|
}
|
|
407
407
|
:global(.ann-target.ann-active.date) {
|
|
408
|
-
background:
|
|
409
|
-
box-shadow: 0 0 0 2px
|
|
408
|
+
background: color-mix(in srgb, var(--ann-date) 12%, transparent) !important;
|
|
409
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-date) 15%, transparent);
|
|
410
410
|
}
|
|
411
411
|
:global(.ann-target.ann-active.allusion) {
|
|
412
|
-
background:
|
|
413
|
-
box-shadow: 0 0 0 2px
|
|
412
|
+
background: color-mix(in srgb, var(--ann-allusion) 12%, transparent) !important;
|
|
413
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-allusion) 15%, transparent);
|
|
414
414
|
}
|
|
415
415
|
:global(.ann-target.ann-active.etymology) {
|
|
416
|
-
background:
|
|
417
|
-
box-shadow: 0 0 0 2px
|
|
416
|
+
background: color-mix(in srgb, var(--ann-etymology) 12%, transparent) !important;
|
|
417
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-etymology) 15%, transparent);
|
|
418
418
|
}
|
|
419
419
|
:global(.ann-target.ann-active.commentary) {
|
|
420
|
-
background:
|
|
421
|
-
box-shadow: 0 0 0 2px
|
|
420
|
+
background: color-mix(in srgb, var(--ann-commentary) 12%, transparent) !important;
|
|
421
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-commentary) 15%, transparent);
|
|
422
422
|
}
|
|
423
423
|
:global(.ann-target.ann-active.translation) {
|
|
424
|
-
background:
|
|
425
|
-
box-shadow: 0 0 0 2px
|
|
424
|
+
background: color-mix(in srgb, var(--ann-translation) 12%, transparent) !important;
|
|
425
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ann-translation) 15%, transparent);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
@media (min-width: 768px) {
|
|
@@ -72,7 +72,7 @@ onUnmounted(detach)
|
|
|
72
72
|
<style scoped>
|
|
73
73
|
.btt {
|
|
74
74
|
position: fixed;
|
|
75
|
-
bottom: 80px;
|
|
75
|
+
bottom: max(80px, calc(72px + env(safe-area-inset-bottom, 0px)));
|
|
76
76
|
right: 24px;
|
|
77
77
|
width: 40px;
|
|
78
78
|
height: 40px;
|
|
@@ -98,7 +98,7 @@ onUnmounted(detach)
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
@media (max-width: 768px) {
|
|
101
|
-
.btt { bottom: 88px; right: 16px; width: 36px; height: 36px; }
|
|
101
|
+
.btt { bottom: max(88px, calc(80px + env(safe-area-inset-bottom, 0px))); right: 16px; width: 36px; height: 36px; }
|
|
102
102
|
}
|
|
103
103
|
.btt:hover {
|
|
104
104
|
background: var(--vermillion);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
2
3
|
import type { Annotation, VerseLine } from '../types'
|
|
3
|
-
import { buildVerseAnnotations, renderAnnotatedText, resolveHoveredAnnotations
|
|
4
|
+
import { buildVerseAnnotations, renderAnnotatedText, resolveHoveredAnnotations } from '../composables/useAnnotationRenderer'
|
|
4
5
|
|
|
5
6
|
const props = defineProps<{
|
|
6
7
|
title: string
|
|
@@ -15,11 +16,23 @@ const emit = defineEmits<{
|
|
|
15
16
|
annotationTap: [event: MouseEvent, annotations: Annotation[]]
|
|
16
17
|
}>()
|
|
17
18
|
|
|
19
|
+
const allVerseSpans = computed(() =>
|
|
20
|
+
props.verses.map((_, i) => buildVerseAnnotations(props.annotations, i))
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const verseOffsets = computed(() => {
|
|
24
|
+
const offsets: number[] = []
|
|
25
|
+
let acc = 0
|
|
26
|
+
for (const spans of allVerseSpans.value) {
|
|
27
|
+
offsets.push(acc)
|
|
28
|
+
acc += spans.length
|
|
29
|
+
}
|
|
30
|
+
return offsets
|
|
31
|
+
})
|
|
32
|
+
|
|
18
33
|
function verseHtml(index: number): string {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const spans = buildVerseAnnotations(props.annotations, index)
|
|
22
|
-
return renderAnnotatedText(props.verses[index].text, spans, false, offset)
|
|
34
|
+
const spans = allVerseSpans.value[index]
|
|
35
|
+
return renderAnnotatedText(props.verses[index].text, spans, false, verseOffsets.value[index])
|
|
23
36
|
}
|
|
24
37
|
|
|
25
38
|
function onHover(event: MouseEvent) {
|
|
@@ -97,8 +97,8 @@ const preview = computed(() => {
|
|
|
97
97
|
box-sizing: border-box;
|
|
98
98
|
overflow: hidden;
|
|
99
99
|
height: auto;
|
|
100
|
-
-webkit-mask-image: linear-gradient(to left, black
|
|
101
|
-
mask-image: linear-gradient(to left, black
|
|
100
|
+
-webkit-mask-image: linear-gradient(to left, black 80%, transparent);
|
|
101
|
+
mask-image: linear-gradient(to left, black 80%, transparent);
|
|
102
102
|
}
|
|
103
103
|
.pc-vertical .pc-num {
|
|
104
104
|
font-size: 11px;
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
2
3
|
import type { PronSegment } from '../types'
|
|
4
|
+
import { useI18n } from '../composables/useI18n'
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
const { t } = useI18n()
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
5
9
|
segment: PronSegment
|
|
6
10
|
}>()
|
|
11
|
+
|
|
12
|
+
const label = computed(() => {
|
|
13
|
+
if (props.segment.lang === 'yue') return t('pron.yue')
|
|
14
|
+
if (props.segment.lang === 'cmn') return t('pron.cmn')
|
|
15
|
+
return props.segment.label
|
|
16
|
+
})
|
|
7
17
|
</script>
|
|
8
18
|
|
|
9
19
|
<template>
|
|
10
20
|
<span class="pron-group">
|
|
11
21
|
<span class="pron-badge" :class="segment.lang === 'yue' ? 'pron-yue' : 'pron-cmn'">
|
|
12
|
-
{{
|
|
22
|
+
{{ label }}
|
|
13
23
|
</span>
|
|
14
24
|
<span class="pron-text">{{ segment.parts.join(' ') }}</span>
|
|
15
25
|
</span>
|
|
@@ -117,13 +117,13 @@ function close() { open.value = false }
|
|
|
117
117
|
<style scoped>
|
|
118
118
|
.rt {
|
|
119
119
|
position: fixed;
|
|
120
|
-
bottom: 24px;
|
|
120
|
+
bottom: max(24px, calc(16px + env(safe-area-inset-bottom, 0px)));
|
|
121
121
|
right: 24px;
|
|
122
122
|
z-index: 500;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
@media (max-width: 768px) {
|
|
126
|
-
.rt { bottom: 16px; right: 16px; }
|
|
126
|
+
.rt { bottom: max(16px, calc(12px + env(safe-area-inset-bottom, 0px))); right: 16px; }
|
|
127
127
|
.rt-panel {
|
|
128
128
|
position: fixed;
|
|
129
129
|
bottom: 0;
|
|
@@ -134,6 +134,7 @@ function close() { open.value = false }
|
|
|
134
134
|
max-height: 60vh;
|
|
135
135
|
overflow-y: auto;
|
|
136
136
|
overscroll-behavior: contain;
|
|
137
|
+
padding-bottom: max(16px, env(safe-area-inset-bottom, 0px));
|
|
137
138
|
animation: slideUpMobile 0.3s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
|
|
138
139
|
}
|
|
139
140
|
@keyframes slideUpMobile {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
|
3
3
|
import { parseAnnotationBlock } from '../utils/annotationParser'
|
|
4
|
+
import { useI18n } from '../composables/useI18n'
|
|
4
5
|
import PronunciationGroup from './PronunciationGroup.vue'
|
|
5
6
|
|
|
7
|
+
const { locale } = useI18n()
|
|
8
|
+
|
|
6
9
|
const props = defineProps<{
|
|
7
10
|
num: string
|
|
8
11
|
label: string
|
|
@@ -49,6 +52,12 @@ const displayNum = computed(() => {
|
|
|
49
52
|
return props.num
|
|
50
53
|
})
|
|
51
54
|
|
|
55
|
+
const displayLabel = computed(() => {
|
|
56
|
+
if (!props.special) return props.label
|
|
57
|
+
if (locale.value === 'en') return props.label
|
|
58
|
+
return '【' + props.label + '】'
|
|
59
|
+
})
|
|
60
|
+
|
|
52
61
|
const entries = computed(() =>
|
|
53
62
|
props.isAnnotations ? parseAnnotationBlock(props.text) : []
|
|
54
63
|
)
|
|
@@ -64,7 +73,7 @@ const paragraphsHtml = computed(() => {
|
|
|
64
73
|
<div v-if="text" ref="rootRef" class="sb-root" :class="{ 'sb-vertical': vertical, 'sb-visible': visible }">
|
|
65
74
|
<div class="sb-header">
|
|
66
75
|
<span v-if="displayNum" class="sb-num" :class="{ special }">{{ displayNum }}</span>
|
|
67
|
-
<h3>{{
|
|
76
|
+
<h3>{{ displayLabel }}</h3>
|
|
68
77
|
</div>
|
|
69
78
|
<div v-if="isAnnotations" class="sb-text sb-ann-list">
|
|
70
79
|
<div v-for="entry in entries" :key="entry.num" class="sb-ann-entry">
|
|
@@ -135,7 +135,7 @@ function toggleSettings() { settingsOpen.value = !settingsOpen.value }
|
|
|
135
135
|
border-left: 1px solid var(--border);
|
|
136
136
|
display: flex; flex-direction: column;
|
|
137
137
|
align-items: center;
|
|
138
|
-
padding: 12px 0;
|
|
138
|
+
padding: max(12px, env(safe-area-inset-top, 0px)) 0 max(12px, env(safe-area-inset-bottom, 0px));
|
|
139
139
|
z-index: 200;
|
|
140
140
|
gap: 8px;
|
|
141
141
|
}
|
|
@@ -347,11 +347,11 @@ function toggleSettings() { settingsOpen.value = !settingsOpen.value }
|
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
@media (max-width: 768px) {
|
|
350
|
-
.sidenav { width: 44px; padding: 8px 0; gap: 5px; }
|
|
350
|
+
.sidenav { width: 44px; padding: max(8px, env(safe-area-inset-top, 0px)) 0 max(8px, env(safe-area-inset-bottom, 0px)); gap: 5px; }
|
|
351
351
|
.sn-brand { width: 30px; height: 36px; margin-bottom: 2px; }
|
|
352
352
|
.sn-seal { font-size: 14px; }
|
|
353
|
-
.sn-btn { width:
|
|
354
|
-
.sn-btn svg { width:
|
|
353
|
+
.sn-btn { width: 32px; height: 32px; }
|
|
354
|
+
.sn-btn svg { width: 16px; height: 16px; }
|
|
355
355
|
.sn-context { font-size: 10px; max-height: 70px; }
|
|
356
356
|
.sn-settings { width: 180px; right: 52px; padding: 12px; }
|
|
357
357
|
.sn-layout-tag { width: 20px; height: 20px; font-size: 10px; }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
2
3
|
import type { Annotation, VerseLine } from '../types'
|
|
3
|
-
import { buildVerseAnnotations, renderAnnotatedText, resolveHoveredAnnotations
|
|
4
|
+
import { buildVerseAnnotations, renderAnnotatedText, resolveHoveredAnnotations } from '../composables/useAnnotationRenderer'
|
|
4
5
|
|
|
5
6
|
const props = defineProps<{
|
|
6
7
|
title: string
|
|
@@ -17,11 +18,23 @@ const emit = defineEmits<{
|
|
|
17
18
|
openAuthor: [name: string]
|
|
18
19
|
}>()
|
|
19
20
|
|
|
21
|
+
const allVerseSpans = computed(() =>
|
|
22
|
+
props.verses.map((_, i) => buildVerseAnnotations(props.annotations, i))
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const verseOffsets = computed(() => {
|
|
26
|
+
const offsets: number[] = []
|
|
27
|
+
let acc = 0
|
|
28
|
+
for (const spans of allVerseSpans.value) {
|
|
29
|
+
offsets.push(acc)
|
|
30
|
+
acc += spans.length
|
|
31
|
+
}
|
|
32
|
+
return offsets
|
|
33
|
+
})
|
|
34
|
+
|
|
20
35
|
function verseHtml(index: number): string {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const spans = buildVerseAnnotations(props.annotations, index)
|
|
24
|
-
return renderAnnotatedText(props.verses[index].text, spans, true, offset)
|
|
36
|
+
const spans = allVerseSpans.value[index]
|
|
37
|
+
return renderAnnotatedText(props.verses[index].text, spans, true, verseOffsets.value[index])
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
function onHover(event: MouseEvent) {
|
|
@@ -60,7 +73,7 @@ function onTap(event: MouseEvent) {
|
|
|
60
73
|
--ann-shadow-y: -2px;
|
|
61
74
|
writing-mode: vertical-rl;
|
|
62
75
|
text-orientation: mixed;
|
|
63
|
-
height: calc(100vh -
|
|
76
|
+
height: calc(100vh - var(--nav-width, 56px));
|
|
64
77
|
padding: 32px 24px;
|
|
65
78
|
background: var(--surface);
|
|
66
79
|
border: 1px solid var(--border);
|
|
@@ -94,8 +94,9 @@ const messages: Record<Locale, Record<string, string>> = {
|
|
|
94
94
|
'theme.sepia': '暖',
|
|
95
95
|
'theme.dark': '暗',
|
|
96
96
|
'theme.oled': '黑',
|
|
97
|
-
'
|
|
98
|
-
'
|
|
97
|
+
'pron.yue': '粵',
|
|
98
|
+
'pron.cmn': '普',
|
|
99
|
+
'annotation.kind.pronunciation': '讀音', 'annotation.kind.semantic': '釋義',
|
|
99
100
|
'annotation.kind.etymology': '詞源',
|
|
100
101
|
'annotation.kind.note': '備注',
|
|
101
102
|
'annotation.kind.definition': '釋義',
|
|
@@ -192,6 +193,8 @@ const messages: Record<Locale, Record<string, string>> = {
|
|
|
192
193
|
'theme.sepia': '暖',
|
|
193
194
|
'theme.dark': '暗',
|
|
194
195
|
'theme.oled': '黑',
|
|
196
|
+
'pron.yue': '粤',
|
|
197
|
+
'pron.cmn': '普',
|
|
195
198
|
'annotation.kind.pronunciation': '读音',
|
|
196
199
|
'annotation.kind.semantic': '释义',
|
|
197
200
|
'annotation.kind.etymology': '词源',
|
|
@@ -290,6 +293,8 @@ const messages: Record<Locale, Record<string, string>> = {
|
|
|
290
293
|
'theme.sepia': 'Warm',
|
|
291
294
|
'theme.dark': 'Dark',
|
|
292
295
|
'theme.oled': 'Black',
|
|
296
|
+
'pron.yue': 'Yue',
|
|
297
|
+
'pron.cmn': 'Man',
|
|
293
298
|
'annotation.kind.pronunciation': 'Pronunciation',
|
|
294
299
|
'annotation.kind.semantic': 'Definition',
|
|
295
300
|
'annotation.kind.etymology': 'Etymology',
|
|
@@ -18,44 +18,44 @@
|
|
|
18
18
|
|
|
19
19
|
/* ─── Annotation kind hover states ─── */
|
|
20
20
|
.ann-target:hover {
|
|
21
|
-
background:
|
|
22
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
21
|
+
background: color-mix(in srgb, var(--vermillion) 10%, transparent);
|
|
22
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--vermillion) 8%, transparent);
|
|
23
23
|
}
|
|
24
24
|
.ann-target.pronunciation:hover {
|
|
25
|
-
background:
|
|
26
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
25
|
+
background: color-mix(in srgb, var(--jade) 10%, transparent);
|
|
26
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--jade) 8%, transparent);
|
|
27
27
|
}
|
|
28
28
|
.ann-target.person:hover {
|
|
29
|
-
background:
|
|
30
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
29
|
+
background: color-mix(in srgb, var(--ann-person) 10%, transparent);
|
|
30
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-person) 8%, transparent);
|
|
31
31
|
}
|
|
32
32
|
.ann-target.place:hover {
|
|
33
|
-
background:
|
|
34
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
33
|
+
background: color-mix(in srgb, var(--ann-place) 10%, transparent);
|
|
34
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-place) 8%, transparent);
|
|
35
35
|
}
|
|
36
36
|
.ann-target.event:hover {
|
|
37
|
-
background:
|
|
38
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
37
|
+
background: color-mix(in srgb, var(--ann-event) 10%, transparent);
|
|
38
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-event) 8%, transparent);
|
|
39
39
|
}
|
|
40
40
|
.ann-target.date:hover {
|
|
41
|
-
background:
|
|
42
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
41
|
+
background: color-mix(in srgb, var(--ann-date) 10%, transparent);
|
|
42
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-date) 8%, transparent);
|
|
43
43
|
}
|
|
44
44
|
.ann-target.allusion:hover {
|
|
45
|
-
background:
|
|
46
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
45
|
+
background: color-mix(in srgb, var(--ann-allusion) 10%, transparent);
|
|
46
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-allusion) 8%, transparent);
|
|
47
47
|
}
|
|
48
48
|
.ann-target.etymology:hover {
|
|
49
|
-
background:
|
|
50
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
49
|
+
background: color-mix(in srgb, var(--ann-etymology) 10%, transparent);
|
|
50
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-etymology) 8%, transparent);
|
|
51
51
|
}
|
|
52
52
|
.ann-target.commentary:hover {
|
|
53
|
-
background:
|
|
54
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
53
|
+
background: color-mix(in srgb, var(--ann-commentary) 10%, transparent);
|
|
54
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-commentary) 8%, transparent);
|
|
55
55
|
}
|
|
56
56
|
.ann-target.translation:hover {
|
|
57
|
-
background:
|
|
58
|
-
box-shadow: 0 var(--ann-shadow-y, 2px) 8px
|
|
57
|
+
background: color-mix(in srgb, var(--ann-translation) 10%, transparent);
|
|
58
|
+
box-shadow: 0 var(--ann-shadow-y, 2px) 8px color-mix(in srgb, var(--ann-translation) 8%, transparent);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/* ─── Annotation number ─── */
|
|
@@ -167,7 +167,7 @@ button:focus-visible {
|
|
|
167
167
|
|
|
168
168
|
/* ===== ACTIVE ANNOTATION FLASH ===== */
|
|
169
169
|
@keyframes ann-flash-anim {
|
|
170
|
-
0% { background:
|
|
170
|
+
0% { background: color-mix(in srgb, var(--vermillion) 25%, transparent); box-shadow: 0 0 12px color-mix(in srgb, var(--vermillion) 15%, transparent); }
|
|
171
171
|
100% { background: transparent; box-shadow: none; }
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -251,7 +251,7 @@ button:focus-visible {
|
|
|
251
251
|
flex-direction: row-reverse;
|
|
252
252
|
overflow-x: auto;
|
|
253
253
|
overflow-y: hidden;
|
|
254
|
-
margin-right: var(--nav-width, 56px);
|
|
254
|
+
margin-right: calc(var(--nav-width, 56px) + env(safe-area-inset-right, 0px));
|
|
255
255
|
scrollbar-width: thin;
|
|
256
256
|
scrollbar-color: var(--gold) transparent;
|
|
257
257
|
scroll-snap-type: x proximity;
|
|
@@ -1332,7 +1332,7 @@ function tcy(n: number): string {
|
|
|
1332
1332
|
|
|
1333
1333
|
@media (max-width: 768px) {
|
|
1334
1334
|
/* ─── 直排模式 ─── */
|
|
1335
|
-
.v-page { margin-right: var(--nav-width, 44px); }
|
|
1335
|
+
.v-page { margin-right: calc(var(--nav-width, 44px) + env(safe-area-inset-right, 0px)); }
|
|
1336
1336
|
.v-title-col { padding: 20px 12px; }
|
|
1337
1337
|
.v-poem-title { font-size: 28px; letter-spacing: 6px; padding-left: 12px; }
|
|
1338
1338
|
.v-poem-author { font-size: 18px; letter-spacing: 4px; }
|