@aicayzer/inkkit 0.0.2 → 0.0.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/README.md +3 -3
- package/dist/callouts.d.ts +24 -0
- package/dist/callouts.js +296 -0
- package/dist/clipboard.d.ts +1 -1
- package/dist/clipboard.js +63 -2
- package/dist/code-fences.d.ts +8 -0
- package/dist/code-fences.js +166 -0
- package/dist/comments.d.ts +55 -0
- package/dist/comments.js +487 -0
- package/dist/dialect.js +12 -1
- package/dist/editor.d.ts +3 -2
- package/dist/editor.js +46 -9
- package/dist/index.d.ts +1 -1
- 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/mermaid.d.ts +8 -0
- package/dist/mermaid.js +216 -0
- package/dist/paste.js +19 -2
- package/dist/preserve.js +66 -9
- package/dist/reference-clipboard.js +5 -1
- package/dist/style.css +106 -0
- package/dist/types.d.ts +7 -1
- package/package.json +7 -1
package/dist/preserve.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parserCtx, remarkCtx } from '@milkdown/kit/core';
|
|
2
2
|
import { serialize } from './dialect.js';
|
|
3
|
+
import { restoreCodeEndings, finalCodeEnding } from './code-fences.js';
|
|
3
4
|
function signature(node) {
|
|
4
5
|
return JSON.stringify(node, (key, value) => ['position', 'data', 'url', 'title', 'marker'].includes(key)
|
|
5
6
|
? undefined
|
|
@@ -10,7 +11,7 @@ function ownShape(node) {
|
|
|
10
11
|
? undefined
|
|
11
12
|
: value);
|
|
12
13
|
}
|
|
13
|
-
function retainText(raw, before, after) {
|
|
14
|
+
function retainText(raw, before, after, quoteIndent = false) {
|
|
14
15
|
before = before.replace(/\r\n?/g, '\n');
|
|
15
16
|
after = after.replace(/\r\n?/g, '\n');
|
|
16
17
|
let prefix = 0;
|
|
@@ -26,6 +27,14 @@ function retainText(raw, before, after) {
|
|
|
26
27
|
const offsets = [0];
|
|
27
28
|
let decoded = '';
|
|
28
29
|
for (let index = 0; index < raw.length;) {
|
|
30
|
+
if (index > 0 && raw[index - 1] === '\n') {
|
|
31
|
+
const gutter = (quoteIndent ? /^(?:[ \t]*>[ \t]*)+/ : /^(?:[ \t]*>[ \t]?)+/).exec(raw.slice(index));
|
|
32
|
+
if (gutter) {
|
|
33
|
+
index += gutter[0].length;
|
|
34
|
+
offsets[offsets.length - 1] = index;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
29
38
|
const escaped = /^\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/.exec(raw.slice(index));
|
|
30
39
|
const entity = /^&(?:#[xX][\da-fA-F]+|#\d+|[A-Za-z][A-Za-z\d]+);/.exec(raw.slice(index));
|
|
31
40
|
let value;
|
|
@@ -53,11 +62,22 @@ function retainText(raw, before, after) {
|
|
|
53
62
|
for (let part = 0; part < value.length; part++)
|
|
54
63
|
offsets.push(index);
|
|
55
64
|
}
|
|
56
|
-
if (decoded !== before)
|
|
57
|
-
|
|
65
|
+
if (decoded !== before) {
|
|
66
|
+
// Continuation indentation is excluded from a quoted paragraph's text.
|
|
67
|
+
return quoteIndent ? after : retainText(raw, before, after, true);
|
|
68
|
+
}
|
|
58
69
|
const inserted = after
|
|
59
70
|
.slice(prefix, after.length - suffix)
|
|
60
|
-
.
|
|
71
|
+
.split('')
|
|
72
|
+
.map((character, index) => {
|
|
73
|
+
const at = prefix + index;
|
|
74
|
+
const delimiter = (character === '%' || character === '=') &&
|
|
75
|
+
(after[at - 1] === character || after[at + 1] === character);
|
|
76
|
+
return delimiter || /[\\`*_[\]<>]/.test(character)
|
|
77
|
+
? `\\${character}`
|
|
78
|
+
: character;
|
|
79
|
+
})
|
|
80
|
+
.join('');
|
|
61
81
|
return (raw.slice(0, offsets[prefix]) +
|
|
62
82
|
inserted +
|
|
63
83
|
raw.slice(offsets[before.length - suffix]));
|
|
@@ -96,6 +116,40 @@ function retainSource(before, after, source, canonical, lineEnding) {
|
|
|
96
116
|
if (start == null || end == null || newStart == null || newEnd == null)
|
|
97
117
|
throw new PreservationError('A source token has no location');
|
|
98
118
|
const raw = source.slice(start, end);
|
|
119
|
+
if (before.type === 'code' &&
|
|
120
|
+
after.type === 'code' &&
|
|
121
|
+
before.lang === after.lang) {
|
|
122
|
+
const opening = /^([ \t>]*)(`{3,}|~{3,})([^\r\n]*)(\r?\n)/.exec(raw);
|
|
123
|
+
if (opening) {
|
|
124
|
+
const lines = raw.slice(opening[0].length).split(/\r?\n/);
|
|
125
|
+
const closing = /^([ \t>]*)(`{3,}|~{3,})([ \t]*)$/.exec(lines.at(-1) ?? '');
|
|
126
|
+
const originalLine = (before.value ?? '').replace(/\r\n?/g, '\n').split('\n')[0] ?? '';
|
|
127
|
+
const first = lines[0] ?? '';
|
|
128
|
+
const gutter = first.endsWith(originalLine)
|
|
129
|
+
? first.slice(0, first.length - originalLine.length)
|
|
130
|
+
: opening[1];
|
|
131
|
+
const body = (after.value ?? '').replace(/\r\n?/g, '\n').split('\n');
|
|
132
|
+
const character = opening[2][0];
|
|
133
|
+
let length = opening[2].length;
|
|
134
|
+
for (const line of body) {
|
|
135
|
+
const run = new RegExp(`^ {0,3}(${character}+)[ \\t]*$`).exec(line);
|
|
136
|
+
if (run)
|
|
137
|
+
length = Math.max(length, run[1].length + 1);
|
|
138
|
+
}
|
|
139
|
+
const fence = character.repeat(length);
|
|
140
|
+
return (opening[1] +
|
|
141
|
+
fence +
|
|
142
|
+
opening[3] +
|
|
143
|
+
lineEnding +
|
|
144
|
+
body.map((line) => gutter + line).join(lineEnding) +
|
|
145
|
+
(closing
|
|
146
|
+
? lineEnding +
|
|
147
|
+
closing[1] +
|
|
148
|
+
character.repeat(Math.max(length, closing[2].length)) +
|
|
149
|
+
closing[3]
|
|
150
|
+
: ''));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
99
153
|
if (before.type === 'definition' &&
|
|
100
154
|
after.type === 'definition' &&
|
|
101
155
|
before.label === after.label &&
|
|
@@ -174,7 +228,7 @@ export class PreservationError extends Error {
|
|
|
174
228
|
}
|
|
175
229
|
}
|
|
176
230
|
function form(text) {
|
|
177
|
-
return text.replace(/\n+$/, '');
|
|
231
|
+
return text.replace(/\r\n?/g, '\n').replace(/\n+$/, '');
|
|
178
232
|
}
|
|
179
233
|
function blockForm(ctx, node) {
|
|
180
234
|
// An empty document is empty Markdown, but an empty block inside a document
|
|
@@ -200,7 +254,7 @@ function semanticSignature(doc) {
|
|
|
200
254
|
continue;
|
|
201
255
|
const token = {
|
|
202
256
|
...child,
|
|
203
|
-
text,
|
|
257
|
+
text: text.replace(/\r\n?/g, '\n'),
|
|
204
258
|
marks: index === 1 ? child.marks : undefined,
|
|
205
259
|
};
|
|
206
260
|
const previous = tokens.at(-1);
|
|
@@ -218,10 +272,12 @@ function semanticSignature(doc) {
|
|
|
218
272
|
[
|
|
219
273
|
'referenceContent',
|
|
220
274
|
'referenceType',
|
|
275
|
+
'listPlaceholder',
|
|
221
276
|
'marker',
|
|
222
277
|
'id',
|
|
223
278
|
'label',
|
|
224
279
|
'spread',
|
|
280
|
+
'authoredFence',
|
|
225
281
|
].includes(key)
|
|
226
282
|
? undefined
|
|
227
283
|
: value));
|
|
@@ -378,16 +434,17 @@ export class Preservation {
|
|
|
378
434
|
output += retainSource(block.ast, positionalNode(currentRawTree, currentTree.children[index]), body, canonical, this.lineEnding);
|
|
379
435
|
}
|
|
380
436
|
else
|
|
381
|
-
output += text.replaceAll('\n', this.lineEnding);
|
|
437
|
+
output += restoreCodeEndings(this.ctx, doc.type.create(null, [doc.child(index)]), text.replaceAll('\n', this.lineEnding));
|
|
382
438
|
previous = position;
|
|
383
439
|
});
|
|
384
440
|
if (previous === this.blocks.length - 1 && this.blocks.length)
|
|
385
441
|
output += this.source.slice(this.blocks[previous].end);
|
|
386
442
|
else if (output)
|
|
387
|
-
output += this.lineEnding;
|
|
443
|
+
output += finalCodeEnding(doc) ?? this.lineEnding;
|
|
388
444
|
const parse = this.ctx.get(parserCtx);
|
|
389
445
|
const reopened = parse(output);
|
|
390
|
-
if (serialize(this.ctx, reopened) ===
|
|
446
|
+
if (serialize(this.ctx, reopened).replace(/\r\n?/g, '\n') ===
|
|
447
|
+
canonical.replace(/\r\n?/g, '\n') &&
|
|
391
448
|
semanticSignature(reopened) === semanticSignature(doc))
|
|
392
449
|
return output;
|
|
393
450
|
throw new PreservationError('The edited Markdown cannot be reopened without changing its content');
|
|
@@ -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,109 @@ 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
|
+
}
|
|
655
|
+
/* Diagram previews are presentation; the authored code remains editable. */
|
|
656
|
+
.inkkit-mermaid-preview {
|
|
657
|
+
padding: 16px;
|
|
658
|
+
overflow: auto;
|
|
659
|
+
background: #fff;
|
|
660
|
+
color: #222;
|
|
661
|
+
}
|
|
662
|
+
.inkkit-mermaid-preview svg {
|
|
663
|
+
display: block;
|
|
664
|
+
max-width: 100%;
|
|
665
|
+
height: auto;
|
|
666
|
+
}
|
|
667
|
+
.inkkit-mermaid-preview[data-state='error'] {
|
|
668
|
+
color: #9b2424;
|
|
669
|
+
font-size: 0.9em;
|
|
670
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface DocumentSnapshot extends DocumentInput {
|
|
|
14
14
|
revision: number;
|
|
15
15
|
dirty: boolean;
|
|
16
16
|
}
|
|
17
|
-
export type EditorErrorCode = 'not-ready' | 'stale-document' | 'composition' | 'operation-pending' | 'destroyed' | 'preservation' | 'image-unavailable';
|
|
17
|
+
export type EditorErrorCode = 'not-ready' | 'stale-document' | 'composition' | 'operation-pending' | 'destroyed' | 'preservation' | 'image-unavailable' | 'diagram-unavailable';
|
|
18
18
|
export declare class InkKitError extends Error {
|
|
19
19
|
readonly code: EditorErrorCode;
|
|
20
20
|
constructor(code: EditorErrorCode, message: string);
|
|
@@ -57,4 +57,10 @@ export interface ClipboardOutput {
|
|
|
57
57
|
html: string;
|
|
58
58
|
markdown: string;
|
|
59
59
|
images: ClipboardImage[];
|
|
60
|
+
diagrams?: ClipboardDiagram[];
|
|
61
|
+
}
|
|
62
|
+
export interface ClipboardDiagram {
|
|
63
|
+
source: string;
|
|
64
|
+
image?: PortableImage;
|
|
65
|
+
error?: string;
|
|
60
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aicayzer/inkkit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "A preserving Markdown editor for offline web views and the web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -46,19 +46,25 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@milkdown/kit": "7.22.1",
|
|
49
|
+
"dompurify": "3.4.16",
|
|
49
50
|
"lowlight": "^3.3.0",
|
|
51
|
+
"mdast-util-from-markdown": "^2.1.0",
|
|
50
52
|
"mdast-util-gfm-autolink-literal": "^2.0.1",
|
|
51
53
|
"mdast-util-gfm-footnote": "^2.1.0",
|
|
52
54
|
"mdast-util-gfm-strikethrough": "^2.0.0",
|
|
53
55
|
"mdast-util-gfm-table": "^2.0.0",
|
|
54
56
|
"mdast-util-gfm-task-list-item": "^2.0.0",
|
|
57
|
+
"mdast-util-highlight-mark": "1.2.2",
|
|
55
58
|
"mdast-util-to-markdown": "2.2.0",
|
|
59
|
+
"mermaid": "12.1.0",
|
|
56
60
|
"micromark-extension-gfm-autolink-literal": "^2.1.0",
|
|
57
61
|
"micromark-extension-gfm-footnote": "^2.1.0",
|
|
58
62
|
"micromark-extension-gfm-strikethrough": "^2.1.0",
|
|
59
63
|
"micromark-extension-gfm-table": "^2.1.1",
|
|
60
64
|
"micromark-extension-gfm-task-list-item": "^2.1.0",
|
|
65
|
+
"micromark-extension-highlight-mark": "1.2.0",
|
|
61
66
|
"micromark-util-normalize-identifier": "^2.0.1",
|
|
67
|
+
"micromark-util-types": "^2.0.3",
|
|
62
68
|
"prosemirror-search": "^1.1.1"
|
|
63
69
|
},
|
|
64
70
|
"devDependencies": {
|