@tnotesjs/ui 0.3.3 → 0.4.1
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/README.md +0 -32
- package/dist/components/Mindmap/markdown.d.ts +7 -2
- package/dist/components/Mindmap/markdown.js +17 -3
- package/package.json +5 -9
- package/src/components/Footprints/Footprints.vue +4 -0
- package/src/components/ImagePreview/ImagePreview.vue +1 -0
- package/src/components/Mermaid/Mermaid.vue +1 -2
- package/src/components/Mindmap/Mindmap.vue +35 -20
- package/src/components/Mindmap/editor/MindmapView.vue +91 -2
- package/src/components/Mindmap/editor/OutlineView.vue +6 -2
- package/src/components/Mindmap/editor/RichInlineEditor.vue +9 -8
- package/src/components/Mindmap/editor/SelectionToolbar.vue +38 -22
- package/src/components/Mindmap/markdown.ts +19 -4
- package/src/components/WordList/RightClickMenu.vue +12 -7
- package/src/components/WordList/WordList.vue +197 -61
- package/src/excalidraw/ExcalidrawSvg.vue +2 -1
- package/src/index.ts +7 -1
- package/src/styles/code.css +1 -1
- package/src/styles/prose.css +49 -23
- package/src/styles/swiper.css +35 -7
- package/src/styles/tokens.css +23 -4
- package/src/swiper/hydrate.ts +8 -0
- package/src/entries/mindmap-editor.ts +0 -37
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
export interface MindmapFenceOptions {
|
|
2
2
|
title?: string
|
|
3
3
|
initialExpandLevel?: number
|
|
4
|
+
/** Pane height in px (`h=480` in the fence info). */
|
|
5
|
+
height?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const MINDMAP_MIN_HEIGHT = 200
|
|
9
|
+
export const MINDMAP_MAX_HEIGHT = 1600
|
|
10
|
+
|
|
11
|
+
export function clampMindmapHeight(value: number): number {
|
|
12
|
+
return Math.round(Math.min(MINDMAP_MAX_HEIGHT, Math.max(MINDMAP_MIN_HEIGHT, value)))
|
|
4
13
|
}
|
|
5
14
|
|
|
6
15
|
function cleanHeadingText(value: string): string {
|
|
@@ -10,7 +19,7 @@ function cleanHeadingText(value: string): string {
|
|
|
10
19
|
.trim()
|
|
11
20
|
}
|
|
12
21
|
|
|
13
|
-
/** Parse the canonical `mindmap [title] 2` fence metadata. */
|
|
22
|
+
/** Parse the canonical `mindmap [title] 2 h=480` fence metadata. */
|
|
14
23
|
export function parseMindmapFence(openLine: string): MindmapFenceOptions | null {
|
|
15
24
|
const fenceBody = openLine.trim().replace(/^`+\s*/, '')
|
|
16
25
|
const nameMatch = fenceBody.match(/^mindmap(?=\s|\[|$)/)
|
|
@@ -25,9 +34,15 @@ export function parseMindmapFence(openLine: string): MindmapFenceOptions | null
|
|
|
25
34
|
`${rest.slice(0, titleMatch.index)} ${rest.slice((titleMatch.index ?? 0) + titleMatch[0].length)}`.trim()
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
for (const token of rest.split(/\s+/).filter(Boolean)) {
|
|
38
|
+
const height = token.match(/^h=(\d+)$/)
|
|
39
|
+
if (height) {
|
|
40
|
+
options.height = clampMindmapHeight(Number(height[1]))
|
|
41
|
+
} else if (/^\d+$/.test(token) && options.initialExpandLevel === undefined) {
|
|
42
|
+
options.initialExpandLevel = Math.max(1, Number(token))
|
|
43
|
+
} else {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
31
46
|
}
|
|
32
47
|
return options
|
|
33
48
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
v-if="show"
|
|
4
|
+
class="rightClickMenu"
|
|
5
|
+
:style="{ left: x + 'px', top: y + 'px' }"
|
|
6
|
+
@mousedown.stop
|
|
7
|
+
>
|
|
3
8
|
<div v-if="showPin" class="menuItem" @click="handlePin">📌 Pin</div>
|
|
4
9
|
<div class="menuItem" @click="(e) => handlePronounce(e, 'en-GB')">📢 Pronounce(英)</div>
|
|
5
10
|
<div class="menuItem" @click="(e) => handlePronounce(e, 'en-US')">📢 Pronounce(美)</div>
|
|
@@ -61,12 +66,12 @@ const handleReset = () => {
|
|
|
61
66
|
.rightClickMenu {
|
|
62
67
|
position: fixed;
|
|
63
68
|
z-index: 99999;
|
|
64
|
-
background:
|
|
65
|
-
border: 1px solid
|
|
66
|
-
border-radius:
|
|
67
|
-
box-shadow:
|
|
69
|
+
background: var(--tn-c-bg-elv, var(--tn-c-bg));
|
|
70
|
+
border: 1px solid var(--tn-c-divider);
|
|
71
|
+
border-radius: 8px;
|
|
72
|
+
box-shadow: var(--tn-shadow-2);
|
|
68
73
|
font-size: 13px;
|
|
69
|
-
color:
|
|
74
|
+
color: var(--tn-c-text);
|
|
70
75
|
cursor: pointer;
|
|
71
76
|
user-select: none;
|
|
72
77
|
}
|
|
@@ -75,7 +80,7 @@ const handleReset = () => {
|
|
|
75
80
|
padding: 8px 16px;
|
|
76
81
|
|
|
77
82
|
&:hover {
|
|
78
|
-
background
|
|
83
|
+
background: var(--tn-c-hover);
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
</style>
|
|
@@ -48,6 +48,7 @@ const sortedWords = computed(() => {
|
|
|
48
48
|
if (!props.needSort) return unique
|
|
49
49
|
return unique.sort((a, b) => a.toLowerCase().charCodeAt(0) - b.toLowerCase().charCodeAt(0))
|
|
50
50
|
})
|
|
51
|
+
const islandWords = computed(() => encodeURIComponent(JSON.stringify(sortedWords.value)))
|
|
51
52
|
const checkedStates = ref({})
|
|
52
53
|
|
|
53
54
|
const updateCheckedState = (word, isChecked) => {
|
|
@@ -84,6 +85,7 @@ const cardX = ref(0)
|
|
|
84
85
|
const cardY = ref(0)
|
|
85
86
|
const cardContent = ref('')
|
|
86
87
|
const wordCache = ref({})
|
|
88
|
+
const wordMarkdown = ref({})
|
|
87
89
|
|
|
88
90
|
// 加载失败的词汇(也就是词库中不存在的词汇)
|
|
89
91
|
const failedWords = ref({})
|
|
@@ -112,6 +114,66 @@ let currentWordForContextMenu = null
|
|
|
112
114
|
// 防抖计时器
|
|
113
115
|
let hoverTimer = null
|
|
114
116
|
|
|
117
|
+
function wordSlug(word) {
|
|
118
|
+
return encodeURIComponent(word.toLowerCase().replaceAll(/\s/g, '_'))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function wordGithubUrl(word) {
|
|
122
|
+
return `${props.wordsBaseUrl}${wordSlug(word)}.md`
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function ensureWord(word) {
|
|
126
|
+
if (wordCache.value[word]) return
|
|
127
|
+
const url = `${props.wordsRawBaseUrl}${wordSlug(word)}.md`
|
|
128
|
+
try {
|
|
129
|
+
const res = await fetch(url)
|
|
130
|
+
if (res.ok) {
|
|
131
|
+
const text = await res.text()
|
|
132
|
+
wordMarkdown.value[word] = text
|
|
133
|
+
wordCache.value[word] = marked.parse(text)
|
|
134
|
+
} else {
|
|
135
|
+
wordCache.value[word] = `<em>无法加载单词内容</em>`
|
|
136
|
+
failedWords.value[word] = true
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error(err)
|
|
140
|
+
wordCache.value[word] = `<em>加载失败</em>`
|
|
141
|
+
failedWords.value[word] = true
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const copiedCardId = ref(null)
|
|
146
|
+
let copiedTimer = null
|
|
147
|
+
|
|
148
|
+
async function copyCardMarkdown(card, event) {
|
|
149
|
+
event.stopPropagation()
|
|
150
|
+
const markdown = wordMarkdown.value[card.word]
|
|
151
|
+
if (!markdown) return
|
|
152
|
+
try {
|
|
153
|
+
await navigator.clipboard.writeText(markdown)
|
|
154
|
+
} catch {
|
|
155
|
+
const field = document.createElement('textarea')
|
|
156
|
+
field.value = markdown
|
|
157
|
+
field.setAttribute('readonly', '')
|
|
158
|
+
field.style.position = 'fixed'
|
|
159
|
+
field.style.left = '-9999px'
|
|
160
|
+
document.body.append(field)
|
|
161
|
+
field.select()
|
|
162
|
+
document.execCommand('copy')
|
|
163
|
+
field.remove()
|
|
164
|
+
}
|
|
165
|
+
copiedCardId.value = card.id
|
|
166
|
+
clearTimeout(copiedTimer)
|
|
167
|
+
copiedTimer = setTimeout(() => {
|
|
168
|
+
copiedCardId.value = null
|
|
169
|
+
}, 1500)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function openCardOnGithub(card, event) {
|
|
173
|
+
event.stopPropagation()
|
|
174
|
+
window.open(wordGithubUrl(card.word), '_blank', 'noopener')
|
|
175
|
+
}
|
|
176
|
+
|
|
115
177
|
/**
|
|
116
178
|
* 显示单词卡片
|
|
117
179
|
*/
|
|
@@ -128,29 +190,8 @@ const showWordCard = async (e, word) => {
|
|
|
128
190
|
cardY.value = clientY + 10
|
|
129
191
|
showCard.value = true
|
|
130
192
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
resolve()
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const url = `${props.wordsRawBaseUrl}${encodeURIComponent(
|
|
138
|
-
word.toLowerCase().replaceAll(/\s/g, '_')
|
|
139
|
-
)}.md`
|
|
140
|
-
try {
|
|
141
|
-
const res = await fetch(url)
|
|
142
|
-
if (res.ok) {
|
|
143
|
-
let text = await res.text()
|
|
144
|
-
text = marked.parse(text)
|
|
145
|
-
wordCache.value[word] = text
|
|
146
|
-
cardContent.value = text
|
|
147
|
-
} else {
|
|
148
|
-
cardContent.value = `<em>无法加载单词内容</em>`
|
|
149
|
-
}
|
|
150
|
-
} catch (err) {
|
|
151
|
-
console.error(err)
|
|
152
|
-
cardContent.value = `<em>加载失败</em>`
|
|
153
|
-
}
|
|
193
|
+
await ensureWord(word)
|
|
194
|
+
cardContent.value = wordCache.value[word] || `<em>加载失败</em>`
|
|
154
195
|
resolve()
|
|
155
196
|
}, 300)
|
|
156
197
|
})
|
|
@@ -208,28 +249,8 @@ const preloadWords = async () => {
|
|
|
208
249
|
for (let i = 0; i < wordsToPreload.length; i++) {
|
|
209
250
|
const word = wordsToPreload[i]
|
|
210
251
|
|
|
211
|
-
// 如果已经缓存过,跳过
|
|
212
252
|
if (wordCache.value[word]) continue
|
|
213
|
-
|
|
214
|
-
const url = `${props.wordsRawBaseUrl}${encodeURIComponent(
|
|
215
|
-
word.toLowerCase().replaceAll(/\s/g, '_')
|
|
216
|
-
)}.md`
|
|
217
|
-
try {
|
|
218
|
-
const res = await fetch(url)
|
|
219
|
-
if (res.ok) {
|
|
220
|
-
let text = await res.text()
|
|
221
|
-
text = marked.parse(text)
|
|
222
|
-
wordCache.value[word] = text
|
|
223
|
-
console.log(`✅ 预加载完成: ${word}`)
|
|
224
|
-
} else {
|
|
225
|
-
wordCache.value[word] = `<em>无法加载单词内容</em>`
|
|
226
|
-
failedWords.value[word] = true
|
|
227
|
-
}
|
|
228
|
-
} catch (err) {
|
|
229
|
-
console.error(`❌ 加载失败: ${word}`, err)
|
|
230
|
-
wordCache.value[word] = `<em>加载失败</em>`
|
|
231
|
-
failedWords.value[word] = true
|
|
232
|
-
}
|
|
253
|
+
await ensureWord(word)
|
|
233
254
|
|
|
234
255
|
// 可选:加个延迟避免并发请求过多
|
|
235
256
|
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
@@ -446,6 +467,7 @@ onMounted(() => {
|
|
|
446
467
|
*/
|
|
447
468
|
onUnmounted(() => {
|
|
448
469
|
clearTimeout(hoverTimer)
|
|
470
|
+
clearTimeout(copiedTimer)
|
|
449
471
|
if (typeof document !== 'undefined') {
|
|
450
472
|
document.removeEventListener('mousemove', onDragging)
|
|
451
473
|
document.removeEventListener('mouseup', stopDrag)
|
|
@@ -455,7 +477,12 @@ onUnmounted(() => {
|
|
|
455
477
|
</script>
|
|
456
478
|
|
|
457
479
|
<template>
|
|
458
|
-
<div
|
|
480
|
+
<div
|
|
481
|
+
class="tn-word-list"
|
|
482
|
+
data-tn-island="word-list"
|
|
483
|
+
:data-words="islandWords"
|
|
484
|
+
:data-need-sort="needSort ? 'true' : 'false'"
|
|
485
|
+
>
|
|
459
486
|
<ol>
|
|
460
487
|
<li
|
|
461
488
|
v-for="(word, index) in sortedWords"
|
|
@@ -515,10 +542,60 @@ onUnmounted(() => {
|
|
|
515
542
|
@mousedown="(e) => startDrag(card, e)"
|
|
516
543
|
@click="bringToFront(card)"
|
|
517
544
|
>
|
|
545
|
+
<div class="wordCardTools">
|
|
546
|
+
<button
|
|
547
|
+
type="button"
|
|
548
|
+
class="cardTool"
|
|
549
|
+
aria-label="在 GitHub 打开"
|
|
550
|
+
title="在 GitHub 打开"
|
|
551
|
+
@mousedown.stop
|
|
552
|
+
@click.stop="openCardOnGithub(card, $event)"
|
|
553
|
+
>
|
|
554
|
+
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
|
|
555
|
+
<path
|
|
556
|
+
fill="currentColor"
|
|
557
|
+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"
|
|
558
|
+
/>
|
|
559
|
+
</svg>
|
|
560
|
+
</button>
|
|
561
|
+
<button
|
|
562
|
+
type="button"
|
|
563
|
+
class="cardTool"
|
|
564
|
+
:aria-label="copiedCardId === card.id ? '已复制' : '复制 Markdown'"
|
|
565
|
+
:title="copiedCardId === card.id ? '已复制' : '复制 Markdown'"
|
|
566
|
+
@mousedown.stop
|
|
567
|
+
@click.stop="copyCardMarkdown(card, $event)"
|
|
568
|
+
>
|
|
569
|
+
<svg
|
|
570
|
+
v-if="copiedCardId !== card.id"
|
|
571
|
+
viewBox="0 0 24 24"
|
|
572
|
+
width="16"
|
|
573
|
+
height="16"
|
|
574
|
+
aria-hidden="true"
|
|
575
|
+
>
|
|
576
|
+
<path
|
|
577
|
+
fill="none"
|
|
578
|
+
stroke="currentColor"
|
|
579
|
+
stroke-width="1.8"
|
|
580
|
+
d="M9 9h10v10H9zM5 15V5h10"
|
|
581
|
+
/>
|
|
582
|
+
</svg>
|
|
583
|
+
<svg v-else viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
|
|
584
|
+
<path
|
|
585
|
+
fill="none"
|
|
586
|
+
stroke="currentColor"
|
|
587
|
+
stroke-width="1.8"
|
|
588
|
+
stroke-linecap="round"
|
|
589
|
+
stroke-linejoin="round"
|
|
590
|
+
d="m5 12 5 5L20 7"
|
|
591
|
+
/>
|
|
592
|
+
</svg>
|
|
593
|
+
</button>
|
|
594
|
+
</div>
|
|
518
595
|
<div class="wordCardContentWrapper">
|
|
519
596
|
<div class="wordCardContent" v-html="card.content"></div>
|
|
520
597
|
</div>
|
|
521
|
-
<button class="closeBtn" @click.stop="removeCard(card.id)">✖</button>
|
|
598
|
+
<button class="closeBtn" @mousedown.stop @click.stop="removeCard(card.id)">✖</button>
|
|
522
599
|
<div class="resizeHandle" @mousedown.stop="startResize(card, $event)"></div>
|
|
523
600
|
</div>
|
|
524
601
|
</template>
|
|
@@ -549,11 +626,41 @@ onUnmounted(() => {
|
|
|
549
626
|
|
|
550
627
|
<style scoped lang="scss">
|
|
551
628
|
.tn-word-list {
|
|
629
|
+
padding: 12px 16px;
|
|
630
|
+
border: 1px solid var(--tn-c-divider);
|
|
631
|
+
border-radius: 8px;
|
|
632
|
+
|
|
552
633
|
// Checkbox 样式
|
|
553
634
|
input[type='checkbox'] {
|
|
554
|
-
|
|
555
|
-
|
|
635
|
+
appearance: none;
|
|
636
|
+
-webkit-appearance: none;
|
|
637
|
+
position: relative;
|
|
638
|
+
box-sizing: border-box;
|
|
639
|
+
flex: none;
|
|
640
|
+
width: 16px;
|
|
641
|
+
height: 16px;
|
|
642
|
+
margin: 0 8px;
|
|
643
|
+
border: 1px solid var(--tn-c-task-check-border);
|
|
644
|
+
border-radius: 4px;
|
|
645
|
+
background: var(--tn-c-task-check-bg);
|
|
556
646
|
cursor: pointer;
|
|
647
|
+
|
|
648
|
+
&:checked {
|
|
649
|
+
border-color: var(--tn-c-task-check);
|
|
650
|
+
background: var(--tn-c-task-check);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
&:checked::after {
|
|
654
|
+
content: '';
|
|
655
|
+
position: absolute;
|
|
656
|
+
left: 4.5px;
|
|
657
|
+
top: 1px;
|
|
658
|
+
width: 4px;
|
|
659
|
+
height: 9px;
|
|
660
|
+
border: solid #fff;
|
|
661
|
+
border-width: 0 2px 2px 0;
|
|
662
|
+
transform: rotate(45deg);
|
|
663
|
+
}
|
|
557
664
|
}
|
|
558
665
|
|
|
559
666
|
// 链接样式
|
|
@@ -577,8 +684,9 @@ onUnmounted(() => {
|
|
|
577
684
|
|
|
578
685
|
// 单词列表样式
|
|
579
686
|
ol {
|
|
580
|
-
list-style
|
|
581
|
-
|
|
687
|
+
list-style: none;
|
|
688
|
+
margin: 0;
|
|
689
|
+
padding-left: 0;
|
|
582
690
|
|
|
583
691
|
li {
|
|
584
692
|
display: flex;
|
|
@@ -599,13 +707,12 @@ onUnmounted(() => {
|
|
|
599
707
|
}
|
|
600
708
|
}
|
|
601
709
|
|
|
602
|
-
// 单词卡片样式(🌑 暗色悬浮卡片)
|
|
603
710
|
.wordCard {
|
|
604
711
|
position: fixed;
|
|
605
712
|
z-index: 9999;
|
|
606
|
-
background:
|
|
607
|
-
border: 1px solid
|
|
608
|
-
box-shadow:
|
|
713
|
+
background: var(--tn-c-bg-elv, var(--tn-c-bg));
|
|
714
|
+
border: 1px solid var(--tn-c-divider);
|
|
715
|
+
box-shadow: var(--tn-shadow-2);
|
|
609
716
|
padding: 12px 16px;
|
|
610
717
|
max-width: 600px;
|
|
611
718
|
min-width: 200px;
|
|
@@ -613,12 +720,11 @@ onUnmounted(() => {
|
|
|
613
720
|
font-size: 14px;
|
|
614
721
|
line-height: 1.4;
|
|
615
722
|
border-radius: 8px;
|
|
616
|
-
color:
|
|
723
|
+
color: var(--tn-c-text);
|
|
617
724
|
pointer-events: auto;
|
|
618
|
-
font-family: sans-serif;
|
|
725
|
+
font-family: var(--tn-font-sans, sans-serif);
|
|
619
726
|
cursor: move;
|
|
620
727
|
|
|
621
|
-
// 关闭按钮
|
|
622
728
|
.closeBtn {
|
|
623
729
|
position: absolute;
|
|
624
730
|
right: 5px;
|
|
@@ -627,19 +733,49 @@ onUnmounted(() => {
|
|
|
627
733
|
border: none;
|
|
628
734
|
font-size: 16px;
|
|
629
735
|
cursor: pointer;
|
|
630
|
-
color:
|
|
736
|
+
color: var(--tn-c-text-2);
|
|
631
737
|
|
|
632
738
|
&:hover {
|
|
633
|
-
color:
|
|
739
|
+
color: var(--tn-c-text);
|
|
634
740
|
}
|
|
635
741
|
}
|
|
636
742
|
}
|
|
637
743
|
|
|
638
744
|
// 卡片内容包裹器
|
|
745
|
+
.wordCardTools {
|
|
746
|
+
position: absolute;
|
|
747
|
+
top: 6px;
|
|
748
|
+
left: 8px;
|
|
749
|
+
z-index: 2;
|
|
750
|
+
display: flex;
|
|
751
|
+
gap: 2px;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.cardTool {
|
|
755
|
+
display: inline-flex;
|
|
756
|
+
align-items: center;
|
|
757
|
+
justify-content: center;
|
|
758
|
+
width: 24px;
|
|
759
|
+
height: 24px;
|
|
760
|
+
padding: 0;
|
|
761
|
+
border: 0;
|
|
762
|
+
border-radius: 4px;
|
|
763
|
+
background: transparent;
|
|
764
|
+
color: var(--tn-c-text-2);
|
|
765
|
+
cursor: pointer;
|
|
766
|
+
|
|
767
|
+
&:hover {
|
|
768
|
+
background: var(--tn-c-hover);
|
|
769
|
+
color: var(--tn-c-text);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
639
773
|
.wordCardContentWrapper {
|
|
640
774
|
width: 100%;
|
|
641
775
|
height: 100%;
|
|
642
776
|
overflow: auto;
|
|
777
|
+
box-sizing: border-box;
|
|
778
|
+
padding-top: 22px;
|
|
643
779
|
}
|
|
644
780
|
|
|
645
781
|
// 卡片内容样式
|
|
@@ -657,13 +793,13 @@ onUnmounted(() => {
|
|
|
657
793
|
bottom: 0;
|
|
658
794
|
width: 12px;
|
|
659
795
|
height: 12px;
|
|
660
|
-
background-color:
|
|
796
|
+
background-color: var(--tn-c-text-2);
|
|
661
797
|
cursor: nwse-resize;
|
|
662
798
|
z-index: 2;
|
|
663
799
|
border-radius: 50%;
|
|
664
800
|
|
|
665
801
|
&:hover {
|
|
666
|
-
background-color:
|
|
802
|
+
background-color: var(--tn-c-text);
|
|
667
803
|
}
|
|
668
804
|
}
|
|
669
805
|
</style>
|
|
@@ -105,12 +105,13 @@ defineExpose({ state, errorReason })
|
|
|
105
105
|
align-items: center;
|
|
106
106
|
justify-content: center;
|
|
107
107
|
overflow: hidden;
|
|
108
|
-
border-radius:
|
|
108
|
+
border-radius: 4px;
|
|
109
109
|
background: var(--tn-excalidraw-bg, transparent);
|
|
110
110
|
}
|
|
111
111
|
.tn-excalidraw-view__image {
|
|
112
112
|
max-width: 100%;
|
|
113
113
|
max-height: 100%;
|
|
114
|
+
border-radius: 4px;
|
|
114
115
|
object-fit: contain;
|
|
115
116
|
}
|
|
116
117
|
.tn-excalidraw-view__placeholder {
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,13 @@ export {
|
|
|
11
11
|
resolveWordListFeatures
|
|
12
12
|
} from './components/WordList/wordListFeatures'
|
|
13
13
|
export type { WordListFeatures } from './components/WordList/wordListFeatures'
|
|
14
|
-
export {
|
|
14
|
+
export {
|
|
15
|
+
clampMindmapHeight,
|
|
16
|
+
MINDMAP_MAX_HEIGHT,
|
|
17
|
+
MINDMAP_MIN_HEIGHT,
|
|
18
|
+
normalizeMindmapMarkdown,
|
|
19
|
+
parseMindmapFence
|
|
20
|
+
} from './components/Mindmap/markdown'
|
|
15
21
|
export type { MindmapFenceOptions, NormalizeMindmapOptions } from './components/Mindmap/markdown'
|
|
16
22
|
export type { NotesTableRow } from './components/NotesTable/types'
|
|
17
23
|
export {
|
package/src/styles/code.css
CHANGED
package/src/styles/prose.css
CHANGED
|
@@ -47,15 +47,15 @@
|
|
|
47
47
|
line-height: var(--tn-heading-6-line);
|
|
48
48
|
}
|
|
49
49
|
:is(.tn-prose, .vp-doc) :where(a) {
|
|
50
|
-
color: var(--tn-c-
|
|
50
|
+
color: var(--tn-c-link);
|
|
51
51
|
text-decoration: none;
|
|
52
|
-
font-weight:
|
|
52
|
+
font-weight: 400;
|
|
53
53
|
overflow-wrap: anywhere;
|
|
54
54
|
word-break: break-word;
|
|
55
55
|
}
|
|
56
56
|
:is(.tn-prose, .vp-doc) :where(a:hover) {
|
|
57
|
-
color: var(--tn-c-
|
|
58
|
-
text-decoration:
|
|
57
|
+
color: var(--tn-c-link);
|
|
58
|
+
text-decoration: none;
|
|
59
59
|
}
|
|
60
60
|
:is(.tn-prose, .vp-doc) :where(ul, ol) {
|
|
61
61
|
padding-left: 1.5rem;
|
|
@@ -106,11 +106,21 @@
|
|
|
106
106
|
background-size: 12px 12px;
|
|
107
107
|
}
|
|
108
108
|
:is(.tn-prose, .vp-doc) :where(:not(pre) > code) {
|
|
109
|
-
color:
|
|
110
|
-
background: var(--tn-c-
|
|
109
|
+
color: inherit;
|
|
110
|
+
background: var(--tn-c-inline-code-bg);
|
|
111
|
+
border: 1px solid var(--tn-c-inline-code-border);
|
|
111
112
|
border-radius: 4px;
|
|
112
|
-
|
|
113
|
-
padding:
|
|
113
|
+
margin: 1px 3px;
|
|
114
|
+
padding: 0 4px;
|
|
115
|
+
font-family: var(--tn-font-mono);
|
|
116
|
+
font-size: 1em;
|
|
117
|
+
font-weight: 400;
|
|
118
|
+
}
|
|
119
|
+
:is(.tn-prose, .vp-doc) :where(mark) {
|
|
120
|
+
color: inherit;
|
|
121
|
+
background: var(--tn-c-mark-bg);
|
|
122
|
+
border-radius: 2px;
|
|
123
|
+
padding: 0 2px;
|
|
114
124
|
}
|
|
115
125
|
:is(.tn-prose, .vp-doc) :where(blockquote) {
|
|
116
126
|
margin: 16px 0;
|
|
@@ -155,6 +165,13 @@
|
|
|
155
165
|
max-width: 100%;
|
|
156
166
|
height: auto;
|
|
157
167
|
}
|
|
168
|
+
/* 笔记里的图片默认 4px 圆角。Desk 和静态站共用;按钮里的图标保持直角。 */
|
|
169
|
+
:is(.tn-prose, .vp-doc, .live-editor) :where(img) {
|
|
170
|
+
border-radius: 4px;
|
|
171
|
+
}
|
|
172
|
+
:is(.tn-prose, .vp-doc, .live-editor) button img {
|
|
173
|
+
border-radius: 0;
|
|
174
|
+
}
|
|
158
175
|
:is(.tn-prose, .vp-doc) :where(figure.tn-image) {
|
|
159
176
|
display: block;
|
|
160
177
|
width: fit-content;
|
|
@@ -174,6 +191,14 @@
|
|
|
174
191
|
width: 100%;
|
|
175
192
|
height: auto;
|
|
176
193
|
}
|
|
194
|
+
|
|
195
|
+
/* 正文图片边界。Desk 可视化图和静态站图共用;Swiper 的边画在 .tn-swiper 上。 */
|
|
196
|
+
:is(.tn-prose, .vp-doc) :where(figure.tn-image > img),
|
|
197
|
+
.cm-lp-image-frame > img,
|
|
198
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block img) {
|
|
199
|
+
border: 1px solid var(--tn-c-media-edge);
|
|
200
|
+
border-radius: 4px;
|
|
201
|
+
}
|
|
177
202
|
:is(.tn-prose, .vp-doc) :where(figure.tn-image > figcaption),
|
|
178
203
|
:is(.tn-prose, .vp-doc) :where(figcaption) {
|
|
179
204
|
display: block;
|
|
@@ -185,7 +210,7 @@
|
|
|
185
210
|
}
|
|
186
211
|
|
|
187
212
|
/* Callouts — same soft-tint cards as Desk visual (no left accent bar). */
|
|
188
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block) {
|
|
213
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block) {
|
|
189
214
|
box-sizing: border-box;
|
|
190
215
|
margin: 16px 0;
|
|
191
216
|
padding: 12px 16px;
|
|
@@ -195,37 +220,37 @@
|
|
|
195
220
|
font-size: inherit;
|
|
196
221
|
line-height: 1.5;
|
|
197
222
|
}
|
|
198
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.tip) {
|
|
223
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.tip) {
|
|
199
224
|
background-color: var(--tn-c-highlight);
|
|
200
225
|
}
|
|
201
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.info),
|
|
202
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.note),
|
|
203
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.details) {
|
|
226
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.info),
|
|
227
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.note),
|
|
228
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.details) {
|
|
204
229
|
background-color: var(--tn-c-hover);
|
|
205
230
|
}
|
|
206
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.warning) {
|
|
231
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.warning) {
|
|
207
232
|
background-color: var(--tn-c-warning-soft);
|
|
208
233
|
}
|
|
209
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.danger) {
|
|
234
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.danger) {
|
|
210
235
|
background-color: var(--tn-c-danger-soft);
|
|
211
236
|
}
|
|
212
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block-title) {
|
|
237
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block-title) {
|
|
213
238
|
margin: 0 0 2px;
|
|
214
239
|
font-size: inherit;
|
|
215
240
|
font-weight: 600;
|
|
216
241
|
line-height: 1.5;
|
|
217
242
|
color: var(--tn-c-text);
|
|
218
243
|
}
|
|
219
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block > p:not(.tn-custom-block-title)) {
|
|
244
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block > p:not(.tn-custom-block-title)) {
|
|
220
245
|
margin: 0;
|
|
221
246
|
padding: 4px 0;
|
|
222
247
|
font-size: 1em;
|
|
223
248
|
line-height: 1.5;
|
|
224
249
|
}
|
|
225
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block > :last-child) {
|
|
250
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block > :last-child) {
|
|
226
251
|
margin-bottom: 0;
|
|
227
252
|
}
|
|
228
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block > summary) {
|
|
253
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block > summary) {
|
|
229
254
|
display: flex;
|
|
230
255
|
align-items: center;
|
|
231
256
|
gap: 8px;
|
|
@@ -236,13 +261,14 @@
|
|
|
236
261
|
font-weight: 600;
|
|
237
262
|
line-height: 1.5;
|
|
238
263
|
}
|
|
239
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block:not([open]) > summary) {
|
|
264
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block:not([open]) > summary) {
|
|
240
265
|
margin-bottom: 0;
|
|
241
266
|
}
|
|
242
|
-
:is(.tn-prose, .vp-doc
|
|
267
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card)
|
|
268
|
+
:where(details.tn-custom-block > summary::-webkit-details-marker) {
|
|
243
269
|
display: none;
|
|
244
270
|
}
|
|
245
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block > summary::before) {
|
|
271
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block > summary::before) {
|
|
246
272
|
content: '';
|
|
247
273
|
flex: none;
|
|
248
274
|
box-sizing: border-box;
|
|
@@ -256,7 +282,7 @@
|
|
|
256
282
|
transform-origin: 50% 50%;
|
|
257
283
|
transition: transform 120ms ease;
|
|
258
284
|
}
|
|
259
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block[open] > summary::before) {
|
|
285
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block[open] > summary::before) {
|
|
260
286
|
margin-top: -0.05em;
|
|
261
287
|
transform: rotate(45deg);
|
|
262
288
|
}
|