@railway/inkwell 1.4.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 CHANGED
@@ -3163,6 +3163,71 @@ function CopyCodeBlock({
3163
3163
  /* @__PURE__ */ jsxRuntime.jsx("pre", { ref: preRef, ...props, children })
3164
3164
  ] });
3165
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
3166
3231
  var MENTION_TAG_PREFIX = "inkwell-mention-";
3167
3232
  function rehypeMentions(mentions) {
3168
3233
  return () => (tree) => {
@@ -3225,7 +3290,14 @@ function rehypeMentions(mentions) {
3225
3290
  };
3226
3291
  }
3227
3292
  function createProcessor2(options = {}) {
3228
- const proc = unified.unified().use(remarkParse__default.default).use(remarkGfm__default.default).use(remarkNoTables).use(remarkFlattenBlockquotes).use(remarkRehype__default.default);
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);
3229
3301
  const plugins = options.rehypePlugins ?? [
3230
3302
  [rehypeHighlight__default.default, { detect: true }]
3231
3303
  ];
@@ -3284,7 +3356,8 @@ function InkwellRenderer({
3284
3356
  className,
3285
3357
  components,
3286
3358
  rehypePlugins,
3287
- mentions
3359
+ mentions,
3360
+ softBreak
3288
3361
  }) {
3289
3362
  const mergedComponents = react.useMemo(
3290
3363
  () => ({ pre: CopyCodeBlock, ...components }),
@@ -3294,9 +3367,10 @@ function InkwellRenderer({
3294
3367
  () => parseMarkdown(content, {
3295
3368
  components: mergedComponents,
3296
3369
  rehypePlugins,
3297
- mentions
3370
+ mentions,
3371
+ softBreak
3298
3372
  }),
3299
- [content, mergedComponents, rehypePlugins, mentions]
3373
+ [content, mergedComponents, rehypePlugins, mentions, softBreak]
3300
3374
  );
3301
3375
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inkwell-renderer ${className ?? ""}`, children: rendered });
3302
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
@@ -3148,6 +3148,71 @@ function CopyCodeBlock({
3148
3148
  /* @__PURE__ */ jsx("pre", { ref: preRef, ...props, children })
3149
3149
  ] });
3150
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
3151
3216
  var MENTION_TAG_PREFIX = "inkwell-mention-";
3152
3217
  function rehypeMentions(mentions) {
3153
3218
  return () => (tree) => {
@@ -3210,7 +3275,14 @@ function rehypeMentions(mentions) {
3210
3275
  };
3211
3276
  }
3212
3277
  function createProcessor2(options = {}) {
3213
- const proc = unified().use(remarkParse).use(remarkGfm).use(remarkNoTables).use(remarkFlattenBlockquotes).use(remarkRehype);
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);
3214
3286
  const plugins = options.rehypePlugins ?? [
3215
3287
  [rehypeHighlight, { detect: true }]
3216
3288
  ];
@@ -3269,7 +3341,8 @@ function InkwellRenderer({
3269
3341
  className,
3270
3342
  components,
3271
3343
  rehypePlugins,
3272
- mentions
3344
+ mentions,
3345
+ softBreak
3273
3346
  }) {
3274
3347
  const mergedComponents = useMemo(
3275
3348
  () => ({ pre: CopyCodeBlock, ...components }),
@@ -3279,9 +3352,10 @@ function InkwellRenderer({
3279
3352
  () => parseMarkdown(content, {
3280
3353
  components: mergedComponents,
3281
3354
  rehypePlugins,
3282
- mentions
3355
+ mentions,
3356
+ softBreak
3283
3357
  }),
3284
- [content, mergedComponents, rehypePlugins, mentions]
3358
+ [content, mergedComponents, rehypePlugins, mentions, softBreak]
3285
3359
  );
3286
3360
  return /* @__PURE__ */ jsx("div", { className: `inkwell-renderer ${className ?? ""}`, children: rendered });
3287
3361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@railway/inkwell",
3
- "version": "1.4.0",
3
+ "version": "2.0.0",
4
4
  "description": "Inkwell is a Markdown editor and renderer for React with an extensible plugin system.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/styles.css CHANGED
@@ -62,6 +62,34 @@
62
62
  "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo,
63
63
  Consolas, monospace;
64
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;
65
93
  }
66
94
 
67
95
  @media (prefers-color-scheme: dark) {
@@ -118,8 +146,8 @@
118
146
  border-radius: var(--inkwell-radius);
119
147
  background: var(--inkwell-bg);
120
148
  color: var(--inkwell-text);
121
- line-height: 1.6;
122
- font-size: 0.95rem;
149
+ line-height: var(--inkwell-line-height);
150
+ font-size: var(--inkwell-font-size);
123
151
  transition: border-color 0.15s ease;
124
152
  }
125
153
  :where(.inkwell-editor:focus-within) {
@@ -127,8 +155,20 @@
127
155
  }
128
156
 
129
157
  /* `position: relative` on paragraphs is structural — Slate decorations and
130
- inline children position against it. Margin is theming and goes through
131
- `:where()` so a consumer paragraph-spacing utility wins. */
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. */
132
172
  .inkwell-editor p {
133
173
  position: relative;
134
174
  }
@@ -152,42 +192,43 @@
152
192
  padding: 0.1em 0.35em;
153
193
  border-radius: 4px;
154
194
  font-family: var(--inkwell-font-mono);
155
- font-size: 0.85em;
195
+ font-size: var(--inkwell-code-font-size);
156
196
  }
157
197
 
158
198
  :where(.inkwell-editor-blockquote) {
159
199
  border-left: 3px solid var(--inkwell-border-strong);
160
200
  padding-left: 0.85em;
161
- margin: 0.5em 0;
201
+ margin: var(--inkwell-space-blockquote) 0;
162
202
  color: var(--inkwell-text-muted);
163
203
  }
164
204
 
165
205
  :where(.inkwell-editor-heading) {
166
- font-weight: 600;
167
- line-height: 1.3;
206
+ font-weight: var(--inkwell-heading-weight);
207
+ line-height: var(--inkwell-heading-line-height);
208
+ margin: var(--inkwell-space-heading) 0;
168
209
  color: var(--inkwell-text);
169
210
  }
170
211
  :where(.inkwell-editor-heading-1) {
171
- font-size: 1.75em;
212
+ font-size: var(--inkwell-h1-size);
172
213
  }
173
214
  :where(.inkwell-editor-heading-2) {
174
- font-size: 1.4em;
215
+ font-size: var(--inkwell-h2-size);
175
216
  }
176
217
  :where(.inkwell-editor-heading-3) {
177
- font-size: 1.2em;
218
+ font-size: var(--inkwell-h3-size);
178
219
  }
179
220
  :where(.inkwell-editor-heading-4) {
180
- font-size: 1em;
221
+ font-size: var(--inkwell-h4-size);
181
222
  }
182
223
  :where(.inkwell-editor-heading-5) {
183
- font-size: 0.9em;
224
+ font-size: var(--inkwell-h5-size);
184
225
  }
185
226
  :where(.inkwell-editor-heading-6) {
186
- font-size: 0.8em;
227
+ font-size: var(--inkwell-h6-size);
187
228
  }
188
229
 
189
230
  :where(.inkwell-editor-image) {
190
- margin: 0.75em 0;
231
+ margin: var(--inkwell-space-image) 0;
191
232
  border-radius: var(--inkwell-radius);
192
233
  overflow: hidden;
193
234
  border: 1px solid transparent;
@@ -257,7 +298,7 @@
257
298
  :where(.inkwell-editor .inkwell-editor-code-fence),
258
299
  :where(.inkwell-renderer pre code) {
259
300
  font-family: var(--inkwell-font-mono);
260
- font-size: 0.85em;
301
+ font-size: var(--inkwell-code-font-size);
261
302
  line-height: 1.55;
262
303
  }
263
304
  /* Wrapping behavior for code lines stays structural — Slate emits one
@@ -442,40 +483,61 @@
442
483
  `!important`. */
443
484
  :where(.inkwell-renderer) {
444
485
  color: var(--inkwell-text);
445
- line-height: 1.65;
446
- font-size: 0.95rem;
486
+ line-height: var(--inkwell-line-height);
487
+ font-size: var(--inkwell-font-size);
447
488
  }
448
489
  :where(.inkwell-renderer :first-child) {
449
490
  margin-top: 0;
450
491
  }
451
492
  :where(.inkwell-renderer h1) {
452
- font-size: 1.75em;
453
- font-weight: 600;
454
- margin: 0.67em 0;
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;
455
497
  }
456
498
  :where(.inkwell-renderer h2) {
457
- font-size: 1.4em;
458
- font-weight: 600;
459
- margin: 0.75em 0;
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;
460
503
  }
461
504
  :where(.inkwell-renderer h3) {
462
- font-size: 1.2em;
463
- font-weight: 600;
464
- margin: 0.8em 0;
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;
465
527
  }
466
528
  :where(.inkwell-renderer p) {
467
- margin: 0.5em 0;
529
+ margin: var(--inkwell-space-paragraph) 0;
468
530
  }
469
531
  :where(.inkwell-renderer blockquote) {
470
532
  border-left: 3px solid var(--inkwell-border-strong);
471
533
  padding-left: 0.85em;
472
- margin: 1em 0;
534
+ margin: var(--inkwell-space-blockquote) 0;
473
535
  color: var(--inkwell-text-muted);
474
536
  }
475
537
  :where(.inkwell-renderer ul),
476
538
  :where(.inkwell-renderer ol) {
477
- padding-left: 1.5em;
478
- margin: 1em 0;
539
+ padding-left: var(--inkwell-list-indent);
540
+ margin: var(--inkwell-space-list) 0;
479
541
  }
480
542
  :where(.inkwell-renderer ul) {
481
543
  list-style: disc;
@@ -484,7 +546,7 @@
484
546
  list-style: decimal;
485
547
  }
486
548
  :where(.inkwell-renderer li) {
487
- margin: 0.25em 0;
549
+ margin: var(--inkwell-space-list-item) 0;
488
550
  }
489
551
  :where(.inkwell-renderer code) {
490
552
  background: var(--inkwell-code-bg);
@@ -492,7 +554,7 @@
492
554
  padding: 0.1em 0.35em;
493
555
  border-radius: 4px;
494
556
  font-family: var(--inkwell-font-mono);
495
- font-size: 0.85em;
557
+ font-size: var(--inkwell-code-font-size);
496
558
  }
497
559
  /* Code-block wrapper position is structural — the copy button absolutely
498
560
  positions inside it. */
@@ -533,7 +595,7 @@
533
595
  color: var(--inkwell-text);
534
596
  }
535
597
  :where(.inkwell-renderer pre) {
536
- margin: 1em 0;
598
+ margin: var(--inkwell-space-code-block) 0;
537
599
  border-radius: var(--inkwell-radius);
538
600
  overflow: auto;
539
601
  border: 1px solid var(--inkwell-border);
@@ -544,7 +606,6 @@
544
606
  padding: 0.85em 1em;
545
607
  background: transparent;
546
608
  color: var(--inkwell-text);
547
- font-size: 0.82em;
548
609
  }
549
610
  :where(.inkwell-renderer a) {
550
611
  color: var(--inkwell-accent);
@@ -554,7 +615,7 @@
554
615
  :where(.inkwell-renderer hr) {
555
616
  border: none;
556
617
  border-top: 1px solid var(--inkwell-border);
557
- margin: 2em 0;
618
+ margin: var(--inkwell-space-hr) 0;
558
619
  }
559
620
  :where(.inkwell-renderer strong) {
560
621
  font-weight: 600;
@@ -569,5 +630,5 @@
569
630
  max-width: 100%;
570
631
  height: auto;
571
632
  border-radius: var(--inkwell-radius);
572
- margin: 1em 0;
633
+ margin: var(--inkwell-space-image) 0;
573
634
  }