@railway/inkwell 1.3.0 → 2.0.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/dist/index.cjs +93 -4
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +93 -4
- package/package.json +1 -1
- package/src/styles.css +117 -47
package/dist/index.cjs
CHANGED
|
@@ -2020,6 +2020,20 @@ var InkwellEditorClient = react.forwardRef(
|
|
|
2020
2020
|
selectEditor,
|
|
2021
2021
|
stateVersion
|
|
2022
2022
|
]);
|
|
2023
|
+
const renderPlaceholder = react.useCallback(
|
|
2024
|
+
({ attributes, children }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2025
|
+
"span",
|
|
2026
|
+
{
|
|
2027
|
+
...attributes,
|
|
2028
|
+
style: {
|
|
2029
|
+
...attributes.style,
|
|
2030
|
+
paddingBottom: "2rem"
|
|
2031
|
+
},
|
|
2032
|
+
children
|
|
2033
|
+
}
|
|
2034
|
+
),
|
|
2035
|
+
[]
|
|
2036
|
+
);
|
|
2023
2037
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2024
2038
|
"div",
|
|
2025
2039
|
{
|
|
@@ -2067,6 +2081,7 @@ var InkwellEditorClient = react.forwardRef(
|
|
|
2067
2081
|
style: styles?.editor,
|
|
2068
2082
|
renderElement: RenderElement,
|
|
2069
2083
|
renderLeaf: RenderLeaf,
|
|
2084
|
+
renderPlaceholder,
|
|
2070
2085
|
decorate,
|
|
2071
2086
|
placeholder: resolvedPlaceholder,
|
|
2072
2087
|
spellCheck: true,
|
|
@@ -3148,6 +3163,71 @@ function CopyCodeBlock({
|
|
|
3148
3163
|
/* @__PURE__ */ jsxRuntime.jsx("pre", { ref: preRef, ...props, children })
|
|
3149
3164
|
] });
|
|
3150
3165
|
}
|
|
3166
|
+
function splitTextOnNewlines(text) {
|
|
3167
|
+
if (!text.value.includes("\n")) return [text];
|
|
3168
|
+
const parts = text.value.split("\n");
|
|
3169
|
+
const result = [];
|
|
3170
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3171
|
+
if (parts[i] !== "") {
|
|
3172
|
+
result.push({ type: "text", value: parts[i] });
|
|
3173
|
+
}
|
|
3174
|
+
if (i < parts.length - 1) {
|
|
3175
|
+
result.push({ type: "break" });
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
return result;
|
|
3179
|
+
}
|
|
3180
|
+
function expandParagraphChildren(children) {
|
|
3181
|
+
let changed = false;
|
|
3182
|
+
const next = [];
|
|
3183
|
+
for (const child of children) {
|
|
3184
|
+
if (child.type === "text" && child.value.includes("\n")) {
|
|
3185
|
+
next.push(...splitTextOnNewlines(child));
|
|
3186
|
+
changed = true;
|
|
3187
|
+
} else {
|
|
3188
|
+
next.push(child);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
return changed ? next : null;
|
|
3192
|
+
}
|
|
3193
|
+
function remarkSoftBreakAsBreak() {
|
|
3194
|
+
return (tree) => {
|
|
3195
|
+
unistUtilVisit.visit(tree, "paragraph", (node) => {
|
|
3196
|
+
const expanded = expandParagraphChildren(node.children);
|
|
3197
|
+
if (expanded) node.children = expanded;
|
|
3198
|
+
});
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
function remarkSoftBreakAsParagraph() {
|
|
3202
|
+
return (tree) => {
|
|
3203
|
+
unistUtilVisit.visit(tree, "paragraph", (node, index, parent) => {
|
|
3204
|
+
if (!parent || index == null) return;
|
|
3205
|
+
const expanded = expandParagraphChildren(node.children) ?? node.children.slice();
|
|
3206
|
+
const breakIndices = [];
|
|
3207
|
+
for (let i = 0; i < expanded.length; i++) {
|
|
3208
|
+
if (expanded[i].type === "break") breakIndices.push(i);
|
|
3209
|
+
}
|
|
3210
|
+
if (breakIndices.length === 0) {
|
|
3211
|
+
return;
|
|
3212
|
+
}
|
|
3213
|
+
const newParagraphs = [];
|
|
3214
|
+
let start = 0;
|
|
3215
|
+
for (const breakIdx of [...breakIndices, expanded.length]) {
|
|
3216
|
+
if (breakIdx > start) {
|
|
3217
|
+
newParagraphs.push({
|
|
3218
|
+
type: "paragraph",
|
|
3219
|
+
children: expanded.slice(start, breakIdx)
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
start = breakIdx + 1;
|
|
3223
|
+
}
|
|
3224
|
+
parent.children.splice(index, 1, ...newParagraphs);
|
|
3225
|
+
return [unistUtilVisit.SKIP, index + newParagraphs.length];
|
|
3226
|
+
});
|
|
3227
|
+
};
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
// src/renderer/markdown-parser.ts
|
|
3151
3231
|
var MENTION_TAG_PREFIX = "inkwell-mention-";
|
|
3152
3232
|
function rehypeMentions(mentions) {
|
|
3153
3233
|
return () => (tree) => {
|
|
@@ -3210,7 +3290,14 @@ function rehypeMentions(mentions) {
|
|
|
3210
3290
|
};
|
|
3211
3291
|
}
|
|
3212
3292
|
function createProcessor2(options = {}) {
|
|
3213
|
-
const proc = unified.unified().use(remarkParse__default.default).use(remarkGfm__default.default).use(remarkNoTables).use(remarkFlattenBlockquotes)
|
|
3293
|
+
const proc = unified.unified().use(remarkParse__default.default).use(remarkGfm__default.default).use(remarkNoTables).use(remarkFlattenBlockquotes);
|
|
3294
|
+
const softBreak = options.softBreak ?? "paragraph";
|
|
3295
|
+
if (softBreak === "br") {
|
|
3296
|
+
proc.use(remarkSoftBreakAsBreak);
|
|
3297
|
+
} else if (softBreak === "paragraph") {
|
|
3298
|
+
proc.use(remarkSoftBreakAsParagraph);
|
|
3299
|
+
}
|
|
3300
|
+
proc.use(remarkRehype__default.default);
|
|
3214
3301
|
const plugins = options.rehypePlugins ?? [
|
|
3215
3302
|
[rehypeHighlight__default.default, { detect: true }]
|
|
3216
3303
|
];
|
|
@@ -3269,7 +3356,8 @@ function InkwellRenderer({
|
|
|
3269
3356
|
className,
|
|
3270
3357
|
components,
|
|
3271
3358
|
rehypePlugins,
|
|
3272
|
-
mentions
|
|
3359
|
+
mentions,
|
|
3360
|
+
softBreak
|
|
3273
3361
|
}) {
|
|
3274
3362
|
const mergedComponents = react.useMemo(
|
|
3275
3363
|
() => ({ pre: CopyCodeBlock, ...components }),
|
|
@@ -3279,9 +3367,10 @@ function InkwellRenderer({
|
|
|
3279
3367
|
() => parseMarkdown(content, {
|
|
3280
3368
|
components: mergedComponents,
|
|
3281
3369
|
rehypePlugins,
|
|
3282
|
-
mentions
|
|
3370
|
+
mentions,
|
|
3371
|
+
softBreak
|
|
3283
3372
|
}),
|
|
3284
|
-
[content, mergedComponents, rehypePlugins, mentions]
|
|
3373
|
+
[content, mergedComponents, rehypePlugins, mentions, softBreak]
|
|
3285
3374
|
);
|
|
3286
3375
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inkwell-renderer ${className ?? ""}`, children: rendered });
|
|
3287
3376
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -110,6 +110,16 @@ interface InkwellEditorProps {
|
|
|
110
110
|
/** Called when submitOnEnter handles Enter. */
|
|
111
111
|
onSubmit?: (content: string) => void;
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* How `InkwellRenderer` handles single-newline soft breaks in source markdown.
|
|
115
|
+
* - `"paragraph"` (default): split the enclosing paragraph at the soft break,
|
|
116
|
+
* producing two `<p>` elements with normal paragraph margins.
|
|
117
|
+
* - `"br"`: emit a `<br />`. Matches GFM-style behavior (and Showdown with
|
|
118
|
+
* `simpleLineBreaks: true`).
|
|
119
|
+
* - `"preserve"`: keep as a literal `\n` text node. The browser collapses it
|
|
120
|
+
* to whitespace per CSS. Matches strict CommonMark.
|
|
121
|
+
*/
|
|
122
|
+
type InkwellSoftBreakBehavior = "preserve" | "br" | "paragraph";
|
|
113
123
|
interface InkwellRendererProps {
|
|
114
124
|
/** Markdown source content string. */
|
|
115
125
|
content: string;
|
|
@@ -121,11 +131,15 @@ interface InkwellRendererProps {
|
|
|
121
131
|
rehypePlugins?: RehypePluginConfig[];
|
|
122
132
|
/** Mention patterns to expand in rendered text. */
|
|
123
133
|
mentions?: MentionRenderer[];
|
|
134
|
+
/** How to render single-newline soft breaks. Defaults to `"paragraph"`. */
|
|
135
|
+
softBreak?: InkwellSoftBreakBehavior;
|
|
124
136
|
}
|
|
125
137
|
interface ParseMarkdownOptions {
|
|
126
138
|
components?: InkwellComponents;
|
|
127
139
|
rehypePlugins?: RehypePluginConfig[];
|
|
128
140
|
mentions?: MentionRenderer[];
|
|
141
|
+
/** How to render single-newline soft breaks. Defaults to `"paragraph"`. */
|
|
142
|
+
softBreak?: InkwellSoftBreakBehavior;
|
|
129
143
|
}
|
|
130
144
|
interface MentionRenderer {
|
|
131
145
|
/** Regular expression applied to text-node content. */
|
|
@@ -482,11 +496,11 @@ declare function createSnippetsPlugin({ snippets, name, trigger, }: SnippetsPlug
|
|
|
482
496
|
*/
|
|
483
497
|
declare function htmlToMarkdown(html: string): string;
|
|
484
498
|
|
|
485
|
-
declare function InkwellRenderer({ content, className, components, rehypePlugins, mentions, }: InkwellRendererProps): ReactNode;
|
|
499
|
+
declare function InkwellRenderer({ content, className, components, rehypePlugins, mentions, softBreak, }: InkwellRendererProps): ReactNode;
|
|
486
500
|
|
|
487
501
|
/**
|
|
488
502
|
* Parse a markdown string into React elements synchronously
|
|
489
503
|
*/
|
|
490
504
|
declare function parseMarkdown(content: string, options?: ParseMarkdownOptions): ReactNode;
|
|
491
505
|
|
|
492
|
-
export { type Attachment, type AttachmentUploadResult, type AttachmentsHandle, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
|
506
|
+
export { type Attachment, type AttachmentUploadResult, type AttachmentsHandle, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type InkwellSoftBreakBehavior, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,16 @@ interface InkwellEditorProps {
|
|
|
110
110
|
/** Called when submitOnEnter handles Enter. */
|
|
111
111
|
onSubmit?: (content: string) => void;
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* How `InkwellRenderer` handles single-newline soft breaks in source markdown.
|
|
115
|
+
* - `"paragraph"` (default): split the enclosing paragraph at the soft break,
|
|
116
|
+
* producing two `<p>` elements with normal paragraph margins.
|
|
117
|
+
* - `"br"`: emit a `<br />`. Matches GFM-style behavior (and Showdown with
|
|
118
|
+
* `simpleLineBreaks: true`).
|
|
119
|
+
* - `"preserve"`: keep as a literal `\n` text node. The browser collapses it
|
|
120
|
+
* to whitespace per CSS. Matches strict CommonMark.
|
|
121
|
+
*/
|
|
122
|
+
type InkwellSoftBreakBehavior = "preserve" | "br" | "paragraph";
|
|
113
123
|
interface InkwellRendererProps {
|
|
114
124
|
/** Markdown source content string. */
|
|
115
125
|
content: string;
|
|
@@ -121,11 +131,15 @@ interface InkwellRendererProps {
|
|
|
121
131
|
rehypePlugins?: RehypePluginConfig[];
|
|
122
132
|
/** Mention patterns to expand in rendered text. */
|
|
123
133
|
mentions?: MentionRenderer[];
|
|
134
|
+
/** How to render single-newline soft breaks. Defaults to `"paragraph"`. */
|
|
135
|
+
softBreak?: InkwellSoftBreakBehavior;
|
|
124
136
|
}
|
|
125
137
|
interface ParseMarkdownOptions {
|
|
126
138
|
components?: InkwellComponents;
|
|
127
139
|
rehypePlugins?: RehypePluginConfig[];
|
|
128
140
|
mentions?: MentionRenderer[];
|
|
141
|
+
/** How to render single-newline soft breaks. Defaults to `"paragraph"`. */
|
|
142
|
+
softBreak?: InkwellSoftBreakBehavior;
|
|
129
143
|
}
|
|
130
144
|
interface MentionRenderer {
|
|
131
145
|
/** Regular expression applied to text-node content. */
|
|
@@ -482,11 +496,11 @@ declare function createSnippetsPlugin({ snippets, name, trigger, }: SnippetsPlug
|
|
|
482
496
|
*/
|
|
483
497
|
declare function htmlToMarkdown(html: string): string;
|
|
484
498
|
|
|
485
|
-
declare function InkwellRenderer({ content, className, components, rehypePlugins, mentions, }: InkwellRendererProps): ReactNode;
|
|
499
|
+
declare function InkwellRenderer({ content, className, components, rehypePlugins, mentions, softBreak, }: InkwellRendererProps): ReactNode;
|
|
486
500
|
|
|
487
501
|
/**
|
|
488
502
|
* Parse a markdown string into React elements synchronously
|
|
489
503
|
*/
|
|
490
504
|
declare function parseMarkdown(content: string, options?: ParseMarkdownOptions): ReactNode;
|
|
491
505
|
|
|
492
|
-
export { type Attachment, type AttachmentUploadResult, type AttachmentsHandle, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
|
506
|
+
export { type Attachment, type AttachmentUploadResult, type AttachmentsHandle, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type InkwellSoftBreakBehavior, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
package/dist/index.js
CHANGED
|
@@ -2005,6 +2005,20 @@ var InkwellEditorClient = forwardRef(
|
|
|
2005
2005
|
selectEditor,
|
|
2006
2006
|
stateVersion
|
|
2007
2007
|
]);
|
|
2008
|
+
const renderPlaceholder = useCallback(
|
|
2009
|
+
({ attributes, children }) => /* @__PURE__ */ jsx(
|
|
2010
|
+
"span",
|
|
2011
|
+
{
|
|
2012
|
+
...attributes,
|
|
2013
|
+
style: {
|
|
2014
|
+
...attributes.style,
|
|
2015
|
+
paddingBottom: "2rem"
|
|
2016
|
+
},
|
|
2017
|
+
children
|
|
2018
|
+
}
|
|
2019
|
+
),
|
|
2020
|
+
[]
|
|
2021
|
+
);
|
|
2008
2022
|
return /* @__PURE__ */ jsxs(
|
|
2009
2023
|
"div",
|
|
2010
2024
|
{
|
|
@@ -2052,6 +2066,7 @@ var InkwellEditorClient = forwardRef(
|
|
|
2052
2066
|
style: styles?.editor,
|
|
2053
2067
|
renderElement: RenderElement,
|
|
2054
2068
|
renderLeaf: RenderLeaf,
|
|
2069
|
+
renderPlaceholder,
|
|
2055
2070
|
decorate,
|
|
2056
2071
|
placeholder: resolvedPlaceholder,
|
|
2057
2072
|
spellCheck: true,
|
|
@@ -3133,6 +3148,71 @@ function CopyCodeBlock({
|
|
|
3133
3148
|
/* @__PURE__ */ jsx("pre", { ref: preRef, ...props, children })
|
|
3134
3149
|
] });
|
|
3135
3150
|
}
|
|
3151
|
+
function splitTextOnNewlines(text) {
|
|
3152
|
+
if (!text.value.includes("\n")) return [text];
|
|
3153
|
+
const parts = text.value.split("\n");
|
|
3154
|
+
const result = [];
|
|
3155
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3156
|
+
if (parts[i] !== "") {
|
|
3157
|
+
result.push({ type: "text", value: parts[i] });
|
|
3158
|
+
}
|
|
3159
|
+
if (i < parts.length - 1) {
|
|
3160
|
+
result.push({ type: "break" });
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
return result;
|
|
3164
|
+
}
|
|
3165
|
+
function expandParagraphChildren(children) {
|
|
3166
|
+
let changed = false;
|
|
3167
|
+
const next = [];
|
|
3168
|
+
for (const child of children) {
|
|
3169
|
+
if (child.type === "text" && child.value.includes("\n")) {
|
|
3170
|
+
next.push(...splitTextOnNewlines(child));
|
|
3171
|
+
changed = true;
|
|
3172
|
+
} else {
|
|
3173
|
+
next.push(child);
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
return changed ? next : null;
|
|
3177
|
+
}
|
|
3178
|
+
function remarkSoftBreakAsBreak() {
|
|
3179
|
+
return (tree) => {
|
|
3180
|
+
visit(tree, "paragraph", (node) => {
|
|
3181
|
+
const expanded = expandParagraphChildren(node.children);
|
|
3182
|
+
if (expanded) node.children = expanded;
|
|
3183
|
+
});
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
function remarkSoftBreakAsParagraph() {
|
|
3187
|
+
return (tree) => {
|
|
3188
|
+
visit(tree, "paragraph", (node, index, parent) => {
|
|
3189
|
+
if (!parent || index == null) return;
|
|
3190
|
+
const expanded = expandParagraphChildren(node.children) ?? node.children.slice();
|
|
3191
|
+
const breakIndices = [];
|
|
3192
|
+
for (let i = 0; i < expanded.length; i++) {
|
|
3193
|
+
if (expanded[i].type === "break") breakIndices.push(i);
|
|
3194
|
+
}
|
|
3195
|
+
if (breakIndices.length === 0) {
|
|
3196
|
+
return;
|
|
3197
|
+
}
|
|
3198
|
+
const newParagraphs = [];
|
|
3199
|
+
let start = 0;
|
|
3200
|
+
for (const breakIdx of [...breakIndices, expanded.length]) {
|
|
3201
|
+
if (breakIdx > start) {
|
|
3202
|
+
newParagraphs.push({
|
|
3203
|
+
type: "paragraph",
|
|
3204
|
+
children: expanded.slice(start, breakIdx)
|
|
3205
|
+
});
|
|
3206
|
+
}
|
|
3207
|
+
start = breakIdx + 1;
|
|
3208
|
+
}
|
|
3209
|
+
parent.children.splice(index, 1, ...newParagraphs);
|
|
3210
|
+
return [SKIP, index + newParagraphs.length];
|
|
3211
|
+
});
|
|
3212
|
+
};
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
// src/renderer/markdown-parser.ts
|
|
3136
3216
|
var MENTION_TAG_PREFIX = "inkwell-mention-";
|
|
3137
3217
|
function rehypeMentions(mentions) {
|
|
3138
3218
|
return () => (tree) => {
|
|
@@ -3195,7 +3275,14 @@ function rehypeMentions(mentions) {
|
|
|
3195
3275
|
};
|
|
3196
3276
|
}
|
|
3197
3277
|
function createProcessor2(options = {}) {
|
|
3198
|
-
const proc = unified().use(remarkParse).use(remarkGfm).use(remarkNoTables).use(remarkFlattenBlockquotes)
|
|
3278
|
+
const proc = unified().use(remarkParse).use(remarkGfm).use(remarkNoTables).use(remarkFlattenBlockquotes);
|
|
3279
|
+
const softBreak = options.softBreak ?? "paragraph";
|
|
3280
|
+
if (softBreak === "br") {
|
|
3281
|
+
proc.use(remarkSoftBreakAsBreak);
|
|
3282
|
+
} else if (softBreak === "paragraph") {
|
|
3283
|
+
proc.use(remarkSoftBreakAsParagraph);
|
|
3284
|
+
}
|
|
3285
|
+
proc.use(remarkRehype);
|
|
3199
3286
|
const plugins = options.rehypePlugins ?? [
|
|
3200
3287
|
[rehypeHighlight, { detect: true }]
|
|
3201
3288
|
];
|
|
@@ -3254,7 +3341,8 @@ function InkwellRenderer({
|
|
|
3254
3341
|
className,
|
|
3255
3342
|
components,
|
|
3256
3343
|
rehypePlugins,
|
|
3257
|
-
mentions
|
|
3344
|
+
mentions,
|
|
3345
|
+
softBreak
|
|
3258
3346
|
}) {
|
|
3259
3347
|
const mergedComponents = useMemo(
|
|
3260
3348
|
() => ({ pre: CopyCodeBlock, ...components }),
|
|
@@ -3264,9 +3352,10 @@ function InkwellRenderer({
|
|
|
3264
3352
|
() => parseMarkdown(content, {
|
|
3265
3353
|
components: mergedComponents,
|
|
3266
3354
|
rehypePlugins,
|
|
3267
|
-
mentions
|
|
3355
|
+
mentions,
|
|
3356
|
+
softBreak
|
|
3268
3357
|
}),
|
|
3269
|
-
[content, mergedComponents, rehypePlugins, mentions]
|
|
3358
|
+
[content, mergedComponents, rehypePlugins, mentions, softBreak]
|
|
3270
3359
|
);
|
|
3271
3360
|
return /* @__PURE__ */ jsx("div", { className: `inkwell-renderer ${className ?? ""}`, children: rendered });
|
|
3272
3361
|
}
|
package/package.json
CHANGED
package/src/styles.css
CHANGED
|
@@ -22,11 +22,18 @@
|
|
|
22
22
|
|
|
23
23
|
/* ── Tokens ──────────────────────────────────────────────────────── */
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
.inkwell-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
.
|
|
25
|
+
/* Token definitions sit at 0,0,0 specificity so a consumer rule with even
|
|
26
|
+
a single class on a parent (e.g. `.dark .inkwell-renderer { --inkwell-text: ... }`
|
|
27
|
+
for class-driven theming that does not rely on `prefers-color-scheme`)
|
|
28
|
+
beats them automatically. The OS-level dark-mode block below uses the
|
|
29
|
+
same `:where()` wrapper for the same reason. */
|
|
30
|
+
:where(
|
|
31
|
+
.inkwell-editor,
|
|
32
|
+
.inkwell-editor-wrapper,
|
|
33
|
+
.inkwell-renderer,
|
|
34
|
+
.inkwell-plugin-bubble-menu-container,
|
|
35
|
+
.inkwell-plugin-picker-popup
|
|
36
|
+
) {
|
|
30
37
|
/* Surfaces */
|
|
31
38
|
--inkwell-bg: hsl(0, 0%, 100%);
|
|
32
39
|
--inkwell-bg-elevated: hsl(0, 0%, 100%);
|
|
@@ -55,14 +62,44 @@
|
|
|
55
62
|
"JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo,
|
|
56
63
|
Consolas, monospace;
|
|
57
64
|
--inkwell-radius: 6px;
|
|
65
|
+
|
|
66
|
+
/* Typography & spacing. Defined once and consumed by both
|
|
67
|
+
`.inkwell-editor` and `.inkwell-renderer` so the two surfaces stay
|
|
68
|
+
WYSIWYG — what you type matches what renders. Override any token
|
|
69
|
+
on either surface to retune both (or use a per-surface selector
|
|
70
|
+
to retune just one). For chat-composer or compact embeds, set
|
|
71
|
+
`--inkwell-space-paragraph: 0` on the editor surface. */
|
|
72
|
+
--inkwell-font-size: 0.95rem;
|
|
73
|
+
--inkwell-line-height: 1.6;
|
|
74
|
+
--inkwell-heading-weight: 600;
|
|
75
|
+
--inkwell-heading-line-height: 1.3;
|
|
76
|
+
--inkwell-h1-size: 1.75em;
|
|
77
|
+
--inkwell-h2-size: 1.4em;
|
|
78
|
+
--inkwell-h3-size: 1.2em;
|
|
79
|
+
--inkwell-h4-size: 1em;
|
|
80
|
+
--inkwell-h5-size: 0.9em;
|
|
81
|
+
--inkwell-h6-size: 0.8em;
|
|
82
|
+
--inkwell-code-font-size: 0.85em;
|
|
83
|
+
|
|
84
|
+
--inkwell-space-paragraph: 0.5em;
|
|
85
|
+
--inkwell-space-heading: 0.75em;
|
|
86
|
+
--inkwell-space-blockquote: 1em;
|
|
87
|
+
--inkwell-space-list: 1em;
|
|
88
|
+
--inkwell-space-list-item: 0.25em;
|
|
89
|
+
--inkwell-list-indent: 1.5em;
|
|
90
|
+
--inkwell-space-code-block: 1em;
|
|
91
|
+
--inkwell-space-image: 1em;
|
|
92
|
+
--inkwell-space-hr: 2em;
|
|
58
93
|
}
|
|
59
94
|
|
|
60
95
|
@media (prefers-color-scheme: dark) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
96
|
+
:where(
|
|
97
|
+
.inkwell-editor,
|
|
98
|
+
.inkwell-editor-wrapper,
|
|
99
|
+
.inkwell-renderer,
|
|
100
|
+
.inkwell-plugin-bubble-menu-container,
|
|
101
|
+
.inkwell-plugin-picker-popup
|
|
102
|
+
) {
|
|
66
103
|
--inkwell-bg: hsl(220, 13%, 10%);
|
|
67
104
|
--inkwell-bg-elevated: hsl(220, 13%, 13%);
|
|
68
105
|
--inkwell-bg-subtle: hsl(220, 13%, 16%);
|
|
@@ -109,8 +146,8 @@
|
|
|
109
146
|
border-radius: var(--inkwell-radius);
|
|
110
147
|
background: var(--inkwell-bg);
|
|
111
148
|
color: var(--inkwell-text);
|
|
112
|
-
line-height:
|
|
113
|
-
font-size:
|
|
149
|
+
line-height: var(--inkwell-line-height);
|
|
150
|
+
font-size: var(--inkwell-font-size);
|
|
114
151
|
transition: border-color 0.15s ease;
|
|
115
152
|
}
|
|
116
153
|
:where(.inkwell-editor:focus-within) {
|
|
@@ -118,8 +155,20 @@
|
|
|
118
155
|
}
|
|
119
156
|
|
|
120
157
|
/* `position: relative` on paragraphs is structural — Slate decorations and
|
|
121
|
-
inline children position against it.
|
|
122
|
-
|
|
158
|
+
inline children position against it.
|
|
159
|
+
|
|
160
|
+
Margin stays at `0` here even though the renderer's paragraphs use
|
|
161
|
+
`--inkwell-space-paragraph`. Reason: the editor's content model emits
|
|
162
|
+
one `<p>` per source line, so a blank line in Markdown becomes an
|
|
163
|
+
empty `<p>` node between two paragraphs (a cursor target, kept for
|
|
164
|
+
round-trip fidelity). With a non-zero paragraph margin, those empty
|
|
165
|
+
paragraphs add their own top/bottom margin on top of the real
|
|
166
|
+
paragraphs' margins — visually multiplying the gap and breaking the
|
|
167
|
+
WYSIWYG promise in the other direction (editor looks more airy than
|
|
168
|
+
the renderer). Until the empty-paragraph encoding is reworked, the
|
|
169
|
+
editor opts out of the shared paragraph-spacing token. Consumers
|
|
170
|
+
who want non-zero spacing in the editor can set the margin
|
|
171
|
+
themselves with a higher-specificity rule. */
|
|
123
172
|
.inkwell-editor p {
|
|
124
173
|
position: relative;
|
|
125
174
|
}
|
|
@@ -143,42 +192,43 @@
|
|
|
143
192
|
padding: 0.1em 0.35em;
|
|
144
193
|
border-radius: 4px;
|
|
145
194
|
font-family: var(--inkwell-font-mono);
|
|
146
|
-
font-size:
|
|
195
|
+
font-size: var(--inkwell-code-font-size);
|
|
147
196
|
}
|
|
148
197
|
|
|
149
198
|
:where(.inkwell-editor-blockquote) {
|
|
150
199
|
border-left: 3px solid var(--inkwell-border-strong);
|
|
151
200
|
padding-left: 0.85em;
|
|
152
|
-
margin:
|
|
201
|
+
margin: var(--inkwell-space-blockquote) 0;
|
|
153
202
|
color: var(--inkwell-text-muted);
|
|
154
203
|
}
|
|
155
204
|
|
|
156
205
|
:where(.inkwell-editor-heading) {
|
|
157
|
-
font-weight:
|
|
158
|
-
line-height:
|
|
206
|
+
font-weight: var(--inkwell-heading-weight);
|
|
207
|
+
line-height: var(--inkwell-heading-line-height);
|
|
208
|
+
margin: var(--inkwell-space-heading) 0;
|
|
159
209
|
color: var(--inkwell-text);
|
|
160
210
|
}
|
|
161
211
|
:where(.inkwell-editor-heading-1) {
|
|
162
|
-
font-size:
|
|
212
|
+
font-size: var(--inkwell-h1-size);
|
|
163
213
|
}
|
|
164
214
|
:where(.inkwell-editor-heading-2) {
|
|
165
|
-
font-size:
|
|
215
|
+
font-size: var(--inkwell-h2-size);
|
|
166
216
|
}
|
|
167
217
|
:where(.inkwell-editor-heading-3) {
|
|
168
|
-
font-size:
|
|
218
|
+
font-size: var(--inkwell-h3-size);
|
|
169
219
|
}
|
|
170
220
|
:where(.inkwell-editor-heading-4) {
|
|
171
|
-
font-size:
|
|
221
|
+
font-size: var(--inkwell-h4-size);
|
|
172
222
|
}
|
|
173
223
|
:where(.inkwell-editor-heading-5) {
|
|
174
|
-
font-size:
|
|
224
|
+
font-size: var(--inkwell-h5-size);
|
|
175
225
|
}
|
|
176
226
|
:where(.inkwell-editor-heading-6) {
|
|
177
|
-
font-size:
|
|
227
|
+
font-size: var(--inkwell-h6-size);
|
|
178
228
|
}
|
|
179
229
|
|
|
180
230
|
:where(.inkwell-editor-image) {
|
|
181
|
-
margin:
|
|
231
|
+
margin: var(--inkwell-space-image) 0;
|
|
182
232
|
border-radius: var(--inkwell-radius);
|
|
183
233
|
overflow: hidden;
|
|
184
234
|
border: 1px solid transparent;
|
|
@@ -248,7 +298,7 @@
|
|
|
248
298
|
:where(.inkwell-editor .inkwell-editor-code-fence),
|
|
249
299
|
:where(.inkwell-renderer pre code) {
|
|
250
300
|
font-family: var(--inkwell-font-mono);
|
|
251
|
-
font-size:
|
|
301
|
+
font-size: var(--inkwell-code-font-size);
|
|
252
302
|
line-height: 1.55;
|
|
253
303
|
}
|
|
254
304
|
/* Wrapping behavior for code lines stays structural — Slate emits one
|
|
@@ -433,40 +483,61 @@
|
|
|
433
483
|
`!important`. */
|
|
434
484
|
:where(.inkwell-renderer) {
|
|
435
485
|
color: var(--inkwell-text);
|
|
436
|
-
line-height:
|
|
437
|
-
font-size:
|
|
486
|
+
line-height: var(--inkwell-line-height);
|
|
487
|
+
font-size: var(--inkwell-font-size);
|
|
438
488
|
}
|
|
439
489
|
:where(.inkwell-renderer :first-child) {
|
|
440
490
|
margin-top: 0;
|
|
441
491
|
}
|
|
442
492
|
:where(.inkwell-renderer h1) {
|
|
443
|
-
font-size:
|
|
444
|
-
font-weight:
|
|
445
|
-
|
|
493
|
+
font-size: var(--inkwell-h1-size);
|
|
494
|
+
font-weight: var(--inkwell-heading-weight);
|
|
495
|
+
line-height: var(--inkwell-heading-line-height);
|
|
496
|
+
margin: var(--inkwell-space-heading) 0;
|
|
446
497
|
}
|
|
447
498
|
:where(.inkwell-renderer h2) {
|
|
448
|
-
font-size:
|
|
449
|
-
font-weight:
|
|
450
|
-
|
|
499
|
+
font-size: var(--inkwell-h2-size);
|
|
500
|
+
font-weight: var(--inkwell-heading-weight);
|
|
501
|
+
line-height: var(--inkwell-heading-line-height);
|
|
502
|
+
margin: var(--inkwell-space-heading) 0;
|
|
451
503
|
}
|
|
452
504
|
:where(.inkwell-renderer h3) {
|
|
453
|
-
font-size:
|
|
454
|
-
font-weight:
|
|
455
|
-
|
|
505
|
+
font-size: var(--inkwell-h3-size);
|
|
506
|
+
font-weight: var(--inkwell-heading-weight);
|
|
507
|
+
line-height: var(--inkwell-heading-line-height);
|
|
508
|
+
margin: var(--inkwell-space-heading) 0;
|
|
509
|
+
}
|
|
510
|
+
:where(.inkwell-renderer h4) {
|
|
511
|
+
font-size: var(--inkwell-h4-size);
|
|
512
|
+
font-weight: var(--inkwell-heading-weight);
|
|
513
|
+
line-height: var(--inkwell-heading-line-height);
|
|
514
|
+
margin: var(--inkwell-space-heading) 0;
|
|
515
|
+
}
|
|
516
|
+
:where(.inkwell-renderer h5) {
|
|
517
|
+
font-size: var(--inkwell-h5-size);
|
|
518
|
+
font-weight: var(--inkwell-heading-weight);
|
|
519
|
+
line-height: var(--inkwell-heading-line-height);
|
|
520
|
+
margin: var(--inkwell-space-heading) 0;
|
|
521
|
+
}
|
|
522
|
+
:where(.inkwell-renderer h6) {
|
|
523
|
+
font-size: var(--inkwell-h6-size);
|
|
524
|
+
font-weight: var(--inkwell-heading-weight);
|
|
525
|
+
line-height: var(--inkwell-heading-line-height);
|
|
526
|
+
margin: var(--inkwell-space-heading) 0;
|
|
456
527
|
}
|
|
457
528
|
:where(.inkwell-renderer p) {
|
|
458
|
-
margin:
|
|
529
|
+
margin: var(--inkwell-space-paragraph) 0;
|
|
459
530
|
}
|
|
460
531
|
:where(.inkwell-renderer blockquote) {
|
|
461
532
|
border-left: 3px solid var(--inkwell-border-strong);
|
|
462
533
|
padding-left: 0.85em;
|
|
463
|
-
margin:
|
|
534
|
+
margin: var(--inkwell-space-blockquote) 0;
|
|
464
535
|
color: var(--inkwell-text-muted);
|
|
465
536
|
}
|
|
466
537
|
:where(.inkwell-renderer ul),
|
|
467
538
|
:where(.inkwell-renderer ol) {
|
|
468
|
-
padding-left:
|
|
469
|
-
margin:
|
|
539
|
+
padding-left: var(--inkwell-list-indent);
|
|
540
|
+
margin: var(--inkwell-space-list) 0;
|
|
470
541
|
}
|
|
471
542
|
:where(.inkwell-renderer ul) {
|
|
472
543
|
list-style: disc;
|
|
@@ -475,7 +546,7 @@
|
|
|
475
546
|
list-style: decimal;
|
|
476
547
|
}
|
|
477
548
|
:where(.inkwell-renderer li) {
|
|
478
|
-
margin:
|
|
549
|
+
margin: var(--inkwell-space-list-item) 0;
|
|
479
550
|
}
|
|
480
551
|
:where(.inkwell-renderer code) {
|
|
481
552
|
background: var(--inkwell-code-bg);
|
|
@@ -483,7 +554,7 @@
|
|
|
483
554
|
padding: 0.1em 0.35em;
|
|
484
555
|
border-radius: 4px;
|
|
485
556
|
font-family: var(--inkwell-font-mono);
|
|
486
|
-
font-size:
|
|
557
|
+
font-size: var(--inkwell-code-font-size);
|
|
487
558
|
}
|
|
488
559
|
/* Code-block wrapper position is structural — the copy button absolutely
|
|
489
560
|
positions inside it. */
|
|
@@ -524,7 +595,7 @@
|
|
|
524
595
|
color: var(--inkwell-text);
|
|
525
596
|
}
|
|
526
597
|
:where(.inkwell-renderer pre) {
|
|
527
|
-
margin:
|
|
598
|
+
margin: var(--inkwell-space-code-block) 0;
|
|
528
599
|
border-radius: var(--inkwell-radius);
|
|
529
600
|
overflow: auto;
|
|
530
601
|
border: 1px solid var(--inkwell-border);
|
|
@@ -535,7 +606,6 @@
|
|
|
535
606
|
padding: 0.85em 1em;
|
|
536
607
|
background: transparent;
|
|
537
608
|
color: var(--inkwell-text);
|
|
538
|
-
font-size: 0.82em;
|
|
539
609
|
}
|
|
540
610
|
:where(.inkwell-renderer a) {
|
|
541
611
|
color: var(--inkwell-accent);
|
|
@@ -545,7 +615,7 @@
|
|
|
545
615
|
:where(.inkwell-renderer hr) {
|
|
546
616
|
border: none;
|
|
547
617
|
border-top: 1px solid var(--inkwell-border);
|
|
548
|
-
margin:
|
|
618
|
+
margin: var(--inkwell-space-hr) 0;
|
|
549
619
|
}
|
|
550
620
|
:where(.inkwell-renderer strong) {
|
|
551
621
|
font-weight: 600;
|
|
@@ -560,5 +630,5 @@
|
|
|
560
630
|
max-width: 100%;
|
|
561
631
|
height: auto;
|
|
562
632
|
border-radius: var(--inkwell-radius);
|
|
563
|
-
margin:
|
|
633
|
+
margin: var(--inkwell-space-image) 0;
|
|
564
634
|
}
|