@tnotesjs/ui 0.4.2 → 0.4.4
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/dist/alerts/icons.d.ts +8 -0
- package/dist/alerts/icons.js +29 -0
- package/dist/markdown/lineBreaks.d.ts +47 -0
- package/dist/markdown/lineBreaks.js +78 -0
- package/dist/swiper/hydrate.d.ts +4 -1
- package/dist/swiper/hydrate.js +9 -4
- package/package.json +23 -11
- package/src/alerts/icons.ts +32 -0
- package/src/browser/fullscreenLayer.ts +16 -0
- package/src/components/ContributionHeatmap/ContributionHeatmap.vue +638 -0
- package/src/components/ImagePreview/ImagePreview.vue +29 -6
- package/src/components/Mermaid/Mermaid.vue +5 -1
- package/src/components/Mindmap/InlineRuns.ts +4 -1
- package/src/components/Mindmap/Mindmap.vue +27 -12
- package/src/components/Mindmap/editor/AppIcon.vue +0 -3
- package/src/components/Mindmap/editor/MindmapView.vue +16 -1
- package/src/components/Mindmap/editor/OutlineView.vue +44 -3
- package/src/components/Mindmap/editor/RichInlineEditor.vue +56 -22
- package/src/components/Mindmap/editor/SearchBar.vue +8 -2
- package/src/components/Mindmap/editor/SelectionToolbar.vue +2 -7
- package/src/components/Mindmap/editor/mindmapClipboard.ts +4 -4
- package/src/components/Mindmap/inlineOptions.ts +21 -0
- package/src/components/WordList/WordList.vue +29 -9
- package/src/components/WordList/wordListStorage.ts +21 -0
- package/src/index.ts +3 -0
- package/src/markdown/lineBreaks.ts +133 -0
- package/src/styles/prose.css +138 -30
- package/src/swiper/hydrate.ts +15 -4
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** GitHub Alerts 风格的容器图标。`currentColor`,颜色由侧边色条同一变量决定。 */
|
|
2
|
+
declare const ALERT_TYPES: readonly ["tip", "info", "warning", "danger", "details"];
|
|
3
|
+
type AlertType = (typeof ALERT_TYPES)[number];
|
|
4
|
+
declare function isAlertType(name: string): name is AlertType;
|
|
5
|
+
declare function alertIcon(name: string): string;
|
|
6
|
+
declare const ALERT_LABELS: Record<AlertType, string>;
|
|
7
|
+
|
|
8
|
+
export { ALERT_LABELS, ALERT_TYPES, type AlertType, alertIcon, isAlertType };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/alerts/icons.ts
|
|
2
|
+
var ALERT_TYPES = ["tip", "info", "warning", "danger", "details"];
|
|
3
|
+
var PATHS = {
|
|
4
|
+
tip: '<path fill="currentColor" d="M8 1.5a4.5 4.5 0 0 0-2.6 8.17c.3.22.5.5.55.84l.2 1.24h3.7l.2-1.24c.05-.34.25-.62.55-.84A4.5 4.5 0 0 0 8 1.5Zm1.9 11.25H6.1l.15.9c.05.3.3.5.6.5h1.5c.3 0 .55-.2.6-.5l.15-.9ZM8 0a6 6 0 0 1 3.5 10.87l-.35 2.1A2.1 2.1 0 0 1 9.1 15H6.9a2.1 2.1 0 0 1-2.05-1.53l-.35-2.1A6 6 0 0 1 8 0Z"/>',
|
|
5
|
+
info: '<path fill="currentColor" d="M8 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-3.25a.75.75 0 0 1 .75.75v.01a.75.75 0 0 1-1.5 0V5.5A.75.75 0 0 1 8 4.75ZM7.25 7.5a.75.75 0 0 1 1.5 0v4a.75.75 0 0 1-1.5 0v-4Z"/>',
|
|
6
|
+
warning: '<path fill="currentColor" d="M6.46 1.96a1.8 1.8 0 0 1 3.08 0l5.9 9.92A1.8 1.8 0 0 1 13.9 14.5H2.1a1.8 1.8 0 0 1-1.54-2.62l5.9-9.92ZM8 3.15 2.1 13h11.8L8 3.15Zm-.75 3.6a.75.75 0 0 1 1.5 0v2.5a.75.75 0 0 1-1.5 0V6.75Zm.75 5.5a.9.9 0 1 1 0-1.8.9.9 0 0 1 0 1.8Z"/>',
|
|
7
|
+
danger: '<path fill="currentColor" d="M8 0 15.5 4.3v7.4L8 16 0.5 11.7V4.3L8 0Zm0 1.7L1.9 5.1v5.8L8 14.3l6.1-3.4V5.1L8 1.7ZM7.25 4.75a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-1.5 0v-3.5Zm.75 6.5a.9.9 0 1 1 0-1.8.9.9 0 0 1 0 1.8Z"/>',
|
|
8
|
+
details: '<path fill="currentColor" d="M2 3.75A.75.75 0 0 1 2.75 3h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 3.75ZM2 8a.75.75 0 0 1 .75-.75h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 8Zm.75 3.5h6.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5Z"/>'
|
|
9
|
+
};
|
|
10
|
+
function isAlertType(name) {
|
|
11
|
+
return ALERT_TYPES.includes(name);
|
|
12
|
+
}
|
|
13
|
+
function alertIcon(name) {
|
|
14
|
+
const type = isAlertType(name) ? name : "info";
|
|
15
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">${PATHS[type]}</svg>`;
|
|
16
|
+
}
|
|
17
|
+
var ALERT_LABELS = {
|
|
18
|
+
tip: "\u63D0\u793A",
|
|
19
|
+
info: "\u4FE1\u606F",
|
|
20
|
+
warning: "\u8B66\u544A",
|
|
21
|
+
danger: "\u9519\u8BEF",
|
|
22
|
+
details: "\u8BE6\u60C5"
|
|
23
|
+
};
|
|
24
|
+
export {
|
|
25
|
+
ALERT_LABELS,
|
|
26
|
+
ALERT_TYPES,
|
|
27
|
+
alertIcon,
|
|
28
|
+
isAlertType
|
|
29
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kb-level line-break mode (`tnotes.json` → `markdown.lineBreaks`), shared by
|
|
3
|
+
* the SSG and Desk's card renderers so both draw the same note the same way.
|
|
4
|
+
*
|
|
5
|
+
* `visual` keeps what the editor shows: a single newline renders as `<br>`, and
|
|
6
|
+
* every blank line beyond the first between two top-level blocks becomes a
|
|
7
|
+
* one-line spacer; inside callout bodies (title and closing fence included)
|
|
8
|
+
* every blank line does. Lists and quotes keep CommonMark spacing; their inner blank lines
|
|
9
|
+
* decide loose/tight and have no stable visual meaning. Fences, tables and
|
|
10
|
+
* HTML are single tokens, so their contents are never touched.
|
|
11
|
+
*/
|
|
12
|
+
type LineBreakMode = 'commonmark' | 'visual';
|
|
13
|
+
declare const BLANK_LINE_CLASS = "tn-blank-line";
|
|
14
|
+
declare const BLANK_LINE_HTML = "<div class=\"tn-blank-line\" aria-hidden=\"true\"></div>\n";
|
|
15
|
+
declare function resolveLineBreakMode(value: unknown): LineBreakMode;
|
|
16
|
+
/** Structural subset of markdown-it's Token, so this module stays dependency-free. */
|
|
17
|
+
interface BlockToken {
|
|
18
|
+
type: string;
|
|
19
|
+
block: boolean;
|
|
20
|
+
nesting: number;
|
|
21
|
+
level: number;
|
|
22
|
+
map: [number, number] | null;
|
|
23
|
+
content: string;
|
|
24
|
+
}
|
|
25
|
+
interface CoreState {
|
|
26
|
+
src: string;
|
|
27
|
+
tokens: BlockToken[];
|
|
28
|
+
Token: new (type: string, tag: string, nesting: -1 | 0 | 1) => BlockToken;
|
|
29
|
+
}
|
|
30
|
+
interface MarkdownLike {
|
|
31
|
+
set(options: {
|
|
32
|
+
breaks: boolean;
|
|
33
|
+
}): unknown;
|
|
34
|
+
core: {
|
|
35
|
+
ruler: {
|
|
36
|
+
push(name: string, rule: (state: CoreState) => void): void;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Callout bodies get spacers too; other containers render their own way. */
|
|
41
|
+
declare function isSpacedContainer(type: string): boolean;
|
|
42
|
+
/** Inserts `tn-blank-line` spacers for the blank lines `visual` keeps. */
|
|
43
|
+
declare function insertBlankLineSpacers(state: CoreState, spaced?: (type: string) => boolean): void;
|
|
44
|
+
/** Applies the mode to a markdown-it instance; `commonmark` leaves it untouched. */
|
|
45
|
+
declare function useLineBreaks(md: MarkdownLike, mode: LineBreakMode): void;
|
|
46
|
+
|
|
47
|
+
export { BLANK_LINE_CLASS, BLANK_LINE_HTML, type LineBreakMode, insertBlankLineSpacers, isSpacedContainer, resolveLineBreakMode, useLineBreaks };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/markdown/lineBreaks.ts
|
|
2
|
+
var BLANK_LINE_CLASS = "tn-blank-line";
|
|
3
|
+
var BLANK_LINE_HTML = `<div class="${BLANK_LINE_CLASS}" aria-hidden="true"></div>
|
|
4
|
+
`;
|
|
5
|
+
function resolveLineBreakMode(value) {
|
|
6
|
+
return value === "visual" ? "visual" : "commonmark";
|
|
7
|
+
}
|
|
8
|
+
var CALLOUT_OPEN = /^container_(?:info|tip|warning|danger|details)_open$/;
|
|
9
|
+
var CONTAINER_FENCE = /^\s*:{3,}\s*$/;
|
|
10
|
+
function isSpacedContainer(type) {
|
|
11
|
+
return CALLOUT_OPEN.test(type);
|
|
12
|
+
}
|
|
13
|
+
function blockEnd(token, lines) {
|
|
14
|
+
const [start, mapEnd] = token.map;
|
|
15
|
+
let end = mapEnd;
|
|
16
|
+
if (token.type.startsWith("container_") && CONTAINER_FENCE.test(lines[end] ?? "")) end += 1;
|
|
17
|
+
while (end > start + 1 && !(lines[end - 1] ?? "").trim()) end -= 1;
|
|
18
|
+
return end;
|
|
19
|
+
}
|
|
20
|
+
function insertBlankLineSpacers(state, spaced = isSpacedContainer) {
|
|
21
|
+
const lines = state.src.split("\n");
|
|
22
|
+
const out = [];
|
|
23
|
+
const stack = [{ level: 0, lastEnd: null, free: 1 }];
|
|
24
|
+
const pushSpacers = (count, level) => {
|
|
25
|
+
for (; count > 0; count -= 1) {
|
|
26
|
+
const spacer = new state.Token("html_block", "", 0);
|
|
27
|
+
spacer.block = true;
|
|
28
|
+
spacer.level = level;
|
|
29
|
+
spacer.content = BLANK_LINE_HTML;
|
|
30
|
+
out.push(spacer);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
for (const token of state.tokens) {
|
|
34
|
+
const scope = stack[stack.length - 1];
|
|
35
|
+
if (scope.close && token.type === scope.close && token.level === scope.level - 1) {
|
|
36
|
+
if (scope.closeLine !== void 0 && scope.lastEnd !== null) {
|
|
37
|
+
pushSpacers(scope.closeLine - scope.lastEnd - scope.free, scope.level);
|
|
38
|
+
}
|
|
39
|
+
stack.pop();
|
|
40
|
+
out.push(token);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (token.block && token.map && token.nesting !== -1 && token.level === scope.level) {
|
|
44
|
+
if (scope.lastEnd !== null) {
|
|
45
|
+
pushSpacers(token.map[0] - scope.lastEnd - scope.free, scope.level);
|
|
46
|
+
}
|
|
47
|
+
const end = blockEnd(token, lines);
|
|
48
|
+
scope.lastEnd = end;
|
|
49
|
+
out.push(token);
|
|
50
|
+
if (token.nesting === 1 && spaced(token.type)) {
|
|
51
|
+
const closed = CONTAINER_FENCE.test(lines[end - 1] ?? "") && end - 1 > token.map[0];
|
|
52
|
+
stack.push({
|
|
53
|
+
level: token.level + 1,
|
|
54
|
+
lastEnd: token.map[0] + 1,
|
|
55
|
+
close: token.type.replace(/_open$/, "_close"),
|
|
56
|
+
closeLine: closed ? end - 1 : void 0,
|
|
57
|
+
free: 0
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
out.push(token);
|
|
63
|
+
}
|
|
64
|
+
state.tokens = out;
|
|
65
|
+
}
|
|
66
|
+
function useLineBreaks(md, mode) {
|
|
67
|
+
if (mode !== "visual") return;
|
|
68
|
+
md.set({ breaks: true });
|
|
69
|
+
md.core.ruler.push("tn_blank_lines", (state) => insertBlankLineSpacers(state));
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
BLANK_LINE_CLASS,
|
|
73
|
+
BLANK_LINE_HTML,
|
|
74
|
+
insertBlankLineSpacers,
|
|
75
|
+
isSpacedContainer,
|
|
76
|
+
resolveLineBreakMode,
|
|
77
|
+
useLineBreaks
|
|
78
|
+
};
|
package/dist/swiper/hydrate.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ declare function emptySwiperMarkup(): string;
|
|
|
21
21
|
* already-hydrated roots are skipped via `data-tn-swiper-ready`.
|
|
22
22
|
* When `root` itself is a `.tn-swiper`, it is included.
|
|
23
23
|
*/
|
|
24
|
-
declare function hydrateTnSwipers(root?: ParentNode
|
|
24
|
+
declare function hydrateTnSwipers(root?: ParentNode, options?: {
|
|
25
|
+
activeIndex?: number;
|
|
26
|
+
onActivate?: (index: number) => void;
|
|
27
|
+
}): number;
|
|
25
28
|
|
|
26
29
|
export { SWIPER_EMPTY_TEXT, applySwiperTabsPadding, createSwiperTabNav, emptySwiperMarkup, hydrateTnSwipers, wrapSlideIndex };
|
package/dist/swiper/hydrate.js
CHANGED
|
@@ -37,7 +37,7 @@ function emptySwiperMarkup() {
|
|
|
37
37
|
return `<div class="tn-swiper is-empty"><div class="swiper-container"><p class="tn-swiper-empty">${SWIPER_EMPTY_TEXT}</p></div></div>`;
|
|
38
38
|
}
|
|
39
39
|
var READY = "tnSwiperReady";
|
|
40
|
-
function hydrateTnSwipers(root = document) {
|
|
40
|
+
function hydrateTnSwipers(root = document, options) {
|
|
41
41
|
const hosts = [];
|
|
42
42
|
if (root instanceof HTMLElement && root.classList.contains("tn-swiper")) {
|
|
43
43
|
hosts.push(root);
|
|
@@ -49,14 +49,14 @@ function hydrateTnSwipers(root = document) {
|
|
|
49
49
|
if (seen.has(host)) continue;
|
|
50
50
|
seen.add(host);
|
|
51
51
|
if (host.dataset[READY] === "true") continue;
|
|
52
|
-
if (hydrateOne(host)) {
|
|
52
|
+
if (hydrateOne(host, options)) {
|
|
53
53
|
host.dataset[READY] = "true";
|
|
54
54
|
count += 1;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
return count;
|
|
58
58
|
}
|
|
59
|
-
function hydrateOne(host) {
|
|
59
|
+
function hydrateOne(host, options) {
|
|
60
60
|
if (host.classList.contains("is-empty")) return true;
|
|
61
61
|
const tabs = host.querySelector(".tn-swiper-tabs");
|
|
62
62
|
const wrapper = host.querySelector(".swiper-wrapper");
|
|
@@ -84,6 +84,7 @@ function hydrateOne(host) {
|
|
|
84
84
|
slide.classList.toggle("is-active", on);
|
|
85
85
|
slide.hidden = !on;
|
|
86
86
|
});
|
|
87
|
+
options?.onActivate?.(index);
|
|
87
88
|
};
|
|
88
89
|
const activeIndex = () => {
|
|
89
90
|
const found = tabButtons.findIndex((button) => button.classList.contains("active"));
|
|
@@ -113,7 +114,11 @@ function hydrateOne(host) {
|
|
|
113
114
|
} else {
|
|
114
115
|
tabsEl.remove();
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
+
const start = Math.min(
|
|
118
|
+
Math.max(0, options?.activeIndex ?? 0),
|
|
119
|
+
Math.max(0, slides.length - 1)
|
|
120
|
+
);
|
|
121
|
+
activate(start);
|
|
117
122
|
return true;
|
|
118
123
|
}
|
|
119
124
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tnotesjs/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
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": {
|
|
@@ -27,6 +27,12 @@
|
|
|
27
27
|
"import": "./src/markdown/image.ts",
|
|
28
28
|
"default": "./src/markdown/image.ts"
|
|
29
29
|
},
|
|
30
|
+
"./line-breaks": {
|
|
31
|
+
"types": "./src/markdown/lineBreaks.ts",
|
|
32
|
+
"node": "./dist/markdown/lineBreaks.js",
|
|
33
|
+
"import": "./src/markdown/lineBreaks.ts",
|
|
34
|
+
"default": "./src/markdown/lineBreaks.ts"
|
|
35
|
+
},
|
|
30
36
|
"./styles/tokens.css": "./src/styles/tokens.css",
|
|
31
37
|
"./styles/prose.css": "./src/styles/prose.css",
|
|
32
38
|
"./styles/code.css": "./src/styles/code.css",
|
|
@@ -44,6 +50,12 @@
|
|
|
44
50
|
},
|
|
45
51
|
"./code-block": "./src/components/CodeBlock/CodeBlock.vue",
|
|
46
52
|
"./code-group": "./src/components/CodeGroup/CodeGroup.vue",
|
|
53
|
+
"./alerts": {
|
|
54
|
+
"types": "./src/alerts/icons.ts",
|
|
55
|
+
"node": "./dist/alerts/icons.js",
|
|
56
|
+
"import": "./src/alerts/icons.ts",
|
|
57
|
+
"default": "./src/alerts/icons.ts"
|
|
58
|
+
},
|
|
47
59
|
"./badge": "./src/components/Badge/Badge.vue",
|
|
48
60
|
"./image-preview": "./src/components/ImagePreview/ImagePreview.vue",
|
|
49
61
|
"./bilibili-video": "./src/entries/bilibili-video.ts",
|
|
@@ -82,24 +94,24 @@
|
|
|
82
94
|
"mermaid": "^11.17.0",
|
|
83
95
|
"react": "18.3.1",
|
|
84
96
|
"react-dom": "18.3.1",
|
|
85
|
-
"sass": "^1.
|
|
86
|
-
"shiki": "4.
|
|
87
|
-
"@tnotesjs/mindmap-core": "^0.2.
|
|
97
|
+
"sass": "^1.107.1",
|
|
98
|
+
"shiki": "4.6.0",
|
|
99
|
+
"@tnotesjs/mindmap-core": "^0.2.5"
|
|
88
100
|
},
|
|
89
101
|
"devDependencies": {
|
|
90
102
|
"@eslint/js": "^10.0.1",
|
|
91
103
|
"@types/react": "^18.3.31",
|
|
92
104
|
"@types/react-dom": "^18.0.0",
|
|
93
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
94
|
-
"eslint": "^10.
|
|
95
|
-
"eslint-plugin-vue": "^10.
|
|
96
|
-
"globals": "^17.
|
|
105
|
+
"@vitejs/plugin-vue": "^6.0.10",
|
|
106
|
+
"eslint": "^10.14.0",
|
|
107
|
+
"eslint-plugin-vue": "^10.12.0",
|
|
108
|
+
"globals": "^17.14.0",
|
|
97
109
|
"happy-dom": "^20.12.0",
|
|
98
110
|
"tsup": "8.5.1",
|
|
99
111
|
"typescript": "^5.9.3",
|
|
100
|
-
"typescript-eslint": "^8.
|
|
112
|
+
"typescript-eslint": "^8.72.1",
|
|
101
113
|
"vitest": "^4.1.11",
|
|
102
|
-
"vue": "^3.5.
|
|
114
|
+
"vue": "^3.5.44",
|
|
103
115
|
"vue-eslint-parser": "^10.4.1"
|
|
104
116
|
},
|
|
105
117
|
"engines": {
|
|
@@ -127,7 +139,7 @@
|
|
|
127
139
|
],
|
|
128
140
|
"license": "MIT",
|
|
129
141
|
"scripts": {
|
|
130
|
-
"build": "tsup src/code/highlight.ts src/components/Footprints/parse.ts src/components/Mindmap/markdown.ts src/markdown/image.ts src/swiper/hydrate.ts --format esm --dts --out-dir dist --clean",
|
|
142
|
+
"build": "tsup src/alerts/icons.ts src/code/highlight.ts src/components/Footprints/parse.ts src/components/Mindmap/markdown.ts src/markdown/image.ts src/markdown/lineBreaks.ts src/swiper/hydrate.ts --format esm --dts --out-dir dist --clean",
|
|
131
143
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
132
144
|
"test": "vitest run",
|
|
133
145
|
"lint": "eslint . --max-warnings=0",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** GitHub Alerts 风格的容器图标。`currentColor`,颜色由侧边色条同一变量决定。 */
|
|
2
|
+
export const ALERT_TYPES = ['tip', 'info', 'warning', 'danger', 'details'] as const
|
|
3
|
+
|
|
4
|
+
export type AlertType = (typeof ALERT_TYPES)[number]
|
|
5
|
+
|
|
6
|
+
const PATHS: Record<AlertType, string> = {
|
|
7
|
+
tip: '<path fill="currentColor" d="M8 1.5a4.5 4.5 0 0 0-2.6 8.17c.3.22.5.5.55.84l.2 1.24h3.7l.2-1.24c.05-.34.25-.62.55-.84A4.5 4.5 0 0 0 8 1.5Zm1.9 11.25H6.1l.15.9c.05.3.3.5.6.5h1.5c.3 0 .55-.2.6-.5l.15-.9ZM8 0a6 6 0 0 1 3.5 10.87l-.35 2.1A2.1 2.1 0 0 1 9.1 15H6.9a2.1 2.1 0 0 1-2.05-1.53l-.35-2.1A6 6 0 0 1 8 0Z"/>',
|
|
8
|
+
info: '<path fill="currentColor" d="M8 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-3.25a.75.75 0 0 1 .75.75v.01a.75.75 0 0 1-1.5 0V5.5A.75.75 0 0 1 8 4.75ZM7.25 7.5a.75.75 0 0 1 1.5 0v4a.75.75 0 0 1-1.5 0v-4Z"/>',
|
|
9
|
+
warning:
|
|
10
|
+
'<path fill="currentColor" d="M6.46 1.96a1.8 1.8 0 0 1 3.08 0l5.9 9.92A1.8 1.8 0 0 1 13.9 14.5H2.1a1.8 1.8 0 0 1-1.54-2.62l5.9-9.92ZM8 3.15 2.1 13h11.8L8 3.15Zm-.75 3.6a.75.75 0 0 1 1.5 0v2.5a.75.75 0 0 1-1.5 0V6.75Zm.75 5.5a.9.9 0 1 1 0-1.8.9.9 0 0 1 0 1.8Z"/>',
|
|
11
|
+
danger:
|
|
12
|
+
'<path fill="currentColor" d="M8 0 15.5 4.3v7.4L8 16 0.5 11.7V4.3L8 0Zm0 1.7L1.9 5.1v5.8L8 14.3l6.1-3.4V5.1L8 1.7ZM7.25 4.75a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-1.5 0v-3.5Zm.75 6.5a.9.9 0 1 1 0-1.8.9.9 0 0 1 0 1.8Z"/>',
|
|
13
|
+
details:
|
|
14
|
+
'<path fill="currentColor" d="M2 3.75A.75.75 0 0 1 2.75 3h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 3.75ZM2 8a.75.75 0 0 1 .75-.75h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 8Zm.75 3.5h6.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5Z"/>'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isAlertType(name: string): name is AlertType {
|
|
18
|
+
return (ALERT_TYPES as readonly string[]).includes(name)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function alertIcon(name: string): string {
|
|
22
|
+
const type = isAlertType(name) ? name : 'info'
|
|
23
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">${PATHS[type]}</svg>`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const ALERT_LABELS: Record<AlertType, string> = {
|
|
27
|
+
tip: '提示',
|
|
28
|
+
info: '信息',
|
|
29
|
+
warning: '警告',
|
|
30
|
+
danger: '错误',
|
|
31
|
+
details: '详情'
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS 全屏层的文档级标记。
|
|
3
|
+
*
|
|
4
|
+
* 全屏层(mindmap / mermaid / Desk 代码块)往往嵌在编辑器的层叠上下文里,z-index
|
|
5
|
+
* 盖不过应用壳。宿主据 `body[data-tn-fullscreen]` 隐藏壳层,再用
|
|
6
|
+
* `[data-tn-fullscreen-layer]` 把全屏层本身显示回来;DOM 不搬家,编辑器 widget 不会重建。
|
|
7
|
+
*/
|
|
8
|
+
export function setFullscreenLayer(el: HTMLElement | null | undefined, on: boolean): void {
|
|
9
|
+
if (typeof document === 'undefined') return
|
|
10
|
+
if (el) {
|
|
11
|
+
if (on) el.dataset.tnFullscreenLayer = ''
|
|
12
|
+
else delete el.dataset.tnFullscreenLayer
|
|
13
|
+
}
|
|
14
|
+
if (document.querySelector('[data-tn-fullscreen-layer]')) document.body.dataset.tnFullscreen = ''
|
|
15
|
+
else delete document.body.dataset.tnFullscreen
|
|
16
|
+
}
|