Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@milkdown/preset-commonmark

Package Overview
Dependencies
Maintainers
1
Versions
121
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@milkdown/preset-commonmark - npm Package Compare versions

Comparing version 5.5.0 to 6.0.0-next.0

lib/index.d.ts.map

35

lib/index.d.ts

@@ -1,1 +0,34 @@

export * from './src/index'
import { AtomList } from '@milkdown/utils';
import { commonmarkPlugins } from './plugin';
export * from './mark';
export * from './node';
export * from './supported-keys';
export declare const commonmarkNodes: AtomList<(import("@milkdown/utils/lib/types").Metadata<import("@milkdown/utils/lib/types").GetPlugin<"CodeFence", {
languageList?: string[] | undefined;
}>> & import("@milkdown/core").MilkdownPlugin) | (import("@milkdown/utils/lib/types").Metadata<import("@milkdown/utils/lib/types").GetPlugin<string, import("./node").ImageOptions>> & import("@milkdown/core").MilkdownPlugin)>;
export { commonmarkPlugins };
export declare const commonmark: AtomList<(import("@milkdown/utils/lib/types").Metadata<import("@milkdown/utils/lib/types").GetPlugin<"CodeFence", {
languageList?: string[] | undefined;
}>> & import("@milkdown/core").MilkdownPlugin) | (import("@milkdown/utils/lib/types").Metadata<import("@milkdown/utils/lib/types").GetPlugin<string, import("./node").ImageOptions>> & import("@milkdown/core").MilkdownPlugin)>;
export declare const commands: {
readonly ToggleInlineCode: import("@milkdown/core").CmdKey<undefined>;
readonly ToggleItalic: import("@milkdown/core").CmdKey<undefined>;
readonly ToggleLink: import("@milkdown/core").CmdKey<string>;
readonly ToggleBold: import("@milkdown/core").CmdKey<undefined>;
readonly ModifyLink: import("@milkdown/core").CmdKey<string>;
readonly ModifyImage: import("@milkdown/core").CmdKey<string>;
readonly WrapInBlockquote: import("@milkdown/core").CmdKey<undefined>;
readonly WrapInBulletList: import("@milkdown/core").CmdKey<undefined>;
readonly WrapInOrderedList: import("@milkdown/core").CmdKey<undefined>;
readonly TurnIntoCodeFence: import("@milkdown/core").CmdKey<undefined>;
readonly TurnIntoHeading: import("@milkdown/core").CmdKey<number>;
readonly TurnIntoText: import("@milkdown/core").CmdKey<undefined>;
readonly InsertHardbreak: import("@milkdown/core").CmdKey<undefined>;
readonly InsertHr: import("@milkdown/core").CmdKey<string>;
readonly InsertImage: import("@milkdown/core").CmdKey<string>;
readonly SplitListItem: import("@milkdown/core").CmdKey<undefined>;
readonly SinkListItem: import("@milkdown/core").CmdKey<undefined>;
readonly LiftListItem: import("@milkdown/core").CmdKey<undefined>;
};
export declare type Commands = typeof commands;
//# sourceMappingURL=index.d.ts.map

783

lib/index.es.js

@@ -7,3 +7,3 @@ var __defProp = Object.defineProperty;

var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __defNormalProp = (obj, key2, value) => key2 in obj ? __defProp(obj, key2, { enumerable: true, configurable: true, writable: true, value }) : obj[key2] = value;
var __spreadValues = (a, b) => {

@@ -22,4 +22,4 @@ for (var prop in b || (b = {}))

import { createMark, createShortcut, createNode, createPlugin, AtomList } from "@milkdown/utils";
import { createCmdKey, createCmd, schemaCtx, themeToolCtx, editorViewCtx } from "@milkdown/core";
import { markRule, toggleMark, TextSelection, InputRule, wrappingInputRule, wrapIn, textblockTypeInputRule, setBlockType, Plugin, PluginKey, ReplaceStep, AddMarkStep, Selection, findSelectedNodeOfType, splitListItem, sinkListItem, liftListItem } from "@milkdown/prose";
import { createCmdKey, createCmd, schemaCtx, commandsCtx, editorViewCtx } from "@milkdown/core";
import { markRule, toggleMark, PluginKey, TextSelection, InputRule, Plugin, wrappingInputRule, wrapIn, textblockTypeInputRule, setBlockType, ReplaceStep, AddMarkStep, Selection, findSelectedNodeOfType, splitListItem, sinkListItem, liftListItem } from "@milkdown/prose";
import links from "remark-inline-links";

@@ -49,10 +49,2 @@ const SupportedKeys = {

const codeInline = createMark((utils) => {
const style = utils.getStyle(({ palette, size, font }, { css }) => css`
background-color: ${palette("neutral")};
color: ${palette("background")};
border-radius: ${size.radius};
font-weight: 500;
font-family: ${font.code};
padding: 0 0.2rem;
`);
return {

@@ -65,3 +57,3 @@ id: id$a,

parseDOM: [{ tag: "code" }],
toDOM: (mark) => ["code", { class: utils.getClassName(mark.attrs, "code-inline", style) }],
toDOM: (mark) => ["code", { class: utils.getClassName(mark.attrs, "code-inline") }],
parseMarkdown: {

@@ -124,19 +116,7 @@ match: (node) => node.type === "inlineCode",

}));
const key$1 = new PluginKey("MILKDOWN_PLUGIN_LINK_INPUT");
const ToggleLink = createCmdKey("ToggleLink");
const ModifyLink = createCmdKey("ModifyLink");
const id$8 = "link";
const link = createMark((utils) => {
const style = utils.getStyle((themeTool, { css }) => {
const lineColor = themeTool.palette("line");
return css`
color: ${themeTool.palette("secondary")};
cursor: pointer;
transition: all 0.4s ease-in-out;
font-weight: 500;
&:hover {
background-color: ${lineColor};
box-shadow: 0 0.2rem ${lineColor}, 0 -0.2rem ${lineColor};
}
`;
});
const link = createMark((utils, options) => {
return {

@@ -161,3 +141,3 @@ id: id$8,

],
toDOM: (mark) => ["a", __spreadProps(__spreadValues({}, mark.attrs), { class: utils.getClassName(mark.attrs, id$8, style) })],
toDOM: (mark) => ["a", __spreadProps(__spreadValues({}, mark.attrs), { class: utils.getClassName(mark.attrs, id$8) })],
parseMarkdown: {

@@ -223,3 +203,69 @@ match: (node) => node.type === "link",

})
]
],
prosePlugins: (type, ctx) => {
var _a, _b, _c;
const inputChipRenderer = utils.themeManager.get("input-chip", {
placeholder: (_b = (_a = options == null ? void 0 : options.input) == null ? void 0 : _a.placeholder) != null ? _b : "Input Web Link",
buttonText: (_c = options == null ? void 0 : options.input) == null ? void 0 : _c.buttonText,
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyLink, value);
}
});
const shouldDisplay = (view) => {
const { selection, doc: doc2 } = view.state;
const { from, to } = selection;
return selection.empty && selection instanceof TextSelection && doc2.rangeHasMark(from, from === to ? to + 1 : to, type);
};
const getCurrentLink = (view) => {
const { selection } = view.state;
let node;
const { from, to } = selection;
view.state.doc.nodesBetween(from, from === to ? to + 1 : to, (n) => {
if (type.isInSet(n.marks)) {
node = n;
return false;
}
return;
});
if (!node)
return;
const mark = node.marks.find((m) => m.type === type);
if (!mark)
return;
const value = mark.attrs["href"];
return value;
};
const renderByView = (view) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
return [
new Plugin({
key: key$1,
view: (editorView) => {
inputChipRenderer.init(editorView);
renderByView(editorView);
return {
update: (view, prevState) => {
const isEqualSelection = (prevState == null ? void 0 : prevState.doc.eq(view.state.doc)) && prevState.selection.eq(view.state.selection);
if (isEqualSelection)
return;
renderByView(view);
},
destroy: () => {
inputChipRenderer.destroy();
}
};
}
})
];
}
};

@@ -230,5 +276,2 @@ });

const strong = createMark((utils) => {
const style = utils.getStyle((_, { css }) => css`
font-weight: 600;
`);
return {

@@ -242,3 +285,3 @@ id: id$7,

],
toDOM: (mark) => ["strong", { class: utils.getClassName(mark.attrs, id$7, style) }],
toDOM: (mark) => ["strong", { class: utils.getClassName(mark.attrs, id$7) }],
parseMarkdown: {

@@ -273,11 +316,2 @@ match: (node) => node.type === "strong",

const blockquote = createNode((utils) => {
const style = utils.getStyle((themeTool, { css }) => css`
padding-left: 1.875rem;
line-height: 1.75rem;
border-left: 4px solid ${themeTool.palette("primary")};
* {
font-size: 1rem;
line-height: 1.5rem;
}
`);
return {

@@ -290,3 +324,3 @@ id: id$6,

parseDOM: [{ tag: "blockquote" }],
toDOM: (node) => ["blockquote", { class: utils.getClassName(node.attrs, id$6, style) }, 0],
toDOM: (node) => ["blockquote", { class: utils.getClassName(node.attrs, id$6) }, 0],
parseMarkdown: {

@@ -367,114 +401,6 @@ match: ({ type }) => type === id$6,

const codeFence = createNode((utils, options) => {
const style = utils.getStyle(({ palette, mixin, size, font }, { css }) => {
const { shadow, scrollbar, border } = mixin;
const { lineWidth, radius } = size;
return css`
background-color: ${palette("background")};
color: ${palette("neutral")};
font-size: 0.85rem;
padding: 1.2rem 0.4rem 1.4rem;
border-radius: ${radius};
font-family: ${font.typography};
* {
margin: 0;
}
.code-fence_select-wrapper {
position: relative;
}
.code-fence_value {
width: 10.25rem;
box-sizing: border-box;
border-radius: ${size.radius};
margin: 0 1.2rem 1.2rem;
${border()};
${shadow()};
cursor: pointer;
background-color: ${palette("surface")};
position: relative;
display: flex;
color: ${palette("neutral", 0.87)};
letter-spacing: 0.5px;
height: 2.625rem;
align-items: center;
& > .icon {
width: 2.625rem;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: ${palette("solid", 0.87)};
border-left: ${lineWidth} solid ${palette("line")};
text-align: center;
transition: all 0.2s ease-in-out;
&:hover {
background: ${palette("background")};
color: ${palette("primary")};
}
}
> span:first-child {
padding-left: 1rem;
flex: 1;
font-weight: 500;
}
}
.code-fence_select-option {
list-style: none;
line-height: 2rem;
padding-left: 1rem;
cursor: pointer;
:hover {
background: ${palette("secondary", 0.12)};
color: ${palette("primary")};
}
}
.code-fence_select {
&[data-fold='true'] {
display: none;
}
font-weight: 500;
position: absolute;
z-index: 1;
top: 2.625rem;
box-sizing: border-box;
left: 1.2rem;
padding: 0.5rem 0;
max-height: 16.75rem;
width: 10.25rem;
${border()};
${shadow()};
background-color: ${palette("surface")};
border-top: none;
overflow-y: auto;
display: flex;
flex-direction: column;
${scrollbar("y")}
}
code {
line-height: 1.5;
font-family: ${font.code};
}
pre {
font-family: ${font.code};
margin: 0 1.2rem !important;
white-space: pre;
overflow: auto;
${scrollbar("x")};
}
`;
});
const languageList = (options == null ? void 0 : options.languageList) || languageOptions;
return {
id: id$5,
schema: () => ({
schema: (ctx) => ({
content: "text*",

@@ -506,9 +432,45 @@ group: "block",

toDOM: (node) => {
const select = document.createElement("select");
languageList.forEach((lang) => {
const option = document.createElement("option");
option.value = lang;
option.innerText = !lang ? "--" : lang;
if (lang === node.attrs["language"]) {
option.selected = true;
}
select.appendChild(option);
});
select.onchange = (e) => {
const target = e.target;
if (!(target instanceof HTMLSelectElement)) {
return;
}
const view = ctx.get(editorViewCtx);
if (!view.editable) {
target.value = node.attrs["language"];
return;
}
const { top, left } = target.getBoundingClientRect();
const result = view.posAtCoords({ top, left });
if (!result)
return;
const { tr } = view.state;
view.dispatch(tr.setNodeMarkup(result.inside, void 0, __spreadProps(__spreadValues({}, node.attrs), {
language: target.value
})));
};
return [
"pre",
"div",
{
"data-language": node.attrs["language"],
class: utils.getClassName(node.attrs, "code-fence", style)
class: "code-fence-container"
},
["code", { spellCheck: "false" }, 0]
select,
[
"pre",
{
"data-language": node.attrs["language"],
class: utils.getClassName(node.attrs, "code-fence")
},
["code", { spellCheck: "false" }, 0]
]
];

@@ -556,83 +518,45 @@ },

},
view: (ctx) => (node, view, getPos) => {
const container = document.createElement("div");
const selectWrapper = document.createElement("div");
const select = document.createElement("ul");
const pre = document.createElement("pre");
const code = document.createElement("code");
const valueWrapper = document.createElement("div");
valueWrapper.className = "code-fence_value";
const value = document.createElement("span");
valueWrapper.appendChild(value);
if (view.editable) {
valueWrapper.appendChild(ctx.get(themeToolCtx).slots.icon("downArrow"));
}
select.className = "code-fence_select";
select.addEventListener("mousedown", (e) => {
e.preventDefault();
e.stopPropagation();
if (!view.editable)
return;
const el = e.target;
if (!(el instanceof HTMLLIElement))
return;
view: () => (node, view, getPos) => {
let currNode = node;
const onSelectLanguage = (language) => {
const { tr } = view.state;
view.dispatch(tr.setNodeMarkup(getPos(), void 0, {
fold: true,
language: el.dataset["value"]
language
}));
});
valueWrapper.addEventListener("mousedown", (e) => {
e.preventDefault();
e.stopPropagation();
if (!view.editable)
return;
};
const onBlur = () => {
const { tr } = view.state;
view.dispatch(tr.setNodeMarkup(getPos(), void 0, {
fold: false,
language: container.dataset["language"]
}));
});
document.addEventListener("mousedown", () => {
if (!view.editable || select.dataset["fold"] === "true")
return;
view.dispatch(tr.setNodeMarkup(getPos(), void 0, __spreadProps(__spreadValues({}, currNode.attrs), {
fold: true
})));
};
const onFocus = () => {
const { tr } = view.state;
view.dispatch(tr.setNodeMarkup(getPos(), void 0, {
fold: true,
language: container.dataset["language"]
}));
view.dispatch(tr.setNodeMarkup(getPos(), void 0, __spreadProps(__spreadValues({}, currNode.attrs), {
fold: false
})));
};
const renderer = utils.themeManager.get("code-fence", {
onBlur,
onFocus,
onSelectLanguage,
editable: () => view.editable,
languageList
});
((options == null ? void 0 : options.languageList) || languageOptions).forEach((lang) => {
const option = document.createElement("li");
option.className = "code-fence_select-option";
option.innerText = lang || "--";
select.appendChild(option);
option.setAttribute("data-value", lang);
});
code.spellcheck = false;
selectWrapper.className = "code-fence_select-wrapper";
selectWrapper.contentEditable = "false";
selectWrapper.append(valueWrapper);
selectWrapper.append(select);
pre.append(code);
const codeContent = document.createElement("div");
code.append(codeContent);
codeContent.style.whiteSpace = "inherit";
container.append(selectWrapper, pre);
container.setAttribute("class", utils.getClassName(node.attrs, "code-fence", style));
container.setAttribute("data-language", node.attrs["language"]);
value.innerText = node.attrs["language"] || "--";
select.setAttribute("data-fold", node.attrs["fold"] ? "true" : "false");
if (!renderer)
return {};
const { dom, contentDOM, onUpdate, onDestroy } = renderer;
onUpdate(currNode);
return {
dom: container,
contentDOM: codeContent,
dom,
contentDOM,
update: (updatedNode) => {
if (updatedNode.type.name !== id$5)
return false;
const lang = updatedNode.attrs["language"];
container.dataset["language"] = lang;
value.innerText = lang || "--";
select.setAttribute("data-fold", updatedNode.attrs["fold"] ? "true" : "false");
currNode = updatedNode;
onUpdate(currNode);
return true;
}
},
destroy: onDestroy
};

@@ -735,35 +659,2 @@ }

const id2 = "heading";
const style = (level) => utils.getStyle((_, { css }) => {
const headingMap = {
1: css`
font-size: 3rem;
line-height: 3.5rem;
`,
2: css`
font-size: 2.5rem;
line-height: 3rem;
`,
3: css`
font-size: 2.125rem;
line-height: 2.25rem;
`,
4: css`
font-size: 1.75rem;
line-height: 2rem;
`,
5: css`
font-size: 1.5rem;
line-height: 1.5rem;
`,
6: css`
font-size: 1.25rem;
line-height: 1.25rem;
`
};
return css`
${headingMap[level] || ""}
margin: 2.5rem 0 !important;
font-weight: 400;
`;
});
return {

@@ -797,3 +688,3 @@ id: id2,

id: node.attrs["id"] || node.textContent.split(" ").join("-").toLocaleLowerCase(),
class: utils.getClassName(node.attrs, `heading h${node.attrs["level"]}`, style(node.attrs["level"]))
class: utils.getClassName(node.attrs, `heading h${node.attrs["level"]}`)
},

@@ -892,7 +783,2 @@ 0

const hr = createNode((utils) => {
const style = utils.getStyle((themeTool, { css }) => css`
height: ${themeTool.size.lineWidth};
background-color: ${themeTool.palette("line")};
border-width: 0;
`);
return {

@@ -903,3 +789,3 @@ id: id$4,

parseDOM: [{ tag: "hr" }],
toDOM: (node) => ["hr", { class: utils.getClassName(node.attrs, id$4, style) }],
toDOM: (node) => ["hr", { class: utils.getClassName(node.attrs, id$4) }],
parseMarkdown: {

@@ -951,111 +837,4 @@ match: ({ type }) => type === "thematicBreak",

const id$3 = "image";
const key = new PluginKey("MILKDOWN_PLUGIN_IMAGE_INPUT");
const image = createNode((utils, options) => {
var _a, _b;
const placeholder = __spreadValues({
loading: "Loading...",
empty: "Add an Image",
failed: "Image loads failed"
}, (_a = options == null ? void 0 : options.placeholder) != null ? _a : {});
const isBlock = (_b = options == null ? void 0 : options.isBlock) != null ? _b : false;
const containerStyle = utils.getStyle((themeTool, { css }) => css`
display: inline-block;
position: relative;
text-align: center;
font-size: 0;
vertical-align: text-bottom;
line-height: 1;
${isBlock ? `
width: 100%;
margin: 0 auto;
` : ""}
&.ProseMirror-selectednode::after {
content: '';
background: ${themeTool.palette("secondary", 0.38)};
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
img {
max-width: 100%;
height: auto;
object-fit: contain;
margin: 0 2px;
}
.icon,
.placeholder {
display: none;
}
&.system {
width: 100%;
padding: 0 2rem;
img {
width: 0;
height: 0;
display: none;
}
.icon,
.placeholder {
display: inline;
}
box-sizing: border-box;
height: 3rem;
background-color: ${themeTool.palette("background")};
border-radius: ${themeTool.size.radius};
display: inline-flex;
gap: 2rem;
justify-content: flex-start;
align-items: center;
.placeholder {
margin: 0;
line-height: 1;
&::before {
content: '';
font-size: 0.875rem;
color: ${themeTool.palette("neutral", 0.6)};
}
}
}
&.loading {
.placeholder {
&::before {
content: '${placeholder.loading}';
}
}
}
&.empty {
.placeholder {
&::before {
content: '${placeholder.empty}';
}
}
}
&.failed {
.placeholder {
&::before {
content: '${placeholder.failed}';
}
}
}
`);
const style = utils.getStyle((_, { css }) => css`
display: inline-block;
margin: 0 auto;
object-fit: contain;
width: 100%;
position: relative;
height: auto;
text-align: center;
`);
return {

@@ -1075,6 +854,3 @@ id: "image",

alt: { default: null },
title: { default: null },
failed: { default: false },
loading: { default: true },
width: { default: null }
title: { default: null }
},

@@ -1089,4 +865,2 @@ parseDOM: [

return {
failed: dom.classList.contains("failed"),
loading: dom.classList.contains("loading"),
src: dom.getAttribute("src") || "",

@@ -1104,3 +878,3 @@ alt: dom.getAttribute("alt"),

__spreadProps(__spreadValues({}, node.attrs), {
class: utils.getClassName(node.attrs, id$3, node.attrs["failed"] ? "failed" : "", node.attrs["loading"] ? "loading" : "", style)
class: utils.getClassName(node.attrs, id$3)
})

@@ -1165,99 +939,84 @@ ];

],
view: (ctx) => (node, view, getPos) => {
const nodeType = node.type;
const createIcon = ctx.get(themeToolCtx).slots.icon;
const container = document.createElement("span");
container.className = utils.getClassName(node.attrs, id$3, containerStyle);
const content = document.createElement("img");
container.append(content);
let icon = createIcon("image");
const placeholder2 = document.createElement("span");
placeholder2.classList.add("placeholder");
container.append(icon, placeholder2);
const setIcon = (name) => {
const nextIcon = createIcon(name);
container.replaceChild(nextIcon, icon);
icon = nextIcon;
};
const loadImage = (src2) => {
container.classList.add("system", "loading");
setIcon("loading");
const img = document.createElement("img");
img.src = src2;
img.onerror = () => {
const pos = getPos();
if (!pos)
return;
const { tr } = view.state;
const _tr = tr.setNodeMarkup(pos, nodeType, __spreadProps(__spreadValues({}, node.attrs), {
src: src2,
loading: false,
failed: true
}));
view.dispatch(_tr);
};
img.onload = () => {
const { tr } = view.state;
const pos = getPos();
if (!pos)
return;
const _tr = tr.setNodeMarkup(pos, nodeType, __spreadProps(__spreadValues({}, node.attrs), {
width: img.width,
src: src2,
loading: false,
failed: false
}));
view.dispatch(_tr);
};
};
const { src, loading, title, alt, width } = node.attrs;
content.src = src;
content.title = title || alt;
content.alt = alt;
if (width) {
content.width = width;
view: () => (node) => {
var _a, _b;
let currNode = node;
const placeholder = (_a = options == null ? void 0 : options.placeholder) != null ? _a : "Add an Image";
const isBlock = (_b = options == null ? void 0 : options.isBlock) != null ? _b : false;
const renderer = utils.themeManager.get("image", {
placeholder,
isBlock
});
if (!renderer) {
return {};
}
if (src.length === 0) {
container.classList.add("system", "empty");
setIcon("image");
} else if (loading) {
loadImage(src);
}
const { dom, onUpdate } = renderer;
onUpdate(currNode);
return {
dom: container,
dom,
update: (updatedNode) => {
if (updatedNode.type.name !== id$3)
return false;
const { src: src2, alt: alt2, title: title2, loading: loading2, failed, width: width2 } = updatedNode.attrs;
content.src = src2;
content.alt = alt2;
content.title = title2 || alt2;
if (width2) {
content.width = width2;
}
if (loading2) {
loadImage(src2);
return true;
}
if (failed) {
container.classList.remove("loading", "empty");
container.classList.add("system", "failed");
setIcon("brokenImage");
return true;
}
if (src2.length > 0) {
container.classList.remove("system", "empty", "loading");
return true;
}
container.classList.add("system", "empty");
setIcon("image");
currNode = updatedNode;
onUpdate(currNode);
return true;
},
selectNode: () => {
container.classList.add("ProseMirror-selectednode");
dom.classList.add("ProseMirror-selectednode");
},
deselectNode: () => {
container.classList.remove("ProseMirror-selectednode");
dom.classList.remove("ProseMirror-selectednode");
}
};
},
prosePlugins: (type, ctx) => {
var _a, _b, _c;
const inputChipRenderer = utils.themeManager.get("input-chip", {
placeholder: (_b = (_a = options == null ? void 0 : options.input) == null ? void 0 : _a.placeholder) != null ? _b : "Input Image Link",
buttonText: (_c = options == null ? void 0 : options.input) == null ? void 0 : _c.buttonText,
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyImage, value);
}
});
const shouldDisplay = (view) => {
return Boolean(type && findSelectedNodeOfType(view.state.selection, type));
};
const getCurrentLink = (view) => {
const result = findSelectedNodeOfType(view.state.selection, type);
if (!result)
return;
const value = result.node.attrs["src"];
return value;
};
const renderByView = (view) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
return [
new Plugin({
key,
view: (editorView) => {
inputChipRenderer.init(editorView);
renderByView(editorView);
return {
update: (view, prevState) => {
const isEqualSelection = (prevState == null ? void 0 : prevState.doc.eq(view.state.doc)) && prevState.selection.eq(view.state.selection);
if (isEqualSelection)
return;
renderByView(view);
},
destroy: () => {
inputChipRenderer.destroy();
}
};
}
})
];
}

@@ -1270,54 +1029,39 @@ };

const LiftListItem = createCmdKey("LiftListItem");
const listItem = createNode((utils) => {
const style = utils.getStyle((themeTool, { css }) => css`
&,
& > * {
margin: 0.5rem 0;
}
&,
li {
&::marker {
color: ${themeTool.palette("primary")};
}
}
`);
return {
id: id$2,
schema: () => ({
group: "listItem",
content: "paragraph block*",
defining: true,
parseDOM: [{ tag: "li" }],
toDOM: (node) => ["li", { class: utils.getClassName(node.attrs, "list-item", style) }, 0],
parseMarkdown: {
match: ({ type, checked }) => type === "listItem" && checked === null,
runner: (state, node, type) => {
state.openNode(type);
state.next(node.children);
state.closeNode();
}
},
toMarkdown: {
match: (node) => node.type.name === id$2,
runner: (state, node) => {
state.openNode("listItem");
state.next(node.content);
state.closeNode();
}
const listItem = createNode((utils) => ({
id: id$2,
schema: () => ({
group: "listItem",
content: "paragraph block*",
defining: true,
parseDOM: [{ tag: "li" }],
toDOM: (node) => ["li", { class: utils.getClassName(node.attrs, "list-item") }, 0],
parseMarkdown: {
match: ({ type, checked }) => type === "listItem" && checked === null,
runner: (state, node, type) => {
state.openNode(type);
state.next(node.children);
state.closeNode();
}
}),
inputRules: (nodeType) => [wrappingInputRule(/^\s*([-+*])\s$/, nodeType)],
commands: (nodeType) => [
createCmd(SplitListItem, () => splitListItem(nodeType)),
createCmd(SinkListItem, () => sinkListItem(nodeType)),
createCmd(LiftListItem, () => liftListItem(nodeType))
],
shortcuts: {
[SupportedKeys.NextListItem]: createShortcut(SplitListItem, "Enter"),
[SupportedKeys.SinkListItem]: createShortcut(SinkListItem, "Mod-]"),
[SupportedKeys.LiftListItem]: createShortcut(LiftListItem, "Mod-[")
},
toMarkdown: {
match: (node) => node.type.name === id$2,
runner: (state, node) => {
state.openNode("listItem");
state.next(node.content);
state.closeNode();
}
}
};
});
}),
inputRules: (nodeType) => [wrappingInputRule(/^\s*([-+*])\s$/, nodeType)],
commands: (nodeType) => [
createCmd(SplitListItem, () => splitListItem(nodeType)),
createCmd(SinkListItem, () => sinkListItem(nodeType)),
createCmd(LiftListItem, () => liftListItem(nodeType))
],
shortcuts: {
[SupportedKeys.NextListItem]: createShortcut(SplitListItem, "Enter"),
[SupportedKeys.SinkListItem]: createShortcut(SinkListItem, "Mod-]"),
[SupportedKeys.LiftListItem]: createShortcut(LiftListItem, "Mod-[")
}
}));
const WrapInOrderedList = createCmdKey("WrapInOrderedList");

@@ -1379,9 +1123,2 @@ const id$1 = "ordered_list";

const paragraph = createNode((utils) => {
const style = utils.getStyle((_, { css }) => {
return css`
font-size: 1rem;
line-height: 1.5;
letter-spacing: 0.5px;
`;
});
return {

@@ -1393,3 +1130,3 @@ id,

parseDOM: [{ tag: "p" }],
toDOM: (node) => ["p", { class: utils.getClassName(node.attrs, id, style) }, 0],
toDOM: (node) => ["p", { class: utils.getClassName(node.attrs, id) }, 0],
parseMarkdown: {

@@ -1396,0 +1133,0 @@ match: (node) => node.type === "paragraph",

{
"name": "@milkdown/preset-commonmark",
"version": "5.5.0",
"version": "6.0.0-next.0",
"type": "module",

@@ -20,22 +20,45 @@ "main": "./lib/index.es.js",

"devDependencies": {
"@milkdown/core": "5.5.0",
"@milkdown/prose": "5.5.0",
"@milkdown/design-system": "5.5.0"
"@milkdown/core": "6.0.0-next.0",
"@milkdown/prose": "6.0.0-next.0"
},
"peerDependencies": {
"@milkdown/core": "^5.4.0",
"@milkdown/prose": "^5.4.0"
"@milkdown/core": "^6.0.0-next.0",
"@milkdown/prose": "^6.0.0-next.0"
},
"dependencies": {
"@milkdown/utils": "5.5.0",
"@milkdown/utils": "6.0.0-next.0",
"remark-inline-links": "^6.0.0",
"tslib": "^2.3.1"
},
"nx": {
"targets": {
"build": {
"outputs": [
"packages/preset-commonmark/lib"
],
"dependsOn": [
{
"target": "build",
"projects": "dependencies"
}
]
},
"tsc": {
"outputs": [],
"dependsOn": [
{
"target": "build",
"projects": "dependencies"
}
]
}
}
},
"scripts": {
"start": "vite build --watch",
"start": "concurrently -n es,dts \"vite build --watch\" \"tsc --emitDeclarationOnly --watch\"",
"test": "vitest",
"tsc": "tsc --noEmit",
"build": "vite build"
"build": "vite build && tsc --emitDeclarationOnly"
},
"readme": "# @milkdown/preset-commonmark\n\nCommon mark preset for [milkdown](https://saul-mirone.github.io/milkdown/).\nAdd support for commonmark.\n\n# Example Usage\n\n```typescript\nimport { Editor } from '@milkdown/core';\nimport { nord } from '@milkdown/theme-nord';\n\nimport { commonmark } from '@milkdown/preset-commonmark';\n\nEditor.make().use(nord).use(commonmark).create();\n```\n\n## Custom Keymap\n\n```typescript\nimport { commonmarkNodes, commonmarkPlugins, blockquote, SupportedKeys } from '@milkdown/preset-commonmark';\n\nconst nodes = commonmarkNodes.configure(blockquote, {\n keymap: {\n [SupportedKeys.Blockquote]: 'Mod-Shift-b',\n },\n});\n\nEditor.make().use(commonmarkPlugins).use(nodes);\n```\n\nKeymap supported:\n\n- HardBreak\n- Blockquote\n- BulletList\n- OrderedList\n- CodeFence\n- H1\n- H2\n- H3\n- H4\n- H5\n- H6\n- Text\n- CodeInline\n- Em\n- Bold\n- NextListItem\n- SinkListItem\n- LiftListItem\n\n## Custom Style\n\n```typescript\nimport { commonmark, Paragraph, Heading } from '@milkdown/commonmark';\n\nconst nodes = commonmark\n .configure(Paragraph, {\n className: () =>\n 'my-custom-paragraph'\n })\n .configure(Heading, {\n className: (attrs) =>\n `my-custom-heading my-h${attrs.level}`\n })\n\nnew Editor({ ... }).use(nodes);\n```\n\n## Other Options\n\n### Image\n\n- placeholder\n - loading: _string_. The placeholder of loading status.\n - empty: _string_. The placeholder of empty status.\n - failed: _string_. The placeholder of failed status.\n\n### CodeFence\n\n- languageList: _string[]_. The selectable languages list of code fence needs to be enabled.\n\n# License\n\nMilkdown is open sourced software licensed under [MIT license](https://github.com/Saul-Mirone/milkdown/blob/main/LICENSE).\n"
"readme": "# @milkdown/preset-commonmark\n\nCommon mark preset for [milkdown](https://saul-mirone.github.io/milkdown/).\nAdd support for commonmark.\n\n# Example Usage\n\n```typescript\nimport { Editor } from '@milkdown/core';\nimport { nord } from '@milkdown/theme-nord';\n\nimport { commonmark } from '@milkdown/preset-commonmark';\n\nEditor.make().use(nord).use(commonmark).create();\n```\n\n## Custom Keymap\n\n```typescript\nimport { commonmarkNodes, commonmarkPlugins, blockquote, SupportedKeys } from '@milkdown/preset-commonmark';\n\nconst nodes = commonmarkNodes.configure(blockquote, {\n keymap: {\n [SupportedKeys.Blockquote]: 'Mod-Shift-b',\n },\n});\n\nEditor.make().use(commonmarkPlugins).use(nodes);\n```\n\nKeymap supported:\n\n- HardBreak\n- Blockquote\n- BulletList\n- OrderedList\n- CodeFence\n- H1\n- H2\n- H3\n- H4\n- H5\n- H6\n- Text\n- CodeInline\n- Em\n- Bold\n- NextListItem\n- SinkListItem\n- LiftListItem\n\n## Custom Style\n\n```typescript\nimport { commonmark, Paragraph, Heading } from '@milkdown/commonmark';\n\nconst nodes = commonmark\n .configure(Paragraph, {\n className: () =>\n 'my-custom-paragraph'\n })\n .configure(Heading, {\n className: (attrs) =>\n `my-custom-heading my-h${attrs.level}`\n })\n\nnew Editor({ ... }).use(nodes);\n```\n\n## Other Options\n\n### Image\n\n- placeholder: The placeholder of empty status.\n- isBlock: Whether the image is a block (render as a row).\n- input:\n - placeholder: The placeholder of image url input.\n - buttonText: The button text of image url input.\n\n### CodeFence\n\n- languageList: _string[]_. The selectable languages list of code fence needs to be enabled.\n\n# License\n\nMilkdown is open sourced software licensed under [MIT license](https://github.com/Saul-Mirone/milkdown/blob/main/LICENSE).\n"
}

@@ -74,6 +74,7 @@ # @milkdown/preset-commonmark

- placeholder
- loading: _string_. The placeholder of loading status.
- empty: _string_. The placeholder of empty status.
- failed: _string_. The placeholder of failed status.
- placeholder: The placeholder of empty status.
- isBlock: Whether the image is a block (render as a row).
- input:
- placeholder: The placeholder of image url input.
- buttonText: The button text of image url input.

@@ -80,0 +81,0 @@ ### CodeFence

@@ -14,13 +14,2 @@ /* Copyright 2021, Milkdown by Mirone. */

export const codeInline = createMark<Keys>((utils) => {
const style = utils.getStyle(
({ palette, size, font }, { css }) =>
css`
background-color: ${palette('neutral')};
color: ${palette('background')};
border-radius: ${size.radius};
font-weight: 500;
font-family: ${font.code};
padding: 0 0.2rem;
`,
);
return {

@@ -33,3 +22,3 @@ id,

parseDOM: [{ tag: 'code' }],
toDOM: (mark) => ['code', { class: utils.getClassName(mark.attrs, 'code-inline', style) }],
toDOM: (mark) => ['code', { class: utils.getClassName(mark.attrs, 'code-inline') }],
parseMarkdown: {

@@ -36,0 +25,0 @@ match: (node) => node.type === 'inlineCode',

/* Copyright 2021, Milkdown by Mirone. */
import { createCmd, createCmdKey, schemaCtx } from '@milkdown/core';
import { InputRule, Node as ProseNode, TextSelection, toggleMark } from '@milkdown/prose';
import { commandsCtx, createCmd, createCmdKey, schemaCtx, ThemeInputChipType } from '@milkdown/core';
import {
EditorView,
InputRule,
Node as ProseNode,
Plugin,
PluginKey,
TextSelection,
toggleMark,
} from '@milkdown/prose';
import { createMark } from '@milkdown/utils';
const key = new PluginKey('MILKDOWN_PLUGIN_LINK_INPUT');
export const ToggleLink = createCmdKey<string>('ToggleLink');
export const ModifyLink = createCmdKey<string>('ModifyLink');
const id = 'link';
export const link = createMark((utils) => {
const style = utils.getStyle((themeTool, { css }) => {
const lineColor = themeTool.palette('line');
return css`
color: ${themeTool.palette('secondary')};
cursor: pointer;
transition: all 0.4s ease-in-out;
font-weight: 500;
&:hover {
background-color: ${lineColor};
box-shadow: 0 0.2rem ${lineColor}, 0 -0.2rem ${lineColor};
}
`;
});
export type LinkOptions = {
input: {
placeholder: string;
buttonText?: string;
};
};
export const link = createMark<string, LinkOptions>((utils, options) => {
return {

@@ -43,3 +45,3 @@ id,

],
toDOM: (mark) => ['a', { ...mark.attrs, class: utils.getClassName(mark.attrs, id, style) }],
toDOM: (mark) => ['a', { ...mark.attrs, class: utils.getClassName(mark.attrs, id) }],
parseMarkdown: {

@@ -119,3 +121,75 @@ match: (node) => node.type === 'link',

],
prosePlugins: (type, ctx) => {
const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
placeholder: options?.input?.placeholder ?? 'Input Web Link',
buttonText: options?.input?.buttonText,
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyLink, value);
},
});
const shouldDisplay = (view: EditorView) => {
const { selection, doc } = view.state;
const { from, to } = selection;
return (
selection.empty &&
selection instanceof TextSelection &&
doc.rangeHasMark(from, from === to ? to + 1 : to, type)
);
};
const getCurrentLink = (view: EditorView) => {
const { selection } = view.state;
let node: ProseNode | undefined;
const { from, to } = selection;
view.state.doc.nodesBetween(from, from === to ? to + 1 : to, (n) => {
if (type.isInSet(n.marks)) {
node = n;
return false;
}
return;
});
if (!node) return;
const mark = node.marks.find((m) => m.type === type);
if (!mark) return;
const value = mark.attrs['href'];
return value;
};
const renderByView = (view: EditorView) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
return [
new Plugin({
key,
view: (editorView) => {
inputChipRenderer.init(editorView);
renderByView(editorView);
return {
update: (view, prevState) => {
const isEqualSelection =
prevState?.doc.eq(view.state.doc) && prevState.selection.eq(view.state.selection);
if (isEqualSelection) return;
renderByView(view);
},
destroy: () => {
inputChipRenderer.destroy();
},
};
},
}),
];
},
};
});

@@ -12,8 +12,2 @@ /* Copyright 2021, Milkdown by Mirone. */

export const strong = createMark<Keys>((utils) => {
const style = utils.getStyle(
(_, { css }) =>
css`
font-weight: 600;
`,
);
return {

@@ -27,3 +21,3 @@ id,

],
toDOM: (mark) => ['strong', { class: utils.getClassName(mark.attrs, id, style) }],
toDOM: (mark) => ['strong', { class: utils.getClassName(mark.attrs, id) }],
parseMarkdown: {

@@ -30,0 +24,0 @@ match: (node) => node.type === 'strong',

@@ -15,15 +15,2 @@ /* Copyright 2021, Milkdown by Mirone. */

export const blockquote = createNode<Keys>((utils) => {
const style = utils.getStyle(
(themeTool, { css }) =>
css`
padding-left: 1.875rem;
line-height: 1.75rem;
border-left: 4px solid ${themeTool.palette('primary')};
* {
font-size: 1rem;
line-height: 1.5rem;
}
`,
);
return {

@@ -36,3 +23,3 @@ id,

parseDOM: [{ tag: 'blockquote' }],
toDOM: (node) => ['blockquote', { class: utils.getClassName(node.attrs, id, style) }, 0],
toDOM: (node) => ['blockquote', { class: utils.getClassName(node.attrs, id) }, 0],
parseMarkdown: {

@@ -39,0 +26,0 @@ match: ({ type }) => type === id,

/* Copyright 2021, Milkdown by Mirone. */
import { createCmd, createCmdKey, themeToolCtx } from '@milkdown/core';
import { createCmd, createCmdKey, editorViewCtx, ThemeCodeFenceType } from '@milkdown/core';
import { setBlockType, textblockTypeInputRule } from '@milkdown/prose';

@@ -36,115 +36,7 @@ import { createNode, createShortcut } from '@milkdown/utils';

export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, options) => {
const style = utils.getStyle(({ palette, mixin, size, font }, { css }) => {
const { shadow, scrollbar, border } = mixin;
const { lineWidth, radius } = size;
return css`
background-color: ${palette('background')};
color: ${palette('neutral')};
font-size: 0.85rem;
padding: 1.2rem 0.4rem 1.4rem;
border-radius: ${radius};
font-family: ${font.typography};
const languageList = options?.languageList || languageOptions;
* {
margin: 0;
}
.code-fence_select-wrapper {
position: relative;
}
.code-fence_value {
width: 10.25rem;
box-sizing: border-box;
border-radius: ${size.radius};
margin: 0 1.2rem 1.2rem;
${border()};
${shadow()};
cursor: pointer;
background-color: ${palette('surface')};
position: relative;
display: flex;
color: ${palette('neutral', 0.87)};
letter-spacing: 0.5px;
height: 2.625rem;
align-items: center;
& > .icon {
width: 2.625rem;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: ${palette('solid', 0.87)};
border-left: ${lineWidth} solid ${palette('line')};
text-align: center;
transition: all 0.2s ease-in-out;
&:hover {
background: ${palette('background')};
color: ${palette('primary')};
}
}
> span:first-child {
padding-left: 1rem;
flex: 1;
font-weight: 500;
}
}
.code-fence_select-option {
list-style: none;
line-height: 2rem;
padding-left: 1rem;
cursor: pointer;
:hover {
background: ${palette('secondary', 0.12)};
color: ${palette('primary')};
}
}
.code-fence_select {
&[data-fold='true'] {
display: none;
}
font-weight: 500;
position: absolute;
z-index: 1;
top: 2.625rem;
box-sizing: border-box;
left: 1.2rem;
padding: 0.5rem 0;
max-height: 16.75rem;
width: 10.25rem;
${border()};
${shadow()};
background-color: ${palette('surface')};
border-top: none;
overflow-y: auto;
display: flex;
flex-direction: column;
${scrollbar('y')}
}
code {
line-height: 1.5;
font-family: ${font.code};
}
pre {
font-family: ${font.code};
margin: 0 1.2rem !important;
white-space: pre;
overflow: auto;
${scrollbar('x')};
}
`;
});
return {
id,
schema: () => ({
schema: (ctx) => ({
content: 'text*',

@@ -176,9 +68,50 @@ group: 'block',

toDOM: (node) => {
const select = document.createElement('select');
languageList.forEach((lang) => {
const option = document.createElement('option');
option.value = lang;
option.innerText = !lang ? '--' : lang;
if (lang === node.attrs['language']) {
option.selected = true;
}
select.appendChild(option);
});
select.onchange = (e) => {
const target = e.target;
if (!(target instanceof HTMLSelectElement)) {
return;
}
const view = ctx.get(editorViewCtx);
if (!view.editable) {
target.value = node.attrs['language'];
return;
}
const { top, left } = target.getBoundingClientRect();
const result = view.posAtCoords({ top, left });
if (!result) return;
const { tr } = view.state;
view.dispatch(
tr.setNodeMarkup(result.inside, undefined, {
...node.attrs,
language: target.value,
}),
);
};
return [
'pre',
'div',
{
'data-language': node.attrs['language'],
class: utils.getClassName(node.attrs, 'code-fence', style),
class: 'code-fence-container',
},
['code', { spellCheck: 'false' }, 0],
select,
[
'pre',
{
'data-language': node.attrs['language'],
class: utils.getClassName(node.attrs, 'code-fence'),
},
['code', { spellCheck: 'false' }, 0],
],
];

@@ -223,40 +156,15 @@ },

},
view: (ctx) => (node, view, getPos) => {
const container = document.createElement('div');
const selectWrapper = document.createElement('div');
const select = document.createElement('ul');
const pre = document.createElement('pre');
const code = document.createElement('code');
view: () => (node, view, getPos) => {
let currNode = node;
const valueWrapper = document.createElement('div');
valueWrapper.className = 'code-fence_value';
const value = document.createElement('span');
valueWrapper.appendChild(value);
if (view.editable) {
valueWrapper.appendChild(ctx.get(themeToolCtx).slots.icon('downArrow'));
}
select.className = 'code-fence_select';
select.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
if (!view.editable) return;
const el = e.target;
if (!(el instanceof HTMLLIElement)) return;
const onSelectLanguage = (language: string) => {
const { tr } = view.state;
view.dispatch(
tr.setNodeMarkup(getPos(), undefined, {
fold: true,
language: el.dataset['value'],
language,
}),
);
});
valueWrapper.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
if (!view.editable) return;
};
const onBlur = () => {
const { tr } = view.state;

@@ -266,10 +174,8 @@

tr.setNodeMarkup(getPos(), undefined, {
fold: false,
language: container.dataset['language'],
...currNode.attrs,
fold: true,
}),
);
});
document.addEventListener('mousedown', () => {
if (!view.editable || select.dataset['fold'] === 'true') return;
};
const onFocus = () => {
const { tr } = view.state;

@@ -279,45 +185,31 @@

tr.setNodeMarkup(getPos(), undefined, {
fold: true,
language: container.dataset['language'],
...currNode.attrs,
fold: false,
}),
);
});
};
(options?.languageList || languageOptions).forEach((lang) => {
const option = document.createElement('li');
option.className = 'code-fence_select-option';
option.innerText = lang || '--';
select.appendChild(option);
option.setAttribute('data-value', lang);
const renderer = utils.themeManager.get<ThemeCodeFenceType>('code-fence', {
onBlur,
onFocus,
onSelectLanguage,
editable: () => view.editable,
languageList,
});
if (!renderer) return {};
code.spellcheck = false;
selectWrapper.className = 'code-fence_select-wrapper';
selectWrapper.contentEditable = 'false';
selectWrapper.append(valueWrapper);
selectWrapper.append(select);
pre.append(code);
const codeContent = document.createElement('div');
code.append(codeContent);
codeContent.style.whiteSpace = 'inherit';
const { dom, contentDOM, onUpdate, onDestroy } = renderer;
onUpdate(currNode);
container.append(selectWrapper, pre);
container.setAttribute('class', utils.getClassName(node.attrs, 'code-fence', style));
container.setAttribute('data-language', node.attrs['language']);
value.innerText = node.attrs['language'] || '--';
select.setAttribute('data-fold', node.attrs['fold'] ? 'true' : 'false');
return {
dom: container,
contentDOM: codeContent,
dom,
contentDOM,
update: (updatedNode) => {
if (updatedNode.type.name !== id) return false;
currNode = updatedNode;
onUpdate(currNode);
const lang = updatedNode.attrs['language'];
container.dataset['language'] = lang;
value.innerText = lang || '--';
select.setAttribute('data-fold', updatedNode.attrs['fold'] ? 'true' : 'false');
return true;
},
destroy: onDestroy,
};

@@ -324,0 +216,0 @@ },

@@ -35,38 +35,2 @@ /* Copyright 2021, Milkdown by Mirone. */

const style = (level: number) =>
utils.getStyle((_, { css }) => {
const headingMap: Record<number, string> = {
1: css`
font-size: 3rem;
line-height: 3.5rem;
`,
2: css`
font-size: 2.5rem;
line-height: 3rem;
`,
3: css`
font-size: 2.125rem;
line-height: 2.25rem;
`,
4: css`
font-size: 1.75rem;
line-height: 2rem;
`,
5: css`
font-size: 1.5rem;
line-height: 1.5rem;
`,
6: css`
font-size: 1.25rem;
line-height: 1.25rem;
`,
};
return css`
${headingMap[level] || ''}
margin: 2.5rem 0 !important;
font-weight: 400;
`;
});
return {

@@ -100,7 +64,3 @@ id,

id: node.attrs['id'] || node.textContent.split(' ').join('-').toLocaleLowerCase(),
class: utils.getClassName(
node.attrs,
`heading h${node.attrs['level']}`,
style(node.attrs['level']),
),
class: utils.getClassName(node.attrs, `heading h${node.attrs['level']}`),
},

@@ -107,0 +67,0 @@ 0,

@@ -9,9 +9,2 @@ /* Copyright 2021, Milkdown by Mirone. */

export const hr = createNode((utils) => {
const style = utils.getStyle(
(themeTool, { css }) => css`
height: ${themeTool.size.lineWidth};
background-color: ${themeTool.palette('line')};
border-width: 0;
`,
);
return {

@@ -22,3 +15,3 @@ id,

parseDOM: [{ tag: 'hr' }],
toDOM: (node) => ['hr', { class: utils.getClassName(node.attrs, id, style) }],
toDOM: (node) => ['hr', { class: utils.getClassName(node.attrs, id) }],
parseMarkdown: {

@@ -25,0 +18,0 @@ match: ({ type }) => type === 'thematicBreak',

/* Copyright 2021, Milkdown by Mirone. */
import { createCmd, createCmdKey, themeToolCtx } from '@milkdown/core';
import type { Icon } from '@milkdown/design-system';
import { findSelectedNodeOfType, InputRule } from '@milkdown/prose';
import { commandsCtx, createCmd, createCmdKey, ThemeImageType, ThemeInputChipType } from '@milkdown/core';
import { EditorView, findSelectedNodeOfType, InputRule, Plugin, PluginKey } from '@milkdown/prose';
import { createNode } from '@milkdown/utils';

@@ -12,128 +11,11 @@

isBlock: boolean;
placeholder: {
loading: string;
empty: string;
failed: string;
placeholder: string;
input: {
placeholder: string;
buttonText?: string;
};
};
const key = new PluginKey('MILKDOWN_PLUGIN_IMAGE_INPUT');
export const image = createNode<string, ImageOptions>((utils, options) => {
const placeholder = {
loading: 'Loading...',
empty: 'Add an Image',
failed: 'Image loads failed',
...(options?.placeholder ?? {}),
};
const isBlock = options?.isBlock ?? false;
const containerStyle = utils.getStyle(
(themeTool, { css }) =>
css`
display: inline-block;
position: relative;
text-align: center;
font-size: 0;
vertical-align: text-bottom;
line-height: 1;
${isBlock
? `
width: 100%;
margin: 0 auto;
`
: ''}
&.ProseMirror-selectednode::after {
content: '';
background: ${themeTool.palette('secondary', 0.38)};
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
img {
max-width: 100%;
height: auto;
object-fit: contain;
margin: 0 2px;
}
.icon,
.placeholder {
display: none;
}
&.system {
width: 100%;
padding: 0 2rem;
img {
width: 0;
height: 0;
display: none;
}
.icon,
.placeholder {
display: inline;
}
box-sizing: border-box;
height: 3rem;
background-color: ${themeTool.palette('background')};
border-radius: ${themeTool.size.radius};
display: inline-flex;
gap: 2rem;
justify-content: flex-start;
align-items: center;
.placeholder {
margin: 0;
line-height: 1;
&::before {
content: '';
font-size: 0.875rem;
color: ${themeTool.palette('neutral', 0.6)};
}
}
}
&.loading {
.placeholder {
&::before {
content: '${placeholder.loading}';
}
}
}
&.empty {
.placeholder {
&::before {
content: '${placeholder.empty}';
}
}
}
&.failed {
.placeholder {
&::before {
content: '${placeholder.failed}';
}
}
}
`,
);
const style = utils.getStyle(
(_, { css }) =>
css`
display: inline-block;
margin: 0 auto;
object-fit: contain;
width: 100%;
position: relative;
height: auto;
text-align: center;
`,
);
return {

@@ -154,5 +36,2 @@ id: 'image',

title: { default: null },
failed: { default: false },
loading: { default: true },
width: { default: null },
},

@@ -167,4 +46,2 @@ parseDOM: [

return {
failed: dom.classList.contains('failed'),
loading: dom.classList.contains('loading'),
src: dom.getAttribute('src') || '',

@@ -183,9 +60,3 @@ alt: dom.getAttribute('alt'),

...node.attrs,
class: utils.getClassName(
node.attrs,
id,
node.attrs['failed'] ? 'failed' : '',
node.attrs['loading'] ? 'loading' : '',
style,
),
class: utils.getClassName(node.attrs, id),
},

@@ -256,114 +127,91 @@ ];

],
view: (ctx) => (node, view, getPos) => {
const nodeType = node.type;
const createIcon = ctx.get(themeToolCtx).slots.icon;
const container = document.createElement('span');
container.className = utils.getClassName(node.attrs, id, containerStyle);
view: () => (node) => {
let currNode = node;
const content = document.createElement('img');
const placeholder = options?.placeholder ?? 'Add an Image';
const isBlock = options?.isBlock ?? false;
const renderer = utils.themeManager.get<ThemeImageType>('image', {
placeholder,
isBlock,
});
container.append(content);
let icon = createIcon('image');
const placeholder = document.createElement('span');
placeholder.classList.add('placeholder');
container.append(icon, placeholder);
const setIcon = (name: Icon) => {
const nextIcon = createIcon(name);
container.replaceChild(nextIcon, icon);
icon = nextIcon;
};
const loadImage = (src: string) => {
container.classList.add('system', 'loading');
setIcon('loading');
const img = document.createElement('img');
img.src = src;
img.onerror = () => {
const pos = getPos();
if (!pos) return;
const { tr } = view.state;
const _tr = tr.setNodeMarkup(pos, nodeType, {
...node.attrs,
src,
loading: false,
failed: true,
});
view.dispatch(_tr);
};
img.onload = () => {
const { tr } = view.state;
const pos = getPos();
if (!pos) return;
const _tr = tr.setNodeMarkup(pos, nodeType, {
...node.attrs,
width: img.width,
src,
loading: false,
failed: false,
});
view.dispatch(_tr);
};
};
const { src, loading, title, alt, width } = node.attrs;
content.src = src;
content.title = title || alt;
content.alt = alt;
if (width) {
content.width = width;
if (!renderer) {
return {};
}
if (src.length === 0) {
container.classList.add('system', 'empty');
setIcon('image');
} else if (loading) {
loadImage(src);
}
const { dom, onUpdate } = renderer;
onUpdate(currNode);
return {
dom: container,
dom,
update: (updatedNode) => {
if (updatedNode.type.name !== id) return false;
const { src, alt, title, loading, failed, width } = updatedNode.attrs;
content.src = src;
content.alt = alt;
content.title = title || alt;
if (width) {
content.width = width;
}
if (loading) {
loadImage(src);
return true;
}
if (failed) {
container.classList.remove('loading', 'empty');
container.classList.add('system', 'failed');
setIcon('brokenImage');
return true;
}
if (src.length > 0) {
container.classList.remove('system', 'empty', 'loading');
return true;
}
currNode = updatedNode;
onUpdate(currNode);
container.classList.add('system', 'empty');
setIcon('image');
return true;
},
selectNode: () => {
container.classList.add('ProseMirror-selectednode');
dom.classList.add('ProseMirror-selectednode');
},
deselectNode: () => {
container.classList.remove('ProseMirror-selectednode');
dom.classList.remove('ProseMirror-selectednode');
},
};
},
prosePlugins: (type, ctx) => {
const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
placeholder: options?.input?.placeholder ?? 'Input Image Link',
buttonText: options?.input?.buttonText,
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyImage, value);
},
});
const shouldDisplay = (view: EditorView) => {
return Boolean(type && findSelectedNodeOfType(view.state.selection, type));
};
const getCurrentLink = (view: EditorView) => {
const result = findSelectedNodeOfType(view.state.selection, type);
if (!result) return;
const value = result.node.attrs['src'];
return value;
};
const renderByView = (view: EditorView) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
return [
new Plugin({
key,
view: (editorView) => {
inputChipRenderer.init(editorView);
renderByView(editorView);
return {
update: (view, prevState) => {
const isEqualSelection =
prevState?.doc.eq(view.state.doc) && prevState.selection.eq(view.state.selection);
if (isEqualSelection) return;
renderByView(view);
},
destroy: () => {
inputChipRenderer.destroy();
},
};
},
}),
];
},
};
});

@@ -16,57 +16,38 @@ /* Copyright 2021, Milkdown by Mirone. */

export const listItem = createNode<Keys>((utils) => {
const style = utils.getStyle(
(themeTool, { css }) =>
css`
&,
& > * {
margin: 0.5rem 0;
}
&,
li {
&::marker {
color: ${themeTool.palette('primary')};
}
}
`,
);
return {
id,
schema: () => ({
group: 'listItem',
content: 'paragraph block*',
defining: true,
parseDOM: [{ tag: 'li' }],
toDOM: (node) => ['li', { class: utils.getClassName(node.attrs, 'list-item', style) }, 0],
parseMarkdown: {
match: ({ type, checked }) => type === 'listItem' && checked === null,
runner: (state, node, type) => {
state.openNode(type);
state.next(node.children);
state.closeNode();
},
export const listItem = createNode<Keys>((utils) => ({
id,
schema: () => ({
group: 'listItem',
content: 'paragraph block*',
defining: true,
parseDOM: [{ tag: 'li' }],
toDOM: (node) => ['li', { class: utils.getClassName(node.attrs, 'list-item') }, 0],
parseMarkdown: {
match: ({ type, checked }) => type === 'listItem' && checked === null,
runner: (state, node, type) => {
state.openNode(type);
state.next(node.children);
state.closeNode();
},
toMarkdown: {
match: (node) => node.type.name === id,
runner: (state, node) => {
state.openNode('listItem');
state.next(node.content);
state.closeNode();
},
},
toMarkdown: {
match: (node) => node.type.name === id,
runner: (state, node) => {
state.openNode('listItem');
state.next(node.content);
state.closeNode();
},
}),
inputRules: (nodeType) => [wrappingInputRule(/^\s*([-+*])\s$/, nodeType)],
commands: (nodeType) => [
createCmd(SplitListItem, () => splitListItem(nodeType)),
createCmd(SinkListItem, () => sinkListItem(nodeType)),
createCmd(LiftListItem, () => liftListItem(nodeType)),
],
shortcuts: {
[SupportedKeys.NextListItem]: createShortcut(SplitListItem, 'Enter'),
[SupportedKeys.SinkListItem]: createShortcut(SinkListItem, 'Mod-]'),
[SupportedKeys.LiftListItem]: createShortcut(LiftListItem, 'Mod-['),
},
};
});
}),
inputRules: (nodeType) => [wrappingInputRule(/^\s*([-+*])\s$/, nodeType)],
commands: (nodeType) => [
createCmd(SplitListItem, () => splitListItem(nodeType)),
createCmd(SinkListItem, () => sinkListItem(nodeType)),
createCmd(LiftListItem, () => liftListItem(nodeType)),
],
shortcuts: {
[SupportedKeys.NextListItem]: createShortcut(SplitListItem, 'Enter'),
[SupportedKeys.SinkListItem]: createShortcut(SinkListItem, 'Mod-]'),
[SupportedKeys.LiftListItem]: createShortcut(LiftListItem, 'Mod-['),
},
}));

@@ -14,10 +14,2 @@ /* Copyright 2021, Milkdown by Mirone. */

export const paragraph = createNode<Keys>((utils) => {
const style = utils.getStyle((_, { css }) => {
return css`
font-size: 1rem;
line-height: 1.5;
letter-spacing: 0.5px;
`;
});
return {

@@ -29,3 +21,3 @@ id,

parseDOM: [{ tag: 'p' }],
toDOM: (node) => ['p', { class: utils.getClassName(node.attrs, id, style) }, 0],
toDOM: (node) => ['p', { class: utils.getClassName(node.attrs, id) }, 0],
parseMarkdown: {

@@ -32,0 +24,0 @@ match: (node) => node.type === 'paragraph',

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc