@advui/editor 0.5.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.
Files changed (28) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +35 -0
  3. package/dist/components/rich-text-editor/RichTextContent.d.ts +10 -0
  4. package/dist/components/rich-text-editor/RichTextContent.d.ts.map +1 -0
  5. package/dist/components/rich-text-editor/RichTextContent.js +49 -0
  6. package/dist/components/rich-text-editor/RichTextContent.js.map +1 -0
  7. package/dist/components/rich-text-editor/RichTextEditor.d.ts +44 -0
  8. package/dist/components/rich-text-editor/RichTextEditor.d.ts.map +1 -0
  9. package/dist/components/rich-text-editor/RichTextEditor.js +144 -0
  10. package/dist/components/rich-text-editor/RichTextEditor.js.map +1 -0
  11. package/dist/components/rich-text-editor/index.d.ts +4 -0
  12. package/dist/components/rich-text-editor/index.d.ts.map +1 -0
  13. package/dist/components/rich-text-editor/index.js +4 -0
  14. package/dist/components/rich-text-editor/index.js.map +1 -0
  15. package/dist/components/rich-text-editor/markdown.d.ts +56 -0
  16. package/dist/components/rich-text-editor/markdown.d.ts.map +1 -0
  17. package/dist/components/rich-text-editor/markdown.js +203 -0
  18. package/dist/components/rich-text-editor/markdown.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -0
  23. package/package.json +85 -0
  24. package/src/components/rich-text-editor/RichTextContent.tsx +119 -0
  25. package/src/components/rich-text-editor/RichTextEditor.tsx +330 -0
  26. package/src/components/rich-text-editor/index.ts +14 -0
  27. package/src/components/rich-text-editor/markdown.ts +244 -0
  28. package/src/index.ts +2 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * The Markdown subset Rich Text Editor writes and Rich Text Content reads:
3
+ * **bold**, _italic_, ~~strike~~, `code`, [links](https://…), `#` headings,
4
+ * `-` and `1.` lists, and `>` quotes.
5
+ */
6
+ const marks = { bold: '**', italic: '_', strikethrough: '~~', code: '`' };
7
+ const linePatterns = {
8
+ heading: /^#{1,6} /,
9
+ bulletList: /^[-*] /,
10
+ orderedList: /^\d+\. /,
11
+ quote: /^> /,
12
+ };
13
+ /** Lists replace each other; a heading replaces a list. Quotes nest around anything. */
14
+ const replaces = {
15
+ heading: [linePatterns.heading, linePatterns.bulletList, linePatterns.orderedList],
16
+ bulletList: [linePatterns.bulletList, linePatterns.orderedList, linePatterns.heading],
17
+ orderedList: [linePatterns.orderedList, linePatterns.bulletList, linePatterns.heading],
18
+ quote: [linePatterns.quote],
19
+ };
20
+ /**
21
+ * Applies a toolbar tool to the text around the selection. Pressing a tool
22
+ * again removes it. Returns the new text and what to select next.
23
+ */
24
+ export function applyFormat(value, selection, tool, placeholder = tool === 'link' ? 'link text' : 'text') {
25
+ const start = Math.max(0, Math.min(selection.start, selection.end, value.length));
26
+ const end = Math.min(value.length, Math.max(selection.start, selection.end));
27
+ const selected = value.slice(start, end);
28
+ if (tool in marks) {
29
+ const mark = marks[tool];
30
+ const n = mark.length;
31
+ // The marks sit just outside the selection: remove them.
32
+ if (value.slice(start - n, start) === mark && value.slice(end, end + n) === mark) {
33
+ return {
34
+ value: value.slice(0, start - n) + selected + value.slice(end + n),
35
+ selection: { start: start - n, end: end - n },
36
+ };
37
+ }
38
+ // The selection includes the marks: remove them.
39
+ if (selected.length >= 2 * n && selected.startsWith(mark) && selected.endsWith(mark)) {
40
+ const inner = selected.slice(n, -n);
41
+ return {
42
+ value: value.slice(0, start) + inner + value.slice(end),
43
+ selection: { start, end: start + inner.length },
44
+ };
45
+ }
46
+ const text = selected || placeholder;
47
+ return {
48
+ value: value.slice(0, start) + mark + text + mark + value.slice(end),
49
+ selection: { start: start + n, end: start + n + text.length },
50
+ };
51
+ }
52
+ if (tool === 'link') {
53
+ const url = 'https://';
54
+ const text = selected || placeholder;
55
+ const inserted = `[${text}](${url})`;
56
+ // With text selected the address is next to type; otherwise the text is.
57
+ const focus = selected
58
+ ? { start: start + text.length + 3, end: start + text.length + 3 + url.length }
59
+ : { start: start + 1, end: start + 1 + text.length };
60
+ return { value: value.slice(0, start) + inserted + value.slice(end), selection: focus };
61
+ }
62
+ // Line tools work on every line the selection touches.
63
+ const kind = tool;
64
+ const lineStart = value.lastIndexOf('\n', start - 1) + 1;
65
+ const endsAtBreak = end > start && value[end - 1] === '\n';
66
+ let lineEnd = value.indexOf('\n', endsAtBreak ? end - 1 : end);
67
+ if (lineEnd === -1)
68
+ lineEnd = value.length;
69
+ const lines = value.slice(lineStart, lineEnd).split('\n');
70
+ const filled = lines.filter((line) => line.trim() !== '');
71
+ const remove = filled.length > 0 && filled.every((line) => linePatterns[kind].test(line));
72
+ let number = 0;
73
+ const next = lines.map((line) => {
74
+ if (remove)
75
+ return line.replace(linePatterns[kind], '');
76
+ if (line.trim() === '' && lines.length > 1)
77
+ return line;
78
+ const bare = replaces[kind].reduce((text, pattern) => text.replace(pattern, ''), line);
79
+ number += 1;
80
+ const prefix = kind === 'heading'
81
+ ? '## '
82
+ : kind === 'bulletList'
83
+ ? '- '
84
+ : kind === 'orderedList'
85
+ ? `${number}. `
86
+ : '> ';
87
+ return prefix + bare;
88
+ });
89
+ const block = next.join('\n');
90
+ const result = value.slice(0, lineStart) + block + value.slice(lineEnd);
91
+ if (lines.length > 1) {
92
+ return { value: result, selection: { start: lineStart, end: lineStart + block.length } };
93
+ }
94
+ const delta = block.length - (lineEnd - lineStart);
95
+ return {
96
+ value: result,
97
+ selection: {
98
+ start: Math.max(lineStart, start + delta),
99
+ end: Math.max(lineStart, end + delta),
100
+ },
101
+ };
102
+ }
103
+ /** Keyboard shortcuts (with ⌘ or Ctrl). */
104
+ export const shortcuts = {
105
+ b: 'bold',
106
+ i: 'italic',
107
+ k: 'link',
108
+ };
109
+ const inlinePatterns = [
110
+ { type: 'code', pattern: /`([^`\n]+)`/ },
111
+ { type: 'link', pattern: /\[([^\]\n]+)\]\(([^)\s]+)\)/ },
112
+ { type: 'bold', pattern: /\*\*(.+?)\*\*/ },
113
+ { type: 'strike', pattern: /~~(.+?)~~/ },
114
+ { type: 'italic', pattern: /(?<![\w\\])_(.+?)_(?!\w)|(?<!\*)\*([^*\n]+)\*(?!\*)/ },
115
+ ];
116
+ /** Only web and mail links are followed; anything else (e.g. `javascript:`) stays text. */
117
+ export function safeHref(href) {
118
+ return /^(https?:\/\/|mailto:)/i.test(href) ? href : null;
119
+ }
120
+ export function parseInline(text) {
121
+ const nodes = [];
122
+ let rest = text;
123
+ while (rest) {
124
+ let first = null;
125
+ for (const { type, pattern } of inlinePatterns) {
126
+ const match = pattern.exec(rest);
127
+ if (match && (!first || match.index < first.match.index))
128
+ first = { type, match };
129
+ }
130
+ if (!first) {
131
+ nodes.push({ type: 'text', text: rest });
132
+ break;
133
+ }
134
+ const { type, match } = first;
135
+ if (match.index > 0)
136
+ nodes.push({ type: 'text', text: rest.slice(0, match.index) });
137
+ const inner = match[1] ?? match[2] ?? '';
138
+ if (type === 'code')
139
+ nodes.push({ type: 'code', text: inner });
140
+ else if (type === 'link') {
141
+ const href = safeHref(match[2]);
142
+ if (href)
143
+ nodes.push({ type: 'link', href, children: parseInline(inner) });
144
+ else
145
+ nodes.push(...parseInline(inner));
146
+ }
147
+ else
148
+ nodes.push({ type: type, children: parseInline(inner) });
149
+ rest = rest.slice(match.index + match[0].length);
150
+ }
151
+ return nodes;
152
+ }
153
+ export function parseMarkdown(source) {
154
+ const blocks = [];
155
+ const lines = source.replace(/\r\n?/g, '\n').split('\n');
156
+ let i = 0;
157
+ while (i < lines.length) {
158
+ const line = lines[i];
159
+ if (line.trim() === '') {
160
+ i++;
161
+ continue;
162
+ }
163
+ const heading = /^(#{1,6}) (.*)$/.exec(line);
164
+ if (heading) {
165
+ blocks.push({
166
+ type: 'heading',
167
+ level: heading[1].length,
168
+ children: parseInline(heading[2]),
169
+ });
170
+ i++;
171
+ continue;
172
+ }
173
+ if (/^> ?/.test(line)) {
174
+ const quoted = [];
175
+ while (i < lines.length && /^> ?/.test(lines[i]))
176
+ quoted.push(lines[i++].replace(/^> ?/, ''));
177
+ blocks.push({ type: 'quote', children: parseMarkdown(quoted.join('\n')) });
178
+ continue;
179
+ }
180
+ const listItem = /^([-*]|\d+\.) (.*)$/.exec(line);
181
+ if (listItem) {
182
+ const ordered = listItem[1].endsWith('.');
183
+ const items = [];
184
+ while (i < lines.length) {
185
+ const item = /^([-*]|\d+\.) (.*)$/.exec(lines[i]);
186
+ if (!item || item[1].endsWith('.') !== ordered)
187
+ break;
188
+ items.push(parseInline(item[2]));
189
+ i++;
190
+ }
191
+ blocks.push({ type: 'list', ordered, start: ordered ? parseInt(listItem[1], 10) : 1, items });
192
+ continue;
193
+ }
194
+ const paragraph = [];
195
+ while (i < lines.length &&
196
+ lines[i].trim() !== '' &&
197
+ !/^(#{1,6} |> ?|[-*] |\d+\. )/.test(lines[i]))
198
+ paragraph.push(lines[i++]);
199
+ blocks.push({ type: 'paragraph', children: parseInline(paragraph.join('\n')) });
200
+ }
201
+ return blocks;
202
+ }
203
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/components/rich-text-editor/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './components/rich-text-editor';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // Rich text editing: the Markdown editor, its viewer and the Markdown helpers they share.
2
+ export * from './components/rich-text-editor';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,cAAc,+BAA+B,CAAA"}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@advui/editor",
3
+ "version": "0.5.0",
4
+ "description": "Rich text editing for Adv UI: a Markdown editor with a formatting toolbar, shortcuts and a preview, and a viewer for the same Markdown, on web, iOS and Android.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/zinmin232/advui.git",
9
+ "directory": "packages/editor"
10
+ },
11
+ "homepage": "https://github.com/zinmin232/advui/tree/main/packages/editor#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/zinmin232/advui/issues"
14
+ },
15
+ "sideEffects": false,
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "react-native": "./src/index.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "!src/**/*.test.*",
30
+ "!src/**/examples",
31
+ "!src/**/*.meta.ts"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "react-native": "./dist/index.js"
36
+ },
37
+ "dependencies": {
38
+ "@advui/icons": "0.5.0"
39
+ },
40
+ "peerDependencies": {
41
+ "@advui/core": "^0.5.0",
42
+ "react": ">=19",
43
+ "react-native": "*",
44
+ "tamagui": "^2.7.7"
45
+ },
46
+ "devDependencies": {
47
+ "@jest/globals": "^29.7.0",
48
+ "@testing-library/jest-dom": "^7.0.1",
49
+ "@testing-library/react": "^16.3.3",
50
+ "@testing-library/react-native": "^14.0.1",
51
+ "@testing-library/user-event": "^14.6.7",
52
+ "@types/node": "^22.15.0",
53
+ "@types/react": "~19.2.2",
54
+ "@types/react-dom": "~19.2.2",
55
+ "@advui/eslint-config": "0.1.0",
56
+ "@advui/tsconfig": "0.1.0",
57
+ "@vitejs/plugin-react": "^6.1.1",
58
+ "babel-preset-expo": "~57.0.13",
59
+ "expo": "~57.0.25",
60
+ "happy-dom": "^20.14.5",
61
+ "jest": "^29.7.0",
62
+ "jest-expo": "~57.0.5",
63
+ "react": "19.2.3",
64
+ "react-dom": "19.2.3",
65
+ "react-native": "0.86.3",
66
+ "react-native-safe-area-context": "~5.7.0",
67
+ "react-native-svg": "15.15.4",
68
+ "react-native-web": "~0.21.0",
69
+ "tamagui": "2.7.7",
70
+ "test-renderer": "~1.2.0",
71
+ "vite": "^8.3.1",
72
+ "vitest": "^5.0.2",
73
+ "@advui/core": "0.5.0",
74
+ "@advui/theme": "0.5.0"
75
+ },
76
+ "scripts": {
77
+ "build": "tsc -p tsconfig.build.json",
78
+ "typecheck": "tsc --noEmit",
79
+ "lint": "eslint .",
80
+ "test": "vitest run",
81
+ "test:watch": "vitest",
82
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
83
+ "test:native": "jest --config jest.native.config.js"
84
+ }
85
+ }
@@ -0,0 +1,119 @@
1
+ import { Fragment, type ReactNode } from 'react'
2
+ import { Linking } from 'react-native'
3
+ import { type GetProps, View, XStack, isWeb } from 'tamagui'
4
+ import { Text, type TextSize } from '@advui/core'
5
+ import { type BlockNode, type InlineNode, parseMarkdown } from './markdown'
6
+
7
+ const headingSizes = { 1: 'xl', 2: 'lg', 3: 'base' } as const
8
+
9
+ // Nested text takes the block's size: Text's own default would reset it.
10
+ function Inline({ nodes, size }: { nodes: InlineNode[]; size: TextSize }): ReactNode {
11
+ return nodes.map((node, i) => {
12
+ switch (node.type) {
13
+ case 'text':
14
+ return <Fragment key={i}>{node.text}</Fragment>
15
+ case 'code':
16
+ return (
17
+ <Text key={i} mono size={size} backgroundColor="$muted" borderRadius="$xs">
18
+ {` ${node.text} `}
19
+ </Text>
20
+ )
21
+ case 'bold':
22
+ return (
23
+ <Text key={i} size={size} fontWeight="700">
24
+ <Inline nodes={node.children} size={size} />
25
+ </Text>
26
+ )
27
+ case 'italic':
28
+ return (
29
+ <Text key={i} size={size} fontStyle="italic">
30
+ <Inline nodes={node.children} size={size} />
31
+ </Text>
32
+ )
33
+ case 'strike':
34
+ return (
35
+ <Text key={i} size={size} textDecorationLine="line-through">
36
+ <Inline nodes={node.children} size={size} />
37
+ </Text>
38
+ )
39
+ case 'link':
40
+ return (
41
+ <Text
42
+ key={i}
43
+ size={size}
44
+ tone="primary"
45
+ textDecorationLine="underline"
46
+ {...(isWeb
47
+ ? { render: 'a', href: node.href, target: '_blank', rel: 'noopener noreferrer' }
48
+ : { role: 'link', onPress: () => void Linking.openURL(node.href) })}
49
+ >
50
+ <Inline nodes={node.children} size={size} />
51
+ </Text>
52
+ )
53
+ }
54
+ })
55
+ }
56
+
57
+ function Blocks({ blocks, headingOffset }: { blocks: BlockNode[]; headingOffset: number }) {
58
+ return blocks.map((block, i) => {
59
+ switch (block.type) {
60
+ case 'heading': {
61
+ const size = headingSizes[block.level as 1 | 2 | 3] ?? 'base'
62
+ return (
63
+ <Text
64
+ key={i}
65
+ role="heading"
66
+ aria-level={Math.min(6, block.level + headingOffset)}
67
+ size={size}
68
+ weight="semibold"
69
+ >
70
+ <Inline nodes={block.children} size={size} />
71
+ </Text>
72
+ )
73
+ }
74
+ case 'paragraph':
75
+ return (
76
+ <Text key={i} size="sm">
77
+ <Inline nodes={block.children} size="sm" />
78
+ </Text>
79
+ )
80
+ case 'quote':
81
+ return (
82
+ <View key={i} borderLeftWidth={3} borderColor="$border" paddingLeft="$3" gap="$2">
83
+ <Blocks blocks={block.children} headingOffset={headingOffset} />
84
+ </View>
85
+ )
86
+ case 'list':
87
+ return (
88
+ <View key={i} gap="$1" {...(isWeb && { role: 'list' })}>
89
+ {block.items.map((item, j) => (
90
+ <XStack key={j} gap="$2" {...(isWeb && { role: 'listitem' })}>
91
+ <Text aria-hidden size="sm" tone="muted" minWidth="$4" textAlign="right">
92
+ {block.ordered ? `${block.start + j}.` : '•'}
93
+ </Text>
94
+ <Text size="sm" flex={1}>
95
+ <Inline nodes={item} size="sm" />
96
+ </Text>
97
+ </XStack>
98
+ ))}
99
+ </View>
100
+ )
101
+ }
102
+ })
103
+ }
104
+
105
+ export interface RichTextContentProps extends Omit<GetProps<typeof View>, 'children'> {
106
+ /** Markdown, as written by Rich Text Editor. */
107
+ children: string
108
+ /** Added to heading levels: with 1 (default), `#` is an h2 under your page's h1. */
109
+ headingOffset?: number
110
+ }
111
+
112
+ /** Shows Markdown written with Rich Text Editor, with the same styles on every platform. */
113
+ export function RichTextContent({ children, headingOffset = 1, ...props }: RichTextContentProps) {
114
+ return (
115
+ <View gap="$3" {...props}>
116
+ <Blocks blocks={parseMarkdown(children)} headingOffset={headingOffset} />
117
+ </View>
118
+ )
119
+ }