@soroush.tech/markdown 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- [![npm version](https://img.shields.io/npm/v/@soroush.tech/markdown.svg)](https://www.npmjs.com/package/@soroush.tech/markdown)
2
- [![npm downloads](https://img.shields.io/npm/dm/@soroush.tech/markdown.svg)](https://www.npmjs.com/package/@soroush.tech/markdown)
1
+ [![npm version](https://img.shields.io/npm/v/@soroush.tech/markdown.svg?cacheSeconds=86400)](https://www.npmjs.com/package/@soroush.tech/markdown)
2
+ [![npm downloads](https://img.shields.io/npm/dm/@soroush.tech/markdown.svg?cacheSeconds=86400)](https://www.npmjs.com/package/@soroush.tech/markdown)
3
3
  [![coverage](https://codecov.io/gh/soroush-tech/core/branch/main/graph/badge.svg?flag=markdown)](https://app.codecov.io/gh/soroush-tech/core?flags%5B0%5D=markdown)
4
- [![unpacked size](https://img.shields.io/npm/unpacked-size/@soroush.tech/markdown.svg)](https://www.npmjs.com/package/@soroush.tech/markdown)
5
- [![types included](https://img.shields.io/npm/types/@soroush.tech/markdown.svg)](https://www.npmjs.com/package/@soroush.tech/markdown)
6
- [![license](https://img.shields.io/npm/l/@soroush.tech/markdown.svg)](./LICENSE)
4
+ [![unpacked size](https://img.shields.io/npm/unpacked-size/@soroush.tech/markdown.svg?cacheSeconds=86400)](https://www.npmjs.com/package/@soroush.tech/markdown)
5
+ [![types included](https://img.shields.io/npm/types/@soroush.tech/markdown.svg?cacheSeconds=86400)](https://www.npmjs.com/package/@soroush.tech/markdown)
6
+ [![license](https://img.shields.io/npm/l/@soroush.tech/markdown.svg?cacheSeconds=86400)](./LICENSE)
7
7
 
8
8
  ## A **headless markdown editor and preview**.
9
9
 
package/dist/Editor.cjs CHANGED
@@ -26,7 +26,7 @@ const noop = () => {};
26
26
  * driven by context (so the `Toolbar` can act on it); on its own it is a plain controlled field
27
27
  * via `value`/`onChange`.
28
28
  */
29
- function Editor({ value: valueProp, onChange: onChangeProp, placeholder = "Write your article in Markdown…", minRows = 16, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint = true }) {
29
+ function Editor({ value: valueProp, onChange: onChangeProp, placeholder = "Write your article in Markdown…", minRows = 16, maxRows, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint = true }) {
30
30
  const context = (0, react.useContext)(require_MarkdownContext.MarkdownContext);
31
31
  const theme = (0, _soroush_tech_design_system_theme.useTheme)();
32
32
  const textColor = textColorProp ?? (0, _soroush_tech_design_system_theme.themeDefault)(theme, "textColor", "initial");
@@ -106,6 +106,7 @@ function Editor({ value: valueProp, onChange: onChangeProp, placeholder = "Write
106
106
  required,
107
107
  disabled,
108
108
  minRows,
109
+ maxRows,
109
110
  value,
110
111
  placeholder,
111
112
  onChange: (event) => onChange(event.target.value),
package/dist/Editor.d.cts CHANGED
@@ -9,6 +9,11 @@ interface EditorProps {
9
9
  placeholder?: string;
10
10
  /** Minimum visible rows of the source textarea. Default: `16`. */
11
11
  minRows?: number;
12
+ /**
13
+ * Maximum rows before the auto-grow textarea stops growing and scrolls
14
+ * internally — keeps its border box visible inside height-bound panes.
15
+ */
16
+ maxRows?: number;
12
17
  /** Visual style of the source input — forwarded to `TextInput`. Default: `'default'`. */
13
18
  variant?: TextInputVariant;
14
19
  /** Focus/active border colour — forwarded to `TextInput`. */
@@ -35,6 +40,6 @@ interface EditorProps {
35
40
  * driven by context (so the `Toolbar` can act on it); on its own it is a plain controlled field
36
41
  * via `value`/`onChange`.
37
42
  */
38
- declare function Editor({ value: valueProp, onChange: onChangeProp, placeholder, minRows, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint }: Readonly<EditorProps>): import("react").JSX.Element;
43
+ declare function Editor({ value: valueProp, onChange: onChangeProp, placeholder, minRows, maxRows, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint }: Readonly<EditorProps>): import("react").JSX.Element;
39
44
  //#endregion
40
45
  export { Editor, EditorProps };
package/dist/Editor.d.mts CHANGED
@@ -9,6 +9,11 @@ interface EditorProps {
9
9
  placeholder?: string;
10
10
  /** Minimum visible rows of the source textarea. Default: `16`. */
11
11
  minRows?: number;
12
+ /**
13
+ * Maximum rows before the auto-grow textarea stops growing and scrolls
14
+ * internally — keeps its border box visible inside height-bound panes.
15
+ */
16
+ maxRows?: number;
12
17
  /** Visual style of the source input — forwarded to `TextInput`. Default: `'default'`. */
13
18
  variant?: TextInputVariant;
14
19
  /** Focus/active border colour — forwarded to `TextInput`. */
@@ -35,6 +40,6 @@ interface EditorProps {
35
40
  * driven by context (so the `Toolbar` can act on it); on its own it is a plain controlled field
36
41
  * via `value`/`onChange`.
37
42
  */
38
- declare function Editor({ value: valueProp, onChange: onChangeProp, placeholder, minRows, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint }: Readonly<EditorProps>): import("react").JSX.Element;
43
+ declare function Editor({ value: valueProp, onChange: onChangeProp, placeholder, minRows, maxRows, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint }: Readonly<EditorProps>): import("react").JSX.Element;
39
44
  //#endregion
40
45
  export { Editor, EditorProps };
package/dist/Editor.mjs CHANGED
@@ -25,7 +25,7 @@ const noop = () => {};
25
25
  * driven by context (so the `Toolbar` can act on it); on its own it is a plain controlled field
26
26
  * via `value`/`onChange`.
27
27
  */
28
- function Editor({ value: valueProp, onChange: onChangeProp, placeholder = "Write your article in Markdown…", minRows = 16, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint = true }) {
28
+ function Editor({ value: valueProp, onChange: onChangeProp, placeholder = "Write your article in Markdown…", minRows = 16, maxRows, variant, color, textColor: textColorProp, size, name, id, error, required, disabled, showShortcutHint = true }) {
29
29
  const context = useContext(MarkdownContext);
30
30
  const theme = useTheme();
31
31
  const textColor = textColorProp ?? themeDefault(theme, "textColor", "initial");
@@ -105,6 +105,7 @@ function Editor({ value: valueProp, onChange: onChangeProp, placeholder = "Write
105
105
  required,
106
106
  disabled,
107
107
  minRows,
108
+ maxRows,
108
109
  value,
109
110
  placeholder,
110
111
  onChange: (event) => onChange(event.target.value),
@@ -0,0 +1,219 @@
1
+ const require_Preview = require("./Preview-CQ0z3gzx.cjs");
2
+ const require_MarkdownContext = require("./MarkdownContext.cjs");
3
+ let react = require("react");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ let _soroush_tech_design_system = require("@soroush.tech/design-system");
6
+ let _soroush_tech_design_system_View = require("@soroush.tech/design-system/View");
7
+ let remark_gfm = require("remark-gfm");
8
+ remark_gfm = require_Preview.__toESM(remark_gfm, 1);
9
+ let rehype_parse = require("rehype-parse");
10
+ rehype_parse = require_Preview.__toESM(rehype_parse, 1);
11
+ let rehype_remark = require("rehype-remark");
12
+ rehype_remark = require_Preview.__toESM(rehype_remark, 1);
13
+ let remark_stringify = require("remark-stringify");
14
+ remark_stringify = require_Preview.__toESM(remark_stringify, 1);
15
+ //#region src/utils/blockToMarkdown.ts
16
+ const processor = (0, require("unified").unified)().use(rehype_parse.default, { fragment: true }).use(rehype_remark.default).use(remark_gfm.default).use(remark_stringify.default, {
17
+ bullet: "-",
18
+ fence: "`",
19
+ rule: "-"
20
+ });
21
+ /**
22
+ * Serializes an edited block's live DOM back to markdown. The preview renders
23
+ * non-content UI inside a block (the code-copy control, decorative SVGs) —
24
+ * those are stripped from a clone first so only real content round-trips.
25
+ */
26
+ function blockToMarkdown(element) {
27
+ const clone = element.cloneNode(true);
28
+ for (const junk of clone.querySelectorAll(".code-copy, button, svg")) junk.remove();
29
+ return String(processor.processSync(clone.innerHTML)).trimEnd();
30
+ }
31
+ //#endregion
32
+ //#region src/utils/splitBlocks.ts
33
+ const FENCE = /^[ \t]{0,3}(`{3,}|~{3,})/;
34
+ /**
35
+ * The fence state after `line`: unchanged when the line carries no marker, the
36
+ * marker itself when one opens a block, and `null` when one closes it.
37
+ *
38
+ * A closing marker must use the same character, be at least as long, and carry
39
+ * nothing after it. The info string is what separates the two roles — a
40
+ * ```` ```ts ```` line inside an open ```` ``` ```` block opens nothing and closes
41
+ * nothing, it is content. Without that check the block would end early and its
42
+ * remaining blank lines would split it into further blocks.
43
+ */
44
+ const nextFence = (line, fence) => {
45
+ const match = FENCE.exec(line);
46
+ if (!match) return fence;
47
+ const [matched, marker] = match;
48
+ if (fence === null) return marker;
49
+ return line.slice(matched.length).trim() === "" && marker.startsWith(fence[0]) && marker.length >= fence.length ? null : fence;
50
+ };
51
+ /**
52
+ * Splits markdown into top-level blocks separated by blank lines, keeping
53
+ * fenced code blocks whole — blank lines inside a ``` / ~~~ fence never split.
54
+ * Offsets index the original string, so an edited block's source can be
55
+ * spliced back losslessly.
56
+ */
57
+ function splitBlocks(value) {
58
+ const blocks = [];
59
+ let blockStart = -1;
60
+ let blockEnd = -1;
61
+ let fence = null;
62
+ let offset = 0;
63
+ for (const line of value.split("\n")) {
64
+ const isBlank = line.trim() === "";
65
+ if (blockStart >= 0 && isBlank && fence === null) {
66
+ blocks.push({
67
+ source: value.slice(blockStart, blockEnd),
68
+ start: blockStart,
69
+ end: blockEnd
70
+ });
71
+ blockStart = -1;
72
+ } else if (!isBlank || fence !== null) {
73
+ if (blockStart < 0) blockStart = offset;
74
+ blockEnd = offset + line.length;
75
+ fence = nextFence(line, fence);
76
+ }
77
+ offset += line.length + 1;
78
+ }
79
+ if (blockStart >= 0) blocks.push({
80
+ source: value.slice(blockStart, blockEnd),
81
+ start: blockStart,
82
+ end: blockEnd
83
+ });
84
+ return blocks;
85
+ }
86
+ //#endregion
87
+ //#region src/LiveEdit/LiveEdit.tsx
88
+ const LiveEditRoot = (0, _soroush_tech_design_system.styled)(_soroush_tech_design_system_View.View, {
89
+ name: "MarkdownLiveEdit",
90
+ label: "MarkdownLiveEdit"
91
+ })({
92
+ minWidth: 0,
93
+ maxWidth: "100%",
94
+ overflowWrap: "break-word"
95
+ });
96
+ const LiveEditBlock = (0, _soroush_tech_design_system.styled)(_soroush_tech_design_system_View.View, {
97
+ name: "MarkdownLiveEdit",
98
+ slot: "block",
99
+ label: "MarkdownLiveEditBlock"
100
+ })(({ theme }) => ({
101
+ cursor: "text",
102
+ borderRadius: theme?.radii?.sm,
103
+ "&:hover, &:focus-within, &:focus": {
104
+ outline: `${theme?.borderWidths?.thin} dashed ${theme?.border?.primary}`,
105
+ outlineOffset: "2px"
106
+ },
107
+ "&:empty::before": {
108
+ content: "attr(data-placeholder)",
109
+ color: theme?.text?.secondary
110
+ }
111
+ }));
112
+ const MERMAID_FENCE = /^\s{0,3}(?:`{3,}|~{3,})\s*mermaid\b/;
113
+ const noop = () => {};
114
+ /** An empty document still needs one editable block to type into. */
115
+ const toBlocks = (value) => {
116
+ const blocks = splitBlocks(value);
117
+ return blocks.length > 0 ? blocks : [{
118
+ source: "",
119
+ start: 0,
120
+ end: value.length
121
+ }];
122
+ };
123
+ /**
124
+ * One contentEditable block. The preview element is created once per mount —
125
+ * while the browser owns the DOM during typing, React must never reconcile
126
+ * the subtree under the caret. The parent remounts the block (source-derived
127
+ * key) whenever its markdown actually changes.
128
+ */
129
+ function EditableBlock({ index, source, isEditable, placeholder, slotProps, onEdit, onDone }) {
130
+ const [content] = (0, react.useState)(() => source === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Preview.Preview, {
131
+ slotProps,
132
+ children: source
133
+ }));
134
+ const handleKeyDown = (event) => {
135
+ if (event.key !== "Escape") return;
136
+ event.preventDefault();
137
+ event.currentTarget.blur();
138
+ };
139
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveEditBlock, {
140
+ "aria-label": "Edit block",
141
+ tabIndex: isEditable ? 0 : void 0,
142
+ contentEditable: isEditable,
143
+ suppressContentEditableWarning: true,
144
+ "data-placeholder": source === "" ? placeholder : void 0,
145
+ onInput: (event) => onEdit(index, event.currentTarget),
146
+ onBlur: onDone,
147
+ onKeyDown: handleKeyDown,
148
+ children: content
149
+ });
150
+ }
151
+ /**
152
+ * A preview you write on directly: the document renders exactly like
153
+ * `Preview`, split into blocks, and every block is `contentEditable` — no
154
+ * textarea. While a block is being typed in, the browser owns its DOM; each
155
+ * input serializes that block back to markdown and splices it into `value`,
156
+ * so `onChange` stays live and untouched blocks keep their exact source.
157
+ * Blur (or Escape) re-splits the document. Mermaid blocks are read-only —
158
+ * a diagram has no text to edit in place. Inside a `Control` it is driven by
159
+ * context; standalone it is a plain controlled component.
160
+ */
161
+ function LiveEdit({ value: valueProp, onChange: onChangeProp, placeholder = "Click to start writing…", slotProps }) {
162
+ const context = (0, react.useContext)(require_MarkdownContext.MarkdownContext);
163
+ const value = context?.value ?? valueProp ?? "";
164
+ const onChange = context?.onChange ?? onChangeProp ?? noop;
165
+ const [blocks, setBlocks] = (0, react.useState)(() => toBlocks(value));
166
+ const [generation, setGeneration] = (0, react.useState)(0);
167
+ const offsetsRef = (0, react.useRef)(null);
168
+ const getOffsets = () => {
169
+ if (offsetsRef.current?.generation !== generation) offsetsRef.current = {
170
+ generation,
171
+ offsets: blocks.map(({ start, end }) => ({
172
+ start,
173
+ end
174
+ }))
175
+ };
176
+ return offsetsRef.current.offsets;
177
+ };
178
+ const resplit = (next) => {
179
+ setBlocks(toBlocks(next));
180
+ setGeneration((current) => current + 1);
181
+ };
182
+ const syncedValueRef = (0, react.useRef)(value);
183
+ if (value !== syncedValueRef.current) {
184
+ syncedValueRef.current = value;
185
+ resplit(value);
186
+ }
187
+ const handleEdit = (index, element) => {
188
+ const markdown = blockToMarkdown(element);
189
+ const offsets = getOffsets();
190
+ const block = offsets[index];
191
+ const current = syncedValueRef.current;
192
+ const next = current.slice(0, block.start) + markdown + current.slice(block.end);
193
+ const delta = markdown.length - (block.end - block.start);
194
+ block.end = block.start + markdown.length;
195
+ for (const later of offsets.slice(index + 1)) {
196
+ later.start += delta;
197
+ later.end += delta;
198
+ }
199
+ syncedValueRef.current = next;
200
+ onChange(next);
201
+ };
202
+ const handleDone = () => resplit(syncedValueRef.current);
203
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveEditRoot, { children: blocks.map((block, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditableBlock, {
204
+ index,
205
+ source: block.source,
206
+ isEditable: !MERMAID_FENCE.test(block.source),
207
+ placeholder,
208
+ slotProps,
209
+ onEdit: handleEdit,
210
+ onDone: handleDone
211
+ }, `${generation}:${index}`)) });
212
+ }
213
+ //#endregion
214
+ Object.defineProperty(exports, "LiveEdit", {
215
+ enumerable: true,
216
+ get: function() {
217
+ return LiveEdit;
218
+ }
219
+ });
@@ -0,0 +1,211 @@
1
+ import { MarkdownContext } from "./MarkdownContext.mjs";
2
+ import { t as Preview } from "./Preview-nvh2sGBQ.mjs";
3
+ import { useContext, useRef, useState } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { styled } from "@soroush.tech/design-system";
6
+ import { View } from "@soroush.tech/design-system/View";
7
+ import remarkGfm from "remark-gfm";
8
+ import rehypeParse from "rehype-parse";
9
+ import rehypeRemark from "rehype-remark";
10
+ import remarkStringify from "remark-stringify";
11
+ import { unified } from "unified";
12
+ //#region src/utils/blockToMarkdown.ts
13
+ const processor = unified().use(rehypeParse, { fragment: true }).use(rehypeRemark).use(remarkGfm).use(remarkStringify, {
14
+ bullet: "-",
15
+ fence: "`",
16
+ rule: "-"
17
+ });
18
+ /**
19
+ * Serializes an edited block's live DOM back to markdown. The preview renders
20
+ * non-content UI inside a block (the code-copy control, decorative SVGs) —
21
+ * those are stripped from a clone first so only real content round-trips.
22
+ */
23
+ function blockToMarkdown(element) {
24
+ const clone = element.cloneNode(true);
25
+ for (const junk of clone.querySelectorAll(".code-copy, button, svg")) junk.remove();
26
+ return String(processor.processSync(clone.innerHTML)).trimEnd();
27
+ }
28
+ //#endregion
29
+ //#region src/utils/splitBlocks.ts
30
+ const FENCE = /^[ \t]{0,3}(`{3,}|~{3,})/;
31
+ /**
32
+ * The fence state after `line`: unchanged when the line carries no marker, the
33
+ * marker itself when one opens a block, and `null` when one closes it.
34
+ *
35
+ * A closing marker must use the same character, be at least as long, and carry
36
+ * nothing after it. The info string is what separates the two roles — a
37
+ * ```` ```ts ```` line inside an open ```` ``` ```` block opens nothing and closes
38
+ * nothing, it is content. Without that check the block would end early and its
39
+ * remaining blank lines would split it into further blocks.
40
+ */
41
+ const nextFence = (line, fence) => {
42
+ const match = FENCE.exec(line);
43
+ if (!match) return fence;
44
+ const [matched, marker] = match;
45
+ if (fence === null) return marker;
46
+ return line.slice(matched.length).trim() === "" && marker.startsWith(fence[0]) && marker.length >= fence.length ? null : fence;
47
+ };
48
+ /**
49
+ * Splits markdown into top-level blocks separated by blank lines, keeping
50
+ * fenced code blocks whole — blank lines inside a ``` / ~~~ fence never split.
51
+ * Offsets index the original string, so an edited block's source can be
52
+ * spliced back losslessly.
53
+ */
54
+ function splitBlocks(value) {
55
+ const blocks = [];
56
+ let blockStart = -1;
57
+ let blockEnd = -1;
58
+ let fence = null;
59
+ let offset = 0;
60
+ for (const line of value.split("\n")) {
61
+ const isBlank = line.trim() === "";
62
+ if (blockStart >= 0 && isBlank && fence === null) {
63
+ blocks.push({
64
+ source: value.slice(blockStart, blockEnd),
65
+ start: blockStart,
66
+ end: blockEnd
67
+ });
68
+ blockStart = -1;
69
+ } else if (!isBlank || fence !== null) {
70
+ if (blockStart < 0) blockStart = offset;
71
+ blockEnd = offset + line.length;
72
+ fence = nextFence(line, fence);
73
+ }
74
+ offset += line.length + 1;
75
+ }
76
+ if (blockStart >= 0) blocks.push({
77
+ source: value.slice(blockStart, blockEnd),
78
+ start: blockStart,
79
+ end: blockEnd
80
+ });
81
+ return blocks;
82
+ }
83
+ //#endregion
84
+ //#region src/LiveEdit/LiveEdit.tsx
85
+ const LiveEditRoot = styled(View, {
86
+ name: "MarkdownLiveEdit",
87
+ label: "MarkdownLiveEdit"
88
+ })({
89
+ minWidth: 0,
90
+ maxWidth: "100%",
91
+ overflowWrap: "break-word"
92
+ });
93
+ const LiveEditBlock = styled(View, {
94
+ name: "MarkdownLiveEdit",
95
+ slot: "block",
96
+ label: "MarkdownLiveEditBlock"
97
+ })(({ theme }) => ({
98
+ cursor: "text",
99
+ borderRadius: theme?.radii?.sm,
100
+ "&:hover, &:focus-within, &:focus": {
101
+ outline: `${theme?.borderWidths?.thin} dashed ${theme?.border?.primary}`,
102
+ outlineOffset: "2px"
103
+ },
104
+ "&:empty::before": {
105
+ content: "attr(data-placeholder)",
106
+ color: theme?.text?.secondary
107
+ }
108
+ }));
109
+ const MERMAID_FENCE = /^\s{0,3}(?:`{3,}|~{3,})\s*mermaid\b/;
110
+ const noop = () => {};
111
+ /** An empty document still needs one editable block to type into. */
112
+ const toBlocks = (value) => {
113
+ const blocks = splitBlocks(value);
114
+ return blocks.length > 0 ? blocks : [{
115
+ source: "",
116
+ start: 0,
117
+ end: value.length
118
+ }];
119
+ };
120
+ /**
121
+ * One contentEditable block. The preview element is created once per mount —
122
+ * while the browser owns the DOM during typing, React must never reconcile
123
+ * the subtree under the caret. The parent remounts the block (source-derived
124
+ * key) whenever its markdown actually changes.
125
+ */
126
+ function EditableBlock({ index, source, isEditable, placeholder, slotProps, onEdit, onDone }) {
127
+ const [content] = useState(() => source === "" ? null : /* @__PURE__ */ jsx(Preview, {
128
+ slotProps,
129
+ children: source
130
+ }));
131
+ const handleKeyDown = (event) => {
132
+ if (event.key !== "Escape") return;
133
+ event.preventDefault();
134
+ event.currentTarget.blur();
135
+ };
136
+ return /* @__PURE__ */ jsx(LiveEditBlock, {
137
+ "aria-label": "Edit block",
138
+ tabIndex: isEditable ? 0 : void 0,
139
+ contentEditable: isEditable,
140
+ suppressContentEditableWarning: true,
141
+ "data-placeholder": source === "" ? placeholder : void 0,
142
+ onInput: (event) => onEdit(index, event.currentTarget),
143
+ onBlur: onDone,
144
+ onKeyDown: handleKeyDown,
145
+ children: content
146
+ });
147
+ }
148
+ /**
149
+ * A preview you write on directly: the document renders exactly like
150
+ * `Preview`, split into blocks, and every block is `contentEditable` — no
151
+ * textarea. While a block is being typed in, the browser owns its DOM; each
152
+ * input serializes that block back to markdown and splices it into `value`,
153
+ * so `onChange` stays live and untouched blocks keep their exact source.
154
+ * Blur (or Escape) re-splits the document. Mermaid blocks are read-only —
155
+ * a diagram has no text to edit in place. Inside a `Control` it is driven by
156
+ * context; standalone it is a plain controlled component.
157
+ */
158
+ function LiveEdit({ value: valueProp, onChange: onChangeProp, placeholder = "Click to start writing…", slotProps }) {
159
+ const context = useContext(MarkdownContext);
160
+ const value = context?.value ?? valueProp ?? "";
161
+ const onChange = context?.onChange ?? onChangeProp ?? noop;
162
+ const [blocks, setBlocks] = useState(() => toBlocks(value));
163
+ const [generation, setGeneration] = useState(0);
164
+ const offsetsRef = useRef(null);
165
+ const getOffsets = () => {
166
+ if (offsetsRef.current?.generation !== generation) offsetsRef.current = {
167
+ generation,
168
+ offsets: blocks.map(({ start, end }) => ({
169
+ start,
170
+ end
171
+ }))
172
+ };
173
+ return offsetsRef.current.offsets;
174
+ };
175
+ const resplit = (next) => {
176
+ setBlocks(toBlocks(next));
177
+ setGeneration((current) => current + 1);
178
+ };
179
+ const syncedValueRef = useRef(value);
180
+ if (value !== syncedValueRef.current) {
181
+ syncedValueRef.current = value;
182
+ resplit(value);
183
+ }
184
+ const handleEdit = (index, element) => {
185
+ const markdown = blockToMarkdown(element);
186
+ const offsets = getOffsets();
187
+ const block = offsets[index];
188
+ const current = syncedValueRef.current;
189
+ const next = current.slice(0, block.start) + markdown + current.slice(block.end);
190
+ const delta = markdown.length - (block.end - block.start);
191
+ block.end = block.start + markdown.length;
192
+ for (const later of offsets.slice(index + 1)) {
193
+ later.start += delta;
194
+ later.end += delta;
195
+ }
196
+ syncedValueRef.current = next;
197
+ onChange(next);
198
+ };
199
+ const handleDone = () => resplit(syncedValueRef.current);
200
+ return /* @__PURE__ */ jsx(LiveEditRoot, { children: blocks.map((block, index) => /* @__PURE__ */ jsx(EditableBlock, {
201
+ index,
202
+ source: block.source,
203
+ isEditable: !MERMAID_FENCE.test(block.source),
204
+ placeholder,
205
+ slotProps,
206
+ onEdit: handleEdit,
207
+ onDone: handleDone
208
+ }, `${generation}:${index}`)) });
209
+ }
210
+ //#endregion
211
+ export { LiveEdit as t };
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_LiveEdit = require("./LiveEdit-DyBGqI3s.cjs");
3
+ exports.LiveEdit = require_LiveEdit.LiveEdit;
@@ -0,0 +1,25 @@
1
+ import { r as PreviewSlotProps } from "./index-CxdJ1SgP.cjs";
2
+ //#region src/LiveEdit/LiveEdit.d.ts
3
+ interface LiveEditProps {
4
+ /** Standalone source value — used when rendered outside a `Control`. Ignored inside one. */
5
+ value?: string;
6
+ /** Standalone change handler — used when rendered outside a `Control`. Ignored inside one. */
7
+ onChange?: (value: string) => void;
8
+ /** Shown inside the (empty) document's first block until something is typed. */
9
+ placeholder?: string;
10
+ /** Per-element prop overrides forwarded to every block's preview. */
11
+ slotProps?: PreviewSlotProps;
12
+ }
13
+ /**
14
+ * A preview you write on directly: the document renders exactly like
15
+ * `Preview`, split into blocks, and every block is `contentEditable` — no
16
+ * textarea. While a block is being typed in, the browser owns its DOM; each
17
+ * input serializes that block back to markdown and splices it into `value`,
18
+ * so `onChange` stays live and untouched blocks keep their exact source.
19
+ * Blur (or Escape) re-splits the document. Mermaid blocks are read-only —
20
+ * a diagram has no text to edit in place. Inside a `Control` it is driven by
21
+ * context; standalone it is a plain controlled component.
22
+ */
23
+ declare function LiveEdit({ value: valueProp, onChange: onChangeProp, placeholder, slotProps }: Readonly<LiveEditProps>): import("react").JSX.Element;
24
+ //#endregion
25
+ export { LiveEdit, LiveEditProps };
@@ -0,0 +1,25 @@
1
+ import { r as PreviewSlotProps } from "./index-CPWcFD6T.mjs";
2
+ //#region src/LiveEdit/LiveEdit.d.ts
3
+ interface LiveEditProps {
4
+ /** Standalone source value — used when rendered outside a `Control`. Ignored inside one. */
5
+ value?: string;
6
+ /** Standalone change handler — used when rendered outside a `Control`. Ignored inside one. */
7
+ onChange?: (value: string) => void;
8
+ /** Shown inside the (empty) document's first block until something is typed. */
9
+ placeholder?: string;
10
+ /** Per-element prop overrides forwarded to every block's preview. */
11
+ slotProps?: PreviewSlotProps;
12
+ }
13
+ /**
14
+ * A preview you write on directly: the document renders exactly like
15
+ * `Preview`, split into blocks, and every block is `contentEditable` — no
16
+ * textarea. While a block is being typed in, the browser owns its DOM; each
17
+ * input serializes that block back to markdown and splices it into `value`,
18
+ * so `onChange` stays live and untouched blocks keep their exact source.
19
+ * Blur (or Escape) re-splits the document. Mermaid blocks are read-only —
20
+ * a diagram has no text to edit in place. Inside a `Control` it is driven by
21
+ * context; standalone it is a plain controlled component.
22
+ */
23
+ declare function LiveEdit({ value: valueProp, onChange: onChangeProp, placeholder, slotProps }: Readonly<LiveEditProps>): import("react").JSX.Element;
24
+ //#endregion
25
+ export { LiveEdit, LiveEditProps };
@@ -0,0 +1,2 @@
1
+ import { t as LiveEdit } from "./LiveEdit-KDKu7Yvn.mjs";
2
+ export { LiveEdit };
@@ -26,6 +26,7 @@ let react = require("react");
26
26
  let react_jsx_runtime = require("react/jsx-runtime");
27
27
  let _soroush_tech_design_system_Typography = require("@soroush.tech/design-system/Typography");
28
28
  let _soroush_tech_design_system = require("@soroush.tech/design-system");
29
+ let _soroush_tech_design_system_View = require("@soroush.tech/design-system/View");
29
30
  let react_markdown = require("react-markdown");
30
31
  react_markdown = __toESM(react_markdown, 1);
31
32
  let remark_gfm = require("remark-gfm");
@@ -37,7 +38,6 @@ let _soroush_tech_design_system_Checkbox = require("@soroush.tech/design-system/
37
38
  let _soroush_tech_design_system_Image = require("@soroush.tech/design-system/Image");
38
39
  let _soroush_tech_design_system_Link = require("@soroush.tech/design-system/Link");
39
40
  let _soroush_tech_design_system_Table = require("@soroush.tech/design-system/Table");
40
- let _soroush_tech_design_system_View = require("@soroush.tech/design-system/View");
41
41
  function mermaidSource(children) {
42
42
  return (Array.isArray(children) ? children.join("") : String(children ?? "")).replace(/\n$/, "");
43
43
  }
@@ -190,6 +190,7 @@ const components = {
190
190
  const isBlock = classes.includes("language-");
191
191
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_soroush_tech_design_system_Typography.Typography, {
192
192
  as: "code",
193
+ className,
193
194
  color: "initial",
194
195
  variant: "inherit",
195
196
  fontFamily: "mono",
@@ -274,3 +275,9 @@ Object.defineProperty(exports, "Preview", {
274
275
  return Preview;
275
276
  }
276
277
  });
278
+ Object.defineProperty(exports, "__toESM", {
279
+ enumerable: true,
280
+ get: function() {
281
+ return __toESM;
282
+ }
283
+ });
@@ -4,6 +4,7 @@ import { createContext, isValidElement, useContext } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { Typography } from "@soroush.tech/design-system/Typography";
6
6
  import { styled } from "@soroush.tech/design-system";
7
+ import { View } from "@soroush.tech/design-system/View";
7
8
  import ReactMarkdown from "react-markdown";
8
9
  import remarkGfm from "remark-gfm";
9
10
  import rehypeHighlight from "rehype-highlight";
@@ -12,7 +13,6 @@ import { Checkbox } from "@soroush.tech/design-system/Checkbox";
12
13
  import { Image } from "@soroush.tech/design-system/Image";
13
14
  import { Link } from "@soroush.tech/design-system/Link";
14
15
  import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@soroush.tech/design-system/Table";
15
- import { View } from "@soroush.tech/design-system/View";
16
16
  function mermaidSource(children) {
17
17
  return (Array.isArray(children) ? children.join("") : String(children ?? "")).replace(/\n$/, "");
18
18
  }
@@ -165,6 +165,7 @@ const components = {
165
165
  const isBlock = classes.includes("language-");
166
166
  return /* @__PURE__ */ jsx(Typography, {
167
167
  as: "code",
168
+ className,
168
169
  color: "initial",
169
170
  variant: "inherit",
170
171
  fontFamily: "mono",
package/dist/Preview.cjs CHANGED
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_Preview = require("./Preview-BVT70uXa.cjs");
2
+ const require_Preview = require("./Preview-CQ0z3gzx.cjs");
3
3
  exports.Preview = require_Preview.Preview;
@@ -1,53 +1,2 @@
1
- import { a as CodeBlockProps } from "./index-BtMTBOcM.cjs";
2
- import { r as MermaidProps } from "./index-BO0LyUUG.cjs";
3
- import { ViewProps } from "@soroush.tech/design-system/View";
4
- import { QuoteProps } from "@soroush.tech/design-system/Quote";
5
- import { CheckboxProps } from "@soroush.tech/design-system/Checkbox";
6
- import { ImageProps } from "@soroush.tech/design-system/Image";
7
- import { LinkProps } from "@soroush.tech/design-system/Link";
8
- import { TableBodyProps, TableCellProps, TableHeadProps, TableProps, TableRowProps } from "@soroush.tech/design-system/Table";
9
- import { TypographyProps } from "@soroush.tech/design-system/Typography";
10
- //#region src/Preview/Preview.d.ts
11
- /**
12
- * Per-element prop overrides, merged over the design-system defaults (the caller's props win).
13
- * Keyed by markdown element — e.g. `slotProps={{ p: { color: 'primary' }, h1: { variant: 'h2' } }}`.
14
- */
15
- interface PreviewSlotProps {
16
- h1?: TypographyProps;
17
- h2?: TypographyProps;
18
- h3?: TypographyProps;
19
- h4?: TypographyProps;
20
- h5?: TypographyProps;
21
- h6?: TypographyProps;
22
- p?: TypographyProps;
23
- strong?: TypographyProps;
24
- em?: TypographyProps;
25
- li?: TypographyProps;
26
- code?: TypographyProps;
27
- a?: LinkProps;
28
- ul?: ViewProps;
29
- ol?: ViewProps;
30
- hr?: ViewProps;
31
- blockquote?: QuoteProps;
32
- img?: ImageProps;
33
- th?: TableCellProps;
34
- td?: TableCellProps;
35
- pre?: CodeBlockProps;
36
- input?: CheckboxProps;
37
- table?: TableProps;
38
- thead?: TableHeadProps;
39
- tbody?: TableBodyProps;
40
- tr?: TableRowProps;
41
- /** Props for a ` ```mermaid ` fence's `Mermaid` renderer — including `diagram` (viewer) props. */
42
- mermaid?: Omit<MermaidProps, 'chart'>;
43
- }
44
- interface PreviewProps {
45
- /** The markdown source to render. */
46
- children: string;
47
- /** Per-element prop overrides, merged over the defaults. */
48
- slotProps?: PreviewSlotProps;
49
- }
50
- /** Renders a markdown string with every element mapped to a design-system primitive. */
51
- declare function Preview({ children, slotProps }: Readonly<PreviewProps>): import("react").JSX.Element;
52
- //#endregion
1
+ import { n as PreviewProps, r as PreviewSlotProps, t as Preview } from "./index-CxdJ1SgP.cjs";
53
2
  export { Preview, PreviewProps, PreviewSlotProps };
@@ -1,53 +1,2 @@
1
- import { a as CodeBlockProps } from "./index-BtMTBOcM.mjs";
2
- import { r as MermaidProps } from "./index-BO0LyUUG.mjs";
3
- import { TypographyProps } from "@soroush.tech/design-system/Typography";
4
- import { QuoteProps } from "@soroush.tech/design-system/Quote";
5
- import { CheckboxProps } from "@soroush.tech/design-system/Checkbox";
6
- import { ImageProps } from "@soroush.tech/design-system/Image";
7
- import { LinkProps } from "@soroush.tech/design-system/Link";
8
- import { TableBodyProps, TableCellProps, TableHeadProps, TableProps, TableRowProps } from "@soroush.tech/design-system/Table";
9
- import { ViewProps } from "@soroush.tech/design-system/View";
10
- //#region src/Preview/Preview.d.ts
11
- /**
12
- * Per-element prop overrides, merged over the design-system defaults (the caller's props win).
13
- * Keyed by markdown element — e.g. `slotProps={{ p: { color: 'primary' }, h1: { variant: 'h2' } }}`.
14
- */
15
- interface PreviewSlotProps {
16
- h1?: TypographyProps;
17
- h2?: TypographyProps;
18
- h3?: TypographyProps;
19
- h4?: TypographyProps;
20
- h5?: TypographyProps;
21
- h6?: TypographyProps;
22
- p?: TypographyProps;
23
- strong?: TypographyProps;
24
- em?: TypographyProps;
25
- li?: TypographyProps;
26
- code?: TypographyProps;
27
- a?: LinkProps;
28
- ul?: ViewProps;
29
- ol?: ViewProps;
30
- hr?: ViewProps;
31
- blockquote?: QuoteProps;
32
- img?: ImageProps;
33
- th?: TableCellProps;
34
- td?: TableCellProps;
35
- pre?: CodeBlockProps;
36
- input?: CheckboxProps;
37
- table?: TableProps;
38
- thead?: TableHeadProps;
39
- tbody?: TableBodyProps;
40
- tr?: TableRowProps;
41
- /** Props for a ` ```mermaid ` fence's `Mermaid` renderer — including `diagram` (viewer) props. */
42
- mermaid?: Omit<MermaidProps, 'chart'>;
43
- }
44
- interface PreviewProps {
45
- /** The markdown source to render. */
46
- children: string;
47
- /** Per-element prop overrides, merged over the defaults. */
48
- slotProps?: PreviewSlotProps;
49
- }
50
- /** Renders a markdown string with every element mapped to a design-system primitive. */
51
- declare function Preview({ children, slotProps }: Readonly<PreviewProps>): import("react").JSX.Element;
52
- //#endregion
1
+ import { n as PreviewProps, r as PreviewSlotProps, t as Preview } from "./index-CPWcFD6T.mjs";
53
2
  export { Preview, PreviewProps, PreviewSlotProps };
package/dist/Preview.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as Preview } from "./Preview-C1CwRDj5.mjs";
1
+ import { t as Preview } from "./Preview-nvh2sGBQ.mjs";
2
2
  export { Preview };
@@ -0,0 +1,53 @@
1
+ import { a as CodeBlockProps } from "./index-BtMTBOcM.mjs";
2
+ import { r as MermaidProps } from "./index-BO0LyUUG.mjs";
3
+ import { TypographyProps } from "@soroush.tech/design-system/Typography";
4
+ import { ViewProps } from "@soroush.tech/design-system/View";
5
+ import { QuoteProps } from "@soroush.tech/design-system/Quote";
6
+ import { CheckboxProps } from "@soroush.tech/design-system/Checkbox";
7
+ import { ImageProps } from "@soroush.tech/design-system/Image";
8
+ import { LinkProps } from "@soroush.tech/design-system/Link";
9
+ import { TableBodyProps, TableCellProps, TableHeadProps, TableProps, TableRowProps } from "@soroush.tech/design-system/Table";
10
+ //#region src/Preview/Preview.d.ts
11
+ /**
12
+ * Per-element prop overrides, merged over the design-system defaults (the caller's props win).
13
+ * Keyed by markdown element — e.g. `slotProps={{ p: { color: 'primary' }, h1: { variant: 'h2' } }}`.
14
+ */
15
+ interface PreviewSlotProps {
16
+ h1?: TypographyProps;
17
+ h2?: TypographyProps;
18
+ h3?: TypographyProps;
19
+ h4?: TypographyProps;
20
+ h5?: TypographyProps;
21
+ h6?: TypographyProps;
22
+ p?: TypographyProps;
23
+ strong?: TypographyProps;
24
+ em?: TypographyProps;
25
+ li?: TypographyProps;
26
+ code?: TypographyProps;
27
+ a?: LinkProps;
28
+ ul?: ViewProps;
29
+ ol?: ViewProps;
30
+ hr?: ViewProps;
31
+ blockquote?: QuoteProps;
32
+ img?: ImageProps;
33
+ th?: TableCellProps;
34
+ td?: TableCellProps;
35
+ pre?: CodeBlockProps;
36
+ input?: CheckboxProps;
37
+ table?: TableProps;
38
+ thead?: TableHeadProps;
39
+ tbody?: TableBodyProps;
40
+ tr?: TableRowProps;
41
+ /** Props for a ` ```mermaid ` fence's `Mermaid` renderer — including `diagram` (viewer) props. */
42
+ mermaid?: Omit<MermaidProps, 'chart'>;
43
+ }
44
+ interface PreviewProps {
45
+ /** The markdown source to render. */
46
+ children: string;
47
+ /** Per-element prop overrides, merged over the defaults. */
48
+ slotProps?: PreviewSlotProps;
49
+ }
50
+ /** Renders a markdown string with every element mapped to a design-system primitive. */
51
+ declare function Preview({ children, slotProps }: Readonly<PreviewProps>): import("react").JSX.Element;
52
+ //#endregion
53
+ export { PreviewProps as n, PreviewSlotProps as r, Preview as t };
@@ -0,0 +1,53 @@
1
+ import { a as CodeBlockProps } from "./index-BtMTBOcM.cjs";
2
+ import { r as MermaidProps } from "./index-BO0LyUUG.cjs";
3
+ import { ViewProps } from "@soroush.tech/design-system/View";
4
+ import { QuoteProps } from "@soroush.tech/design-system/Quote";
5
+ import { CheckboxProps } from "@soroush.tech/design-system/Checkbox";
6
+ import { ImageProps } from "@soroush.tech/design-system/Image";
7
+ import { LinkProps } from "@soroush.tech/design-system/Link";
8
+ import { TableBodyProps, TableCellProps, TableHeadProps, TableProps, TableRowProps } from "@soroush.tech/design-system/Table";
9
+ import { TypographyProps } from "@soroush.tech/design-system/Typography";
10
+ //#region src/Preview/Preview.d.ts
11
+ /**
12
+ * Per-element prop overrides, merged over the design-system defaults (the caller's props win).
13
+ * Keyed by markdown element — e.g. `slotProps={{ p: { color: 'primary' }, h1: { variant: 'h2' } }}`.
14
+ */
15
+ interface PreviewSlotProps {
16
+ h1?: TypographyProps;
17
+ h2?: TypographyProps;
18
+ h3?: TypographyProps;
19
+ h4?: TypographyProps;
20
+ h5?: TypographyProps;
21
+ h6?: TypographyProps;
22
+ p?: TypographyProps;
23
+ strong?: TypographyProps;
24
+ em?: TypographyProps;
25
+ li?: TypographyProps;
26
+ code?: TypographyProps;
27
+ a?: LinkProps;
28
+ ul?: ViewProps;
29
+ ol?: ViewProps;
30
+ hr?: ViewProps;
31
+ blockquote?: QuoteProps;
32
+ img?: ImageProps;
33
+ th?: TableCellProps;
34
+ td?: TableCellProps;
35
+ pre?: CodeBlockProps;
36
+ input?: CheckboxProps;
37
+ table?: TableProps;
38
+ thead?: TableHeadProps;
39
+ tbody?: TableBodyProps;
40
+ tr?: TableRowProps;
41
+ /** Props for a ` ```mermaid ` fence's `Mermaid` renderer — including `diagram` (viewer) props. */
42
+ mermaid?: Omit<MermaidProps, 'chart'>;
43
+ }
44
+ interface PreviewProps {
45
+ /** The markdown source to render. */
46
+ children: string;
47
+ /** Per-element prop overrides, merged over the defaults. */
48
+ slotProps?: PreviewSlotProps;
49
+ }
50
+ /** Renders a markdown string with every element mapped to a design-system primitive. */
51
+ declare function Preview({ children, slotProps }: Readonly<PreviewProps>): import("react").JSX.Element;
52
+ //#endregion
53
+ export { PreviewProps as n, PreviewSlotProps as r, Preview as t };
package/dist/index.cjs CHANGED
@@ -1,14 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_Preview = require("./Preview-BVT70uXa.cjs");
2
+ const require_Preview = require("./Preview-CQ0z3gzx.cjs");
3
3
  const require_MarkdownContext = require("./MarkdownContext.cjs");
4
4
  const require_Control = require("./Control.cjs");
5
5
  const require_Toolbar = require("./Toolbar-Ba3l-oBU.cjs");
6
6
  const require_Editor = require("./Editor.cjs");
7
7
  const require_CodeBlock = require("./CodeBlock-Cymx1ZAY.cjs");
8
8
  const require_Mermaid = require("./Mermaid-DqP2Q0bt.cjs");
9
+ const require_LiveEdit = require("./LiveEdit-DyBGqI3s.cjs");
9
10
  exports.CodeBlock = require_CodeBlock.CodeBlock;
10
11
  exports.Control = require_Control.Control;
11
12
  exports.Editor = require_Editor.Editor;
13
+ exports.LiveEdit = require_LiveEdit.LiveEdit;
12
14
  exports.MarkdownContext = require_MarkdownContext.MarkdownContext;
13
15
  exports.Mermaid = require_Mermaid.Mermaid;
14
16
  exports.Preview = require_Preview.Preview;
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { a as CodeBlockProps, i as CodeBlock, n as syntaxDark, r as syntaxLight, t as ThemeSyntax } from "./index-BtMTBOcM.cjs";
2
2
  import { Control, ControlProps } from "./Control.cjs";
3
3
  import { Editor, EditorProps } from "./Editor.cjs";
4
- import { i as useMarkdownContext, n as MarkdownContextValue, r as MarkdownSelection, t as MarkdownContext } from "./index-rGZIhUgj.cjs";
5
4
  import { n as Mermaid, r as MermaidProps, t as DiagramProps } from "./index-BO0LyUUG.cjs";
6
- import { Preview, PreviewProps, PreviewSlotProps } from "./Preview.cjs";
5
+ import { n as PreviewProps, r as PreviewSlotProps, t as Preview } from "./index-CxdJ1SgP.cjs";
6
+ import { LiveEdit, LiveEditProps } from "./LiveEdit.cjs";
7
+ import { i as useMarkdownContext, n as MarkdownContextValue, r as MarkdownSelection, t as MarkdownContext } from "./index-rGZIhUgj.cjs";
7
8
  import { TablePicker, TablePickerProps, Toolbar } from "./Toolbar.cjs";
8
9
  //#region src/index.d.ts
9
10
  /**
@@ -31,6 +32,7 @@ interface MermaidThemeVariables {
31
32
  declare module '@soroush.tech/design-system/theme' {
32
33
  interface ThemeComponents {
33
34
  MarkdownEditor?: import('@soroush.tech/design-system/theme').ComponentConfig;
35
+ MarkdownLiveEdit?: import('@soroush.tech/design-system/theme').ComponentConfig<Record<string, unknown>, 'root' | 'block'>;
34
36
  MarkdownPreview?: import('@soroush.tech/design-system/theme').ComponentConfig;
35
37
  MarkdownToolbar?: import('@soroush.tech/design-system/theme').ComponentConfig<Record<string, unknown>, 'root' | 'strike' | 'tablePickerCell'>;
36
38
  CodeBlock?: import('@soroush.tech/design-system/theme').ComponentConfig<Record<string, unknown>, 'root' | 'surface'>;
@@ -47,4 +49,4 @@ declare module '@soroush.tech/design-system/theme' {
47
49
  }
48
50
  }
49
51
  //#endregion
50
- export { CodeBlock, CodeBlockProps, Control, ControlProps, DiagramProps, Editor, EditorProps, MarkdownContext, MarkdownContextValue, MarkdownSelection, Mermaid, MermaidProps, MermaidThemeVariables, Preview, PreviewProps, PreviewSlotProps, TablePicker, TablePickerProps, ThemeSyntax, Toolbar, syntaxDark, syntaxLight, useMarkdownContext };
52
+ export { CodeBlock, CodeBlockProps, Control, ControlProps, DiagramProps, Editor, EditorProps, LiveEdit, LiveEditProps, MarkdownContext, MarkdownContextValue, MarkdownSelection, Mermaid, MermaidProps, MermaidThemeVariables, Preview, PreviewProps, PreviewSlotProps, TablePicker, TablePickerProps, ThemeSyntax, Toolbar, syntaxDark, syntaxLight, useMarkdownContext };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { a as CodeBlockProps, i as CodeBlock, n as syntaxDark, r as syntaxLight, t as ThemeSyntax } from "./index-BtMTBOcM.mjs";
2
2
  import { Control, ControlProps } from "./Control.mjs";
3
3
  import { Editor, EditorProps } from "./Editor.mjs";
4
- import { i as useMarkdownContext, n as MarkdownContextValue, r as MarkdownSelection, t as MarkdownContext } from "./index-rGZIhUgj.mjs";
5
4
  import { n as Mermaid, r as MermaidProps, t as DiagramProps } from "./index-BO0LyUUG.mjs";
6
- import { Preview, PreviewProps, PreviewSlotProps } from "./Preview.mjs";
5
+ import { n as PreviewProps, r as PreviewSlotProps, t as Preview } from "./index-CPWcFD6T.mjs";
6
+ import { LiveEdit, LiveEditProps } from "./LiveEdit.mjs";
7
+ import { i as useMarkdownContext, n as MarkdownContextValue, r as MarkdownSelection, t as MarkdownContext } from "./index-rGZIhUgj.mjs";
7
8
  import { TablePicker, TablePickerProps, Toolbar } from "./Toolbar.mjs";
8
9
  //#region src/index.d.ts
9
10
  /**
@@ -31,6 +32,7 @@ interface MermaidThemeVariables {
31
32
  declare module '@soroush.tech/design-system/theme' {
32
33
  interface ThemeComponents {
33
34
  MarkdownEditor?: import('@soroush.tech/design-system/theme').ComponentConfig;
35
+ MarkdownLiveEdit?: import('@soroush.tech/design-system/theme').ComponentConfig<Record<string, unknown>, 'root' | 'block'>;
34
36
  MarkdownPreview?: import('@soroush.tech/design-system/theme').ComponentConfig;
35
37
  MarkdownToolbar?: import('@soroush.tech/design-system/theme').ComponentConfig<Record<string, unknown>, 'root' | 'strike' | 'tablePickerCell'>;
36
38
  CodeBlock?: import('@soroush.tech/design-system/theme').ComponentConfig<Record<string, unknown>, 'root' | 'surface'>;
@@ -47,4 +49,4 @@ declare module '@soroush.tech/design-system/theme' {
47
49
  }
48
50
  }
49
51
  //#endregion
50
- export { CodeBlock, CodeBlockProps, Control, ControlProps, DiagramProps, Editor, EditorProps, MarkdownContext, MarkdownContextValue, MarkdownSelection, Mermaid, MermaidProps, MermaidThemeVariables, Preview, PreviewProps, PreviewSlotProps, TablePicker, TablePickerProps, ThemeSyntax, Toolbar, syntaxDark, syntaxLight, useMarkdownContext };
52
+ export { CodeBlock, CodeBlockProps, Control, ControlProps, DiagramProps, Editor, EditorProps, LiveEdit, LiveEditProps, MarkdownContext, MarkdownContextValue, MarkdownSelection, Mermaid, MermaidProps, MermaidThemeVariables, Preview, PreviewProps, PreviewSlotProps, TablePicker, TablePickerProps, ThemeSyntax, Toolbar, syntaxDark, syntaxLight, useMarkdownContext };
package/dist/index.mjs CHANGED
@@ -4,5 +4,6 @@ import { n as TablePicker, t as Toolbar } from "./Toolbar-C0SNI-JK.mjs";
4
4
  import { Editor } from "./Editor.mjs";
5
5
  import { n as syntaxLight, r as CodeBlock, t as syntaxDark } from "./CodeBlock-CVlrvVPP.mjs";
6
6
  import { t as Mermaid } from "./Mermaid-BclI9A7e.mjs";
7
- import { t as Preview } from "./Preview-C1CwRDj5.mjs";
8
- export { CodeBlock, Control, Editor, MarkdownContext, Mermaid, Preview, TablePicker, Toolbar, syntaxDark, syntaxLight, useMarkdownContext };
7
+ import { t as Preview } from "./Preview-nvh2sGBQ.mjs";
8
+ import { t as LiveEdit } from "./LiveEdit-KDKu7Yvn.mjs";
9
+ export { CodeBlock, Control, Editor, LiveEdit, MarkdownContext, Mermaid, Preview, TablePicker, Toolbar, syntaxDark, syntaxLight, useMarkdownContext };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soroush.tech/markdown",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "Headless, composable markdown editor + renderer for @soroush.tech/design-system — Control, Toolbar, Editor, and Preview built on react-markdown, remark-gfm, and rehype-highlight.",
5
5
  "keywords": [
6
6
  "markdown",
@@ -58,12 +58,16 @@
58
58
  "mermaid": "^11.4.0",
59
59
  "react-markdown": "^10.1.0",
60
60
  "rehype-highlight": "^7.0.2",
61
- "remark-gfm": "^4.0.1"
61
+ "rehype-parse": "^9.0.1",
62
+ "rehype-remark": "^10.0.1",
63
+ "remark-gfm": "^4.0.1",
64
+ "remark-stringify": "^11.0.0",
65
+ "unified": "^11.0.5"
62
66
  },
63
67
  "peerDependencies": {
64
68
  "react": "^19.0.0",
65
69
  "react-dom": "^19.0.0",
66
- "@soroush.tech/design-system": "^1.0.0"
70
+ "@soroush.tech/design-system": "^1.1.0"
67
71
  },
68
72
  "devDependencies": {
69
73
  "@storybook/react-vite": "^10.5.0",
@@ -86,9 +90,9 @@
86
90
  "tsdown": "^0.22.7",
87
91
  "typescript": "~6.0.3",
88
92
  "vitest": "^4.1.10",
89
- "@soroush.tech/hooks": "0.1.0",
93
+ "@soroush.tech/design-system": "1.1.0",
90
94
  "@soroush.tech/eslint-config": "0.0.0",
91
- "@soroush.tech/design-system": "1.0.0"
95
+ "@soroush.tech/hooks": "0.1.0"
92
96
  },
93
97
  "scripts": {
94
98
  "build": "tsdown",