@aicayzer/inkkit 0.0.2 → 0.0.3
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 +3 -3
- package/dist/callouts.d.ts +24 -0
- package/dist/callouts.js +296 -0
- package/dist/clipboard.js +9 -0
- package/dist/comments.d.ts +55 -0
- package/dist/comments.js +487 -0
- package/dist/dialect.js +8 -0
- package/dist/editor.d.ts +3 -2
- package/dist/editor.js +16 -2
- package/dist/inline-highlight.d.ts +6 -0
- package/dist/inline-highlight.js +112 -0
- package/dist/literals.d.ts +2 -0
- package/dist/literals.js +31 -4
- package/dist/paste.js +1 -0
- package/dist/preserve.js +24 -4
- package/dist/reference-clipboard.js +5 -1
- package/dist/style.css +90 -0
- package/package.json +5 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { commandsCtx, remarkCtx } from '@milkdown/kit/core';
|
|
2
|
+
import { markRule } from '@milkdown/kit/prose';
|
|
3
|
+
import { toggleMark } from '@milkdown/kit/prose/commands';
|
|
4
|
+
import { InputRule } from '@milkdown/kit/prose/inputrules';
|
|
5
|
+
import { EditorState, TextSelection } from '@milkdown/kit/prose/state';
|
|
6
|
+
import { $command, $inputRule, $markSchema, $remark, $useKeymap, } from '@milkdown/kit/utils';
|
|
7
|
+
import { highlightMark } from 'micromark-extension-highlight-mark';
|
|
8
|
+
import { highlightMarkFromMarkdown, highlightMarkToMarkdown, } from 'mdast-util-highlight-mark';
|
|
9
|
+
// The current serializer's attention handling encodes ambiguous whitespace and
|
|
10
|
+
// punctuation boundaries; the highlight provider predates that API.
|
|
11
|
+
const highlight = (node, _parent, state, info) => {
|
|
12
|
+
const exit = state.enter('phrasing');
|
|
13
|
+
const value = state.containerPhrasing({ type: 'root', children: [node] }, info);
|
|
14
|
+
exit();
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
highlight.attention = () => ({
|
|
18
|
+
construct: 'highlight',
|
|
19
|
+
markers: ['='],
|
|
20
|
+
sizes: [2],
|
|
21
|
+
});
|
|
22
|
+
highlight.peek = () => '=';
|
|
23
|
+
function remarkInlineHighlight() {
|
|
24
|
+
const data = this.data();
|
|
25
|
+
(data.micromarkExtensions ??= []).push(highlightMark());
|
|
26
|
+
(data.fromMarkdownExtensions ??= []).push(highlightMarkFromMarkdown);
|
|
27
|
+
(data.toMarkdownExtensions ??= []).push({
|
|
28
|
+
...highlightMarkToMarkdown,
|
|
29
|
+
handlers: { highlight },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export const remarkInlineHighlightPlugin = $remark('inkkitInlineHighlight', () => remarkInlineHighlight);
|
|
33
|
+
export const highlightSchema = $markSchema('highlight', () => ({
|
|
34
|
+
priority: 45,
|
|
35
|
+
parseDOM: [{ tag: 'mark' }],
|
|
36
|
+
toDOM: () => ['mark', 0],
|
|
37
|
+
parseMarkdown: {
|
|
38
|
+
match: (node) => node.type === 'highlight',
|
|
39
|
+
runner(state, node, type) {
|
|
40
|
+
state.openMark(type);
|
|
41
|
+
state.next(node.children);
|
|
42
|
+
state.closeMark(type);
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
toMarkdown: {
|
|
46
|
+
match: (mark) => mark.type.name === 'highlight',
|
|
47
|
+
runner(state, mark, node) {
|
|
48
|
+
if (node.isText && /^\s*$/.test(node.text ?? '')) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
state.withMark(mark, 'highlight');
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
export const toggleHighlightCommand = $command('ToggleHighlight', (ctx) => () => toggleMark(highlightSchema.type(ctx)));
|
|
56
|
+
export const highlightInputRule = $inputRule((ctx) => {
|
|
57
|
+
const pattern = /==([^\n]+)==$/;
|
|
58
|
+
const rule = markRule(pattern, highlightSchema.type(ctx));
|
|
59
|
+
const apply = rule.handler;
|
|
60
|
+
return new InputRule(pattern, (state, match, start, end) => {
|
|
61
|
+
const parentStart = state.selection.$from.start();
|
|
62
|
+
const prefix = state.selection.$from.parent.textBetween(0, start - parentStart);
|
|
63
|
+
const source = prefix + match[0];
|
|
64
|
+
const tree = ctx.get(remarkCtx).parse(source);
|
|
65
|
+
let valid = false;
|
|
66
|
+
const walk = (node) => {
|
|
67
|
+
if (node.type === 'highlight' &&
|
|
68
|
+
node.position?.start.offset === prefix.length &&
|
|
69
|
+
node.position.end.offset === source.length)
|
|
70
|
+
valid = true;
|
|
71
|
+
if ('children' in node)
|
|
72
|
+
node.children.forEach(walk);
|
|
73
|
+
};
|
|
74
|
+
walk(tree);
|
|
75
|
+
if (!valid)
|
|
76
|
+
return null;
|
|
77
|
+
const { from, to } = state.selection;
|
|
78
|
+
const typed = match[0].slice(from - start);
|
|
79
|
+
if (typed.length <= 1 && state.selection.empty)
|
|
80
|
+
return apply(state, match, start, end);
|
|
81
|
+
// Milkdown's mark rule expects only the final character to be absent.
|
|
82
|
+
// Prepare buffered input without applying plugins or dispatching twice.
|
|
83
|
+
const tr = state.tr.insertText(typed.slice(0, -1), from, to);
|
|
84
|
+
const preparedEnd = from + typed.length - 1;
|
|
85
|
+
const prepared = EditorState.create({
|
|
86
|
+
schema: state.schema,
|
|
87
|
+
doc: tr.doc,
|
|
88
|
+
selection: TextSelection.create(tr.doc, preparedEnd),
|
|
89
|
+
storedMarks: state.storedMarks,
|
|
90
|
+
});
|
|
91
|
+
const transformed = apply(prepared, match, start, preparedEnd);
|
|
92
|
+
if (!transformed)
|
|
93
|
+
return null;
|
|
94
|
+
transformed.steps.forEach((step) => tr.step(step));
|
|
95
|
+
tr.setSelection(TextSelection.create(tr.doc, transformed.selection.from, transformed.selection.to));
|
|
96
|
+
tr.setStoredMarks(transformed.storedMarks);
|
|
97
|
+
return tr;
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
export const highlightKeymap = $useKeymap('highlightKeymap', {
|
|
101
|
+
ToggleHighlight: {
|
|
102
|
+
shortcuts: ['Mod-Shift-h'],
|
|
103
|
+
command: (ctx) => () => ctx.get(commandsCtx).call(toggleHighlightCommand.key),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
export const inlineHighlight = [
|
|
107
|
+
remarkInlineHighlightPlugin,
|
|
108
|
+
highlightSchema,
|
|
109
|
+
toggleHighlightCommand,
|
|
110
|
+
highlightInputRule,
|
|
111
|
+
highlightKeymap,
|
|
112
|
+
].flat();
|
package/dist/literals.d.ts
CHANGED
package/dist/literals.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { $node, $remark } from '@milkdown/kit/utils';
|
|
2
|
+
import { commentSource, literalCommentSpans, } from './comments.js';
|
|
2
3
|
const unsupported = new Set(['html', 'image', 'imageReference']);
|
|
3
4
|
function restoreInlineBreaks(node) {
|
|
4
5
|
if ((node.type === 'paragraph' || node.type === 'heading') &&
|
|
@@ -68,7 +69,12 @@ export function createLiteralPreservation(images) {
|
|
|
68
69
|
// Definition indentation is restored by the footnote serializer.
|
|
69
70
|
for (let index = 0; index < depth; index++)
|
|
70
71
|
raw = raw.replace(/\r?\n(?: {4}|\t)/g, '\n');
|
|
71
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
type: 'inkkitLiteral',
|
|
74
|
+
value: raw,
|
|
75
|
+
comments: literalCommentSpans(raw),
|
|
76
|
+
position: node.position,
|
|
77
|
+
};
|
|
72
78
|
};
|
|
73
79
|
const blocks = [];
|
|
74
80
|
if (frontmatter)
|
|
@@ -105,6 +111,8 @@ export function createLiteralPreservation(images) {
|
|
|
105
111
|
});
|
|
106
112
|
continue;
|
|
107
113
|
}
|
|
114
|
+
if (node.type === 'inkkitLiteral')
|
|
115
|
+
node.comments = literalCommentSpans(node.value);
|
|
108
116
|
blocks.push(preserveBlock(node));
|
|
109
117
|
}
|
|
110
118
|
tree.children = blocks;
|
|
@@ -113,7 +121,7 @@ export function createLiteralPreservation(images) {
|
|
|
113
121
|
export const preserveLiterals = createLiteralPreservation(false);
|
|
114
122
|
export const literalBlock = $node('literal_markdown', () => ({
|
|
115
123
|
group: 'block',
|
|
116
|
-
content: 'text*',
|
|
124
|
+
content: '(text | comment_inline)*',
|
|
117
125
|
marks: '',
|
|
118
126
|
code: true,
|
|
119
127
|
defining: true,
|
|
@@ -129,14 +137,33 @@ export const literalBlock = $node('literal_markdown', () => ({
|
|
|
129
137
|
match: (node) => node.type === 'inkkitLiteral',
|
|
130
138
|
runner: (state, node, type) => {
|
|
131
139
|
state.openNode(type);
|
|
132
|
-
|
|
140
|
+
const value = String(node.value ?? '');
|
|
141
|
+
let cursor = 0;
|
|
142
|
+
for (const span of node.comments ?? []) {
|
|
143
|
+
if (span.start > cursor)
|
|
144
|
+
state.addText(value.slice(cursor, span.start));
|
|
145
|
+
state.openNode(state.schema.nodes.comment_inline, {
|
|
146
|
+
syntax: span.syntax,
|
|
147
|
+
block: Boolean(span.block),
|
|
148
|
+
});
|
|
149
|
+
if (span.value)
|
|
150
|
+
state.addText(span.value);
|
|
151
|
+
state.closeNode();
|
|
152
|
+
cursor = span.end;
|
|
153
|
+
}
|
|
154
|
+
if (cursor < value.length)
|
|
155
|
+
state.addText(value.slice(cursor));
|
|
133
156
|
state.closeNode();
|
|
134
157
|
},
|
|
135
158
|
},
|
|
136
159
|
toMarkdown: {
|
|
137
160
|
match: (node) => node.type.name === 'literal_markdown',
|
|
138
161
|
runner: (state, node) => {
|
|
139
|
-
|
|
162
|
+
let source = '';
|
|
163
|
+
node.forEach((child) => {
|
|
164
|
+
source += child.isText ? child.text : commentSource(child);
|
|
165
|
+
});
|
|
166
|
+
state.addNode('html', undefined, source);
|
|
140
167
|
},
|
|
141
168
|
},
|
|
142
169
|
}));
|
package/dist/paste.js
CHANGED
package/dist/preserve.js
CHANGED
|
@@ -10,7 +10,7 @@ function ownShape(node) {
|
|
|
10
10
|
? undefined
|
|
11
11
|
: value);
|
|
12
12
|
}
|
|
13
|
-
function retainText(raw, before, after) {
|
|
13
|
+
function retainText(raw, before, after, quoteIndent = false) {
|
|
14
14
|
before = before.replace(/\r\n?/g, '\n');
|
|
15
15
|
after = after.replace(/\r\n?/g, '\n');
|
|
16
16
|
let prefix = 0;
|
|
@@ -26,6 +26,14 @@ function retainText(raw, before, after) {
|
|
|
26
26
|
const offsets = [0];
|
|
27
27
|
let decoded = '';
|
|
28
28
|
for (let index = 0; index < raw.length;) {
|
|
29
|
+
if (index > 0 && raw[index - 1] === '\n') {
|
|
30
|
+
const gutter = (quoteIndent ? /^(?:[ \t]*>[ \t]*)+/ : /^(?:[ \t]*>[ \t]?)+/).exec(raw.slice(index));
|
|
31
|
+
if (gutter) {
|
|
32
|
+
index += gutter[0].length;
|
|
33
|
+
offsets[offsets.length - 1] = index;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
29
37
|
const escaped = /^\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/.exec(raw.slice(index));
|
|
30
38
|
const entity = /^&(?:#[xX][\da-fA-F]+|#\d+|[A-Za-z][A-Za-z\d]+);/.exec(raw.slice(index));
|
|
31
39
|
let value;
|
|
@@ -53,11 +61,22 @@ function retainText(raw, before, after) {
|
|
|
53
61
|
for (let part = 0; part < value.length; part++)
|
|
54
62
|
offsets.push(index);
|
|
55
63
|
}
|
|
56
|
-
if (decoded !== before)
|
|
57
|
-
|
|
64
|
+
if (decoded !== before) {
|
|
65
|
+
// Continuation indentation is excluded from a quoted paragraph's text.
|
|
66
|
+
return quoteIndent ? after : retainText(raw, before, after, true);
|
|
67
|
+
}
|
|
58
68
|
const inserted = after
|
|
59
69
|
.slice(prefix, after.length - suffix)
|
|
60
|
-
.
|
|
70
|
+
.split('')
|
|
71
|
+
.map((character, index) => {
|
|
72
|
+
const at = prefix + index;
|
|
73
|
+
const delimiter = (character === '%' || character === '=') &&
|
|
74
|
+
(after[at - 1] === character || after[at + 1] === character);
|
|
75
|
+
return delimiter || /[\\`*_[\]<>]/.test(character)
|
|
76
|
+
? `\\${character}`
|
|
77
|
+
: character;
|
|
78
|
+
})
|
|
79
|
+
.join('');
|
|
61
80
|
return (raw.slice(0, offsets[prefix]) +
|
|
62
81
|
inserted +
|
|
63
82
|
raw.slice(offsets[before.length - suffix]));
|
|
@@ -218,6 +237,7 @@ function semanticSignature(doc) {
|
|
|
218
237
|
[
|
|
219
238
|
'referenceContent',
|
|
220
239
|
'referenceType',
|
|
240
|
+
'listPlaceholder',
|
|
221
241
|
'marker',
|
|
222
242
|
'id',
|
|
223
243
|
'label',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment } from '@milkdown/kit/prose/model';
|
|
2
2
|
import { serialize } from './dialect.js';
|
|
3
3
|
import { normaliseLabel } from './references.js';
|
|
4
|
+
import { shareableFragment } from './comments.js';
|
|
4
5
|
function key(node) {
|
|
5
6
|
if (node.type.name === 'reference_definition')
|
|
6
7
|
return `link:${node.attrs.identifier}`;
|
|
@@ -70,6 +71,8 @@ export function selectionMarkdown(ctx, doc, content) {
|
|
|
70
71
|
return serialize(ctx, doc.type.create(null, selectionContent(doc, content)));
|
|
71
72
|
}
|
|
72
73
|
export function referenceMetadata(ctx, doc, content) {
|
|
74
|
+
doc = doc.copy(shareableFragment(doc.content));
|
|
75
|
+
content = shareableFragment(content);
|
|
73
76
|
let references = false, index = 0;
|
|
74
77
|
const rewrite = (node) => {
|
|
75
78
|
if (key(node) ||
|
|
@@ -121,7 +124,8 @@ function allLabels(doc) {
|
|
|
121
124
|
}
|
|
122
125
|
const rawReferences = /(?<![\\!])\[([^\]]+)\](?:\[([^\]]*)\])?(?!\()/g;
|
|
123
126
|
function isCode(node) {
|
|
124
|
-
return (node.type.name
|
|
127
|
+
return (node.type.name.startsWith('comment_') ||
|
|
128
|
+
node.type.name === 'code_block' ||
|
|
125
129
|
node.marks.some((mark) => mark.type.name === 'inlineCode'));
|
|
126
130
|
}
|
|
127
131
|
// Rename only the incoming document. Definitions belong to the insertion's undo transaction.
|
package/dist/style.css
CHANGED
|
@@ -562,3 +562,93 @@ body {
|
|
|
562
562
|
font-size: 0.8em;
|
|
563
563
|
opacity: 0.7;
|
|
564
564
|
}
|
|
565
|
+
|
|
566
|
+
.ProseMirror mark {
|
|
567
|
+
background: color-mix(in srgb, #e5c041 38%, transparent);
|
|
568
|
+
color: inherit;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.ProseMirror .inkkit-callout {
|
|
572
|
+
--callout-color: var(--accent);
|
|
573
|
+
border-inline-start: 3px solid var(--callout-color);
|
|
574
|
+
padding: 0.65rem 1rem;
|
|
575
|
+
background: color-mix(in srgb, var(--callout-color) 7%, transparent);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.ProseMirror [data-inkkit-callout='TIP'] {
|
|
579
|
+
--callout-color: #30a46c;
|
|
580
|
+
}
|
|
581
|
+
.ProseMirror [data-inkkit-callout='IMPORTANT'] {
|
|
582
|
+
--callout-color: #8e4ec6;
|
|
583
|
+
}
|
|
584
|
+
.ProseMirror [data-inkkit-callout='WARNING'] {
|
|
585
|
+
--callout-color: #d28b00;
|
|
586
|
+
}
|
|
587
|
+
.ProseMirror [data-inkkit-callout='CAUTION'] {
|
|
588
|
+
--callout-color: #e5484d;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.inkkit-callout-header {
|
|
592
|
+
display: flex;
|
|
593
|
+
align-items: baseline;
|
|
594
|
+
gap: 0.75rem;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
[data-inkkit-callout-toggle] {
|
|
598
|
+
color: var(--muted);
|
|
599
|
+
background: transparent;
|
|
600
|
+
border: 0;
|
|
601
|
+
padding: 0.25rem;
|
|
602
|
+
cursor: pointer;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.inkkit-callout[data-inkkit-folded='true'] > .inkkit-callout-body {
|
|
606
|
+
display: none;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.inkkit-comment {
|
|
610
|
+
display: none;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
[data-inkkit-comments-visible='true'] .inkkit-comment {
|
|
614
|
+
display: inline;
|
|
615
|
+
white-space: pre-wrap;
|
|
616
|
+
font-family: var(--mono);
|
|
617
|
+
color: var(--muted);
|
|
618
|
+
background: var(--code-bg);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
[data-inkkit-comments-visible='true'] .inkkit-comment-block {
|
|
622
|
+
display: block;
|
|
623
|
+
padding: 0.5rem;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
@media print {
|
|
627
|
+
html,
|
|
628
|
+
body,
|
|
629
|
+
#editor,
|
|
630
|
+
.ProseMirror {
|
|
631
|
+
height: auto;
|
|
632
|
+
min-height: 0;
|
|
633
|
+
overflow: visible;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.milkdown-table-block .table-wrapper,
|
|
637
|
+
.ProseMirror pre {
|
|
638
|
+
overflow: visible;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.inkkit-comment,
|
|
642
|
+
[data-inkkit-comments-visible='true'] .inkkit-comment,
|
|
643
|
+
[data-inkkit-callout-toggle],
|
|
644
|
+
.ProseMirror pre .copy,
|
|
645
|
+
.milkdown-table-block .handle,
|
|
646
|
+
.milkdown-table-block .button-group,
|
|
647
|
+
.milkdown-table-block .line-handle {
|
|
648
|
+
display: none !important;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.inkkit-callout[data-inkkit-folded='true'] > .inkkit-callout-body {
|
|
652
|
+
display: block !important;
|
|
653
|
+
}
|
|
654
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aicayzer/inkkit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "A preserving Markdown editor for offline web views and the web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -47,18 +47,22 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@milkdown/kit": "7.22.1",
|
|
49
49
|
"lowlight": "^3.3.0",
|
|
50
|
+
"mdast-util-from-markdown": "^2.1.0",
|
|
50
51
|
"mdast-util-gfm-autolink-literal": "^2.0.1",
|
|
51
52
|
"mdast-util-gfm-footnote": "^2.1.0",
|
|
52
53
|
"mdast-util-gfm-strikethrough": "^2.0.0",
|
|
53
54
|
"mdast-util-gfm-table": "^2.0.0",
|
|
54
55
|
"mdast-util-gfm-task-list-item": "^2.0.0",
|
|
56
|
+
"mdast-util-highlight-mark": "1.2.2",
|
|
55
57
|
"mdast-util-to-markdown": "2.2.0",
|
|
56
58
|
"micromark-extension-gfm-autolink-literal": "^2.1.0",
|
|
57
59
|
"micromark-extension-gfm-footnote": "^2.1.0",
|
|
58
60
|
"micromark-extension-gfm-strikethrough": "^2.1.0",
|
|
59
61
|
"micromark-extension-gfm-table": "^2.1.1",
|
|
60
62
|
"micromark-extension-gfm-task-list-item": "^2.1.0",
|
|
63
|
+
"micromark-extension-highlight-mark": "1.2.0",
|
|
61
64
|
"micromark-util-normalize-identifier": "^2.0.1",
|
|
65
|
+
"micromark-util-types": "^2.0.3",
|
|
62
66
|
"prosemirror-search": "^1.1.1"
|
|
63
67
|
},
|
|
64
68
|
"devDependencies": {
|