tiptap-extension-code-block-shiki 1.0.0 → 1.2.0
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
CHANGED
|
@@ -82,6 +82,26 @@ html.dark .tiptap .shiki span {
|
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
### Custom Theme Support
|
|
86
|
+
|
|
87
|
+
You can optionally supply your own custom themes
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import type { ThemeRegistration } from 'shiki'
|
|
91
|
+
|
|
92
|
+
const myTheme: ThemeRegistration = {
|
|
93
|
+
name: 'my-theme',
|
|
94
|
+
type: 'dark',
|
|
95
|
+
colors: { 'editor.background': '#1e1e2e' },
|
|
96
|
+
tokenColors: [/* ... */]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
CodeBlockShiki.configure({
|
|
100
|
+
defaultTheme: 'my-theme',
|
|
101
|
+
customThemes: [myTheme],
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
85
105
|
## Demo
|
|
86
106
|
|
|
87
107
|
I posted a small screen recording here: https://mastodon.social/@timomeh/112282962825285237
|
|
@@ -102,6 +122,10 @@ Optionally specify themes for light and dark mode. See https://shiki.matsu.io/gu
|
|
|
102
122
|
|
|
103
123
|
Which language to use, when no language was provided. See https://shiki.style/languages.
|
|
104
124
|
|
|
125
|
+
### `customThemes`
|
|
126
|
+
|
|
127
|
+
Register custom themes. See https://shiki.style/guide/load-theme#load-custom-themes.
|
|
128
|
+
|
|
105
129
|
## Notes
|
|
106
130
|
|
|
107
131
|
### Lazy loading
|
|
@@ -2,6 +2,7 @@ import { BundledLanguage } from 'shiki';
|
|
|
2
2
|
import { BundledTheme } from 'shiki';
|
|
3
3
|
import { CodeBlockOptions } from '@tiptap/extension-code-block';
|
|
4
4
|
import { Node as Node_2 } from '@tiptap/core';
|
|
5
|
+
import { ThemeRegistration } from 'shiki';
|
|
5
6
|
|
|
6
7
|
declare const CodeBlockShiki: Node_2<CodeBlockShikiOptions, any>;
|
|
7
8
|
export { CodeBlockShiki }
|
|
@@ -9,11 +10,12 @@ export default CodeBlockShiki;
|
|
|
9
10
|
|
|
10
11
|
export declare interface CodeBlockShikiOptions extends CodeBlockOptions {
|
|
11
12
|
defaultLanguage: BundledLanguage | null | undefined;
|
|
12
|
-
defaultTheme: BundledTheme;
|
|
13
|
+
defaultTheme: BundledTheme | (string & {});
|
|
13
14
|
themes: {
|
|
14
15
|
light: BundledTheme;
|
|
15
16
|
dark: BundledTheme;
|
|
16
17
|
} | null | undefined;
|
|
18
|
+
customThemes: ThemeRegistration[] | null | undefined;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export { }
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Node as
|
|
2
|
-
import { Plugin as
|
|
3
|
-
import { DecorationSet as F, Decoration as
|
|
4
|
-
import { bundledThemes as E, bundledLanguages as
|
|
5
|
-
var
|
|
1
|
+
import { Node as O, textblockTypeInputRule as M, mergeAttributes as R, findChildren as L } from "@tiptap/core";
|
|
2
|
+
import { Plugin as z, PluginKey as I, TextSelection as $, Selection as j } from "@tiptap/pm/state";
|
|
3
|
+
import { DecorationSet as F, Decoration as P } from "@tiptap/pm/view";
|
|
4
|
+
import { bundledThemes as E, bundledLanguages as W, createHighlighter as K } from "shiki";
|
|
5
|
+
var A = 4, U = /^```([a-z]+)?[\s\n]$/, V = /^~~~([a-z]+)?[\s\n]$/, J = O.create({
|
|
6
6
|
name: "codeBlock",
|
|
7
7
|
addOptions() {
|
|
8
8
|
return {
|
|
@@ -11,7 +11,7 @@ var B = 4, U = /^```([a-z]+)?[\s\n]$/, K = /^~~~([a-z]+)?[\s\n]$/, V = _.create(
|
|
|
11
11
|
exitOnArrowDown: !0,
|
|
12
12
|
defaultLanguage: null,
|
|
13
13
|
enableTabIndentation: !1,
|
|
14
|
-
tabSize:
|
|
14
|
+
tabSize: A,
|
|
15
15
|
HTMLAttributes: {}
|
|
16
16
|
};
|
|
17
17
|
},
|
|
@@ -25,12 +25,12 @@ var B = 4, U = /^```([a-z]+)?[\s\n]$/, K = /^~~~([a-z]+)?[\s\n]$/, V = _.create(
|
|
|
25
25
|
language: {
|
|
26
26
|
default: this.options.defaultLanguage,
|
|
27
27
|
parseHTML: (e) => {
|
|
28
|
-
var
|
|
28
|
+
var t;
|
|
29
29
|
const { languageClassPrefix: i } = this.options;
|
|
30
30
|
if (!i)
|
|
31
31
|
return null;
|
|
32
|
-
const
|
|
33
|
-
return
|
|
32
|
+
const c = [...((t = e.firstElementChild) == null ? void 0 : t.classList) || []].filter((r) => r.startsWith(i)).map((r) => r.replace(i, ""))[0];
|
|
33
|
+
return c || null;
|
|
34
34
|
},
|
|
35
35
|
rendered: !1
|
|
36
36
|
}
|
|
@@ -44,10 +44,10 @@ var B = 4, U = /^```([a-z]+)?[\s\n]$/, K = /^~~~([a-z]+)?[\s\n]$/, V = _.create(
|
|
|
44
44
|
}
|
|
45
45
|
];
|
|
46
46
|
},
|
|
47
|
-
renderHTML({ node: e, HTMLAttributes:
|
|
47
|
+
renderHTML({ node: e, HTMLAttributes: t }) {
|
|
48
48
|
return [
|
|
49
49
|
"pre",
|
|
50
|
-
R(this.options.HTMLAttributes,
|
|
50
|
+
R(this.options.HTMLAttributes, t),
|
|
51
51
|
[
|
|
52
52
|
"code",
|
|
53
53
|
{
|
|
@@ -57,10 +57,28 @@ var B = 4, U = /^```([a-z]+)?[\s\n]$/, K = /^~~~([a-z]+)?[\s\n]$/, V = _.create(
|
|
|
57
57
|
]
|
|
58
58
|
];
|
|
59
59
|
},
|
|
60
|
+
markdownTokenName: "code",
|
|
61
|
+
parseMarkdown: (e, t) => {
|
|
62
|
+
var i, o;
|
|
63
|
+
return ((i = e.raw) == null ? void 0 : i.startsWith("```")) === !1 && ((o = e.raw) == null ? void 0 : o.startsWith("~~~")) === !1 && e.codeBlockStyle !== "indented" ? [] : t.createNode(
|
|
64
|
+
"codeBlock",
|
|
65
|
+
{ language: e.lang || null },
|
|
66
|
+
e.text ? [t.createTextNode(e.text)] : []
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
renderMarkdown: (e, t) => {
|
|
70
|
+
var i;
|
|
71
|
+
let o = "";
|
|
72
|
+
const n = ((i = e.attrs) == null ? void 0 : i.language) || "";
|
|
73
|
+
return e.content ? o = [`\`\`\`${n}`, t.renderChildren(e.content), "```"].join(`
|
|
74
|
+
`) : o = `\`\`\`${n}
|
|
75
|
+
|
|
76
|
+
\`\`\``, o;
|
|
77
|
+
},
|
|
60
78
|
addCommands() {
|
|
61
79
|
return {
|
|
62
|
-
setCodeBlock: (e) => ({ commands:
|
|
63
|
-
toggleCodeBlock: (e) => ({ commands:
|
|
80
|
+
setCodeBlock: (e) => ({ commands: t }) => t.setNode(this.name, e),
|
|
81
|
+
toggleCodeBlock: (e) => ({ commands: t }) => t.toggleNode(this.name, "paragraph", e)
|
|
64
82
|
};
|
|
65
83
|
},
|
|
66
84
|
addKeyboardShortcuts() {
|
|
@@ -68,103 +86,103 @@ var B = 4, U = /^```([a-z]+)?[\s\n]$/, K = /^~~~([a-z]+)?[\s\n]$/, V = _.create(
|
|
|
68
86
|
"Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(),
|
|
69
87
|
// remove code block when at start of document or code block is empty
|
|
70
88
|
Backspace: () => {
|
|
71
|
-
const { empty: e, $anchor:
|
|
72
|
-
return !e ||
|
|
89
|
+
const { empty: e, $anchor: t } = this.editor.state.selection, i = t.pos === 1;
|
|
90
|
+
return !e || t.parent.type.name !== this.name ? !1 : i || !t.parent.textContent.length ? this.editor.commands.clearNodes() : !1;
|
|
73
91
|
},
|
|
74
92
|
// handle tab indentation
|
|
75
93
|
Tab: ({ editor: e }) => {
|
|
76
|
-
var
|
|
94
|
+
var t;
|
|
77
95
|
if (!this.options.enableTabIndentation)
|
|
78
96
|
return !1;
|
|
79
|
-
const i = (
|
|
80
|
-
if (
|
|
97
|
+
const i = (t = this.options.tabSize) != null ? t : A, { state: o } = e, { selection: n } = o, { $from: c, empty: r } = n;
|
|
98
|
+
if (c.parent.type !== this.type)
|
|
81
99
|
return !1;
|
|
82
|
-
const
|
|
83
|
-
return
|
|
84
|
-
const { from:
|
|
100
|
+
const s = " ".repeat(i);
|
|
101
|
+
return r ? e.commands.insertContent(s) : e.commands.command(({ tr: d }) => {
|
|
102
|
+
const { from: a, to: g } = n, l = o.doc.textBetween(a, g, `
|
|
85
103
|
`, `
|
|
86
104
|
`).split(`
|
|
87
|
-
`).map((
|
|
105
|
+
`).map((h) => s + h).join(`
|
|
88
106
|
`);
|
|
89
|
-
return
|
|
107
|
+
return d.replaceWith(a, g, o.schema.text(l)), !0;
|
|
90
108
|
});
|
|
91
109
|
},
|
|
92
110
|
// handle shift+tab reverse indentation
|
|
93
111
|
"Shift-Tab": ({ editor: e }) => {
|
|
94
|
-
var
|
|
112
|
+
var t;
|
|
95
113
|
if (!this.options.enableTabIndentation)
|
|
96
114
|
return !1;
|
|
97
|
-
const i = (
|
|
98
|
-
return
|
|
99
|
-
var
|
|
100
|
-
const { pos:
|
|
115
|
+
const i = (t = this.options.tabSize) != null ? t : A, { state: o } = e, { selection: n } = o, { $from: c, empty: r } = n;
|
|
116
|
+
return c.parent.type !== this.type ? !1 : r ? e.commands.command(({ tr: s }) => {
|
|
117
|
+
var d;
|
|
118
|
+
const { pos: a } = c, g = c.start(), y = c.end(), l = o.doc.textBetween(g, y, `
|
|
101
119
|
`, `
|
|
102
120
|
`).split(`
|
|
103
121
|
`);
|
|
104
|
-
let
|
|
105
|
-
const
|
|
106
|
-
for (let T = 0; T <
|
|
107
|
-
if (
|
|
108
|
-
|
|
122
|
+
let h = 0, u = 0;
|
|
123
|
+
const p = a - g;
|
|
124
|
+
for (let T = 0; T < l.length; T += 1) {
|
|
125
|
+
if (u + l[T].length >= p) {
|
|
126
|
+
h = T;
|
|
109
127
|
break;
|
|
110
128
|
}
|
|
111
|
-
|
|
129
|
+
u += l[T].length + 1;
|
|
112
130
|
}
|
|
113
|
-
const
|
|
131
|
+
const v = ((d = l[h].match(/^ */)) == null ? void 0 : d[0]) || "", x = Math.min(v.length, i);
|
|
114
132
|
if (x === 0)
|
|
115
133
|
return !0;
|
|
116
134
|
let k = g;
|
|
117
|
-
for (let T = 0; T <
|
|
118
|
-
k +=
|
|
119
|
-
return
|
|
120
|
-
}) : e.commands.command(({ tr:
|
|
121
|
-
const { from:
|
|
135
|
+
for (let T = 0; T < h; T += 1)
|
|
136
|
+
k += l[T].length + 1;
|
|
137
|
+
return s.delete(k, k + x), a - k <= x && s.setSelection($.create(s.doc, k)), !0;
|
|
138
|
+
}) : e.commands.command(({ tr: s }) => {
|
|
139
|
+
const { from: d, to: a } = n, m = o.doc.textBetween(d, a, `
|
|
122
140
|
`, `
|
|
123
141
|
`).split(`
|
|
124
|
-
`).map((
|
|
125
|
-
var
|
|
126
|
-
const
|
|
127
|
-
return
|
|
142
|
+
`).map((l) => {
|
|
143
|
+
var h;
|
|
144
|
+
const u = ((h = l.match(/^ */)) == null ? void 0 : h[0]) || "", p = Math.min(u.length, i);
|
|
145
|
+
return l.slice(p);
|
|
128
146
|
}).join(`
|
|
129
147
|
`);
|
|
130
|
-
return
|
|
148
|
+
return s.replaceWith(d, a, o.schema.text(m)), !0;
|
|
131
149
|
});
|
|
132
150
|
},
|
|
133
151
|
// exit node on triple enter
|
|
134
152
|
Enter: ({ editor: e }) => {
|
|
135
153
|
if (!this.options.exitOnTripleEnter)
|
|
136
154
|
return !1;
|
|
137
|
-
const { state:
|
|
138
|
-
if (!
|
|
155
|
+
const { state: t } = e, { selection: i } = t, { $from: o, empty: n } = i;
|
|
156
|
+
if (!n || o.parent.type !== this.type)
|
|
139
157
|
return !1;
|
|
140
|
-
const
|
|
158
|
+
const c = o.parentOffset === o.parent.nodeSize - 2, r = o.parent.textContent.endsWith(`
|
|
141
159
|
|
|
142
160
|
`);
|
|
143
|
-
return !
|
|
161
|
+
return !c || !r ? !1 : e.chain().command(({ tr: s }) => (s.delete(o.pos - 2, o.pos), !0)).exitCode().run();
|
|
144
162
|
},
|
|
145
163
|
// exit node on arrow down
|
|
146
164
|
ArrowDown: ({ editor: e }) => {
|
|
147
165
|
if (!this.options.exitOnArrowDown)
|
|
148
166
|
return !1;
|
|
149
|
-
const { state:
|
|
150
|
-
if (!
|
|
167
|
+
const { state: t } = e, { selection: i, doc: o } = t, { $from: n, empty: c } = i;
|
|
168
|
+
if (!c || n.parent.type !== this.type || !(n.parentOffset === n.parent.nodeSize - 2))
|
|
151
169
|
return !1;
|
|
152
|
-
const
|
|
153
|
-
return
|
|
170
|
+
const s = n.after();
|
|
171
|
+
return s === void 0 ? !1 : o.nodeAt(s) ? e.commands.command(({ tr: a }) => (a.setSelection(j.near(o.resolve(s))), !0)) : e.commands.exitCode();
|
|
154
172
|
}
|
|
155
173
|
};
|
|
156
174
|
},
|
|
157
175
|
addInputRules() {
|
|
158
176
|
return [
|
|
159
|
-
|
|
177
|
+
M({
|
|
160
178
|
find: U,
|
|
161
179
|
type: this.type,
|
|
162
180
|
getAttributes: (e) => ({
|
|
163
181
|
language: e[1]
|
|
164
182
|
})
|
|
165
183
|
}),
|
|
166
|
-
|
|
167
|
-
find:
|
|
184
|
+
M({
|
|
185
|
+
find: V,
|
|
168
186
|
type: this.type,
|
|
169
187
|
getAttributes: (e) => ({
|
|
170
188
|
language: e[1]
|
|
@@ -176,145 +194,159 @@ var B = 4, U = /^```([a-z]+)?[\s\n]$/, K = /^~~~([a-z]+)?[\s\n]$/, V = _.create(
|
|
|
176
194
|
return [
|
|
177
195
|
// this plugin creates a code block for pasted content from VS Code
|
|
178
196
|
// we can also detect the copied code language
|
|
179
|
-
new
|
|
180
|
-
key: new
|
|
197
|
+
new z({
|
|
198
|
+
key: new I("codeBlockVSCodeHandler"),
|
|
181
199
|
props: {
|
|
182
|
-
handlePaste: (e,
|
|
183
|
-
if (!
|
|
200
|
+
handlePaste: (e, t) => {
|
|
201
|
+
if (!t.clipboardData || this.editor.isActive(this.type.name))
|
|
184
202
|
return !1;
|
|
185
|
-
const i =
|
|
186
|
-
if (!i || !
|
|
203
|
+
const i = t.clipboardData.getData("text/plain"), o = t.clipboardData.getData("vscode-editor-data"), n = o ? JSON.parse(o) : void 0, c = n?.mode;
|
|
204
|
+
if (!i || !c)
|
|
187
205
|
return !1;
|
|
188
|
-
const { tr:
|
|
206
|
+
const { tr: r, schema: s } = e.state, d = s.text(i.replace(/\r\n?/g, `
|
|
189
207
|
`));
|
|
190
|
-
return
|
|
208
|
+
return r.replaceSelectionWith(this.type.create({ language: c }, d)), r.selection.$from.parent.type !== this.type && r.setSelection($.near(r.doc.resolve(Math.max(0, r.selection.from - 2)))), r.setMeta("paste", !0), e.dispatch(r), !0;
|
|
191
209
|
}
|
|
192
210
|
}
|
|
193
211
|
})
|
|
194
212
|
];
|
|
195
213
|
}
|
|
196
|
-
}),
|
|
197
|
-
let
|
|
198
|
-
const
|
|
199
|
-
function Z() {
|
|
200
|
-
return m;
|
|
201
|
-
}
|
|
214
|
+
}), Z = J;
|
|
215
|
+
let f, w;
|
|
216
|
+
const N = /* @__PURE__ */ new Set(), D = /* @__PURE__ */ new Set(), B = /* @__PURE__ */ new Map();
|
|
202
217
|
function q(e) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
218
|
+
return e in E || B.has(e);
|
|
219
|
+
}
|
|
220
|
+
function G() {
|
|
221
|
+
return f;
|
|
222
|
+
}
|
|
223
|
+
function Q(e) {
|
|
224
|
+
if (!f && !w) {
|
|
225
|
+
if (e.customThemes)
|
|
226
|
+
for (const n of e.customThemes)
|
|
227
|
+
n.name && B.set(n.name, n);
|
|
228
|
+
const t = e.themes.filter(
|
|
229
|
+
(n) => !!n && n in E
|
|
206
230
|
), i = e.languages.filter(
|
|
207
|
-
(
|
|
208
|
-
);
|
|
209
|
-
return
|
|
210
|
-
|
|
211
|
-
}),
|
|
231
|
+
(n) => !!n && n in W
|
|
232
|
+
), o = [...t, ...B.values()];
|
|
233
|
+
return w = K({ themes: o, langs: i }).then((n) => {
|
|
234
|
+
f = n;
|
|
235
|
+
}), w;
|
|
212
236
|
}
|
|
213
|
-
if (
|
|
214
|
-
return
|
|
237
|
+
if (w)
|
|
238
|
+
return w;
|
|
215
239
|
}
|
|
216
|
-
async function
|
|
217
|
-
|
|
240
|
+
async function b(e) {
|
|
241
|
+
if (f && !f.getLoadedThemes().includes(e) && !D.has(e) && q(e)) {
|
|
242
|
+
D.add(e);
|
|
243
|
+
const t = B.get(e) ?? e;
|
|
244
|
+
return await f.loadTheme(t), D.delete(e), !0;
|
|
245
|
+
}
|
|
246
|
+
return !1;
|
|
218
247
|
}
|
|
219
|
-
async function
|
|
220
|
-
return
|
|
248
|
+
async function H(e) {
|
|
249
|
+
return f && !f.getLoadedLanguages().includes(e) && !N.has(e) && e in W ? (N.add(e), await f.loadLanguage(e), N.delete(e), !0) : !1;
|
|
221
250
|
}
|
|
222
|
-
async function
|
|
251
|
+
async function X({
|
|
223
252
|
doc: e,
|
|
224
|
-
name:
|
|
253
|
+
name: t,
|
|
225
254
|
defaultTheme: i,
|
|
226
|
-
defaultLanguage:
|
|
227
|
-
themeModes:
|
|
255
|
+
defaultLanguage: o,
|
|
256
|
+
themeModes: n,
|
|
257
|
+
customThemes: c
|
|
228
258
|
}) {
|
|
229
|
-
const
|
|
230
|
-
...
|
|
259
|
+
const r = L(e, (a) => a.type.name === t), s = [
|
|
260
|
+
...r.map((a) => a.node.attrs.theme),
|
|
231
261
|
i
|
|
232
|
-
],
|
|
233
|
-
...
|
|
234
|
-
|
|
262
|
+
], d = [
|
|
263
|
+
...r.map((a) => a.node.attrs.language),
|
|
264
|
+
o
|
|
235
265
|
];
|
|
236
|
-
if (
|
|
266
|
+
if (f)
|
|
237
267
|
await Promise.all([
|
|
238
|
-
...
|
|
239
|
-
...
|
|
268
|
+
...s.flatMap((a) => b(a)),
|
|
269
|
+
...d.flatMap((a) => !!a && H(a))
|
|
240
270
|
]);
|
|
241
271
|
else {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
languages:
|
|
245
|
-
themes:
|
|
272
|
+
const a = [...s];
|
|
273
|
+
n && (n.light && !a.includes(n.light) && a.push(n.light), n.dark && !a.includes(n.dark) && a.push(n.dark)), await Q({
|
|
274
|
+
languages: d,
|
|
275
|
+
themes: a,
|
|
276
|
+
customThemes: c
|
|
246
277
|
});
|
|
247
278
|
}
|
|
248
279
|
}
|
|
249
|
-
function
|
|
250
|
-
return Object.entries(e).map(([
|
|
280
|
+
function C(e) {
|
|
281
|
+
return Object.entries(e).map(([t, i]) => `${t}:${i}`).join(";");
|
|
251
282
|
}
|
|
252
|
-
function
|
|
283
|
+
function _({
|
|
253
284
|
doc: e,
|
|
254
|
-
name:
|
|
285
|
+
name: t,
|
|
255
286
|
defaultTheme: i,
|
|
256
|
-
defaultLanguage:
|
|
257
|
-
themes:
|
|
287
|
+
defaultLanguage: o,
|
|
288
|
+
themes: n
|
|
258
289
|
}) {
|
|
259
|
-
const
|
|
260
|
-
return
|
|
261
|
-
let
|
|
262
|
-
const g =
|
|
263
|
-
if (!
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
let
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
lang:
|
|
290
|
+
const c = [];
|
|
291
|
+
return L(e, (s) => s.type.name === t).forEach((s) => {
|
|
292
|
+
let d = s.pos + 1, a = s.node.attrs.language || o;
|
|
293
|
+
const g = s.node.attrs.theme || i, y = s.node.attrs.themes?.light || n?.light, m = s.node.attrs.themes?.dark || n?.dark, l = G();
|
|
294
|
+
if (!l) return;
|
|
295
|
+
l.getLoadedLanguages().includes(a) || (a = "plaintext");
|
|
296
|
+
const h = (p) => l.getLoadedThemes().includes(p) ? p : l.getLoadedThemes()[0];
|
|
297
|
+
let u;
|
|
298
|
+
if (n) {
|
|
299
|
+
u = l.codeToTokens(s.node.textContent, {
|
|
300
|
+
lang: a,
|
|
270
301
|
themes: {
|
|
271
|
-
light:
|
|
272
|
-
dark:
|
|
302
|
+
light: h(y),
|
|
303
|
+
dark: h(m)
|
|
273
304
|
}
|
|
274
305
|
});
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
style:
|
|
306
|
+
const p = {};
|
|
307
|
+
u.bg && (p["background-color"] = u.bg), u.fg && (p.color = u.fg), c.push(
|
|
308
|
+
P.node(s.pos, s.pos + s.node.nodeSize, {
|
|
309
|
+
style: C(p),
|
|
279
310
|
class: "shiki"
|
|
280
311
|
})
|
|
281
312
|
);
|
|
282
313
|
} else {
|
|
283
|
-
|
|
284
|
-
lang:
|
|
285
|
-
theme:
|
|
314
|
+
u = l.codeToTokens(s.node.textContent, {
|
|
315
|
+
lang: a,
|
|
316
|
+
theme: h(g)
|
|
286
317
|
});
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
style:
|
|
318
|
+
const p = l.getLoadedThemes().includes(g) ? g : l.getLoadedThemes()[0], S = l.getTheme(p);
|
|
319
|
+
c.push(
|
|
320
|
+
P.node(s.pos, s.pos + s.node.nodeSize, {
|
|
321
|
+
style: C({ "background-color": S.bg })
|
|
291
322
|
})
|
|
292
323
|
);
|
|
293
324
|
}
|
|
294
|
-
for (const
|
|
295
|
-
for (const S of
|
|
296
|
-
const
|
|
325
|
+
for (const p of u.tokens) {
|
|
326
|
+
for (const S of p) {
|
|
327
|
+
const v = d + S.content.length;
|
|
297
328
|
let x = "";
|
|
298
|
-
|
|
299
|
-
const k =
|
|
329
|
+
n ? x = C(S.htmlStyle || {}) : x = C({ color: S.color || "inherit" });
|
|
330
|
+
const k = P.inline(d, v, {
|
|
300
331
|
style: x
|
|
301
332
|
});
|
|
302
|
-
|
|
333
|
+
c.push(k), d = v;
|
|
303
334
|
}
|
|
304
|
-
|
|
335
|
+
d += 1;
|
|
305
336
|
}
|
|
306
|
-
}), F.create(e,
|
|
337
|
+
}), F.create(e, c);
|
|
307
338
|
}
|
|
308
|
-
function
|
|
339
|
+
function Y({
|
|
309
340
|
name: e,
|
|
310
|
-
defaultLanguage:
|
|
341
|
+
defaultLanguage: t,
|
|
311
342
|
defaultTheme: i,
|
|
312
|
-
themes:
|
|
343
|
+
themes: o,
|
|
344
|
+
customThemes: n
|
|
313
345
|
}) {
|
|
314
|
-
const
|
|
315
|
-
key: new
|
|
316
|
-
view(
|
|
317
|
-
class
|
|
346
|
+
const c = new z({
|
|
347
|
+
key: new I("shiki"),
|
|
348
|
+
view(r) {
|
|
349
|
+
class s {
|
|
318
350
|
constructor() {
|
|
319
351
|
this.initDecorations();
|
|
320
352
|
}
|
|
@@ -325,110 +357,130 @@ function Q({
|
|
|
325
357
|
}
|
|
326
358
|
// Initialize shiki async, and then highlight initial document
|
|
327
359
|
async initDecorations() {
|
|
328
|
-
const
|
|
329
|
-
await
|
|
330
|
-
doc:
|
|
360
|
+
const a = r.state.doc;
|
|
361
|
+
await X({
|
|
362
|
+
doc: a,
|
|
331
363
|
name: e,
|
|
332
|
-
defaultLanguage:
|
|
364
|
+
defaultLanguage: t,
|
|
333
365
|
defaultTheme: i,
|
|
334
|
-
themeModes:
|
|
366
|
+
themeModes: o,
|
|
367
|
+
customThemes: n ?? void 0
|
|
335
368
|
});
|
|
336
|
-
const
|
|
337
|
-
|
|
369
|
+
const g = r.state.tr.setMeta("shikiPluginForceDecoration", !0);
|
|
370
|
+
r.dispatch(g);
|
|
338
371
|
}
|
|
339
372
|
// When new codeblocks were added and they have missing themes or
|
|
340
373
|
// languages, load those and then add code decorations once again.
|
|
341
374
|
async checkUndecoratedBlocks() {
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
(
|
|
345
|
-
),
|
|
346
|
-
const
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
),
|
|
375
|
+
const a = L(
|
|
376
|
+
r.state.doc,
|
|
377
|
+
(l) => l.type.name === e
|
|
378
|
+
), g = (l) => {
|
|
379
|
+
const h = [H(l.node.attrs.language)];
|
|
380
|
+
return o ? (h.push(
|
|
381
|
+
b(l.node.attrs.themes?.light || o.light)
|
|
382
|
+
), h.push(b(l.node.attrs.themes?.dark || o.dark))) : h.push(b(l.node.attrs.theme)), h;
|
|
350
383
|
};
|
|
351
384
|
if ((await Promise.all(
|
|
352
|
-
|
|
385
|
+
a.flatMap((l) => g(l))
|
|
353
386
|
)).includes(!0)) {
|
|
354
|
-
const
|
|
355
|
-
|
|
387
|
+
const l = r.state.tr.setMeta("shikiPluginForceDecoration", !0);
|
|
388
|
+
r.dispatch(l);
|
|
356
389
|
}
|
|
357
390
|
}
|
|
358
391
|
}
|
|
359
|
-
return new
|
|
392
|
+
return new s();
|
|
360
393
|
},
|
|
361
394
|
state: {
|
|
362
|
-
init: (
|
|
363
|
-
doc:
|
|
395
|
+
init: (r, { doc: s }) => _({
|
|
396
|
+
doc: s,
|
|
364
397
|
name: e,
|
|
365
|
-
defaultLanguage:
|
|
398
|
+
defaultLanguage: t,
|
|
366
399
|
defaultTheme: i,
|
|
367
|
-
themes:
|
|
400
|
+
themes: o
|
|
368
401
|
}),
|
|
369
|
-
apply: (
|
|
370
|
-
const
|
|
371
|
-
|
|
402
|
+
apply: (r, s, d, a) => {
|
|
403
|
+
const g = d.selection.$head.parent.type.name, y = a.selection.$head.parent.type.name, m = L(
|
|
404
|
+
d.doc,
|
|
372
405
|
(u) => u.type.name === e
|
|
373
|
-
),
|
|
374
|
-
|
|
406
|
+
), l = L(
|
|
407
|
+
a.doc,
|
|
375
408
|
(u) => u.type.name === e
|
|
376
|
-
),
|
|
409
|
+
), h = r.docChanged && // Apply decorations if:
|
|
377
410
|
// selection includes named node,
|
|
378
|
-
([
|
|
379
|
-
|
|
411
|
+
([g, y].includes(e) || // OR transaction adds/removes named node,
|
|
412
|
+
l.length !== m.length || // OR transaction has changes that completely encapsulte a node
|
|
380
413
|
// (for example, a transaction that affects the entire document).
|
|
381
414
|
// Such transactions can happen during collab syncing via y-prosemirror, for example.
|
|
382
|
-
|
|
415
|
+
r.steps.some((u) => (
|
|
383
416
|
// @ts-expect-error
|
|
384
417
|
u.from !== void 0 && // @ts-expect-error
|
|
385
|
-
u.to !== void 0 &&
|
|
418
|
+
u.to !== void 0 && m.some((p) => (
|
|
386
419
|
// @ts-expect-error
|
|
387
|
-
|
|
388
|
-
|
|
420
|
+
p.pos >= u.from && // @ts-expect-error
|
|
421
|
+
p.pos + p.node.nodeSize <= u.to
|
|
389
422
|
))
|
|
390
423
|
)));
|
|
391
|
-
return
|
|
392
|
-
doc:
|
|
424
|
+
return r.getMeta("shikiPluginForceDecoration") || h ? _({
|
|
425
|
+
doc: r.doc,
|
|
393
426
|
name: e,
|
|
394
|
-
defaultLanguage:
|
|
427
|
+
defaultLanguage: t,
|
|
395
428
|
defaultTheme: i,
|
|
396
|
-
themes:
|
|
397
|
-
}) :
|
|
429
|
+
themes: o
|
|
430
|
+
}) : s.map(r.mapping, r.doc);
|
|
398
431
|
}
|
|
399
432
|
},
|
|
400
433
|
props: {
|
|
401
|
-
decorations(
|
|
402
|
-
return
|
|
434
|
+
decorations(r) {
|
|
435
|
+
return c.getState(r);
|
|
403
436
|
}
|
|
404
437
|
}
|
|
405
438
|
});
|
|
406
|
-
return
|
|
439
|
+
return c;
|
|
407
440
|
}
|
|
408
441
|
/* v8 ignore start -- @preserve */
|
|
409
442
|
/* v8 ignore stop -- @preserve */
|
|
410
|
-
const
|
|
443
|
+
const re = Z.extend({
|
|
411
444
|
addOptions() {
|
|
412
445
|
return {
|
|
413
446
|
...this.parent?.(),
|
|
414
447
|
defaultLanguage: null,
|
|
415
448
|
defaultTheme: "github-dark",
|
|
416
|
-
themes: null
|
|
449
|
+
themes: null,
|
|
450
|
+
customThemes: null
|
|
417
451
|
};
|
|
418
452
|
},
|
|
453
|
+
markdownTokenName: "code",
|
|
454
|
+
parseMarkdown(e, t) {
|
|
455
|
+
return e.raw?.startsWith("```") === !1 && e.raw?.startsWith("~~~") === !1 && e.codeBlockStyle !== "indented" ? [] : t.createNode(
|
|
456
|
+
"codeBlock",
|
|
457
|
+
{ language: e.lang || null },
|
|
458
|
+
e.text ? [t.createTextNode(e.text)] : []
|
|
459
|
+
);
|
|
460
|
+
},
|
|
461
|
+
renderMarkdown(e, t, i) {
|
|
462
|
+
const o = e.attrs?.language || "";
|
|
463
|
+
return e.content ? [`\`\`\`${o}`, t.renderChildren(e.content), "```"].join(
|
|
464
|
+
`
|
|
465
|
+
`
|
|
466
|
+
) : `\`\`\`${o}
|
|
467
|
+
|
|
468
|
+
\`\`\``;
|
|
469
|
+
},
|
|
419
470
|
addProseMirrorPlugins() {
|
|
420
471
|
return [
|
|
421
472
|
...this.parent?.() || [],
|
|
422
|
-
|
|
473
|
+
Y({
|
|
423
474
|
name: this.name,
|
|
424
475
|
defaultLanguage: this.options.defaultLanguage,
|
|
425
476
|
defaultTheme: this.options.defaultTheme,
|
|
426
|
-
themes: this.options.themes
|
|
477
|
+
themes: this.options.themes,
|
|
478
|
+
customThemes: this.options.customThemes
|
|
427
479
|
})
|
|
428
480
|
];
|
|
429
481
|
}
|
|
430
482
|
});
|
|
431
483
|
export {
|
|
432
|
-
|
|
433
|
-
|
|
484
|
+
re as CodeBlockShiki,
|
|
485
|
+
re as default
|
|
434
486
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiptap-extension-code-block-shiki",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Use Shiki syntax highlighting for codeblocks in TipTap.",
|
|
5
5
|
"repository": "timomeh/tiptap-extension-code-block-shiki",
|
|
6
6
|
"author": {
|
|
@@ -32,13 +32,14 @@
|
|
|
32
32
|
"@biomejs/biome": "2.2.6",
|
|
33
33
|
"@codecov/vite-plugin": "^1.9.1",
|
|
34
34
|
"@microsoft/api-extractor": "^7.53.1",
|
|
35
|
-
"@tiptap/core": "^3.
|
|
36
|
-
"@tiptap/extension-code-block": "^3.
|
|
37
|
-
"@tiptap/
|
|
38
|
-
"@tiptap/
|
|
35
|
+
"@tiptap/core": "^3.21.0",
|
|
36
|
+
"@tiptap/extension-code-block": "^3.21.0",
|
|
37
|
+
"@tiptap/markdown": "^3.21.0",
|
|
38
|
+
"@tiptap/pm": "^3.21.0",
|
|
39
|
+
"@tiptap/starter-kit": "^3.21.0",
|
|
39
40
|
"@vitest/coverage-v8": "^3.2.4",
|
|
40
41
|
"happy-dom": "^20.0.0",
|
|
41
|
-
"shiki": "^
|
|
42
|
+
"shiki": "^4.0.0",
|
|
42
43
|
"typescript": "^5.9.3",
|
|
43
44
|
"unplugin-dts": "1.0.0-beta.0",
|
|
44
45
|
"vite": "^7.1.9",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"peerDependencies": {
|
|
48
49
|
"@tiptap/core": "^2.3.0 || ^3.0.0",
|
|
49
50
|
"@tiptap/pm": "^2.3.0 || ^3.0.0",
|
|
50
|
-
"shiki": "^3.0.0"
|
|
51
|
+
"shiki": "^3.0.0 || ^4.0.0"
|
|
51
52
|
},
|
|
52
53
|
"keywords": [
|
|
53
54
|
"tiptap",
|