@tnotesjs/ui 0.3.3 → 0.4.0
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/Mindmap/Mindmap.vue +23 -13
- package/src/components/Mindmap/editor/MindmapView.vue +91 -2
- package/src/components/Mindmap/editor/OutlineView.vue +5 -1
- package/src/components/Mindmap/editor/RichInlineEditor.vue +1 -2
- package/src/components/Mindmap/editor/SelectionToolbar.vue +38 -22
- package/src/components/Mindmap/markdown.ts +19 -4
- package/src/index.ts +7 -1
- package/src/styles/code.css +1 -1
- package/src/styles/prose.css +22 -15
- package/src/styles/tokens.css +3 -0
- package/src/entries/mindmap-editor.ts +0 -37
package/README.md
CHANGED
|
@@ -100,35 +100,3 @@ console.log('a')
|
|
|
100
100
|
| `enableCopy` | `true` |
|
|
101
101
|
| `enableFullscreen` | `true` |
|
|
102
102
|
| `enableCenterToggle` | `true` |
|
|
103
|
-
|
|
104
|
-
### `MindmapEditor`(`@tnotesjs/ui/mindmap-editor`)
|
|
105
|
-
|
|
106
|
-
思维导图编辑器的**唯一实现**。`@tnotesjs/ui/mindmap` 那个块(`Mindmap.vue`)是给 SSG / Desk 笔记
|
|
107
|
-
内嵌用的「预览 + 可选编辑」;本入口导出它内部复用的编辑器组件,供自带页面外壳的宿主直接组装。
|
|
108
|
-
当前宿主:`apps/mindmap-web`、`apps/mindmap-vscode`。
|
|
109
|
-
|
|
110
|
-
| 导出 | 说明 |
|
|
111
|
-
| ----------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
112
|
-
| `MindmapView` | 脑图画布(含选区工具条 / 右键菜单 / 链接浮层) |
|
|
113
|
-
| `OutlineView` | 大纲视图(拖拽、行内富文本、折叠) |
|
|
114
|
-
| `MarkdownView` | 源码视图(防抖回流 + 诊断定位) |
|
|
115
|
-
| `SearchBar` | 三视图共用的搜索条 |
|
|
116
|
-
| `FocusBreadcrumbs` | 进入主题后的面包屑(与主 barrel 同源) |
|
|
117
|
-
| `AppIcon` / `IconButton` / `CollapseMenu` | 外壳按钮与图标 |
|
|
118
|
-
| `CanvasContextMenu` / `LinkPopover` / `RichInlineEditor` / `SelectionToolbar` | 上面几个组件的组成件,供未来宿主自行组装 |
|
|
119
|
-
| `insertImageIntoSource` | 源码视图的整节点图片插入 |
|
|
120
|
-
| `pasteCanvasOutline` / `readMindmapClipboard` / `writeMindmapClipboard` | 脑图剪贴板(含无权限时的进程内回退) |
|
|
121
|
-
| `resolveAfterDropLevel` / `DropLevel` | 大纲拖拽落点解析 |
|
|
122
|
-
| `primaryShortcut` / `altShortcut` / `isApplePlatform` | 平台快捷键文案 |
|
|
123
|
-
|
|
124
|
-
**浮层挂载点(`teleportTo`)**:`MindmapView`(并透传给 `CanvasContextMenu` / `LinkPopover`)接受
|
|
125
|
-
`teleportTo?: string`。默认 `undefined` = 就地渲染——浮层是 `position: fixed`,嵌在笔记里的宿主
|
|
126
|
-
(Desk / SSG)teleport 到 `body` 会脱离笔记的层叠上下文。整页宿主传 `'body'`。
|
|
127
|
-
|
|
128
|
-
**宿主职责(不属于本包)**:`MindmapSession` 的归属与生命周期、`--mm-*` 主题变量
|
|
129
|
-
(`--mm-canvas-bg` / `--mm-panel-bg` / `--mm-border` / `--mm-text` / `--mm-text-dim` / `--mm-hover` /
|
|
130
|
-
`--mm-selected-bg` / `--mm-accent` / `--mm-edit-bg`)、落盘、图片写入、宿主快捷键到组件
|
|
131
|
-
`defineExpose` 钩子的桥接(`OutlineView` 的 `locateNode` / `selectAllFromHost` / `undoFromHost` /
|
|
132
|
-
`redoFromHost`,`MarkdownView` 的 `selectAllFromHost` / `flushDraft`)。
|
|
133
|
-
|
|
134
|
-
改公共交互只改这里一处,三个宿主同时生效。
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
interface MindmapFenceOptions {
|
|
2
2
|
title?: string;
|
|
3
3
|
initialExpandLevel?: number;
|
|
4
|
+
/** Pane height in px (`h=480` in the fence info). */
|
|
5
|
+
height?: number;
|
|
4
6
|
}
|
|
5
|
-
|
|
7
|
+
declare const MINDMAP_MIN_HEIGHT = 200;
|
|
8
|
+
declare const MINDMAP_MAX_HEIGHT = 1600;
|
|
9
|
+
declare function clampMindmapHeight(value: number): number;
|
|
10
|
+
/** Parse the canonical `mindmap [title] 2 h=480` fence metadata. */
|
|
6
11
|
declare function parseMindmapFence(openLine: string): MindmapFenceOptions | null;
|
|
7
12
|
interface NormalizeMindmapOptions {
|
|
8
13
|
title?: string;
|
|
@@ -11,4 +16,4 @@ interface NormalizeMindmapOptions {
|
|
|
11
16
|
/** Ensure canonical mindmap Markdown has exactly one H1 root title. */
|
|
12
17
|
declare function normalizeMindmapMarkdown(source: string, options?: NormalizeMindmapOptions): string;
|
|
13
18
|
|
|
14
|
-
export { type MindmapFenceOptions, type NormalizeMindmapOptions, normalizeMindmapMarkdown, parseMindmapFence };
|
|
19
|
+
export { MINDMAP_MAX_HEIGHT, MINDMAP_MIN_HEIGHT, type MindmapFenceOptions, type NormalizeMindmapOptions, clampMindmapHeight, normalizeMindmapMarkdown, parseMindmapFence };
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// src/components/Mindmap/markdown.ts
|
|
2
|
+
var MINDMAP_MIN_HEIGHT = 200;
|
|
3
|
+
var MINDMAP_MAX_HEIGHT = 1600;
|
|
4
|
+
function clampMindmapHeight(value) {
|
|
5
|
+
return Math.round(Math.min(MINDMAP_MAX_HEIGHT, Math.max(MINDMAP_MIN_HEIGHT, value)));
|
|
6
|
+
}
|
|
2
7
|
function cleanHeadingText(value) {
|
|
3
8
|
return value.trim().replace(/\s+#+\s*$/, "").trim();
|
|
4
9
|
}
|
|
@@ -13,9 +18,15 @@ function parseMindmapFence(openLine) {
|
|
|
13
18
|
options.title = cleanHeadingText(titleMatch[1]) || void 0;
|
|
14
19
|
rest = `${rest.slice(0, titleMatch.index)} ${rest.slice((titleMatch.index ?? 0) + titleMatch[0].length)}`.trim();
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
for (const token of rest.split(/\s+/).filter(Boolean)) {
|
|
22
|
+
const height = token.match(/^h=(\d+)$/);
|
|
23
|
+
if (height) {
|
|
24
|
+
options.height = clampMindmapHeight(Number(height[1]));
|
|
25
|
+
} else if (/^\d+$/.test(token) && options.initialExpandLevel === void 0) {
|
|
26
|
+
options.initialExpandLevel = Math.max(1, Number(token));
|
|
27
|
+
} else {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
19
30
|
}
|
|
20
31
|
return options;
|
|
21
32
|
}
|
|
@@ -41,6 +52,9 @@ ${body.join("\n")}
|
|
|
41
52
|
`;
|
|
42
53
|
}
|
|
43
54
|
export {
|
|
55
|
+
MINDMAP_MAX_HEIGHT,
|
|
56
|
+
MINDMAP_MIN_HEIGHT,
|
|
57
|
+
clampMindmapHeight,
|
|
44
58
|
normalizeMindmapMarkdown,
|
|
45
59
|
parseMindmapFence
|
|
46
60
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tnotesjs/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Shared Vue UI for TNotes built-in blocks — consumed by the TNotes SSG and Desk.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -60,11 +60,6 @@
|
|
|
60
60
|
"types": "./src/entries/excalidraw-editor.ts",
|
|
61
61
|
"import": "./src/entries/excalidraw-editor.ts",
|
|
62
62
|
"default": "./src/entries/excalidraw-editor.ts"
|
|
63
|
-
},
|
|
64
|
-
"./mindmap-editor": {
|
|
65
|
-
"types": "./src/entries/mindmap-editor.ts",
|
|
66
|
-
"import": "./src/entries/mindmap-editor.ts",
|
|
67
|
-
"default": "./src/entries/mindmap-editor.ts"
|
|
68
63
|
}
|
|
69
64
|
},
|
|
70
65
|
"files": [
|
|
@@ -88,7 +83,7 @@
|
|
|
88
83
|
"react-dom": "18.3.1",
|
|
89
84
|
"sass": "^1.90.0",
|
|
90
85
|
"shiki": "4.4.2",
|
|
91
|
-
"@tnotesjs/mindmap-core": "^0.2.
|
|
86
|
+
"@tnotesjs/mindmap-core": "^0.2.3"
|
|
92
87
|
},
|
|
93
88
|
"devDependencies": {
|
|
94
89
|
"@eslint/js": "^10.0.1",
|
|
@@ -111,7 +106,8 @@
|
|
|
111
106
|
},
|
|
112
107
|
"publishConfig": {
|
|
113
108
|
"access": "public",
|
|
114
|
-
"registry": "https://registry.npmjs.org/"
|
|
109
|
+
"registry": "https://registry.npmjs.org/",
|
|
110
|
+
"provenance": true
|
|
115
111
|
},
|
|
116
112
|
"repository": {
|
|
117
113
|
"type": "git",
|
|
@@ -136,4 +132,4 @@
|
|
|
136
132
|
"lint": "eslint . --max-warnings=0",
|
|
137
133
|
"lint:fix": "eslint . --fix"
|
|
138
134
|
}
|
|
139
|
-
}
|
|
135
|
+
}
|
|
@@ -82,6 +82,8 @@ const props = withDefaults(
|
|
|
82
82
|
editable?: boolean
|
|
83
83
|
/** Show expand-level control on the chrome (Desk). */
|
|
84
84
|
expandLevelControl?: boolean
|
|
85
|
+
/** Pane height in px; omit for the default 440px canvas. */
|
|
86
|
+
height?: number
|
|
85
87
|
/** Explicit dark mode; omit to auto-detect html.dark / data-theme=dark. */
|
|
86
88
|
isDark?: boolean
|
|
87
89
|
/**
|
|
@@ -101,6 +103,7 @@ const props = withDefaults(
|
|
|
101
103
|
initialExpandLevel: 3,
|
|
102
104
|
editable: false,
|
|
103
105
|
expandLevelControl: false,
|
|
106
|
+
height: undefined,
|
|
104
107
|
isDark: undefined,
|
|
105
108
|
resolveImageSrc: undefined,
|
|
106
109
|
writeAsset: undefined
|
|
@@ -161,6 +164,9 @@ function decodeContent(value: string): string {
|
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
const paneStyle = computed(() =>
|
|
168
|
+
props.height ? { '--mindmap-pane-height': `${props.height}px` } : undefined
|
|
169
|
+
)
|
|
164
170
|
const rawContent = computed(() => props.source || decodeContent(props.content || ''))
|
|
165
171
|
const normalizedContent = computed(() => normalizeMindmapMarkdown(rawContent.value))
|
|
166
172
|
|
|
@@ -508,9 +514,14 @@ function zoomToFit(): void {
|
|
|
508
514
|
viewer?.zoomToFit()
|
|
509
515
|
}
|
|
510
516
|
|
|
511
|
-
|
|
517
|
+
/** 画布上的浮层自己管焦点;被抢走会让节点编辑提前提交,工具栏点了也不生效 */
|
|
518
|
+
const CANVAS_OVERLAY_SELECTOR =
|
|
519
|
+
'.selection-toolbar, .mm-edit-input, .link-popover, .canvas-context-menu'
|
|
520
|
+
|
|
521
|
+
function activateCanvas(event: PointerEvent): void {
|
|
512
522
|
isCanvasActive.value = true
|
|
513
523
|
if (!props.editable) return
|
|
524
|
+
if (event.target instanceof Element && event.target.closest(CANVAS_OVERLAY_SELECTOR)) return
|
|
514
525
|
const editorEl = canvasHost.value?.querySelector<HTMLElement>('.mm-editor')
|
|
515
526
|
editorEl?.focus({ preventScroll: true })
|
|
516
527
|
}
|
|
@@ -729,6 +740,7 @@ onBeforeUnmount(() => {
|
|
|
729
740
|
<section
|
|
730
741
|
ref="previewRoot"
|
|
731
742
|
class="mindmap-preview"
|
|
743
|
+
:style="paneStyle"
|
|
732
744
|
:class="{
|
|
733
745
|
'is-dark': dark,
|
|
734
746
|
'is-fullscreen': isFullscreen,
|
|
@@ -1039,7 +1051,7 @@ onBeforeUnmount(() => {
|
|
|
1039
1051
|
.mindmap-canvas-host {
|
|
1040
1052
|
position: relative;
|
|
1041
1053
|
width: 100%;
|
|
1042
|
-
height: 440px;
|
|
1054
|
+
height: var(--mindmap-pane-height, 440px);
|
|
1043
1055
|
overflow: hidden;
|
|
1044
1056
|
/*
|
|
1045
1057
|
* No background of its own: the canvas is cleared to transparent, and the
|
|
@@ -1051,7 +1063,7 @@ onBeforeUnmount(() => {
|
|
|
1051
1063
|
touch-action: none;
|
|
1052
1064
|
user-select: none;
|
|
1053
1065
|
|
|
1054
|
-
/* CanvasEditor overlay + chrome tokens
|
|
1066
|
+
/* CanvasEditor overlay + chrome tokens. */
|
|
1055
1067
|
--mm-canvas-bg: var(--tn-c-bg);
|
|
1056
1068
|
--mm-panel-bg: var(--tn-c-bg-soft, var(--tn-c-bg));
|
|
1057
1069
|
--mm-text: var(--tn-c-text);
|
|
@@ -1091,7 +1103,7 @@ onBeforeUnmount(() => {
|
|
|
1091
1103
|
pointer-events: none;
|
|
1092
1104
|
}
|
|
1093
1105
|
|
|
1094
|
-
/* Marquee box — required for box-select feedback
|
|
1106
|
+
/* Marquee box — required for box-select feedback. */
|
|
1095
1107
|
.mindmap-canvas-host:deep(.mm-selection-box) {
|
|
1096
1108
|
position: absolute;
|
|
1097
1109
|
z-index: 8;
|
|
@@ -1169,8 +1181,7 @@ onBeforeUnmount(() => {
|
|
|
1169
1181
|
.mindmap-canvas-host:deep(.mm-edit-input .inline-run.highlight:not(.code)) {
|
|
1170
1182
|
padding: 0 1px;
|
|
1171
1183
|
border-radius: 2px;
|
|
1172
|
-
background:
|
|
1173
|
-
color: #242424;
|
|
1184
|
+
background: var(--tn-c-mark-bg, rgba(255, 221, 0, 0.45));
|
|
1174
1185
|
}
|
|
1175
1186
|
.mindmap-canvas-host:deep(.mm-edit-input .inline-run.code) {
|
|
1176
1187
|
padding: 0 4px;
|
|
@@ -1213,13 +1224,13 @@ onBeforeUnmount(() => {
|
|
|
1213
1224
|
}
|
|
1214
1225
|
|
|
1215
1226
|
.mindmap-outline {
|
|
1216
|
-
max-height: 560px;
|
|
1227
|
+
max-height: var(--mindmap-pane-height, 560px);
|
|
1217
1228
|
padding: 18px 22px 22px;
|
|
1218
1229
|
overflow: auto;
|
|
1219
1230
|
}
|
|
1220
1231
|
|
|
1221
1232
|
.mindmap-outline.is-editable {
|
|
1222
|
-
height: 440px;
|
|
1233
|
+
height: var(--mindmap-pane-height, 440px);
|
|
1223
1234
|
max-height: none;
|
|
1224
1235
|
padding: 0;
|
|
1225
1236
|
overflow: hidden;
|
|
@@ -1228,7 +1239,7 @@ onBeforeUnmount(() => {
|
|
|
1228
1239
|
}
|
|
1229
1240
|
|
|
1230
1241
|
.mindmap-source-wrap.is-editable {
|
|
1231
|
-
height: 440px;
|
|
1242
|
+
height: var(--mindmap-pane-height, 440px);
|
|
1232
1243
|
overflow: hidden;
|
|
1233
1244
|
display: flex;
|
|
1234
1245
|
flex-direction: column;
|
|
@@ -1332,8 +1343,7 @@ onBeforeUnmount(() => {
|
|
|
1332
1343
|
.mindmap-outline :deep(.is-highlight) {
|
|
1333
1344
|
padding: 0 2px;
|
|
1334
1345
|
border-radius: 2px;
|
|
1335
|
-
background:
|
|
1336
|
-
color: #252525;
|
|
1346
|
+
background: var(--tn-c-mark-bg, rgba(255, 221, 0, 0.45));
|
|
1337
1347
|
}
|
|
1338
1348
|
.mindmap-outline :deep(.is-code) {
|
|
1339
1349
|
padding: 1px 5px;
|
|
@@ -1349,7 +1359,7 @@ onBeforeUnmount(() => {
|
|
|
1349
1359
|
}
|
|
1350
1360
|
|
|
1351
1361
|
.mindmap-source {
|
|
1352
|
-
max-height: 560px;
|
|
1362
|
+
max-height: var(--mindmap-pane-height, 560px);
|
|
1353
1363
|
margin: 0;
|
|
1354
1364
|
padding: 18px 22px;
|
|
1355
1365
|
overflow: auto;
|
|
@@ -1431,7 +1441,7 @@ onBeforeUnmount(() => {
|
|
|
1431
1441
|
|
|
1432
1442
|
@media (max-width: 768px) {
|
|
1433
1443
|
.mindmap-canvas-host {
|
|
1434
|
-
height: 360px;
|
|
1444
|
+
height: var(--mindmap-pane-height, 360px);
|
|
1435
1445
|
}
|
|
1436
1446
|
.mindmap-preview-actions {
|
|
1437
1447
|
top: 6px;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
2
|
+
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
|
3
3
|
import { CanvasEditor, serializeSubtree } from '@tnotesjs/mindmap-core'
|
|
4
4
|
import type {
|
|
5
5
|
CanvasContextRequest,
|
|
6
|
+
CanvasEditState,
|
|
6
7
|
CanvasLinkHover,
|
|
8
|
+
CanvasLinkRequest,
|
|
7
9
|
InlineFormat,
|
|
8
10
|
MindmapNode,
|
|
9
11
|
MindmapSession
|
|
@@ -33,6 +35,27 @@ const emit = defineEmits<{
|
|
|
33
35
|
const host = ref<HTMLElement>()
|
|
34
36
|
let editor: CanvasEditor | null = null
|
|
35
37
|
const selectedCount = ref(0)
|
|
38
|
+
/** 选中的不全是图片节点;图片说明是纯文本,不出格式按钮 */
|
|
39
|
+
const selectionFormattable = ref(true)
|
|
40
|
+
/** 内联编辑态:编辑框里有文字选区时,弹出与大纲视图同一套文字格式工具栏 */
|
|
41
|
+
const editState = ref<CanvasEditState | null>(null)
|
|
42
|
+
const selectionLink = ref<CanvasLinkRequest | null>(null)
|
|
43
|
+
const textSelection = computed(() => {
|
|
44
|
+
const state = editState.value
|
|
45
|
+
return state && !state.image ? state.selection : null
|
|
46
|
+
})
|
|
47
|
+
const textFormats = computed<Partial<Record<InlineFormat, boolean>>>(() => {
|
|
48
|
+
const state = editState.value
|
|
49
|
+
const selection = textSelection.value
|
|
50
|
+
if (!state || !selection) return {}
|
|
51
|
+
const formats: InlineFormat[] = ['bold', 'italic', 'underline', 'strike', 'highlight', 'code']
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
formats.map((format) => [
|
|
54
|
+
format,
|
|
55
|
+
props.session.inlineFormatActive(state.nodeId, selection.start, selection.end, format)
|
|
56
|
+
])
|
|
57
|
+
)
|
|
58
|
+
})
|
|
36
59
|
const linkEditor = ref<CanvasLinkHover | null>(null)
|
|
37
60
|
const contextMenu = ref<CanvasContextRequest | null>(null)
|
|
38
61
|
let linkCloseTimer: ReturnType<typeof setTimeout> | null = null
|
|
@@ -87,6 +110,41 @@ function applyNodeFormat(format: InlineFormat) {
|
|
|
87
110
|
props.session.formatSelectedNodes(format)
|
|
88
111
|
}
|
|
89
112
|
|
|
113
|
+
function formatEditing(format: InlineFormat) {
|
|
114
|
+
editor?.formatEditing(format)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function clearEditingFormats() {
|
|
118
|
+
editor?.clearEditingFormats()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function linkEditing() {
|
|
122
|
+
editor?.linkEditing()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function toggleEditingTask() {
|
|
126
|
+
editor?.commitEditing()
|
|
127
|
+
props.session.toggleTaskSelectedNodes()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function removeEditingNode() {
|
|
131
|
+
editor?.commitEditing()
|
|
132
|
+
props.session.removeSelectedNodes()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function saveSelectionLink(url: string) {
|
|
136
|
+
const request = selectionLink.value
|
|
137
|
+
selectionLink.value = null
|
|
138
|
+
if (!request) return
|
|
139
|
+
props.session.setNodeInlineLink(request.nodeId, request.start, request.end, url.trim() || null)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function removeSelectionLink() {
|
|
143
|
+
const request = selectionLink.value
|
|
144
|
+
selectionLink.value = null
|
|
145
|
+
if (request) props.session.setNodeInlineLink(request.nodeId, request.start, request.end, null)
|
|
146
|
+
}
|
|
147
|
+
|
|
90
148
|
function selectedMarkdown() {
|
|
91
149
|
return selectedRoots()
|
|
92
150
|
.map((node) => serializeSubtree(node))
|
|
@@ -167,6 +225,14 @@ onMounted(() => {
|
|
|
167
225
|
onSelectionPositionChange: (_position, count) => {
|
|
168
226
|
// Canvas toolbar is absolutely anchored; only the selection count gates visibility.
|
|
169
227
|
selectedCount.value = count
|
|
228
|
+
selectionFormattable.value = !props.session.selectedNodes.every((node) => node.content.image)
|
|
229
|
+
},
|
|
230
|
+
onEditStateChange: (state) => {
|
|
231
|
+
editState.value = state
|
|
232
|
+
},
|
|
233
|
+
onRequestLink: (request) => {
|
|
234
|
+
linkEditor.value = null
|
|
235
|
+
selectionLink.value = request
|
|
170
236
|
},
|
|
171
237
|
onLinkHover: onCanvasLinkHover,
|
|
172
238
|
onContextMenu: (request) => {
|
|
@@ -204,9 +270,22 @@ onBeforeUnmount(() => {
|
|
|
204
270
|
<div class="mindmap-view-root">
|
|
205
271
|
<div ref="host" class="mindmap-view-host" />
|
|
206
272
|
<SelectionToolbar
|
|
207
|
-
v-if="
|
|
273
|
+
v-if="textSelection && !selectionLink"
|
|
274
|
+
mode="text"
|
|
275
|
+
:position="textSelection.position"
|
|
276
|
+
:active-formats="textFormats"
|
|
277
|
+
:can-insert-image="false"
|
|
278
|
+
@format="formatEditing"
|
|
279
|
+
@task="toggleEditingTask"
|
|
280
|
+
@link="linkEditing"
|
|
281
|
+
@clear="clearEditingFormats"
|
|
282
|
+
@delete="removeEditingNode"
|
|
283
|
+
/>
|
|
284
|
+
<SelectionToolbar
|
|
285
|
+
v-else-if="selectedCount > 0 && !editState"
|
|
208
286
|
mode="nodes"
|
|
209
287
|
placement="canvas-bottom"
|
|
288
|
+
:formattable="selectionFormattable"
|
|
210
289
|
@format="applyNodeFormat"
|
|
211
290
|
@task="props.session.toggleTaskSelectedNodes()"
|
|
212
291
|
@copy="copySelected"
|
|
@@ -224,6 +303,16 @@ onBeforeUnmount(() => {
|
|
|
224
303
|
@leave="closeLinkPopoverSoon"
|
|
225
304
|
@close="linkEditor = null"
|
|
226
305
|
/>
|
|
306
|
+
<LinkPopover
|
|
307
|
+
v-if="selectionLink"
|
|
308
|
+
:url="selectionLink.url"
|
|
309
|
+
:position="selectionLink.position"
|
|
310
|
+
:teleport-to="teleportTo"
|
|
311
|
+
start-editing
|
|
312
|
+
@save="saveSelectionLink"
|
|
313
|
+
@remove="removeSelectionLink"
|
|
314
|
+
@close="selectionLink = null"
|
|
315
|
+
/>
|
|
227
316
|
<CanvasContextMenu
|
|
228
317
|
v-if="contextMenu"
|
|
229
318
|
:position="contextMenu.position"
|
|
@@ -361,7 +361,7 @@ function selectionPosition(input: RichInlineEditorElement): { left: number; top:
|
|
|
361
361
|
function syncTextSelection(nodeId: string, input: RichInlineEditorElement) {
|
|
362
362
|
const start = input.selectionStart ?? 0
|
|
363
363
|
const end = input.selectionEnd ?? 0
|
|
364
|
-
if (start === end) {
|
|
364
|
+
if (start === end || props.session.document.find(nodeId)?.content.image) {
|
|
365
365
|
if (textSelection.value?.nodeId === nodeId) textSelection.value = null
|
|
366
366
|
return
|
|
367
367
|
}
|
|
@@ -713,6 +713,8 @@ function applyInputFormatShortcut(
|
|
|
713
713
|
input: RichInlineEditorElement,
|
|
714
714
|
format: InlineFormat
|
|
715
715
|
) {
|
|
716
|
+
// 图片节点的文字是图片说明:纯文本,不接受行内格式
|
|
717
|
+
if (node.content.image) return
|
|
716
718
|
const caretStart = input.selectionStart ?? 0
|
|
717
719
|
const caretEnd = input.selectionEnd ?? 0
|
|
718
720
|
if (input.value.length === 0) return
|
|
@@ -741,6 +743,8 @@ function applyInputFormatShortcut(
|
|
|
741
743
|
}
|
|
742
744
|
|
|
743
745
|
function applyInputClearFormats(node: MindmapNode, input: RichInlineEditorElement) {
|
|
746
|
+
// 图片节点的文字是图片说明:纯文本,不接受行内格式
|
|
747
|
+
if (node.content.image) return
|
|
744
748
|
const caretStart = input.selectionStart ?? 0
|
|
745
749
|
const caretEnd = input.selectionEnd ?? 0
|
|
746
750
|
if (input.value.length === 0) return
|
|
@@ -416,8 +416,7 @@ onBeforeUnmount(emitCommit)
|
|
|
416
416
|
.rich-inline-editor :deep(.inline-run.highlight:not(.code)) {
|
|
417
417
|
padding: 0 1px;
|
|
418
418
|
border-radius: 2px;
|
|
419
|
-
background:
|
|
420
|
-
color: #242424;
|
|
419
|
+
background: var(--tn-c-mark-bg, rgba(255, 221, 0, 0.45));
|
|
421
420
|
}
|
|
422
421
|
.rich-inline-editor :deep(.inline-run.code) {
|
|
423
422
|
padding: 2px 6px;
|
|
@@ -16,8 +16,18 @@ const props = withDefaults(
|
|
|
16
16
|
*/
|
|
17
17
|
placement?: 'fixed' | 'canvas-bottom'
|
|
18
18
|
activeFormats?: Partial<Record<InlineFormat, boolean>>
|
|
19
|
+
/** 宿主没有插入图片入口时隐藏(脑图编辑框) */
|
|
20
|
+
canInsertImage?: boolean
|
|
21
|
+
/** 选中的全是图片节点时为 false:图片说明是纯文本,不出格式按钮 */
|
|
22
|
+
formattable?: boolean
|
|
19
23
|
}>(),
|
|
20
|
-
{
|
|
24
|
+
{
|
|
25
|
+
placement: 'fixed',
|
|
26
|
+
position: undefined,
|
|
27
|
+
activeFormats: undefined,
|
|
28
|
+
canInsertImage: true,
|
|
29
|
+
formattable: true
|
|
30
|
+
}
|
|
21
31
|
)
|
|
22
32
|
|
|
23
33
|
const emit = defineEmits<{
|
|
@@ -90,21 +100,24 @@ onBeforeUnmount(() => window.removeEventListener('resize', measureToolbar))
|
|
|
90
100
|
role="toolbar"
|
|
91
101
|
:aria-label="mode === 'text' ? '文字格式工具栏' : '多主题工具栏'"
|
|
92
102
|
@pointerdown.prevent
|
|
103
|
+
@mousedown.prevent
|
|
93
104
|
>
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
<template v-if="formattable">
|
|
106
|
+
<button
|
|
107
|
+
v-for="item in formats"
|
|
108
|
+
:key="item.id"
|
|
109
|
+
type="button"
|
|
110
|
+
class="format-button"
|
|
111
|
+
:class="[{ active: activeFormats?.[item.id] }, `is-${item.id}`]"
|
|
112
|
+
:data-tooltip="item.label"
|
|
113
|
+
:aria-label="item.label"
|
|
114
|
+
@click="emit('format', item.id)"
|
|
115
|
+
>
|
|
116
|
+
<AppIcon v-if="item.id === 'highlight'" name="highlight" :size="21" />
|
|
117
|
+
<template v-else>{{ item.text }}</template>
|
|
118
|
+
</button>
|
|
119
|
+
<span class="toolbar-divider" />
|
|
120
|
+
</template>
|
|
108
121
|
<button
|
|
109
122
|
type="button"
|
|
110
123
|
class="tool-button"
|
|
@@ -116,6 +129,7 @@ onBeforeUnmount(() => window.removeEventListener('resize', measureToolbar))
|
|
|
116
129
|
</button>
|
|
117
130
|
<template v-if="mode === 'text'">
|
|
118
131
|
<button
|
|
132
|
+
v-if="canInsertImage"
|
|
119
133
|
type="button"
|
|
120
134
|
class="tool-button"
|
|
121
135
|
:data-tooltip="`添加图片 (${altShortcut('Enter')})`"
|
|
@@ -135,12 +149,12 @@ onBeforeUnmount(() => window.removeEventListener('resize', measureToolbar))
|
|
|
135
149
|
</button>
|
|
136
150
|
<button
|
|
137
151
|
type="button"
|
|
138
|
-
class="tool-button
|
|
152
|
+
class="tool-button"
|
|
139
153
|
:data-tooltip="`行内代码 (${primaryShortcut('E')})`"
|
|
140
154
|
aria-label="行内代码"
|
|
141
155
|
@click="emit('format', 'code')"
|
|
142
156
|
>
|
|
143
|
-
|
|
157
|
+
<AppIcon name="code" :size="20" />
|
|
144
158
|
</button>
|
|
145
159
|
</template>
|
|
146
160
|
<button
|
|
@@ -155,6 +169,7 @@ onBeforeUnmount(() => window.removeEventListener('resize', measureToolbar))
|
|
|
155
169
|
</button>
|
|
156
170
|
<span class="toolbar-divider" />
|
|
157
171
|
<button
|
|
172
|
+
v-if="formattable"
|
|
158
173
|
type="button"
|
|
159
174
|
class="tool-button"
|
|
160
175
|
:data-tooltip="`清除样式 (${primaryShortcut('\\')})`"
|
|
@@ -217,7 +232,13 @@ onBeforeUnmount(() => window.removeEventListener('resize', measureToolbar))
|
|
|
217
232
|
.tool-button::after {
|
|
218
233
|
position: absolute;
|
|
219
234
|
z-index: 2;
|
|
235
|
+
/* 宿主可能有全局 [data-tooltip]::after(例如 Desk 往下弹),这里把方向和显隐都定死 */
|
|
236
|
+
top: auto;
|
|
220
237
|
bottom: calc(100% + 9px);
|
|
238
|
+
min-width: 0;
|
|
239
|
+
border: 0;
|
|
240
|
+
visibility: visible;
|
|
241
|
+
transition: none;
|
|
221
242
|
left: 50%;
|
|
222
243
|
width: max-content;
|
|
223
244
|
max-width: min(260px, calc(100vw - 16px));
|
|
@@ -266,11 +287,6 @@ onBeforeUnmount(() => window.removeEventListener('resize', measureToolbar))
|
|
|
266
287
|
.format-button.is-highlight {
|
|
267
288
|
color: #d3c900;
|
|
268
289
|
}
|
|
269
|
-
.tool-button.code-button {
|
|
270
|
-
font-family: ui-monospace, monospace;
|
|
271
|
-
font-size: 15px;
|
|
272
|
-
font-weight: 700;
|
|
273
|
-
}
|
|
274
290
|
.tool-button.danger {
|
|
275
291
|
color: #e14f5b;
|
|
276
292
|
}
|
|
@@ -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
|
}
|
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
|
@@ -112,6 +112,12 @@
|
|
|
112
112
|
font: 0.875em var(--tn-font-mono);
|
|
113
113
|
padding: 3px 6px;
|
|
114
114
|
}
|
|
115
|
+
:is(.tn-prose, .vp-doc) :where(mark) {
|
|
116
|
+
color: inherit;
|
|
117
|
+
background: var(--tn-c-mark-bg);
|
|
118
|
+
border-radius: 2px;
|
|
119
|
+
padding: 0 2px;
|
|
120
|
+
}
|
|
115
121
|
:is(.tn-prose, .vp-doc) :where(blockquote) {
|
|
116
122
|
margin: 16px 0;
|
|
117
123
|
border-left: 2px solid var(--tn-c-divider);
|
|
@@ -185,7 +191,7 @@
|
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
/* Callouts — same soft-tint cards as Desk visual (no left accent bar). */
|
|
188
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block) {
|
|
194
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block) {
|
|
189
195
|
box-sizing: border-box;
|
|
190
196
|
margin: 16px 0;
|
|
191
197
|
padding: 12px 16px;
|
|
@@ -195,37 +201,37 @@
|
|
|
195
201
|
font-size: inherit;
|
|
196
202
|
line-height: 1.5;
|
|
197
203
|
}
|
|
198
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.tip) {
|
|
204
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.tip) {
|
|
199
205
|
background-color: var(--tn-c-highlight);
|
|
200
206
|
}
|
|
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) {
|
|
207
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.info),
|
|
208
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.note),
|
|
209
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.details) {
|
|
204
210
|
background-color: var(--tn-c-hover);
|
|
205
211
|
}
|
|
206
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.warning) {
|
|
212
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.warning) {
|
|
207
213
|
background-color: var(--tn-c-warning-soft);
|
|
208
214
|
}
|
|
209
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block.danger) {
|
|
215
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block.danger) {
|
|
210
216
|
background-color: var(--tn-c-danger-soft);
|
|
211
217
|
}
|
|
212
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block-title) {
|
|
218
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block-title) {
|
|
213
219
|
margin: 0 0 2px;
|
|
214
220
|
font-size: inherit;
|
|
215
221
|
font-weight: 600;
|
|
216
222
|
line-height: 1.5;
|
|
217
223
|
color: var(--tn-c-text);
|
|
218
224
|
}
|
|
219
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block > p:not(.tn-custom-block-title)) {
|
|
225
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block > p:not(.tn-custom-block-title)) {
|
|
220
226
|
margin: 0;
|
|
221
227
|
padding: 4px 0;
|
|
222
228
|
font-size: 1em;
|
|
223
229
|
line-height: 1.5;
|
|
224
230
|
}
|
|
225
|
-
:is(.tn-prose, .vp-doc) :where(.tn-custom-block > :last-child) {
|
|
231
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(.tn-custom-block > :last-child) {
|
|
226
232
|
margin-bottom: 0;
|
|
227
233
|
}
|
|
228
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block > summary) {
|
|
234
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block > summary) {
|
|
229
235
|
display: flex;
|
|
230
236
|
align-items: center;
|
|
231
237
|
gap: 8px;
|
|
@@ -236,13 +242,14 @@
|
|
|
236
242
|
font-weight: 600;
|
|
237
243
|
line-height: 1.5;
|
|
238
244
|
}
|
|
239
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block:not([open]) > summary) {
|
|
245
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block:not([open]) > summary) {
|
|
240
246
|
margin-bottom: 0;
|
|
241
247
|
}
|
|
242
|
-
:is(.tn-prose, .vp-doc
|
|
248
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card)
|
|
249
|
+
:where(details.tn-custom-block > summary::-webkit-details-marker) {
|
|
243
250
|
display: none;
|
|
244
251
|
}
|
|
245
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block > summary::before) {
|
|
252
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block > summary::before) {
|
|
246
253
|
content: '';
|
|
247
254
|
flex: none;
|
|
248
255
|
box-sizing: border-box;
|
|
@@ -256,7 +263,7 @@
|
|
|
256
263
|
transform-origin: 50% 50%;
|
|
257
264
|
transition: transform 120ms ease;
|
|
258
265
|
}
|
|
259
|
-
:is(.tn-prose, .vp-doc) :where(details.tn-custom-block[open] > summary::before) {
|
|
266
|
+
:is(.tn-prose, .vp-doc, .cm-lp-card) :where(details.tn-custom-block[open] > summary::before) {
|
|
260
267
|
margin-top: -0.05em;
|
|
261
268
|
transform: rotate(45deg);
|
|
262
269
|
}
|
package/src/styles/tokens.css
CHANGED
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
--tn-c-warning-soft: rgba(234, 179, 8, 0.14);
|
|
23
23
|
--tn-c-danger-soft: rgba(244, 63, 94, 0.14);
|
|
24
24
|
--tn-c-default-soft: #e8e8e9;
|
|
25
|
+
/* ==高亮== 的底色:正文、Desk 编辑器、SSG 共用 */
|
|
26
|
+
--tn-c-mark-bg: rgba(255, 221, 0, 0.45);
|
|
25
27
|
/* 任务列表复选框(对齐语雀):16px 方框、圆角 4px、选中填充品牌绿 + 白色对勾。 */
|
|
26
28
|
--tn-c-task-check: #00b96b;
|
|
27
29
|
--tn-c-task-check-border: #d9d9d9;
|
|
@@ -69,6 +71,7 @@
|
|
|
69
71
|
--tn-c-warning-soft: rgba(234, 179, 8, 0.16);
|
|
70
72
|
--tn-c-danger-soft: rgba(244, 63, 94, 0.14);
|
|
71
73
|
--tn-c-default-soft: #2e2e32;
|
|
74
|
+
--tn-c-mark-bg: rgba(250, 204, 21, 0.32);
|
|
72
75
|
--tn-c-task-check-border: #4a4d54;
|
|
73
76
|
--tn-c-task-check-bg: transparent;
|
|
74
77
|
--tn-shadow-2: 0 4px 12px rgb(0 0 0 / 0.35);
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 思维导图编辑器外壳入口:Web 与 VS Code 扩展共用同一套三视图 UI。
|
|
3
|
-
*
|
|
4
|
-
* 与 `./mindmap` 的分工:`./mindmap` 是 SSG / Desk 笔记里内嵌的「预览 + 可选编辑」块
|
|
5
|
-
* (`components/Mindmap/Mindmap.vue`,自带 `--mm-*` 主题变量与只读渲染);本入口导出它
|
|
6
|
-
* 内部复用的编辑器组件,供自带页面外壳的宿主(Web 整页、VS Code webview)直接组装。
|
|
7
|
-
*
|
|
8
|
-
* 宿主职责:`MindmapSession` 归属、`--mm-*` 变量定义、落盘 / 剪贴板 / 图片写入
|
|
9
|
-
* (`apps/mindmap-web`、`apps/mindmap-vscode`)。浮层默认不 teleport,嵌入笔记的宿主
|
|
10
|
-
* 保持默认即可;整页宿主传 `teleport-to="body"`。
|
|
11
|
-
*/
|
|
12
|
-
export { default as AppIcon } from '../components/Mindmap/editor/AppIcon.vue'
|
|
13
|
-
export { default as CanvasContextMenu } from '../components/Mindmap/editor/CanvasContextMenu.vue'
|
|
14
|
-
export { default as CollapseMenu } from '../components/Mindmap/editor/CollapseMenu.vue'
|
|
15
|
-
export { default as IconButton } from '../components/Mindmap/editor/IconButton.vue'
|
|
16
|
-
export { default as LinkPopover } from '../components/Mindmap/editor/LinkPopover.vue'
|
|
17
|
-
export { default as MarkdownView } from '../components/Mindmap/editor/MarkdownView.vue'
|
|
18
|
-
export { default as MindmapView } from '../components/Mindmap/editor/MindmapView.vue'
|
|
19
|
-
export { default as OutlineView } from '../components/Mindmap/editor/OutlineView.vue'
|
|
20
|
-
export { default as RichInlineEditor } from '../components/Mindmap/editor/RichInlineEditor.vue'
|
|
21
|
-
export { default as SearchBar } from '../components/Mindmap/editor/SearchBar.vue'
|
|
22
|
-
export { default as SelectionToolbar } from '../components/Mindmap/editor/SelectionToolbar.vue'
|
|
23
|
-
export { default as FocusBreadcrumbs } from '../components/Mindmap/FocusBreadcrumbs.vue'
|
|
24
|
-
|
|
25
|
-
export { insertImageIntoSource } from '../components/Mindmap/editor/imagePaste'
|
|
26
|
-
export {
|
|
27
|
-
pasteCanvasOutline,
|
|
28
|
-
readMindmapClipboard,
|
|
29
|
-
writeMindmapClipboard
|
|
30
|
-
} from '../components/Mindmap/editor/mindmapClipboard'
|
|
31
|
-
export { resolveAfterDropLevel } from '../components/Mindmap/editor/outlineDrag'
|
|
32
|
-
export type { DropLevel } from '../components/Mindmap/editor/outlineDrag'
|
|
33
|
-
export {
|
|
34
|
-
altShortcut,
|
|
35
|
-
isApplePlatform,
|
|
36
|
-
primaryShortcut
|
|
37
|
-
} from '../components/Mindmap/editor/platform'
|