mossmd 0.8.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +21 -0
  3. package/README.md +336 -0
  4. package/dist/collab/index.d.ts +14 -0
  5. package/dist/collab/index.d.ts.map +1 -0
  6. package/dist/collab/index.js +6 -0
  7. package/dist/collab/index.js.map +1 -0
  8. package/dist/core/code-languages.d.ts +3 -0
  9. package/dist/core/code-languages.d.ts.map +1 -0
  10. package/dist/core/code-languages.js +130 -0
  11. package/dist/core/code-languages.js.map +1 -0
  12. package/dist/core/decoration-utils.d.ts +4 -0
  13. package/dist/core/decoration-utils.d.ts.map +1 -0
  14. package/dist/core/decoration-utils.js +25 -0
  15. package/dist/core/decoration-utils.js.map +1 -0
  16. package/dist/core/edit-helpers.d.ts +13 -0
  17. package/dist/core/edit-helpers.d.ts.map +1 -0
  18. package/dist/core/edit-helpers.js +318 -0
  19. package/dist/core/edit-helpers.js.map +1 -0
  20. package/dist/core/icons.d.ts +16 -0
  21. package/dist/core/icons.d.ts.map +1 -0
  22. package/dist/core/icons.js +86 -0
  23. package/dist/core/icons.js.map +1 -0
  24. package/dist/core/inline-preview.d.ts +32 -0
  25. package/dist/core/inline-preview.d.ts.map +1 -0
  26. package/dist/core/inline-preview.js +925 -0
  27. package/dist/core/inline-preview.js.map +1 -0
  28. package/dist/core/list-editing.d.ts +10 -0
  29. package/dist/core/list-editing.d.ts.map +1 -0
  30. package/dist/core/list-editing.js +244 -0
  31. package/dist/core/list-editing.js.map +1 -0
  32. package/dist/core/list-model.d.ts +40 -0
  33. package/dist/core/list-model.d.ts.map +1 -0
  34. package/dist/core/list-model.js +114 -0
  35. package/dist/core/list-model.js.map +1 -0
  36. package/dist/core/list-navigation.d.ts +3 -0
  37. package/dist/core/list-navigation.d.ts.map +1 -0
  38. package/dist/core/list-navigation.js +234 -0
  39. package/dist/core/list-navigation.js.map +1 -0
  40. package/dist/core/markdown-context.d.ts +3 -0
  41. package/dist/core/markdown-context.d.ts.map +1 -0
  42. package/dist/core/markdown-context.js +10 -0
  43. package/dist/core/markdown-context.js.map +1 -0
  44. package/dist/core/preview-activity.d.ts +18 -0
  45. package/dist/core/preview-activity.d.ts.map +1 -0
  46. package/dist/core/preview-activity.js +199 -0
  47. package/dist/core/preview-activity.js.map +1 -0
  48. package/dist/core/preview-widgets.d.ts +65 -0
  49. package/dist/core/preview-widgets.d.ts.map +1 -0
  50. package/dist/core/preview-widgets.js +359 -0
  51. package/dist/core/preview-widgets.js.map +1 -0
  52. package/dist/core/read-only.d.ts +26 -0
  53. package/dist/core/read-only.d.ts.map +1 -0
  54. package/dist/core/read-only.js +49 -0
  55. package/dist/core/read-only.js.map +1 -0
  56. package/dist/core/tree-progress.d.ts +29 -0
  57. package/dist/core/tree-progress.d.ts.map +1 -0
  58. package/dist/core/tree-progress.js +143 -0
  59. package/dist/core/tree-progress.js.map +1 -0
  60. package/dist/editor.d.ts +219 -0
  61. package/dist/editor.d.ts.map +1 -0
  62. package/dist/editor.js +551 -0
  63. package/dist/editor.js.map +1 -0
  64. package/dist/features/callout/index.d.ts +9 -0
  65. package/dist/features/callout/index.d.ts.map +1 -0
  66. package/dist/features/callout/index.js +223 -0
  67. package/dist/features/callout/index.js.map +1 -0
  68. package/dist/features/file-blocks/index.d.ts +12 -0
  69. package/dist/features/file-blocks/index.d.ts.map +1 -0
  70. package/dist/features/file-blocks/index.js +397 -0
  71. package/dist/features/file-blocks/index.js.map +1 -0
  72. package/dist/features/image/index.d.ts +28 -0
  73. package/dist/features/image/index.d.ts.map +1 -0
  74. package/dist/features/image/index.js +699 -0
  75. package/dist/features/image/index.js.map +1 -0
  76. package/dist/features/index.d.ts +17 -0
  77. package/dist/features/index.d.ts.map +1 -0
  78. package/dist/features/index.js +9 -0
  79. package/dist/features/index.js.map +1 -0
  80. package/dist/features/search/index.d.ts +4 -0
  81. package/dist/features/search/index.d.ts.map +1 -0
  82. package/dist/features/search/index.js +131 -0
  83. package/dist/features/search/index.js.map +1 -0
  84. package/dist/features/slash-commands/index.d.ts +52 -0
  85. package/dist/features/slash-commands/index.d.ts.map +1 -0
  86. package/dist/features/slash-commands/index.js +422 -0
  87. package/dist/features/slash-commands/index.js.map +1 -0
  88. package/dist/features/table/index.d.ts +44 -0
  89. package/dist/features/table/index.d.ts.map +1 -0
  90. package/dist/features/table/index.js +1262 -0
  91. package/dist/features/table/index.js.map +1 -0
  92. package/dist/features/upload/index.d.ts +80 -0
  93. package/dist/features/upload/index.d.ts.map +1 -0
  94. package/dist/features/upload/index.js +790 -0
  95. package/dist/features/upload/index.js.map +1 -0
  96. package/dist/features/wiki-links/index.d.ts +27 -0
  97. package/dist/features/wiki-links/index.d.ts.map +1 -0
  98. package/dist/features/wiki-links/index.js +577 -0
  99. package/dist/features/wiki-links/index.js.map +1 -0
  100. package/dist/index.d.ts +17 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +12 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/styles/content.css +247 -0
  105. package/dist/styles/inline-preview.css +2116 -0
  106. package/dist/styles/tokens.css +234 -0
  107. package/dist/syntax/highlight.d.ts +10 -0
  108. package/dist/syntax/highlight.d.ts.map +1 -0
  109. package/dist/syntax/highlight.js +75 -0
  110. package/dist/syntax/highlight.js.map +1 -0
  111. package/dist/syntax/index.d.ts +16 -0
  112. package/dist/syntax/index.d.ts.map +1 -0
  113. package/dist/syntax/index.js +29 -0
  114. package/dist/syntax/index.js.map +1 -0
  115. package/dist/theme/index.d.ts +6 -0
  116. package/dist/theme/index.d.ts.map +1 -0
  117. package/dist/theme/index.js +204 -0
  118. package/dist/theme/index.js.map +1 -0
  119. package/package.json +237 -0
@@ -0,0 +1,223 @@
1
+ import { Decoration, ViewPlugin, WidgetType, } from '@codemirror/view';
2
+ import { readOnlyFacet } from '../../core/read-only';
3
+ import { previewFrozenField } from '../../core/preview-activity';
4
+ import { defineMossSyntax } from '../../syntax';
5
+ const CALLOUT_START_RE = /^(\s{0,3}>\s?)(\[!([A-Za-z][\w-]*)\][+-]?)([ \t]*)/;
6
+ const BLOCKQUOTE_LINE_RE = /^\s{0,3}>/;
7
+ const DEFAULT_ALIASES = {
8
+ summary: 'abstract',
9
+ tldr: 'abstract',
10
+ hint: 'tip',
11
+ check: 'success',
12
+ done: 'success',
13
+ help: 'question',
14
+ faq: 'question',
15
+ caution: 'warning',
16
+ attention: 'warning',
17
+ fail: 'failure',
18
+ missing: 'failure',
19
+ error: 'danger',
20
+ cite: 'quote',
21
+ };
22
+ class CalloutMarkerWidget extends WidgetType {
23
+ constructor(type, label, lineFrom) {
24
+ super();
25
+ Object.defineProperty(this, "type", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: type
30
+ });
31
+ Object.defineProperty(this, "label", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: label
36
+ });
37
+ Object.defineProperty(this, "lineFrom", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: lineFrom
42
+ });
43
+ }
44
+ eq(other) {
45
+ return (this.type === other.type &&
46
+ this.label === other.label &&
47
+ this.lineFrom === other.lineFrom);
48
+ }
49
+ toDOM(view) {
50
+ const span = document.createElement('span');
51
+ span.className = `cm-moss-callout-label cm-moss-callout-label-${this.type}`;
52
+ span.textContent = this.label;
53
+ span.addEventListener('pointerdown', (event) => {
54
+ if (event.button !== 0)
55
+ return;
56
+ event.preventDefault();
57
+ event.stopPropagation();
58
+ view.focus();
59
+ view.dispatch({
60
+ selection: { anchor: this.lineFrom },
61
+ userEvent: 'select.pointer',
62
+ });
63
+ });
64
+ return span;
65
+ }
66
+ ignoreEvent() {
67
+ return false;
68
+ }
69
+ }
70
+ export function mossCallouts(config = {}) {
71
+ const aliases = {
72
+ ...DEFAULT_ALIASES,
73
+ ...normalizeAliases(config.aliases ?? {}),
74
+ };
75
+ return ViewPlugin.fromClass(class {
76
+ constructor(view) {
77
+ Object.defineProperty(this, "view", {
78
+ enumerable: true,
79
+ configurable: true,
80
+ writable: true,
81
+ value: view
82
+ });
83
+ Object.defineProperty(this, "decorations", {
84
+ enumerable: true,
85
+ configurable: true,
86
+ writable: true,
87
+ value: void 0
88
+ });
89
+ this.decorations = buildCalloutDecorations(view, aliases);
90
+ }
91
+ update(update) {
92
+ const wasFrozen = update.startState.field(previewFrozenField, false) ?? false;
93
+ const isFrozen = update.state.field(previewFrozenField, false) ?? false;
94
+ const justUnfroze = wasFrozen && !isFrozen;
95
+ if (isFrozen && !justUnfroze && !update.docChanged)
96
+ return;
97
+ const readOnlyChanged = update.startState.facet(readOnlyFacet) !==
98
+ update.state.facet(readOnlyFacet);
99
+ if (justUnfroze ||
100
+ update.docChanged ||
101
+ update.selectionSet ||
102
+ update.focusChanged ||
103
+ readOnlyChanged) {
104
+ this.decorations = buildCalloutDecorations(update.view, aliases);
105
+ }
106
+ }
107
+ }, {
108
+ decorations: (plugin) => plugin.decorations,
109
+ });
110
+ }
111
+ export function mossCalloutSyntax(config = {}) {
112
+ return defineMossSyntax({
113
+ name: 'callout',
114
+ description: 'Obsidian-style blockquote callouts',
115
+ extensions: mossCallouts(config),
116
+ });
117
+ }
118
+ function buildCalloutDecorations(view, aliases) {
119
+ const ranges = [];
120
+ const doc = view.state.doc;
121
+ const activeLines = findActiveLines(view);
122
+ for (let lineNumber = 1; lineNumber <= doc.lines; lineNumber++) {
123
+ const line = doc.line(lineNumber);
124
+ const start = parseCalloutStart(line.text, line.from, aliases);
125
+ if (!start)
126
+ continue;
127
+ start.number = line.number;
128
+ const lines = [start];
129
+ let nextNumber = lineNumber + 1;
130
+ while (nextNumber <= doc.lines) {
131
+ const nextLine = doc.line(nextNumber);
132
+ if (!BLOCKQUOTE_LINE_RE.test(nextLine.text))
133
+ break;
134
+ if (parseCalloutStart(nextLine.text, nextLine.from, aliases))
135
+ break;
136
+ lines.push({
137
+ from: nextLine.from,
138
+ to: nextLine.to,
139
+ number: nextLine.number,
140
+ type: start.type,
141
+ label: start.label,
142
+ markerFrom: nextLine.from,
143
+ markerTo: nextLine.from,
144
+ titleFrom: nextLine.from,
145
+ hasTitle: false,
146
+ });
147
+ nextNumber++;
148
+ }
149
+ for (let i = 0; i < lines.length; i++) {
150
+ const calloutLine = lines[i];
151
+ const positionClasses = [
152
+ i === 0 ? 'cm-moss-callout-first' : '',
153
+ i === lines.length - 1 ? 'cm-moss-callout-last' : '',
154
+ i > 0 && i < lines.length - 1 ? 'cm-moss-callout-middle' : '',
155
+ ].filter(Boolean).join(' ');
156
+ ranges.push(Decoration.line({
157
+ class: `cm-moss-callout cm-moss-callout-${calloutLine.type} ${positionClasses}`,
158
+ }).range(calloutLine.from));
159
+ }
160
+ if (!activeLines.has(start.number)) {
161
+ ranges.push(Decoration.replace({
162
+ widget: new CalloutMarkerWidget(start.type, start.label, start.from),
163
+ }).range(start.markerFrom, start.markerTo));
164
+ }
165
+ if (start.hasTitle && start.titleFrom < start.to) {
166
+ ranges.push(Decoration.mark({ class: 'cm-moss-callout-title' }).range(start.titleFrom, start.to));
167
+ }
168
+ lineNumber = nextNumber - 1;
169
+ }
170
+ return Decoration.set(ranges, true);
171
+ }
172
+ function findActiveLines(view) {
173
+ const activeLines = new Set();
174
+ if (view.state.facet(readOnlyFacet) || !view.hasFocus)
175
+ return activeLines;
176
+ for (const range of view.state.selection.ranges) {
177
+ const first = view.state.doc.lineAt(range.from).number;
178
+ const last = view.state.doc.lineAt(range.to).number;
179
+ for (let number = first; number <= last; number++) {
180
+ activeLines.add(number);
181
+ }
182
+ }
183
+ return activeLines;
184
+ }
185
+ function parseCalloutStart(text, lineFrom, aliases) {
186
+ const match = text.match(CALLOUT_START_RE);
187
+ if (!match)
188
+ return null;
189
+ const rawType = match[3].toLowerCase();
190
+ const type = normalizeType(aliases[rawType] ?? rawType);
191
+ const markerFrom = lineFrom + match[1].length;
192
+ const markerTo = lineFrom + match[0].length;
193
+ const titleFrom = markerTo;
194
+ return {
195
+ from: lineFrom,
196
+ to: lineFrom + text.length,
197
+ number: 0,
198
+ type,
199
+ label: labelForType(type),
200
+ markerFrom,
201
+ markerTo,
202
+ titleFrom,
203
+ hasTitle: text.slice(match[0].length).trim().length > 0,
204
+ };
205
+ }
206
+ function normalizeAliases(aliases) {
207
+ const normalized = {};
208
+ for (const [from, to] of Object.entries(aliases)) {
209
+ normalized[normalizeType(from)] = normalizeType(to);
210
+ }
211
+ return normalized;
212
+ }
213
+ function normalizeType(type) {
214
+ return type.trim().toLowerCase().replace(/[^a-z0-9-]/g, '-');
215
+ }
216
+ function labelForType(type) {
217
+ return type
218
+ .split('-')
219
+ .filter(Boolean)
220
+ .map((part) => part.slice(0, 1).toUpperCase() + part.slice(1))
221
+ .join(' ');
222
+ }
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/features/callout/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type Extension } from '@codemirror/state';
2
+ import { type MossIconRenderer } from '../../core/icons';
3
+ export interface MossFileBlockIcons {
4
+ file: MossIconRenderer;
5
+ download: MossIconRenderer;
6
+ delete: MossIconRenderer;
7
+ }
8
+ export interface MossFileBlocksConfig {
9
+ icons?: Partial<MossFileBlockIcons>;
10
+ }
11
+ export declare function mossFileBlocks(config?: MossFileBlocksConfig): Extension;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/file-blocks/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,SAAS,EAGf,MAAM,mBAAmB,CAAC;AAU3B,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACrC;AAuXD,wBAAgB,cAAc,CAAC,MAAM,GAAE,oBAAyB,GAAG,SAAS,CAmB3E"}
@@ -0,0 +1,397 @@
1
+ // File link blocks.
2
+ //
3
+ // When a markdown link `[label](url)` refers to a non-image file (by
4
+ // extension) and sits alone on its paragraph, we render a card-style
5
+ // block widget below the source line — big file glyph + extension
6
+ // badge + file name + size hint. This mirrors the image-block
7
+ // treatment so uploaded files get a real visual placeholder instead
8
+ // of disappearing into plain blue underlines.
9
+ //
10
+ // Raw markdown is the only source of truth; the widget is a read-only
11
+ // decoration. Its actions operate on the original link in the document.
12
+ import { ensureSyntaxTree, syntaxTree } from '@codemirror/language';
13
+ import { Prec, StateField, } from '@codemirror/state';
14
+ import { Decoration, EditorView, ViewPlugin, WidgetType, } from '@codemirror/view';
15
+ import { Download, File as FileIconLucide, Trash2 } from 'lucide-react';
16
+ import { appendMossIcon, mossLucideIcon, } from '../../core/icons';
17
+ import { readOnlyFacet } from '../../core/read-only';
18
+ import { treeGrowthEffect, treeProgressPlugin } from '../../core/tree-progress';
19
+ const DEFAULT_FILE_BLOCK_ICONS = {
20
+ file: mossLucideIcon(FileIconLucide, { size: 40 }),
21
+ download: mossLucideIcon(Download, { size: 16 }),
22
+ delete: mossLucideIcon(Trash2, { size: 16 }),
23
+ };
24
+ // Non-image file extensions that we'll turn into a file card. The URL
25
+ // regex alone isn't enough — we need to skip links that are obviously
26
+ // web pages (html/php/aspx) and keep those that look like downloads.
27
+ const FILE_EXT_RE = /\.(pdf|docx?|xlsx?|pptx?|odt|ods|odp|rtf|txt|md|csv|zip|rar|7z|tar|gz|bz2|json|yaml|yml|xml|svg|ps|ai|sketch|fig|mp3|wav|flac|aac|ogg|mp4|mov|avi|mkv|webm|exe|msi|dmg|pkg|deb|rpm|apk|ipa|epub|mobi|key|numbers|pages)$/i;
28
+ function isFileUrl(url) {
29
+ // Strip query + hash before testing extension.
30
+ const clean = url.split('?')[0].split('#')[0];
31
+ if (FILE_EXT_RE.test(clean))
32
+ return true;
33
+ // Object URLs (file uploads via URL.createObjectURL) always look
34
+ // like files — they have no extension in the URL string but are
35
+ // definitely attachments.
36
+ if (clean.startsWith('blob:'))
37
+ return true;
38
+ return false;
39
+ }
40
+ function isInlineImage(url) {
41
+ const clean = url.split('?')[0].split('#')[0];
42
+ return /\.(png|jpe?g|gif|webp|avif|svg|bmp|ico)$/i.test(clean);
43
+ }
44
+ // A file link is "alone on its line" when the line it lives on has
45
+ // no other visible content besides whitespace. That's the same
46
+ // convention image blocks use for their widget placement.
47
+ function linkIsAloneOnLine(state, linkFrom, linkTo) {
48
+ const line = state.doc.lineAt(linkFrom);
49
+ if (state.doc.lineAt(linkTo).from !== line.from)
50
+ return false; // multi-line → treat as inline
51
+ const before = state.doc.sliceString(line.from, linkFrom);
52
+ const after = state.doc.sliceString(linkTo, line.to);
53
+ return before.trim() === '' && after.trim() === '';
54
+ }
55
+ function parseFileLink(raw) {
56
+ const match = raw.match(/^\[([^\]]*)\]\(([^\s)"']+)(?:\s+["'][^)]*["'])?\)$/);
57
+ if (!match || !match[2] || !isFileUrl(match[2]))
58
+ return null;
59
+ return { label: match[1], url: match[2] };
60
+ }
61
+ const dimensionCache = new Map();
62
+ function fileRangeAtWidget(view, wrap, expectedUrl) {
63
+ const widgetPos = view.posAtDOM(wrap);
64
+ if (widgetPos < 0)
65
+ return null;
66
+ const line = view.state.doc.lineAt(Math.max(0, widgetPos - 1));
67
+ const tree = ensureSyntaxTree(view.state, view.state.doc.length, 200) ?? syntaxTree(view.state);
68
+ let result = null;
69
+ tree.iterate({
70
+ from: line.from,
71
+ to: line.to,
72
+ enter: (node) => {
73
+ if (result || node.name !== 'Link')
74
+ return;
75
+ const parsed = parseFileLink(view.state.doc.sliceString(node.from, node.to));
76
+ if (parsed?.url === expectedUrl && linkIsAloneOnLine(view.state, node.from, node.to)) {
77
+ result = { from: node.from, to: node.to };
78
+ }
79
+ },
80
+ });
81
+ return result;
82
+ }
83
+ function downloadFile(file) {
84
+ const link = document.createElement('a');
85
+ link.href = file.url;
86
+ link.download = file.label || 'download';
87
+ link.rel = 'noopener';
88
+ document.body.appendChild(link);
89
+ link.click();
90
+ link.remove();
91
+ }
92
+ function deleteFileBlock(view, wrap, expectedUrl) {
93
+ if (view.state.facet(readOnlyFacet))
94
+ return;
95
+ const range = fileRangeAtWidget(view, wrap, expectedUrl);
96
+ if (!range)
97
+ return;
98
+ const line = view.state.doc.lineAt(range.from);
99
+ let from = line.from;
100
+ let to = line.to;
101
+ // Remove the source line break as well. If the block has a blank line
102
+ // directly after it, consume that separator too so deletion does not
103
+ // leave an empty row where the block used to be.
104
+ if (line.to < view.state.doc.length) {
105
+ to = line.to + 1;
106
+ const nextLine = view.state.doc.lineAt(to);
107
+ if (nextLine.text.trim() === '') {
108
+ to = nextLine.to < view.state.doc.length ? nextLine.to + 1 : nextLine.to;
109
+ }
110
+ }
111
+ else if (line.from > 0) {
112
+ from = line.from - 1;
113
+ const previousLine = view.state.doc.lineAt(from);
114
+ if (previousLine.text.trim() === '') {
115
+ from = previousLine.from > 0 ? previousLine.from - 1 : previousLine.from;
116
+ }
117
+ }
118
+ view.dispatch({ changes: { from, to } });
119
+ }
120
+ class FileBlockWidget extends WidgetType {
121
+ constructor(label, url, ext, canDelete, icons) {
122
+ super();
123
+ Object.defineProperty(this, "label", {
124
+ enumerable: true,
125
+ configurable: true,
126
+ writable: true,
127
+ value: label
128
+ });
129
+ Object.defineProperty(this, "url", {
130
+ enumerable: true,
131
+ configurable: true,
132
+ writable: true,
133
+ value: url
134
+ });
135
+ Object.defineProperty(this, "ext", {
136
+ enumerable: true,
137
+ configurable: true,
138
+ writable: true,
139
+ value: ext
140
+ });
141
+ Object.defineProperty(this, "canDelete", {
142
+ enumerable: true,
143
+ configurable: true,
144
+ writable: true,
145
+ value: canDelete
146
+ });
147
+ Object.defineProperty(this, "icons", {
148
+ enumerable: true,
149
+ configurable: true,
150
+ writable: true,
151
+ value: icons
152
+ });
153
+ }
154
+ eq(other) {
155
+ return (other.label === this.label &&
156
+ other.url === this.url &&
157
+ other.ext === this.ext &&
158
+ other.canDelete === this.canDelete &&
159
+ other.icons.file === this.icons.file &&
160
+ other.icons.download === this.icons.download &&
161
+ other.icons.delete === this.icons.delete);
162
+ }
163
+ toDOM(view) {
164
+ const wrap = document.createElement('div');
165
+ wrap.className = 'cm-moss-file-block';
166
+ const preview = document.createElement('div');
167
+ preview.className = 'cm-moss-file-block-preview';
168
+ // For image URLs (jpg/png/webp…), show a thumbnail instead of the
169
+ // generic file glyph. Keeps the card layout consistent even when
170
+ // the URL resolves to an image but the link form `[]()` was used
171
+ // instead of `![]()`.
172
+ if (isInlineImage(this.url)) {
173
+ const img = document.createElement('img');
174
+ img.src = this.url;
175
+ img.alt = this.label;
176
+ img.loading = 'lazy';
177
+ const cached = dimensionCache.get(this.url);
178
+ if (cached) {
179
+ img.width = cached.w;
180
+ img.height = cached.h;
181
+ }
182
+ else {
183
+ img.addEventListener('load', () => {
184
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
185
+ dimensionCache.set(this.url, {
186
+ w: img.naturalWidth,
187
+ h: img.naturalHeight,
188
+ });
189
+ }
190
+ });
191
+ }
192
+ preview.classList.add('cm-moss-file-block-preview-image');
193
+ preview.appendChild(img);
194
+ }
195
+ else {
196
+ const glyph = document.createElement('span');
197
+ glyph.className = 'cm-moss-file-block-glyph';
198
+ appendMossIcon(glyph, this.icons.file);
199
+ const ext = document.createElement('span');
200
+ ext.className = 'cm-moss-file-block-ext';
201
+ ext.textContent = this.ext || 'FILE';
202
+ preview.classList.add('cm-moss-file-block-preview-file');
203
+ preview.append(glyph, ext);
204
+ }
205
+ const meta = document.createElement('div');
206
+ meta.className = 'cm-moss-file-block-meta';
207
+ const name = document.createElement('div');
208
+ name.className = 'cm-moss-file-block-name';
209
+ name.textContent = this.label;
210
+ const info = document.createElement('div');
211
+ info.className = 'cm-moss-file-block-info';
212
+ info.textContent = `${this.ext || 'FILE'} · attachment`;
213
+ meta.append(name, info);
214
+ wrap.append(preview, meta);
215
+ const actions = document.createElement('div');
216
+ actions.className = 'cm-moss-file-block-actions';
217
+ const stopEditorEvent = (event) => {
218
+ event.preventDefault();
219
+ event.stopPropagation();
220
+ };
221
+ const download = document.createElement('button');
222
+ download.type = 'button';
223
+ download.className = 'cm-moss-file-block-download';
224
+ appendMossIcon(download, this.icons.download);
225
+ download.setAttribute('aria-label', 'Download file');
226
+ download.title = 'Download file';
227
+ download.addEventListener('pointerdown', stopEditorEvent);
228
+ download.addEventListener('click', (event) => {
229
+ stopEditorEvent(event);
230
+ downloadFile({ label: this.label, url: this.url });
231
+ });
232
+ actions.appendChild(download);
233
+ if (this.canDelete) {
234
+ const remove = document.createElement('button');
235
+ remove.type = 'button';
236
+ remove.className = 'cm-moss-file-block-delete';
237
+ appendMossIcon(remove, this.icons.delete);
238
+ remove.setAttribute('aria-label', 'Delete file');
239
+ remove.title = 'Delete file';
240
+ remove.addEventListener('pointerdown', stopEditorEvent);
241
+ remove.addEventListener('click', (event) => {
242
+ stopEditorEvent(event);
243
+ deleteFileBlock(view, wrap, this.url);
244
+ });
245
+ actions.appendChild(remove);
246
+ }
247
+ wrap.appendChild(actions);
248
+ return wrap;
249
+ }
250
+ ignoreEvent() {
251
+ return true;
252
+ }
253
+ }
254
+ function buildFileSourceDecorations(view) {
255
+ const ranges = [];
256
+ const tree = ensureSyntaxTree(view.state, view.state.doc.length, 200) ?? syntaxTree(view.state);
257
+ tree.iterate({
258
+ enter: (node) => {
259
+ if (node.name !== 'Link')
260
+ return;
261
+ for (let p = node.node.parent; p; p = p.parent) {
262
+ if (p.name === 'Table' || p.name === 'Image' || p.name === 'Footnote')
263
+ return;
264
+ }
265
+ const file = parseFileLink(view.state.doc.sliceString(node.from, node.to));
266
+ if (!file || !linkIsAloneOnLine(view.state, node.from, node.to))
267
+ return;
268
+ ranges.push(Decoration.replace({}).range(node.from, node.to));
269
+ },
270
+ });
271
+ return Decoration.set(ranges, true);
272
+ }
273
+ const fileSourcePreviewPlugin = ViewPlugin.fromClass(class {
274
+ constructor(view) {
275
+ Object.defineProperty(this, "view", {
276
+ enumerable: true,
277
+ configurable: true,
278
+ writable: true,
279
+ value: view
280
+ });
281
+ Object.defineProperty(this, "decorations", {
282
+ enumerable: true,
283
+ configurable: true,
284
+ writable: true,
285
+ value: void 0
286
+ });
287
+ this.decorations = buildFileSourceDecorations(view);
288
+ }
289
+ update(update) {
290
+ const treeGrew = update.transactions.some((transaction) => transaction.effects.some((effect) => effect.is(treeGrowthEffect)));
291
+ if (update.docChanged || treeGrew) {
292
+ this.decorations = buildFileSourceDecorations(update.view);
293
+ }
294
+ }
295
+ }, { decorations: (plugin) => plugin.decorations });
296
+ function extOf(url, label) {
297
+ const cleanUrl = url.split('?')[0].split('#')[0];
298
+ const dot = cleanUrl.lastIndexOf('.');
299
+ if (dot > 0) {
300
+ return cleanUrl.slice(dot + 1).toUpperCase();
301
+ }
302
+ // Fall back to the extension in the label if the URL is a blob
303
+ // (which has no extension of its own).
304
+ const dotLabel = label.lastIndexOf('.');
305
+ if (dotLabel > 0) {
306
+ return label.slice(dotLabel + 1).toUpperCase();
307
+ }
308
+ return '';
309
+ }
310
+ function buildFileBlocks(state, config) {
311
+ const ranges = [];
312
+ const icons = {
313
+ ...DEFAULT_FILE_BLOCK_ICONS,
314
+ ...config.icons,
315
+ };
316
+ const tree = ensureSyntaxTree(state, state.doc.length, 200) ?? syntaxTree(state);
317
+ tree.iterate({
318
+ enter: (node) => {
319
+ if (node.name !== 'Link')
320
+ return;
321
+ // Skip links inside tables / images / other blocks.
322
+ for (let p = node.node.parent; p; p = p.parent) {
323
+ if (p.name === 'Table')
324
+ return;
325
+ if (p.name === 'Image')
326
+ return;
327
+ if (p.name === 'Footnote')
328
+ return;
329
+ }
330
+ const raw = state.doc.sliceString(node.from, node.to);
331
+ const file = parseFileLink(raw);
332
+ if (!file)
333
+ return;
334
+ if (!linkIsAloneOnLine(state, node.from, node.to))
335
+ return;
336
+ const ext = extOf(file.url, file.label);
337
+ const line = state.doc.lineAt(node.from);
338
+ ranges.push(Decoration.line({ class: 'cm-moss-file-block-source-line' }).range(line.from));
339
+ ranges.push(Decoration.widget({
340
+ widget: new FileBlockWidget(file.label, file.url, ext, !state.facet(readOnlyFacet), icons),
341
+ block: true,
342
+ side: 1,
343
+ }).range(line.to));
344
+ },
345
+ });
346
+ return Decoration.set(ranges, true);
347
+ }
348
+ function changeAffectsFileBlocks(tr, existing) {
349
+ let affected = false;
350
+ tr.changes.iterChanges((fromA, toA) => {
351
+ if (affected)
352
+ return;
353
+ existing.between(fromA, toA, () => {
354
+ affected = true;
355
+ return false;
356
+ });
357
+ });
358
+ if (affected)
359
+ return true;
360
+ const state = tr.state;
361
+ tr.changes.iterChanges((_fromA, _toA, fromB, toB) => {
362
+ if (affected)
363
+ return;
364
+ const startLine = state.doc.lineAt(fromB);
365
+ const endLine = toB > startLine.to ? state.doc.lineAt(toB) : startLine;
366
+ for (let n = startLine.number; n <= endLine.number; n++) {
367
+ if (state.doc.line(n).text.includes('](')) {
368
+ affected = true;
369
+ break;
370
+ }
371
+ }
372
+ });
373
+ return affected;
374
+ }
375
+ export function mossFileBlocks(config = {}) {
376
+ const fileBlocksField = StateField.define({
377
+ create: (state) => buildFileBlocks(state, config),
378
+ update(deco, tr) {
379
+ for (const effect of tr.effects) {
380
+ if (effect.is(treeGrowthEffect))
381
+ return buildFileBlocks(tr.state, config);
382
+ }
383
+ const readOnlyChanged = tr.startState.facet(readOnlyFacet) !== tr.state.facet(readOnlyFacet);
384
+ if (readOnlyChanged)
385
+ return buildFileBlocks(tr.state, config);
386
+ if (!tr.docChanged)
387
+ return deco;
388
+ const mapped = deco.map(tr.changes);
389
+ if (!changeAffectsFileBlocks(tr, deco))
390
+ return mapped;
391
+ return buildFileBlocks(tr.state, config);
392
+ },
393
+ provide: (f) => EditorView.decorations.from(f),
394
+ });
395
+ return [fileBlocksField, Prec.highest(fileSourcePreviewPlugin), treeProgressPlugin];
396
+ }
397
+ //# sourceMappingURL=index.js.map