@lemoncat7/dsh-knowledge 2.2.10 → 2.2.13

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.
Files changed (74) hide show
  1. package/README.md +3 -3
  2. package/docs/architecture.md +6 -2
  3. package/lib/api.d.ts.map +1 -1
  4. package/lib/api.js +20 -0
  5. package/lib/api.js.map +1 -1
  6. package/lib/client.js +22 -22
  7. package/lib/client.js.map +2 -2
  8. package/lib/local-provider.d.ts +8 -1
  9. package/lib/local-provider.d.ts.map +1 -1
  10. package/lib/local-provider.js +12 -0
  11. package/lib/local-provider.js.map +1 -1
  12. package/lib/management-proxy.js +1 -1
  13. package/lib/management-proxy.js.map +1 -1
  14. package/lib/notes/domain.d.ts +10 -0
  15. package/lib/notes/domain.d.ts.map +1 -1
  16. package/lib/notes/domain.js.map +1 -1
  17. package/lib/notes/store.d.ts +15 -1
  18. package/lib/notes/store.d.ts.map +1 -1
  19. package/lib/notes/store.js +277 -28
  20. package/lib/notes/store.js.map +1 -1
  21. package/lib/provider.d.ts +8 -1
  22. package/lib/provider.d.ts.map +1 -1
  23. package/lib/remote-provider.d.ts +8 -1
  24. package/lib/remote-provider.d.ts.map +1 -1
  25. package/lib/remote-provider.js +22 -0
  26. package/lib/remote-provider.js.map +1 -1
  27. package/lib/theme-bridge.js +7 -7
  28. package/lib/theme-bridge.js.map +1 -1
  29. package/lib/tool-authorization.js +11 -16
  30. package/lib/tool-authorization.js.map +1 -1
  31. package/lib/web-note-editor-chrome.d.ts +17 -0
  32. package/lib/web-note-editor-chrome.d.ts.map +1 -0
  33. package/lib/web-note-editor-chrome.js +38 -0
  34. package/lib/web-note-editor-chrome.js.map +1 -0
  35. package/lib/web-note-editor-find.d.ts +17 -0
  36. package/lib/web-note-editor-find.d.ts.map +1 -0
  37. package/lib/web-note-editor-find.js +225 -0
  38. package/lib/web-note-editor-find.js.map +1 -0
  39. package/lib/web-note-editor-outline.d.ts +15 -0
  40. package/lib/web-note-editor-outline.d.ts.map +1 -0
  41. package/lib/web-note-editor-outline.js +169 -0
  42. package/lib/web-note-editor-outline.js.map +1 -0
  43. package/lib/web-note-editor-search.d.ts +30 -0
  44. package/lib/web-note-editor-search.d.ts.map +1 -0
  45. package/lib/web-note-editor-search.js +143 -0
  46. package/lib/web-note-editor-search.js.map +1 -0
  47. package/lib/web-note-editor-selection.d.ts +15 -0
  48. package/lib/web-note-editor-selection.d.ts.map +1 -0
  49. package/lib/web-note-editor-selection.js +255 -0
  50. package/lib/web-note-editor-selection.js.map +1 -0
  51. package/lib/web-note-editor.d.ts +7 -0
  52. package/lib/web-note-editor.d.ts.map +1 -1
  53. package/lib/web-note-editor.js +36 -6
  54. package/lib/web-note-editor.js.map +1 -1
  55. package/lib/web-note-history.d.ts +28 -0
  56. package/lib/web-note-history.d.ts.map +1 -0
  57. package/lib/web-note-history.js +163 -0
  58. package/lib/web-note-history.js.map +1 -0
  59. package/lib/web-workspace-effects.d.ts.map +1 -1
  60. package/lib/web-workspace-effects.js +1 -2
  61. package/lib/web-workspace-effects.js.map +1 -1
  62. package/lib/web.d.ts.map +1 -1
  63. package/lib/web.js +4 -0
  64. package/lib/web.js.map +1 -1
  65. package/package.json +1 -1
  66. package/web/api-client.js +94 -0
  67. package/web/app.js +248 -230
  68. package/web/host-theme.js +63 -0
  69. package/web/index.html +1 -0
  70. package/web/note-editor.js +70 -70
  71. package/web/note-history.js +1 -0
  72. package/web/styles.css +353 -154
  73. package/web/ui-primitives.js +77 -0
  74. package/web/workspace-effects.js +1 -1
@@ -0,0 +1,169 @@
1
+ const REBUILD_DELAY = 140;
2
+ function collectHeadings(editor) {
3
+ const headings = [];
4
+ editor.state.doc.descendants((node, position) => {
5
+ if (node.type.name !== 'heading')
6
+ return true;
7
+ const text = node.textContent.trim();
8
+ if (!text)
9
+ return false;
10
+ headings.push({
11
+ level: Number(node.attrs.level) || 1,
12
+ position,
13
+ text: text.slice(0, 160),
14
+ element: editor.view.nodeDOM(position),
15
+ });
16
+ return false;
17
+ });
18
+ return headings;
19
+ }
20
+ export function createNoteOutlineController(options) {
21
+ const { editor, frame, host, toggleButton } = options;
22
+ host.replaceChildren();
23
+ const header = document.createElement('header');
24
+ header.className = 'notes-editor-outline-header';
25
+ const title = document.createElement('strong');
26
+ title.textContent = '文档大纲';
27
+ const count = document.createElement('span');
28
+ const closeButton = document.createElement('button');
29
+ closeButton.type = 'button';
30
+ closeButton.className = 'notes-editor-outline-close';
31
+ closeButton.textContent = '关闭';
32
+ closeButton.setAttribute('aria-label', '关闭文档大纲');
33
+ header.append(title, count, closeButton);
34
+ const list = document.createElement('nav');
35
+ list.className = 'notes-editor-outline-list';
36
+ list.setAttribute('aria-label', '笔记标题导航');
37
+ host.append(header, list);
38
+ let headings = [];
39
+ let open = false;
40
+ let manuallyToggled = false;
41
+ let rebuildTimer;
42
+ let observer;
43
+ function syncOpenState() {
44
+ frame.dataset.outlineOpen = String(open);
45
+ host.setAttribute('aria-hidden', String(!open));
46
+ host.inert = !open;
47
+ toggleButton?.setAttribute('aria-pressed', String(open));
48
+ toggleButton?.setAttribute('aria-expanded', String(open));
49
+ if (open)
50
+ observeHeadings();
51
+ else
52
+ observer?.disconnect();
53
+ }
54
+ function setActive(position) {
55
+ for (const button of list.querySelectorAll('[data-heading-position]')) {
56
+ const active = Number(button.dataset.headingPosition) === position;
57
+ if (active)
58
+ button.setAttribute('aria-current', 'location');
59
+ else
60
+ button.removeAttribute('aria-current');
61
+ }
62
+ }
63
+ function jumpToHeading(item) {
64
+ editor.commands.setTextSelection(item.position + 1);
65
+ editor.view.dom.focus({ preventScroll: true });
66
+ item.element?.scrollIntoView({
67
+ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',
68
+ block: 'start',
69
+ });
70
+ setActive(item.position);
71
+ if (window.matchMedia('(max-width: 760px)').matches) {
72
+ open = false;
73
+ syncOpenState();
74
+ }
75
+ }
76
+ function renderHeadings() {
77
+ count.textContent = headings.length ? `${headings.length} 个标题` : '';
78
+ list.replaceChildren();
79
+ if (!headings.length) {
80
+ const empty = document.createElement('p');
81
+ empty.className = 'notes-editor-outline-empty';
82
+ empty.textContent = '使用标题 1–3 后,会在这里生成可定位的大纲。';
83
+ list.append(empty);
84
+ return;
85
+ }
86
+ const fragment = document.createDocumentFragment();
87
+ for (const item of headings) {
88
+ const link = document.createElement('button');
89
+ link.type = 'button';
90
+ link.className = 'notes-editor-outline-link';
91
+ link.textContent = item.text;
92
+ link.dataset.headingPosition = String(item.position);
93
+ link.style.setProperty('--outline-depth', String(Math.max(0, Math.min(4, item.level - 1))));
94
+ link.addEventListener('click', () => jumpToHeading(item));
95
+ fragment.append(link);
96
+ }
97
+ list.append(fragment);
98
+ }
99
+ function observeHeadings() {
100
+ observer?.disconnect();
101
+ if (!open || !('IntersectionObserver' in window))
102
+ return;
103
+ observer = new IntersectionObserver(entries => {
104
+ const visible = entries
105
+ .filter(entry => entry.isIntersecting)
106
+ .sort((left, right) => left.boundingClientRect.top - right.boundingClientRect.top);
107
+ const target = visible[0]?.target;
108
+ const item = headings.find(heading => heading.element === target);
109
+ if (item)
110
+ setActive(item.position);
111
+ }, {
112
+ root: frame.querySelector('.notes-document-scroll'),
113
+ rootMargin: '-12px 0px -78% 0px',
114
+ threshold: 0,
115
+ });
116
+ for (const item of headings)
117
+ if (item.element)
118
+ observer.observe(item.element);
119
+ }
120
+ function rebuild() {
121
+ rebuildTimer = undefined;
122
+ if (editor.isDestroyed)
123
+ return;
124
+ const next = collectHeadings(editor);
125
+ const changed = next.length !== headings.length || next.some((item, index) => {
126
+ const previous = headings[index];
127
+ return !previous || previous.position !== item.position || previous.level !== item.level || previous.text !== item.text;
128
+ });
129
+ headings = next;
130
+ if (changed)
131
+ renderHeadings();
132
+ if (!manuallyToggled && headings.length && !window.matchMedia('(max-width: 980px)').matches)
133
+ open = true;
134
+ if (!headings.length && !manuallyToggled)
135
+ open = false;
136
+ syncOpenState();
137
+ }
138
+ function scheduleRebuild() {
139
+ if (rebuildTimer !== undefined)
140
+ window.clearTimeout(rebuildTimer);
141
+ rebuildTimer = window.setTimeout(rebuild, REBUILD_DELAY);
142
+ }
143
+ function toggle() {
144
+ manuallyToggled = true;
145
+ open = !open;
146
+ syncOpenState();
147
+ }
148
+ function close() {
149
+ manuallyToggled = true;
150
+ open = false;
151
+ syncOpenState();
152
+ }
153
+ closeButton.addEventListener('click', close);
154
+ editor.on('update', scheduleRebuild);
155
+ rebuild();
156
+ return {
157
+ toggle,
158
+ close,
159
+ destroy: () => {
160
+ editor.off('update', scheduleRebuild);
161
+ if (rebuildTimer !== undefined)
162
+ window.clearTimeout(rebuildTimer);
163
+ observer?.disconnect();
164
+ host.replaceChildren();
165
+ delete frame.dataset.outlineOpen;
166
+ },
167
+ };
168
+ }
169
+ //# sourceMappingURL=web-note-editor-outline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-note-editor-outline.js","sourceRoot":"","sources":["../src/web-note-editor-outline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { Extension, type Editor } from '@tiptap/core';
2
+ import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
3
+ import { PluginKey } from '@tiptap/pm/state';
4
+ import { DecorationSet } from '@tiptap/pm/view';
5
+ export interface NoteSearchRange {
6
+ from: number;
7
+ to: number;
8
+ }
9
+ export interface NoteSearchState {
10
+ query: string;
11
+ caseSensitive: boolean;
12
+ activeIndex: number;
13
+ results: NoteSearchRange[];
14
+ decorations: DecorationSet;
15
+ }
16
+ interface NoteSearchMeta {
17
+ query?: string;
18
+ caseSensitive?: boolean;
19
+ activeIndex?: number;
20
+ }
21
+ export declare const noteSearchPluginKey: PluginKey<NoteSearchState>;
22
+ /** Finds literal matches per text block, including text split across inline marks. */
23
+ export declare function findNoteSearchRanges(document: ProseMirrorNode, rawQuery: string, caseSensitive: boolean): NoteSearchRange[];
24
+ export declare const NoteSearch: Extension<any, any>;
25
+ export declare function getNoteSearchState(editor: Editor): NoteSearchState | undefined;
26
+ export declare function updateNoteSearch(editor: Editor, meta: NoteSearchMeta): void;
27
+ export declare function replaceNoteSearchResult(editor: Editor, range: NoteSearchRange, replacement: string): void;
28
+ export declare function replaceAllNoteSearchResults(editor: Editor, ranges: NoteSearchRange[], replacement: string): void;
29
+ export {};
30
+ //# sourceMappingURL=web-note-editor-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-note-editor-search.d.ts","sourceRoot":"","sources":["../src/web-note-editor-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC/D,OAAO,EAAU,SAAS,EAAoB,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAc,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,OAAO,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,WAAW,EAAE,aAAa,CAAA;CAC3B;AAED,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAKD,eAAO,MAAM,mBAAmB,4BAA2D,CAAA;AAW3F,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,eAAe,EAAE,CA6C3H;AA+BD,eAAO,MAAM,UAAU,qBAoBrB,CAAA;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE9E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAG3E;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAMzG;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAUhH"}
@@ -0,0 +1,143 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state';
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view';
4
+ const MAX_QUERY_LENGTH = 256;
5
+ const MAX_RESULTS = 1_000;
6
+ export const noteSearchPluginKey = new PluginKey('dshKnowledgeNoteSearch');
7
+ function normalizeQuery(query) {
8
+ return query.slice(0, MAX_QUERY_LENGTH);
9
+ }
10
+ function clampActiveIndex(index, resultCount) {
11
+ if (resultCount === 0)
12
+ return 0;
13
+ return Math.max(0, Math.min(Math.trunc(index), resultCount - 1));
14
+ }
15
+ /** Finds literal matches per text block, including text split across inline marks. */
16
+ export function findNoteSearchRanges(document, rawQuery, caseSensitive) {
17
+ const query = normalizeQuery(rawQuery);
18
+ if (!query)
19
+ return [];
20
+ const needle = caseSensitive ? query : query.toLocaleLowerCase();
21
+ const results = [];
22
+ document.descendants((node, position) => {
23
+ if (!node.isTextblock || results.length >= MAX_RESULTS)
24
+ return results.length < MAX_RESULTS;
25
+ let segment = '';
26
+ let segmentOffset = 0;
27
+ const flush = () => {
28
+ if (!segment)
29
+ return;
30
+ const haystack = caseSensitive ? segment : segment.toLocaleLowerCase();
31
+ let offset = 0;
32
+ while (offset <= haystack.length - needle.length && results.length < MAX_RESULTS) {
33
+ const match = haystack.indexOf(needle, offset);
34
+ if (match < 0)
35
+ break;
36
+ const from = position + 1 + segmentOffset + match;
37
+ results.push({ from, to: from + query.length });
38
+ offset = match + Math.max(1, needle.length);
39
+ }
40
+ segment = '';
41
+ };
42
+ node.forEach((child, offset) => {
43
+ if (!child.isText) {
44
+ flush();
45
+ return;
46
+ }
47
+ if (!segment)
48
+ segmentOffset = offset;
49
+ else if (offset !== segmentOffset + segment.length) {
50
+ flush();
51
+ segmentOffset = offset;
52
+ }
53
+ segment += child.text ?? '';
54
+ });
55
+ flush();
56
+ if (results.length >= MAX_RESULTS) {
57
+ return false;
58
+ }
59
+ return false;
60
+ });
61
+ return results;
62
+ }
63
+ function buildDecorations(document, results, activeIndex) {
64
+ return DecorationSet.create(document, results.map((range, index) => Decoration.inline(range.from, range.to, {
65
+ class: index === activeIndex ? 'notes-search-result notes-search-result-current' : 'notes-search-result',
66
+ 'data-note-search-result': index === activeIndex ? 'current' : 'match',
67
+ })));
68
+ }
69
+ function nextSearchState(transaction, previous) {
70
+ const meta = transaction.getMeta(noteSearchPluginKey);
71
+ if (!transaction.docChanged && !meta)
72
+ return previous;
73
+ const query = normalizeQuery(meta?.query ?? previous.query);
74
+ const caseSensitive = meta?.caseSensitive ?? previous.caseSensitive;
75
+ const mustRescan = transaction.docChanged || query !== previous.query || caseSensitive !== previous.caseSensitive;
76
+ const results = mustRescan
77
+ ? findNoteSearchRanges(transaction.doc, query, caseSensitive)
78
+ : previous.results;
79
+ const requestedIndex = meta?.activeIndex ?? previous.activeIndex;
80
+ const activeIndex = clampActiveIndex(requestedIndex, results.length);
81
+ return {
82
+ query,
83
+ caseSensitive,
84
+ activeIndex,
85
+ results,
86
+ decorations: buildDecorations(transaction.doc, results, activeIndex),
87
+ };
88
+ }
89
+ export const NoteSearch = Extension.create({
90
+ name: 'dshKnowledgeNoteSearch',
91
+ addProseMirrorPlugins() {
92
+ return [new Plugin({
93
+ key: noteSearchPluginKey,
94
+ state: {
95
+ init: (_, state) => ({
96
+ query: '',
97
+ caseSensitive: false,
98
+ activeIndex: 0,
99
+ results: [],
100
+ decorations: DecorationSet.empty,
101
+ }),
102
+ apply: nextSearchState,
103
+ },
104
+ props: {
105
+ decorations: state => noteSearchPluginKey.getState(state)?.decorations ?? DecorationSet.empty,
106
+ },
107
+ })];
108
+ },
109
+ });
110
+ export function getNoteSearchState(editor) {
111
+ return noteSearchPluginKey.getState(editor.state);
112
+ }
113
+ export function updateNoteSearch(editor, meta) {
114
+ if (editor.isDestroyed)
115
+ return;
116
+ editor.view.dispatch(editor.state.tr.setMeta(noteSearchPluginKey, meta));
117
+ }
118
+ export function replaceNoteSearchResult(editor, range, replacement) {
119
+ if (editor.isDestroyed)
120
+ return;
121
+ const transaction = editor.state.tr;
122
+ if (replacement)
123
+ transaction.replaceWith(range.from, range.to, editor.state.schema.text(replacement, editor.state.doc.resolve(range.from).marks()));
124
+ else
125
+ transaction.delete(range.from, range.to);
126
+ editor.view.dispatch(transaction);
127
+ }
128
+ export function replaceAllNoteSearchResults(editor, ranges, replacement) {
129
+ if (editor.isDestroyed || ranges.length === 0)
130
+ return;
131
+ const transaction = editor.state.tr;
132
+ for (let index = ranges.length - 1; index >= 0; index -= 1) {
133
+ const range = ranges[index];
134
+ if (!range)
135
+ continue;
136
+ if (replacement)
137
+ transaction.replaceWith(range.from, range.to, editor.state.schema.text(replacement, editor.state.doc.resolve(range.from).marks()));
138
+ else
139
+ transaction.delete(range.from, range.to);
140
+ }
141
+ editor.view.dispatch(transaction);
142
+ }
143
+ //# sourceMappingURL=web-note-editor-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-note-editor-search.js","sourceRoot":"","sources":["../src/web-note-editor-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ interface NoteSelectionMenuOptions {
3
+ editor: Editor;
4
+ frame: HTMLElement;
5
+ scrollHost: HTMLElement;
6
+ findIsOpen(): boolean;
7
+ }
8
+ export interface NoteSelectionMenuController {
9
+ refresh(): void;
10
+ hide(): void;
11
+ destroy(): void;
12
+ }
13
+ export declare function createNoteSelectionMenu(options: NoteSelectionMenuOptions): NoteSelectionMenuController;
14
+ export {};
15
+ //# sourceMappingURL=web-note-editor-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-note-editor-selection.d.ts","sourceRoot":"","sources":["../src/web-note-editor-selection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,UAAU,wBAAwB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,WAAW,CAAA;IAClB,UAAU,EAAE,WAAW,CAAA;IACvB,UAAU,IAAI,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,IAAI,IAAI,CAAA;IACf,IAAI,IAAI,IAAI,CAAA;IACZ,OAAO,IAAI,IAAI,CAAA;CAChB;AA2BD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,2BAA2B,CA2OtG"}
@@ -0,0 +1,255 @@
1
+ function control(label, ariaLabel, onClick) {
2
+ const button = document.createElement('button');
3
+ button.type = 'button';
4
+ button.className = 'notes-selection-control';
5
+ button.textContent = label;
6
+ button.setAttribute('aria-label', ariaLabel);
7
+ button.addEventListener('pointerdown', event => event.preventDefault());
8
+ button.addEventListener('click', onClick);
9
+ return button;
10
+ }
11
+ function safeLink(value) {
12
+ const href = value.trim();
13
+ if (!href)
14
+ return '';
15
+ if (/^(https?:\/\/|mailto:|\/|#)/i.test(href))
16
+ return href;
17
+ if (/^[\w.-]+\.[a-z]{2,}(?:[/?#].*)?$/i.test(href))
18
+ return `https://${href}`;
19
+ return null;
20
+ }
21
+ export function createNoteSelectionMenu(options) {
22
+ const { editor, frame, scrollHost } = options;
23
+ const menu = document.createElement('div');
24
+ menu.className = 'notes-selection-menu';
25
+ menu.setAttribute('role', 'toolbar');
26
+ menu.setAttribute('aria-label', '所选文字格式');
27
+ menu.hidden = true;
28
+ const primary = document.createElement('div');
29
+ primary.className = 'notes-selection-primary';
30
+ const blockToggle = control('正文', '更改段落格式', () => {
31
+ const expanded = blockToggle.getAttribute('aria-expanded') !== 'true';
32
+ blockToggle.setAttribute('aria-expanded', String(expanded));
33
+ blockMenu.hidden = !expanded;
34
+ linkForm.hidden = true;
35
+ positionMenu();
36
+ });
37
+ blockToggle.classList.add('notes-selection-block-toggle');
38
+ blockToggle.setAttribute('aria-haspopup', 'menu');
39
+ blockToggle.setAttribute('aria-expanded', 'false');
40
+ const markActions = [
41
+ { label: 'B', active: () => editor.isActive('bold'), run: () => { editor.chain().focus().toggleBold().run(); } },
42
+ { label: 'I', active: () => editor.isActive('italic'), run: () => { editor.chain().focus().toggleItalic().run(); } },
43
+ { label: 'U', active: () => editor.isActive('underline'), run: () => { editor.chain().focus().toggleUnderline().run(); } },
44
+ { label: 'S', active: () => editor.isActive('strike'), run: () => { editor.chain().focus().toggleStrike().run(); } },
45
+ { label: '</>', active: () => editor.isActive('code'), run: () => { editor.chain().focus().toggleCode().run(); } },
46
+ ];
47
+ const markLabels = ['加粗', '斜体', '下划线', '删除线', '行内代码'];
48
+ const markButtons = markActions.map((action, index) => {
49
+ const button = control(action.label, markLabels[index] ?? action.label, () => {
50
+ action.run();
51
+ refreshActiveStates();
52
+ });
53
+ button.classList.add('notes-selection-mark');
54
+ return button;
55
+ });
56
+ const linkToggle = control('链接', '添加或编辑链接', () => {
57
+ blockMenu.hidden = true;
58
+ blockToggle.setAttribute('aria-expanded', 'false');
59
+ linkForm.hidden = false;
60
+ const href = editor.getAttributes('link').href;
61
+ linkInput.value = typeof href === 'string' ? href : '';
62
+ linkError.textContent = '';
63
+ positionMenu();
64
+ window.requestAnimationFrame(() => { linkInput.focus(); linkInput.select(); });
65
+ });
66
+ const copyButton = control('复制', '复制所选文字', () => {
67
+ const { from, to } = editor.state.selection;
68
+ const text = editor.state.doc.textBetween(from, to, ' ');
69
+ void navigator.clipboard?.writeText(text).catch(() => { });
70
+ });
71
+ primary.append(blockToggle, ...markButtons, linkToggle, copyButton);
72
+ const blockMenu = document.createElement('div');
73
+ blockMenu.className = 'notes-selection-block-menu';
74
+ blockMenu.setAttribute('role', 'menu');
75
+ blockMenu.hidden = true;
76
+ const blockActions = [
77
+ { label: '正文', active: () => editor.isActive('paragraph'), run: () => { editor.chain().focus().setParagraph().run(); } },
78
+ { label: '标题 1', active: () => editor.isActive('heading', { level: 1 }), run: () => { editor.chain().focus().toggleHeading({ level: 1 }).run(); } },
79
+ { label: '标题 2', active: () => editor.isActive('heading', { level: 2 }), run: () => { editor.chain().focus().toggleHeading({ level: 2 }).run(); } },
80
+ { label: '标题 3', active: () => editor.isActive('heading', { level: 3 }), run: () => { editor.chain().focus().toggleHeading({ level: 3 }).run(); } },
81
+ { label: '项目符号', active: () => editor.isActive('bulletList'), run: () => { editor.chain().focus().toggleBulletList().run(); } },
82
+ { label: '编号列表', active: () => editor.isActive('orderedList'), run: () => { editor.chain().focus().toggleOrderedList().run(); } },
83
+ { label: '引用', active: () => editor.isActive('blockquote'), run: () => { editor.chain().focus().toggleBlockquote().run(); } },
84
+ { label: '代码块', active: () => editor.isActive('codeBlock'), run: () => { editor.chain().focus().toggleCodeBlock().run(); } },
85
+ ];
86
+ const blockButtons = blockActions.map(action => {
87
+ const button = control(action.label, action.label, () => {
88
+ action.run();
89
+ blockMenu.hidden = true;
90
+ blockToggle.setAttribute('aria-expanded', 'false');
91
+ refreshActiveStates();
92
+ schedulePosition();
93
+ });
94
+ button.setAttribute('role', 'menuitemradio');
95
+ blockMenu.append(button);
96
+ return button;
97
+ });
98
+ const linkForm = document.createElement('form');
99
+ linkForm.className = 'notes-selection-link-form';
100
+ linkForm.hidden = true;
101
+ const linkInput = document.createElement('input');
102
+ linkInput.type = 'text';
103
+ linkInput.inputMode = 'url';
104
+ linkInput.className = 'notes-selection-link-input';
105
+ linkInput.placeholder = 'https://example.com';
106
+ linkInput.setAttribute('aria-label', '链接地址');
107
+ linkInput.autocomplete = 'off';
108
+ const applyLink = control('应用', '应用链接', () => submitLink());
109
+ const removeLink = control('移除', '移除链接', () => {
110
+ editor.chain().focus().extendMarkRange('link').unsetLink().run();
111
+ linkForm.hidden = true;
112
+ refreshActiveStates();
113
+ });
114
+ const linkError = document.createElement('span');
115
+ linkError.className = 'notes-selection-link-error';
116
+ linkError.setAttribute('role', 'alert');
117
+ linkForm.append(linkInput, applyLink, removeLink, linkError);
118
+ linkForm.addEventListener('submit', event => {
119
+ event.preventDefault();
120
+ submitLink();
121
+ });
122
+ menu.append(primary, blockMenu, linkForm);
123
+ frame.append(menu);
124
+ let positionFrame;
125
+ let scrollTimer;
126
+ function currentBlockLabel() {
127
+ if (editor.isActive('heading', { level: 1 }))
128
+ return '标题 1';
129
+ if (editor.isActive('heading', { level: 2 }))
130
+ return '标题 2';
131
+ if (editor.isActive('heading', { level: 3 }))
132
+ return '标题 3';
133
+ if (editor.isActive('bulletList'))
134
+ return '项目符号';
135
+ if (editor.isActive('orderedList'))
136
+ return '编号列表';
137
+ if (editor.isActive('blockquote'))
138
+ return '引用';
139
+ if (editor.isActive('codeBlock'))
140
+ return '代码块';
141
+ return '正文';
142
+ }
143
+ function refreshActiveStates() {
144
+ blockToggle.textContent = currentBlockLabel();
145
+ markButtons.forEach((button, index) => button.setAttribute('aria-pressed', String(markActions[index]?.active() ?? false)));
146
+ blockButtons.forEach((button, index) => button.setAttribute('aria-checked', String(blockActions[index]?.active() ?? false)));
147
+ linkToggle.setAttribute('aria-pressed', String(editor.isActive('link')));
148
+ }
149
+ function submitLink() {
150
+ const href = safeLink(linkInput.value);
151
+ if (href === null) {
152
+ linkError.textContent = '请输入有效的网页或邮件地址。';
153
+ linkInput.focus();
154
+ return;
155
+ }
156
+ if (href)
157
+ editor.chain().focus().extendMarkRange('link').setLink({ href }).run();
158
+ else
159
+ editor.chain().focus().extendMarkRange('link').unsetLink().run();
160
+ linkError.textContent = '';
161
+ linkForm.hidden = true;
162
+ refreshActiveStates();
163
+ }
164
+ function shouldShow() {
165
+ if (editor.isDestroyed || options.findIsOpen())
166
+ return false;
167
+ const { from, to, empty } = editor.state.selection;
168
+ if (empty || !editor.isFocused)
169
+ return false;
170
+ return Boolean(editor.state.doc.textBetween(from, to, ' ').trim());
171
+ }
172
+ function positionMenu() {
173
+ positionFrame = undefined;
174
+ if (menu.hidden || editor.isDestroyed)
175
+ return;
176
+ if (window.matchMedia('(max-width: 760px)').matches) {
177
+ menu.dataset.placement = 'bottom';
178
+ menu.style.left = 'max(8px, env(safe-area-inset-left))';
179
+ menu.style.right = 'max(8px, env(safe-area-inset-right))';
180
+ menu.style.bottom = 'max(8px, env(safe-area-inset-bottom))';
181
+ menu.style.top = 'auto';
182
+ return;
183
+ }
184
+ menu.style.right = '';
185
+ menu.style.bottom = '';
186
+ const { from, to } = editor.state.selection;
187
+ const start = editor.view.coordsAtPos(from);
188
+ const end = editor.view.coordsAtPos(to);
189
+ const rect = menu.getBoundingClientRect();
190
+ const margin = 8;
191
+ const center = (Math.min(start.left, end.left) + Math.max(start.right, end.right)) / 2;
192
+ const left = Math.max(margin, Math.min(window.innerWidth - rect.width - margin, center - rect.width / 2));
193
+ let top = Math.min(start.top, end.top) - rect.height - 9;
194
+ menu.dataset.placement = 'top';
195
+ if (top < margin) {
196
+ top = Math.max(start.bottom, end.bottom) + 9;
197
+ menu.dataset.placement = 'bottom';
198
+ }
199
+ menu.style.left = `${Math.round(left)}px`;
200
+ menu.style.top = `${Math.round(top)}px`;
201
+ }
202
+ function schedulePosition() {
203
+ if (positionFrame !== undefined)
204
+ window.cancelAnimationFrame(positionFrame);
205
+ positionFrame = window.requestAnimationFrame(positionMenu);
206
+ }
207
+ function refresh() {
208
+ if (!shouldShow()) {
209
+ hide();
210
+ return;
211
+ }
212
+ menu.hidden = false;
213
+ refreshActiveStates();
214
+ schedulePosition();
215
+ }
216
+ function hide() {
217
+ menu.hidden = true;
218
+ blockMenu.hidden = true;
219
+ linkForm.hidden = true;
220
+ blockToggle.setAttribute('aria-expanded', 'false');
221
+ linkError.textContent = '';
222
+ }
223
+ function onScroll() {
224
+ hide();
225
+ if (scrollTimer !== undefined)
226
+ window.clearTimeout(scrollTimer);
227
+ scrollTimer = window.setTimeout(refresh, 90);
228
+ }
229
+ function onOutsidePointer(event) {
230
+ if (!menu.hidden && !menu.contains(event.target) && !editor.view.dom.contains(event.target))
231
+ hide();
232
+ }
233
+ editor.on('selectionUpdate', refresh);
234
+ editor.on('transaction', refresh);
235
+ scrollHost.addEventListener('scroll', onScroll, { passive: true });
236
+ window.addEventListener('resize', schedulePosition, { passive: true });
237
+ document.addEventListener('pointerdown', onOutsidePointer);
238
+ return {
239
+ refresh,
240
+ hide,
241
+ destroy: () => {
242
+ editor.off('selectionUpdate', refresh);
243
+ editor.off('transaction', refresh);
244
+ scrollHost.removeEventListener('scroll', onScroll);
245
+ window.removeEventListener('resize', schedulePosition);
246
+ document.removeEventListener('pointerdown', onOutsidePointer);
247
+ if (positionFrame !== undefined)
248
+ window.cancelAnimationFrame(positionFrame);
249
+ if (scrollTimer !== undefined)
250
+ window.clearTimeout(scrollTimer);
251
+ menu.remove();
252
+ },
253
+ };
254
+ }
255
+ //# sourceMappingURL=web-note-editor-selection.js.map