@hanology/cham-browser 0.4.57 → 0.4.59

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanology/cham-browser",
3
- "version": "0.4.57",
3
+ "version": "0.4.59",
4
4
  "description": "CHAM — browser-compatible parser, serializer, and site generator for Classical Han Annotated Markdown",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -127,17 +127,18 @@ const sourceLabel = (() => {
127
127
  :deep(.ann-target) {
128
128
  border-bottom: 2px solid var(--vermillion);
129
129
  cursor: help;
130
- transition: background 0.15s;
130
+ transition: background 0.2s ease, box-shadow 0.2s ease;
131
+ -webkit-box-decoration-break: clone;
132
+ box-decoration-break: clone;
131
133
  }
132
134
  :deep(.ann-target.ann-overlap) {
133
135
  border-bottom-width: 3px;
134
136
  border-bottom-style: double;
135
137
  }
136
-
137
138
  :deep(.ann-target:hover) {
138
- background: rgba(194, 58, 43, 0.08);
139
+ background: rgba(194, 58, 43, 0.1);
140
+ box-shadow: 0 2px 8px rgba(194, 58, 43, 0.08);
139
141
  }
140
-
141
142
  :deep(.ann-num) {
142
143
  font-size: 10px;
143
144
  color: var(--vermillion);
@@ -147,13 +148,47 @@ const sourceLabel = (() => {
147
148
  margin-right: 1px;
148
149
  letter-spacing: 0;
149
150
  }
150
-
151
151
  :deep(.ann-target.pronunciation) {
152
152
  border-bottom-color: var(--jade);
153
153
  }
154
-
155
154
  :deep(.ann-target.pronunciation:hover) {
156
- background: rgba(58, 107, 94, 0.08);
155
+ background: rgba(58, 107, 94, 0.1);
156
+ box-shadow: 0 2px 8px rgba(58, 107, 94, 0.08);
157
+ }
158
+ :deep(.ann-target.person) {
159
+ border-bottom-color: var(--ann-person);
160
+ }
161
+ :deep(.ann-target.place) {
162
+ border-bottom-color: var(--ann-place);
163
+ }
164
+ :deep(.ann-target.event) {
165
+ border-bottom-color: var(--ann-event);
166
+ }
167
+ :deep(.ann-target.date) {
168
+ border-bottom-color: var(--ann-date);
169
+ }
170
+ :deep(.ann-target.allusion) {
171
+ border-bottom-color: var(--ann-allusion);
172
+ }
173
+ :deep(.ann-target.person:hover) {
174
+ background: rgba(58, 90, 140, 0.1);
175
+ box-shadow: 0 2px 8px rgba(58, 90, 140, 0.08);
176
+ }
177
+ :deep(.ann-target.place:hover) {
178
+ background: rgba(139, 105, 20, 0.1);
179
+ box-shadow: 0 2px 8px rgba(139, 105, 20, 0.08);
180
+ }
181
+ :deep(.ann-target.event:hover) {
182
+ background: rgba(107, 76, 138, 0.1);
183
+ box-shadow: 0 2px 8px rgba(107, 76, 138, 0.08);
184
+ }
185
+ :deep(.ann-target.date:hover) {
186
+ background: rgba(42, 122, 122, 0.1);
187
+ box-shadow: 0 2px 8px rgba(42, 122, 122, 0.08);
188
+ }
189
+ :deep(.ann-target.allusion:hover) {
190
+ background: rgba(181, 101, 29, 0.1);
191
+ box-shadow: 0 2px 8px rgba(181, 101, 29, 0.08);
157
192
  }
158
193
 
159
194
  .part-block--vertical :deep(.ann-target) {
@@ -166,14 +201,57 @@ const sourceLabel = (() => {
166
201
  border-left-style: double;
167
202
  padding-left: 3px;
168
203
  }
169
-
170
204
  .part-block--vertical :deep(.ann-target.pronunciation) {
171
205
  border-left-color: var(--jade);
172
206
  }
173
-
207
+ .part-block--vertical :deep(.ann-target.pronunciation.semantic) {
208
+ border-left-color: var(--gold);
209
+ }
210
+ .part-block--vertical :deep(.ann-target.person) {
211
+ border-left-color: var(--ann-person);
212
+ }
213
+ .part-block--vertical :deep(.ann-target.place) {
214
+ border-left-color: var(--ann-place);
215
+ }
216
+ .part-block--vertical :deep(.ann-target.event) {
217
+ border-left-color: var(--ann-event);
218
+ }
219
+ .part-block--vertical :deep(.ann-target.date) {
220
+ border-left-color: var(--ann-date);
221
+ }
222
+ .part-block--vertical :deep(.ann-target.allusion) {
223
+ border-left-color: var(--ann-allusion);
224
+ }
225
+ .part-block--vertical :deep(.ann-target:hover) {
226
+ background: rgba(194, 58, 43, 0.1);
227
+ box-shadow: 0 -2px 8px rgba(194, 58, 43, 0.08);
228
+ }
229
+ .part-block--vertical :deep(.ann-target.pronunciation:hover) {
230
+ background: rgba(58, 107, 94, 0.1);
231
+ box-shadow: 0 -2px 8px rgba(58, 107, 94, 0.08);
232
+ }
233
+ .part-block--vertical :deep(.ann-target.person:hover) {
234
+ background: rgba(58, 90, 140, 0.1);
235
+ box-shadow: 0 -2px 8px rgba(58, 90, 140, 0.08);
236
+ }
237
+ .part-block--vertical :deep(.ann-target.place:hover) {
238
+ background: rgba(139, 105, 20, 0.1);
239
+ box-shadow: 0 -2px 8px rgba(139, 105, 20, 0.08);
240
+ }
241
+ .part-block--vertical :deep(.ann-target.event:hover) {
242
+ background: rgba(107, 76, 138, 0.1);
243
+ box-shadow: 0 -2px 8px rgba(107, 76, 138, 0.08);
244
+ }
245
+ .part-block--vertical :deep(.ann-target.date:hover) {
246
+ background: rgba(42, 122, 122, 0.1);
247
+ box-shadow: 0 -2px 8px rgba(42, 122, 122, 0.08);
248
+ }
249
+ .part-block--vertical :deep(.ann-target.allusion:hover) {
250
+ background: rgba(181, 101, 29, 0.1);
251
+ box-shadow: 0 -2px 8px rgba(181, 101, 29, 0.08);
252
+ }
174
253
  .part-block--vertical :deep(.ann-num) {
175
254
  font-size: 0.45em;
176
- text-combine-upright: all;
177
255
  text-align: end;
178
256
  letter-spacing: 0;
179
257
  vertical-align: baseline;
@@ -91,20 +91,23 @@ function openBook(bookId: string) {
91
91
  <div class="v-divider"></div>
92
92
  </section>
93
93
 
94
- <section class="v-cards-col">
95
- <div
96
- v-for="book in books"
97
- :key="book.id"
98
- class="v-book-card"
99
- @click="openBook(book.id)"
100
- >
101
- <div class="v-book-accent"></div>
102
- <h2 class="v-book-title">{{ book.title }}</h2>
103
- <p v-if="book.subtitle" class="v-book-sub">{{ book.subtitle }}</p>
104
- <div class="v-book-stats">
105
- <span class="v-book-count">{{ t('stat.pieceCount', { count: book.count }) }}</span>
94
+ <section class="v-shelf">
95
+ <template v-for="group in groupedBooks" :key="group.category">
96
+ <div class="v-shelf-cat">
97
+ <span class="v-cat-label">{{ group.category }}</span>
106
98
  </div>
107
- </div>
99
+ <div
100
+ v-for="book in group.books"
101
+ :key="book.id"
102
+ class="v-spine"
103
+ :class="'v-spine-' + bookCategory(book).replace(/\s/g, '-')"
104
+ @click="openBook(book.id)"
105
+ >
106
+ <span class="v-spine-accent"></span>
107
+ <span class="v-spine-title">{{ book.title }}</span>
108
+ <span class="v-spine-badge">{{ book.count }}</span>
109
+ </div>
110
+ </template>
108
111
  </section>
109
112
  </div>
110
113
  </div>
@@ -189,71 +192,126 @@ function openBook(bookId: string) {
189
192
  margin-left: 20px;
190
193
  }
191
194
 
192
- .v-cards-col {
195
+ .v-shelf {
193
196
  writing-mode: vertical-rl;
194
197
  text-orientation: mixed;
195
198
  flex-shrink: 0;
196
199
  display: flex;
197
200
  flex-direction: column;
198
- gap: 0;
199
- padding: 40px 16px;
200
201
  height: 100vh;
201
202
  box-sizing: border-box;
202
203
  overflow-x: auto;
203
204
  overflow-y: hidden;
204
- align-items: flex-start;
205
+ padding: 0;
206
+ scrollbar-width: thin;
207
+ scrollbar-color: var(--gold) transparent;
205
208
  }
209
+ .v-shelf::-webkit-scrollbar { height: 3px; }
210
+ .v-shelf::-webkit-scrollbar-thumb { background: var(--gold); border-radius: 2px; }
206
211
 
207
- .v-book-card {
212
+ /* ─── Category separator ─── */
213
+ .v-shelf-cat {
208
214
  writing-mode: vertical-rl;
209
215
  text-orientation: mixed;
210
216
  display: flex;
211
217
  flex-direction: column;
212
- align-items: flex-start;
213
- padding: 24px 16px;
218
+ align-items: center;
219
+ justify-content: center;
220
+ padding: 16px 6px;
221
+ flex-shrink: 0;
222
+ }
223
+ .v-cat-label {
224
+ writing-mode: vertical-rl;
225
+ text-orientation: mixed;
226
+ font-family: var(--sans);
227
+ font-size: 11px;
228
+ font-weight: 600;
229
+ letter-spacing: 4px;
230
+ color: var(--ink-faint);
231
+ padding: 8px 3px;
232
+ border-left: 1px solid var(--border-light);
233
+ border-right: 1px solid var(--border-light);
234
+ white-space: nowrap;
235
+ }
236
+
237
+ /* ─── Book spine ─── */
238
+ .v-spine {
239
+ writing-mode: vertical-rl;
240
+ text-orientation: mixed;
241
+ display: flex;
242
+ flex-direction: column;
243
+ align-items: center;
244
+ justify-content: flex-start;
245
+ height: 100vh;
246
+ width: 44px;
247
+ padding: 16px 0;
214
248
  border-left: 1px solid var(--border-light);
215
249
  cursor: pointer;
216
- transition: all 0.3s ease;
217
250
  position: relative;
251
+ box-sizing: border-box;
252
+ transition: background 0.25s ease, width 0.3s cubic-bezier(0.16, 1, 0.3, 1);
253
+ overflow: hidden;
254
+ flex-shrink: 0;
218
255
  }
219
- .v-book-card:hover {
220
- background: var(--surface);
256
+ .v-spine:hover {
257
+ background: linear-gradient(90deg, rgba(var(--shadow-rgb), 0.04), rgba(var(--shadow-rgb), 0.01));
258
+ width: 56px;
259
+ }
260
+ .v-spine:active {
261
+ transform: scale(0.97);
221
262
  }
222
- .v-book-accent {
263
+
264
+ .v-spine-accent {
223
265
  position: absolute;
224
- top: 0; right: 0;
225
- width: 0; height: 3px;
226
- background: var(--vermillion);
227
- transition: width 0.35s ease;
228
- }
229
- .v-book-card:hover .v-book-accent { width: 100%; }
230
- .v-book-title {
231
- font-size: 32px; font-weight: 900;
232
- letter-spacing: 8px; color: var(--ink);
233
- margin-left: 16px; padding-left: 16px;
234
- border-left: 3px solid var(--vermillion);
235
- line-height: 1.6;
266
+ top: 0;
267
+ left: 0;
268
+ width: 3px;
269
+ height: 0;
270
+ transition: height 0.4s cubic-bezier(0.16, 1, 0.3, 1);
236
271
  }
237
- .v-book-sub {
272
+ .v-spine:hover .v-spine-accent {
273
+ height: 100%;
274
+ }
275
+
276
+ /* Accent colors per category */
277
+ .v-spine .v-spine-accent { background: var(--vermillion); }
278
+ .v-spine.v-spine-教科書 .v-spine-accent { background: var(--gold); }
279
+ .v-spine.v-spine-四庫全書 .v-spine-accent { background: var(--jade); }
280
+
281
+ .v-spine-title {
282
+ writing-mode: vertical-rl;
283
+ text-orientation: mixed;
238
284
  font-size: 14px;
239
- color: var(--ink-light);
285
+ font-weight: 700;
240
286
  letter-spacing: 3px;
241
- margin-left: 12px;
242
- font-family: var(--sans);
287
+ color: var(--ink);
288
+ line-height: 1.6;
289
+ margin: 12px 0;
290
+ white-space: nowrap;
291
+ transition: color 0.2s ease;
243
292
  }
244
- .v-book-stats {
245
- margin-left: 12px;
246
- padding-left: 12px;
247
- border-left: 1px solid var(--border);
293
+ .v-spine:hover .v-spine-title {
294
+ color: var(--vermillion);
248
295
  }
249
- .v-book-count {
250
- font-size: 13px;
251
- color: var(--ink-faint);
252
- letter-spacing: 2px;
296
+ .v-spine.v-spine-教科書:hover .v-spine-title { color: var(--gold); }
297
+ .v-spine.v-spine-四庫全書:hover .v-spine-title { color: var(--jade); }
298
+
299
+ .v-spine-badge {
300
+ writing-mode: horizontal-tb;
253
301
  font-family: var(--sans);
254
- padding: 2px 8px;
302
+ font-size: 9px;
303
+ font-weight: 700;
304
+ color: var(--ink-faint);
255
305
  background: var(--surface-warm);
256
- border-radius: 4px;
306
+ border: 1px solid var(--border-light);
307
+ border-radius: 8px;
308
+ padding: 1px 6px;
309
+ margin-top: auto;
310
+ opacity: 0;
311
+ transition: opacity 0.25s ease;
312
+ }
313
+ .v-spine:hover .v-spine-badge {
314
+ opacity: 1;
257
315
  }
258
316
 
259
317
  /* ═══════ 橫排模式 ═══════ */
@@ -403,6 +461,9 @@ function openBook(bookId: string) {
403
461
  @media (max-width: 768px) {
404
462
  .v-page { padding: 0 16px; }
405
463
  .v-title { font-size: 36px; letter-spacing: 10px; }
464
+ .v-spine { width: 38px; }
465
+ .v-spine:hover { width: 48px; }
466
+ .v-spine-title { font-size: 13px; letter-spacing: 2px; }
406
467
  .lib-root { padding: 40px 16px 80px; }
407
468
  .lib-hero { margin-bottom: 32px; }
408
469
  .lib-hero h1 { font-size: 28px; letter-spacing: 4px; }
@@ -789,7 +789,8 @@ function tcy(n: number): string {
789
789
  flex-direction: row-reverse;
790
790
  align-items: flex-start;
791
791
  gap: 0;
792
- max-height: calc(100vh - 100px);
792
+ height: 100vh;
793
+ box-sizing: border-box;
793
794
  overflow-x: auto;
794
795
  overflow-y: hidden;
795
796
  padding: 20px 16px;