Skip to content

Commit 83e3fc6

Browse files
authored
Merge pull request #936 from refly-ai/fix/ducoument-share-issue
Fix/ducoument share issue
2 parents 49edc02 + ae1ccfd commit 83e3fc6

File tree

8 files changed

+225
-4
lines changed

8 files changed

+225
-4
lines changed

packages/ai-workspace-common/src/components/document/collab-editor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,8 @@ export const CollaborativeEditor = memo(
533533
handleDrop: (view, event, _slice, moved) =>
534534
handleImageDrop(view, event, moved, uploadFn),
535535
attributes: {
536-
class: `prose prose-md prose-headings:font-title font-default focus:outline-none max-w-full prose-img:cursor-pointer ${isDarkMode ? '!text-white' : '!text-black'}`,
536+
class:
537+
'prose prose-md prose-headings:font-title font-default focus:outline-none max-w-full prose-img:cursor-pointer dark:text-white',
537538

538539
'data-doc-id': docId,
539540
},

packages/ai-workspace-common/src/components/document/readonly-editor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export const ReadonlyEditor = memo(
108108
},
109109
attributes: {
110110
class:
111-
'prose prose-md prose-headings:font-title font-default focus:outline-none max-w-full',
111+
'prose prose-md prose-headings:font-title font-default focus:outline-none max-w-full dark:text-white',
112112
'data-doc-id': docId,
113113
},
114114
}}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { visit } from 'unist-util-visit';
2+
import type { Node } from 'unist';
3+
4+
// Define minimal Plugin type inline to avoid dependency on 'unified'
5+
type Plugin = () => (tree: any) => void;
6+
7+
/**
8+
* Rehype plugin to transform text nodes with ==highlight== syntax to proper HTML elements.
9+
*/
10+
const rehypeHighlight: Plugin = () => {
11+
return (tree: Node) => {
12+
// Process text nodes for special syntax
13+
visit(tree, 'text', (node: any, index: number | null, parent: any) => {
14+
if (!parent || index === null) return;
15+
16+
const { value } = node;
17+
if (typeof value !== 'string') return;
18+
19+
// Match patterns for highlight
20+
const highlightRegex = /==(.*?)==/g;
21+
22+
// Check if we have any matches
23+
if (!highlightRegex.test(value)) return;
24+
25+
// Reset regex lastIndex
26+
highlightRegex.lastIndex = 0;
27+
28+
// Process the text to create HTML elements
29+
const parts = [];
30+
let lastIndex = 0;
31+
const text = value;
32+
33+
// Process highlights
34+
let match: any = highlightRegex.exec(text);
35+
while (match !== null) {
36+
// Add text before the match
37+
if (match.index > lastIndex) {
38+
parts.push({ type: 'text', value: text.slice(lastIndex, match.index) });
39+
}
40+
41+
// Add the highlight element
42+
parts.push({
43+
type: 'element',
44+
tagName: 'mark',
45+
properties: {},
46+
children: [{ type: 'text', value: match[1] }],
47+
});
48+
49+
lastIndex = match.index + match[0].length;
50+
match = highlightRegex.exec(text);
51+
}
52+
53+
// Add any remaining text
54+
if (lastIndex < text.length) {
55+
parts.push({ type: 'text', value: text.slice(lastIndex) });
56+
}
57+
58+
// Replace the original node with our processed parts
59+
if (parts.length > 0) {
60+
parent.children.splice(index, 1, ...parts);
61+
}
62+
});
63+
};
64+
};
65+
66+
export default rehypeHighlight;

packages/ai-workspace-common/src/components/markdown/index.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { memo, useEffect, useRef, useState, Suspense, useMemo } from 'react';
33
import ReactMarkdown from 'react-markdown';
44

55
import RemarkBreaks from 'remark-breaks';
6+
import RemarkGfm from 'remark-gfm';
67

78
import { cn, markdownCitationParse } from '@refly/utils';
89

910
// plugins
1011
import LinkElement from './plugins/link';
12+
import rehypeHighlight from './custom-plugins/rehype-highlight';
1113

1214
// styles
1315
import './styles/markdown.scss';
@@ -48,6 +50,17 @@ const MarkdownImage = memo(({ src, alt, ...props }: React.ImgHTMLAttributes<HTML
4850
);
4951
});
5052

53+
// Custom Components for enhanced Markdown rendering
54+
const HighlightComponent = ({ children }: { children: React.ReactNode }) => (
55+
<mark className="bg-yellow-200 dark:bg-yellow-800 dark:text-gray-200 rounded-sm px-1 text-inherit">
56+
{children}
57+
</mark>
58+
);
59+
60+
const StrikethroughComponent = ({ children }: { children: React.ReactNode }) => (
61+
<del>{children}</del>
62+
);
63+
5164
export const Markdown = memo(
5265
(
5366
props: {
@@ -127,9 +140,10 @@ export const Markdown = memo(
127140
plugins.RehypeKatex &&
128141
plugins.RehypeHighlight && (
129142
<ReactMarkdown
130-
remarkPlugins={[RemarkBreaks, plugins.RemarkMath]}
143+
remarkPlugins={[RemarkBreaks, plugins.RemarkMath, RemarkGfm]}
131144
rehypePlugins={[
132145
...rehypePlugins,
146+
rehypeHighlight,
133147
plugins.RehypeKatex,
134148
[
135149
plugins.RehypeHighlight,
@@ -143,6 +157,8 @@ export const Markdown = memo(
143157
...artifactComponents,
144158
a: (args) => LinkElement.Component(args, props?.sources || []),
145159
img: MarkdownImage,
160+
mark: HighlightComponent,
161+
del: StrikethroughComponent,
146162
}}
147163
linkTarget={'_blank'}
148164
>

packages/ai-workspace-common/src/components/markdown/styles/markdown.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@
9898
background-color: var(--color-canvas-default);
9999
line-height: 1.5;
100100
word-wrap: break-word;
101+
102+
// Custom formatting styles
103+
del {
104+
text-decoration: line-through;
105+
color: var(--color-fg-subtle);
106+
}
107+
108+
u, .underline {
109+
text-decoration: underline;
110+
text-decoration-thickness: 0.1em;
111+
text-underline-offset: 0.1em;
112+
}
101113
}
102114

103115
.light {

packages/ai-workspace-common/src/modules/multilingual-search/components/action-menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export const ActionMenu: React.FC<ActionMenuProps> = (props) => {
159159

160160
return (
161161
<Affix offsetBottom={0} target={props.getTarget}>
162-
<div className="intergation-footer bg-white dark:bg-[#1f1f1f] border-[#e5e5e5] dark:!border-[#2f2f2f]">
162+
<div className="intergation-footer bg-white dark:bg-[#1f1f1f] !border-[#e5e5e5] dark:!border-[#2f2f2f]">
163163
<div className="footer-location">
164164
<Checkbox
165165
checked={selectedItems.length && selectedItems.length === results.length}

packages/utils/src/editor/schema.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,30 @@ export const schema = new Schema({
273273
},
274274
} as MarkSpec,
275275

276+
underline: {
277+
parseDOM: [
278+
{ tag: 'u' },
279+
{ style: 'text-decoration=underline' },
280+
{ style: 'text-decoration-line=underline' },
281+
],
282+
toDOM() {
283+
return ['u', 0];
284+
},
285+
},
286+
287+
strike: {
288+
parseDOM: [
289+
{ tag: 's' },
290+
{ tag: 'strike' },
291+
{ tag: 'del' },
292+
{ style: 'text-decoration=line-through' },
293+
{ style: 'text-decoration-line=line-through' },
294+
],
295+
toDOM() {
296+
return ['s', 0];
297+
},
298+
},
299+
276300
link: {
277301
attrs: {
278302
href: {},
@@ -301,5 +325,81 @@ export const schema = new Schema({
301325
return ['code'];
302326
},
303327
},
328+
329+
highlight: {
330+
attrs: {
331+
color: { default: null },
332+
},
333+
parseDOM: [
334+
{
335+
tag: 'mark',
336+
getAttrs: (node) => {
337+
const dom = node as HTMLElement;
338+
return {
339+
color: dom.getAttribute('data-color') || dom.style.backgroundColor,
340+
};
341+
},
342+
},
343+
],
344+
toDOM(node) {
345+
const attrs: Record<string, string> = {};
346+
if (node.attrs.color) {
347+
attrs['data-color'] = node.attrs.color;
348+
attrs.style = `background-color: ${node.attrs.color}; color: inherit`;
349+
}
350+
return ['mark', attrs, 0];
351+
},
352+
},
353+
354+
textStyle: {
355+
attrs: {
356+
color: { default: null },
357+
backgroundColor: { default: null },
358+
fontSize: { default: null },
359+
fontFamily: { default: null },
360+
fontWeight: { default: null },
361+
textDecoration: { default: null },
362+
},
363+
parseDOM: [
364+
{
365+
tag: 'span[style]',
366+
getAttrs: (node) => {
367+
const dom = node as HTMLElement;
368+
return {
369+
color: dom.style.color,
370+
backgroundColor: dom.style.backgroundColor,
371+
fontSize: dom.style.fontSize,
372+
fontFamily: dom.style.fontFamily,
373+
fontWeight: dom.style.fontWeight,
374+
textDecoration: dom.style.textDecoration,
375+
};
376+
},
377+
},
378+
],
379+
toDOM(node) {
380+
const attrs: Record<string, string> = { style: '' };
381+
382+
if (node.attrs.color) {
383+
attrs.style += `color: ${node.attrs.color};`;
384+
}
385+
if (node.attrs.backgroundColor) {
386+
attrs.style += `background-color: ${node.attrs.backgroundColor}; color: inherit;`;
387+
}
388+
if (node.attrs.fontSize) {
389+
attrs.style += `font-size: ${node.attrs.fontSize};`;
390+
}
391+
if (node.attrs.fontFamily) {
392+
attrs.style += `font-family: ${node.attrs.fontFamily};`;
393+
}
394+
if (node.attrs.fontWeight) {
395+
attrs.style += `font-weight: ${node.attrs.fontWeight};`;
396+
}
397+
if (node.attrs.textDecoration) {
398+
attrs.style += `text-decoration: ${node.attrs.textDecoration};`;
399+
}
400+
401+
return ['span', attrs, 0];
402+
},
403+
},
304404
},
305405
});

packages/utils/src/editor/to_markdown.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ export const defaultMarkdownSerializer = new MarkdownSerializer(
230230
mixable: true,
231231
expelEnclosingWhitespace: true,
232232
},
233+
underline: {
234+
open: '',
235+
close: '',
236+
mixable: true,
237+
expelEnclosingWhitespace: true,
238+
},
233239
link: {
234240
open(state, mark, parent, index) {
235241
state.inAutolink = isPlainURL(mark, parent, index);
@@ -264,6 +270,26 @@ export const defaultMarkdownSerializer = new MarkdownSerializer(
264270
mixable: true,
265271
expelEnclosingWhitespace: true,
266272
},
273+
textStyle: {
274+
open(_state, mark, _parent, _index) {
275+
// Check if there's backgroundColor and treat it as highlight
276+
if (mark.attrs.backgroundColor) {
277+
return '==';
278+
}
279+
// For other text styles, don't add any markdown markers
280+
return '';
281+
},
282+
close(_state, mark, _parent, _index) {
283+
// Close highlight syntax if there's backgroundColor
284+
if (mark.attrs.backgroundColor) {
285+
return '==';
286+
}
287+
// For other text styles, don't add any markdown markers
288+
return '';
289+
},
290+
mixable: true,
291+
expelEnclosingWhitespace: true,
292+
},
267293
},
268294
);
269295

0 commit comments

Comments
 (0)