修正MD编辑器
parent
70763f502d
commit
06f1b959b4
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>慧会议</title>
|
||||
<title>iMeeting</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-markdown": "^6.5.0",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.38.6",
|
||||
"@uiw/react-codemirror": "^4.25.3",
|
||||
"@uiw/react-md-editor": "^4.0.8",
|
||||
"antd": "^5.27.3",
|
||||
"axios": "^1.6.2",
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ function App() {
|
|||
user ? <EditKnowledgeBase user={user} /> : <Navigate to="/" />
|
||||
} />
|
||||
<Route path="/downloads" element={<ClientDownloadPage />} />
|
||||
<Route path="/meetings/:meetingId/preview" element={<MeetingPreview />} />
|
||||
<Route path="/meetings/preview/:meeting_id" element={<MeetingPreview />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</Router>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
/* Markdown Editor Component */
|
||||
.markdown-editor-wrapper {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.editor-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
background: #f8fafc;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: white;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #374155;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toolbar-btn:hover {
|
||||
background: #f0f4ff;
|
||||
border-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.toolbar-btn.active {
|
||||
background: #667eea;
|
||||
border-color: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.toolbar-btn.active:hover {
|
||||
background: #5a67d8;
|
||||
border-color: #5a67d8;
|
||||
}
|
||||
|
||||
.toolbar-btn strong,
|
||||
.toolbar-btn em {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toolbar-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: #d1d5db;
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
/* 标题下拉菜单 */
|
||||
.toolbar-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin-top: 0.25rem;
|
||||
background: white;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 100;
|
||||
min-width: 160px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dropdown-menu button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border: none;
|
||||
background: white;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
font-family: inherit;
|
||||
color: #374155;
|
||||
}
|
||||
|
||||
.dropdown-menu button:hover {
|
||||
background: #f0f4ff;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.dropdown-menu button h1,
|
||||
.dropdown-menu button h2,
|
||||
.dropdown-menu button h3,
|
||||
.dropdown-menu button h4,
|
||||
.dropdown-menu button h5,
|
||||
.dropdown-menu button h6 {
|
||||
font-weight: 600;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* CodeMirror 样式覆盖 */
|
||||
.markdown-editor-wrapper .cm-editor {
|
||||
border-top: none !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
}
|
||||
|
||||
/* 预览区域 */
|
||||
.markdown-preview {
|
||||
border: 2px solid #e2e8f0;
|
||||
border-top: none;
|
||||
border-radius: 0 0 8px 8px;
|
||||
padding: 2rem;
|
||||
background: white;
|
||||
min-height: 400px;
|
||||
font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
.markdown-preview h1,
|
||||
.markdown-preview h2,
|
||||
.markdown-preview h3,
|
||||
.markdown-preview h4,
|
||||
.markdown-preview h5,
|
||||
.markdown-preview h6 {
|
||||
margin: 1.5rem 0 0.75rem 0;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.markdown-preview h1:first-child,
|
||||
.markdown-preview h2:first-child,
|
||||
.markdown-preview h3:first-child,
|
||||
.markdown-preview h4:first-child,
|
||||
.markdown-preview h5:first-child,
|
||||
.markdown-preview h6:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markdown-preview h1 { font-size: 1.875rem; }
|
||||
.markdown-preview h2 { font-size: 1.5rem; }
|
||||
.markdown-preview h3 { font-size: 1.25rem; }
|
||||
.markdown-preview h4 { font-size: 1.125rem; }
|
||||
.markdown-preview h5 { font-size: 1rem; }
|
||||
.markdown-preview h6 { font-size: 0.875rem; }
|
||||
|
||||
.markdown-preview p {
|
||||
margin: 0.75rem 0;
|
||||
line-height: 1.7;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.markdown-preview ul,
|
||||
.markdown-preview ol {
|
||||
margin: 1rem 0;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.markdown-preview li {
|
||||
margin: 0.5rem 0;
|
||||
line-height: 1.6;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.markdown-preview strong {
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.markdown-preview em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.markdown-preview code {
|
||||
background: #f1f5f9;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
|
||||
font-size: 0.875rem;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.markdown-preview pre {
|
||||
background: #f8fafc;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
border: 1px solid #e2e8f0;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.markdown-preview pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.markdown-preview blockquote {
|
||||
border-left: 4px solid #667eea;
|
||||
padding-left: 1rem;
|
||||
margin: 1rem 0;
|
||||
font-style: italic;
|
||||
color: #64748b;
|
||||
background: #f8fafc;
|
||||
padding: 1rem;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.markdown-preview table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1.5rem 0;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.markdown-preview th,
|
||||
.markdown-preview td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-preview th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.markdown-preview hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
background: #e2e8f0;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.markdown-preview img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.markdown-preview a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.markdown-preview a:hover {
|
||||
border-bottom-color: #667eea;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.editor-toolbar {
|
||||
gap: 0.125rem;
|
||||
padding: 0.375rem;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.markdown-preview {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import './MarkdownEditor.css';
|
||||
|
||||
const MarkdownEditor = ({
|
||||
value,
|
||||
onChange,
|
||||
onImageUpload,
|
||||
placeholder = '在这里编写内容...',
|
||||
height = 400,
|
||||
showImageUpload = true
|
||||
}) => {
|
||||
const editorRef = useRef(null);
|
||||
const imageInputRef = useRef(null);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [showHeadingMenu, setShowHeadingMenu] = useState(false);
|
||||
|
||||
// CodeMirror extensions
|
||||
const editorExtensions = useMemo(() => [
|
||||
markdown({ base: markdownLanguage }),
|
||||
EditorView.lineWrapping,
|
||||
EditorView.theme({
|
||||
"&": {
|
||||
fontSize: "14px",
|
||||
border: "2px solid #e2e8f0",
|
||||
borderRadius: "0 0 8px 8px",
|
||||
borderTop: "none",
|
||||
},
|
||||
".cm-content": {
|
||||
fontFamily: "'Monaco', 'Menlo', 'Consolas', monospace",
|
||||
padding: "1rem",
|
||||
minHeight: `${height}px`,
|
||||
},
|
||||
".cm-scroller": {
|
||||
fontFamily: "'Monaco', 'Menlo', 'Consolas', monospace",
|
||||
},
|
||||
"&.cm-focused": {
|
||||
outline: "none",
|
||||
borderColor: "#667eea",
|
||||
boxShadow: "0 0 0 3px rgba(102, 126, 234, 0.1)",
|
||||
}
|
||||
})
|
||||
], [height]);
|
||||
|
||||
// Markdown 插入函数
|
||||
const insertMarkdown = (before, after = '', placeholder = '') => {
|
||||
if (!editorRef.current?.view) return;
|
||||
|
||||
const view = editorRef.current.view;
|
||||
const selection = view.state.selection.main;
|
||||
const selectedText = view.state.doc.sliceString(selection.from, selection.to);
|
||||
const text = selectedText || placeholder;
|
||||
const newText = `${before}${text}${after}`;
|
||||
|
||||
view.dispatch({
|
||||
changes: { from: selection.from, to: selection.to, insert: newText },
|
||||
selection: { anchor: selection.from + before.length, head: selection.from + before.length + text.length }
|
||||
});
|
||||
view.focus();
|
||||
};
|
||||
|
||||
// 工具栏操作
|
||||
const toolbarActions = {
|
||||
bold: () => insertMarkdown('**', '**', '粗体文字'),
|
||||
italic: () => insertMarkdown('*', '*', '斜体文字'),
|
||||
heading: (level) => {
|
||||
setShowHeadingMenu(false);
|
||||
insertMarkdown('#'.repeat(level) + ' ', '', '标题');
|
||||
},
|
||||
quote: () => insertMarkdown('> ', '', '引用内容'),
|
||||
code: () => insertMarkdown('`', '`', '代码'),
|
||||
codeBlock: () => insertMarkdown('```\n', '\n```', '代码块'),
|
||||
link: () => insertMarkdown('[', '](url)', '链接文字'),
|
||||
unorderedList: () => insertMarkdown('- ', '', '列表项'),
|
||||
orderedList: () => insertMarkdown('1. ', '', '列表项'),
|
||||
table: () => {
|
||||
const tableTemplate = '\n| 列1 | 列2 | 列3 |\n| --- | --- | --- |\n| 单元格 | 单元格 | 单元格 |\n| 单元格 | 单元格 | 单元格 |\n';
|
||||
insertMarkdown(tableTemplate, '', '');
|
||||
},
|
||||
hr: () => insertMarkdown('\n---\n', '', ''),
|
||||
image: () => imageInputRef.current?.click(),
|
||||
};
|
||||
|
||||
// 图片上传处理
|
||||
const handleImageSelect = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file && onImageUpload) {
|
||||
const imageUrl = await onImageUpload(file);
|
||||
if (imageUrl) {
|
||||
insertMarkdown(``, '', '');
|
||||
}
|
||||
}
|
||||
// Reset file input
|
||||
if (imageInputRef.current) {
|
||||
imageInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="markdown-editor-wrapper">
|
||||
<div className="editor-toolbar">
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.bold} title="粗体 (Ctrl+B)">
|
||||
<strong>B</strong>
|
||||
</button>
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.italic} title="斜体 (Ctrl+I)">
|
||||
<em>I</em>
|
||||
</button>
|
||||
|
||||
{/* 多级标题下拉菜单 */}
|
||||
<div className="toolbar-dropdown">
|
||||
<button
|
||||
type="button"
|
||||
className="toolbar-btn"
|
||||
onClick={() => setShowHeadingMenu(!showHeadingMenu)}
|
||||
title="标题"
|
||||
>
|
||||
H ▾
|
||||
</button>
|
||||
{showHeadingMenu && (
|
||||
<div className="dropdown-menu">
|
||||
<button type="button" onClick={() => toolbarActions.heading(1)}>
|
||||
<h1 style={{ fontSize: '1.5rem', margin: 0 }}>标题 1</h1>
|
||||
</button>
|
||||
<button type="button" onClick={() => toolbarActions.heading(2)}>
|
||||
<h2 style={{ fontSize: '1.3rem', margin: 0 }}>标题 2</h2>
|
||||
</button>
|
||||
<button type="button" onClick={() => toolbarActions.heading(3)}>
|
||||
<h3 style={{ fontSize: '1.1rem', margin: 0 }}>标题 3</h3>
|
||||
</button>
|
||||
<button type="button" onClick={() => toolbarActions.heading(4)}>
|
||||
<h4 style={{ fontSize: '1rem', margin: 0 }}>标题 4</h4>
|
||||
</button>
|
||||
<button type="button" onClick={() => toolbarActions.heading(5)}>
|
||||
<h5 style={{ fontSize: '0.9rem', margin: 0 }}>标题 5</h5>
|
||||
</button>
|
||||
<button type="button" onClick={() => toolbarActions.heading(6)}>
|
||||
<h6 style={{ fontSize: '0.85rem', margin: 0 }}>标题 6</h6>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className="toolbar-divider"></span>
|
||||
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.quote} title="引用">
|
||||
"
|
||||
</button>
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.code} title="代码">
|
||||
{'<>'}
|
||||
</button>
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.link} title="链接">
|
||||
🔗
|
||||
</button>
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.table} title="表格">
|
||||
⊞
|
||||
</button>
|
||||
|
||||
{showImageUpload && (
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.image} title="上传图片">
|
||||
⬆︎
|
||||
</button>
|
||||
)}
|
||||
|
||||
<span className="toolbar-divider"></span>
|
||||
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.unorderedList} title="无序列表">
|
||||
•
|
||||
</button>
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.orderedList} title="有序列表">
|
||||
1.
|
||||
</button>
|
||||
<button type="button" className="toolbar-btn" onClick={toolbarActions.hr} title="分隔线">
|
||||
—
|
||||
</button>
|
||||
|
||||
<span className="toolbar-divider"></span>
|
||||
|
||||
{/* 预览按钮 */}
|
||||
<button
|
||||
type="button"
|
||||
className={`toolbar-btn ${showPreview ? 'active' : ''}`}
|
||||
onClick={() => setShowPreview(!showPreview)}
|
||||
title={showPreview ? "编辑" : "预览"}
|
||||
>
|
||||
{showPreview ? '编辑' : '预览'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showPreview ? (
|
||||
<div className="markdown-preview">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw, rehypeSanitize]}
|
||||
>
|
||||
{value || '*暂无内容*'}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
) : (
|
||||
<CodeMirror
|
||||
ref={editorRef}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
extensions={editorExtensions}
|
||||
placeholder={placeholder}
|
||||
basicSetup={{
|
||||
lineNumbers: false,
|
||||
foldGutter: false,
|
||||
highlightActiveLineGutter: false,
|
||||
highlightActiveLine: false,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showImageUpload && (
|
||||
<input
|
||||
ref={imageInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageSelect}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownEditor;
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import apiClient from '../utils/apiClient';
|
||||
import { ArrowLeft, FileText, Tag, Save } from 'lucide-react';
|
||||
import MDEditor, * as commands from '@uiw/react-md-editor';
|
||||
import '@uiw/react-md-editor/markdown-editor.css';
|
||||
import { buildApiUrl, API_ENDPOINTS } from '../config/api';
|
||||
import TagEditor from '../components/TagEditor';
|
||||
import MarkdownEditor from '../components/MarkdownEditor';
|
||||
import './EditKnowledgeBase.css';
|
||||
|
||||
const EditKnowledgeBase = ({ user }) => {
|
||||
|
|
@ -21,9 +20,6 @@ const EditKnowledgeBase = ({ user }) => {
|
|||
const [error, setError] = useState('');
|
||||
const [kb, setKb] = useState(null);
|
||||
|
||||
const handleContentChange = useCallback((value) => {
|
||||
setFormData(prev => ({ ...prev, content: value || '' }));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchKbData();
|
||||
|
|
@ -88,44 +84,6 @@ const EditKnowledgeBase = ({ user }) => {
|
|||
}
|
||||
};
|
||||
|
||||
// 自定义工具栏命令配置
|
||||
const customCommands = [
|
||||
commands.bold,
|
||||
commands.italic,
|
||||
commands.strikethrough,
|
||||
commands.hr,
|
||||
commands.group([
|
||||
commands.title1,
|
||||
commands.title2,
|
||||
commands.title3,
|
||||
commands.title4,
|
||||
commands.title5,
|
||||
commands.title6,
|
||||
], {
|
||||
name: 'title',
|
||||
groupName: 'title',
|
||||
buttonProps: { 'aria-label': '插入标题', title: '插入标题' }
|
||||
}),
|
||||
commands.divider,
|
||||
commands.link,
|
||||
commands.quote,
|
||||
commands.code,
|
||||
commands.codeBlock,
|
||||
commands.image,
|
||||
commands.divider,
|
||||
commands.unorderedListCommand,
|
||||
commands.orderedListCommand,
|
||||
commands.checkedListCommand,
|
||||
];
|
||||
|
||||
// 右侧额外命令(预览、全屏等)
|
||||
const customExtraCommands = [
|
||||
commands.codeEdit,
|
||||
commands.codeLive,
|
||||
commands.codePreview,
|
||||
commands.divider,
|
||||
commands.fullscreen,
|
||||
];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
|
@ -192,35 +150,15 @@ const EditKnowledgeBase = ({ user }) => {
|
|||
内容总结
|
||||
</label>
|
||||
</div>
|
||||
<div className="markdown-editor-container">
|
||||
<MDEditor
|
||||
key="content-editor"
|
||||
value={formData.content}
|
||||
onChange={handleContentChange}
|
||||
data-color-mode="light"
|
||||
height={500}
|
||||
preview="edit"
|
||||
hideToolbar={false}
|
||||
toolbarBottom={false}
|
||||
commands={customCommands}
|
||||
extraCommands={customExtraCommands}
|
||||
autoFocus={false}
|
||||
textareaProps={{
|
||||
placeholder: '在这里编写知识库内容摘要...',
|
||||
style: {
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.5',
|
||||
fontFamily: 'inherit'
|
||||
},
|
||||
spellCheck: false,
|
||||
autoComplete: 'off',
|
||||
autoCapitalize: 'off',
|
||||
autoCorrect: 'off'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<MarkdownEditor
|
||||
value={formData.content}
|
||||
onChange={(value) => setFormData(prev => ({ ...prev, content: value || '' }))}
|
||||
placeholder="在这里编写知识库内容摘要..."
|
||||
height={500}
|
||||
showImageUpload={false}
|
||||
/>
|
||||
<div className="markdown-hint">
|
||||
<small>使用Markdown格式编写知识库内容,支持**粗体**、*斜体*、# 标题、- 列表等格式。</small>
|
||||
<small>使用Markdown格式编写知识库内容,支持**粗体**、*斜体*、# 标题、- 列表、表格等格式。</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -498,50 +498,6 @@
|
|||
transform: none;
|
||||
}
|
||||
|
||||
/* Markdown Editor */
|
||||
.markdown-editor-container {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor-text-input,
|
||||
.markdown-editor-container .w-md-editor-text-textarea,
|
||||
.markdown-editor-container .w-md-editor-text {
|
||||
font-size: 0.9rem !important;
|
||||
line-height: 1.6 !important;
|
||||
caret-color: #667eea !important;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor-text-input {
|
||||
resize: none !important;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor-text-textarea {
|
||||
resize: none !important;
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor-toolbar {
|
||||
background-color: #f8fafc;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor-toolbar button {
|
||||
color: #64748b;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-editor-container .w-md-editor-toolbar button:hover {
|
||||
background-color: #e2e8f0;
|
||||
color: #334155;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Upload indicator */
|
||||
.uploading-indicator {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import apiClient from '../utils/apiClient';
|
||||
import configService from '../utils/configService';
|
||||
import { ArrowLeft, Users, Calendar, FileText, X, User, Save, Upload, Plus, Image, Tag } from 'lucide-react';
|
||||
import MDEditor, * as commands from '@uiw/react-md-editor';
|
||||
import '@uiw/react-md-editor/markdown-editor.css';
|
||||
import { ArrowLeft, Users, Calendar, FileText, X, User, Save, Upload, Plus, Tag } from 'lucide-react';
|
||||
import { buildApiUrl, API_ENDPOINTS, API_BASE_URL } from '../config/api';
|
||||
import DateTimePicker from '../components/DateTimePicker';
|
||||
import TagEditor from '../components/TagEditor';
|
||||
import MarkdownEditor from '../components/MarkdownEditor';
|
||||
import './EditMeeting.css';
|
||||
|
||||
const EditMeeting = ({ user }) => {
|
||||
const navigate = useNavigate();
|
||||
const { meeting_id } = useParams();
|
||||
const imageInputRef = useRef(null);
|
||||
const [formData, setFormData] = useState({
|
||||
title: '',
|
||||
meeting_time: '',
|
||||
|
|
@ -36,10 +34,6 @@ const EditMeeting = ({ user }) => {
|
|||
const [maxFileSize, setMaxFileSize] = useState(100 * 1024 * 1024); // 默认100MB
|
||||
const [maxImageSize, setMaxImageSize] = useState(10 * 1024 * 1024); // 默认10MB
|
||||
|
||||
const handleSummaryChange = useCallback((value) => {
|
||||
setFormData(prev => ({ ...prev, summary: value || '' }));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchMeetingData();
|
||||
fetchUsers();
|
||||
|
|
@ -232,7 +226,7 @@ const EditMeeting = ({ user }) => {
|
|||
formData.append('image_file', file);
|
||||
|
||||
const response = await apiClient.post(
|
||||
buildApiUrl(API_ENDPOINTS.MEETINGS.UPLOAD_IMAGE(meeting_id)),
|
||||
buildApiUrl(API_ENDPOINTS.MEETINGS.UPLOAD_IMAGE(meeting_id)),
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
|
|
@ -250,98 +244,6 @@ const EditMeeting = ({ user }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const insertImageMarkdown = (imageUrl, altText = '图片') => {
|
||||
const imageMarkdown = ``;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
summary: prev.summary + '\n\n' + imageMarkdown
|
||||
}));
|
||||
};
|
||||
|
||||
const handleImageSelect = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const imageUrl = await handleImageUpload(file);
|
||||
if (imageUrl) {
|
||||
insertImageMarkdown(imageUrl, file.name);
|
||||
}
|
||||
}
|
||||
// Reset file input
|
||||
if (imageInputRef.current) {
|
||||
imageInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 创建自定义上传图片命令(只显示文字,不显示图标)
|
||||
const uploadImageCommand = {
|
||||
name: 'upload-image',
|
||||
keyCommand: 'upload-image',
|
||||
buttonProps: { 'aria-label': '上传本地图片', title: '上传本地图片' },
|
||||
icon: <span style={{fontSize:12}}>UploadImage</span>,
|
||||
execute: () => {
|
||||
imageInputRef.current?.click();
|
||||
}
|
||||
};
|
||||
|
||||
// 创建修改后的图片URL命令
|
||||
const imageUrlCommand = {
|
||||
...commands.image,
|
||||
name: 'image-url',
|
||||
icon: <span style={{fontSize:12}}>AddImageURL</span>,
|
||||
};
|
||||
|
||||
// 自定义工具栏命令配置
|
||||
const customCommands = [
|
||||
commands.bold,
|
||||
commands.italic,
|
||||
commands.strikethrough,
|
||||
commands.hr,
|
||||
commands.group([
|
||||
commands.title1,
|
||||
commands.title2,
|
||||
commands.title3,
|
||||
commands.title4,
|
||||
commands.title5,
|
||||
commands.title6,
|
||||
], {
|
||||
name: 'title',
|
||||
groupName: 'title',
|
||||
buttonProps: { 'aria-label': '插入标题', title: '插入标题' }
|
||||
}),
|
||||
commands.divider,
|
||||
commands.link,
|
||||
commands.quote,
|
||||
commands.code,
|
||||
commands.codeBlock,
|
||||
// 创建图片功能组,使用系统自带的image命令和自定义上传命令
|
||||
commands.group([
|
||||
imageUrlCommand,
|
||||
uploadImageCommand
|
||||
], {
|
||||
name: 'image-group',
|
||||
groupName: 'image',
|
||||
buttonProps: { 'aria-label': '添加图片', title: '添加图片' },
|
||||
icon: (
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
||||
</svg>
|
||||
)
|
||||
}),
|
||||
commands.divider,
|
||||
commands.unorderedListCommand,
|
||||
commands.orderedListCommand,
|
||||
commands.checkedListCommand,
|
||||
];
|
||||
|
||||
// 右侧额外命令(预览、全屏等)
|
||||
const customExtraCommands = [
|
||||
commands.codeEdit,
|
||||
commands.codeLive,
|
||||
commands.codePreview,
|
||||
commands.divider,
|
||||
commands.fullscreen,
|
||||
];
|
||||
|
||||
const filteredUsers = availableUsers.filter(user => {
|
||||
// Exclude users already selected as attendees
|
||||
const isAlreadySelected = formData.attendees.some(attendee => attendee.user_id === user.user_id);
|
||||
|
|
@ -591,42 +493,16 @@ const EditMeeting = ({ user }) => {
|
|||
会议摘要
|
||||
</label>
|
||||
</div>
|
||||
<div className="markdown-editor-container">
|
||||
<MDEditor
|
||||
key="summary-editor"
|
||||
value={formData.summary}
|
||||
onChange={handleSummaryChange}
|
||||
data-color-mode="light"
|
||||
height={400}
|
||||
preview="edit"
|
||||
hideToolbar={false}
|
||||
toolbarBottom={false}
|
||||
commands={customCommands}
|
||||
extraCommands={customExtraCommands}
|
||||
autoFocus={false}
|
||||
textareaProps={{
|
||||
placeholder: '在这里编写会议摘要...',
|
||||
style: {
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.5',
|
||||
fontFamily: 'inherit'
|
||||
},
|
||||
spellCheck: false,
|
||||
autoComplete: 'off',
|
||||
autoCapitalize: 'off',
|
||||
autoCorrect: 'off'
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
ref={imageInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageSelect}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
<MarkdownEditor
|
||||
value={formData.summary}
|
||||
onChange={(value) => setFormData(prev => ({ ...prev, summary: value || '' }))}
|
||||
onImageUpload={handleImageUpload}
|
||||
placeholder="在这里编写会议摘要..."
|
||||
height={400}
|
||||
showImageUpload={true}
|
||||
/>
|
||||
<div className="markdown-hint">
|
||||
<small>使用Markdown格式编写会议摘要,支持**粗体**、*斜体*、# 标题、- 列表等格式。工具栏中可以上传图片或插入图片URL。</small>
|
||||
<small>使用Markdown格式编写会议摘要,支持**粗体**、*斜体*、# 标题、- 列表、表格等格式。</small>
|
||||
</div>
|
||||
{isUploadingImage && (
|
||||
<div className="uploading-indicator">
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ const HomePage = ({ onLogin }) => {
|
|||
{/* Hero Section */}
|
||||
<section className="hero">
|
||||
<div className="hero-content">
|
||||
<h1 className="hero-title">“慧会议” —— 让会议更智能</h1>
|
||||
<h1 className="hero-title">iMeeting —— 让会议更智能</h1>
|
||||
<p className="hero-subtitle">
|
||||
通过AI将会议音频转录并自动总结,对齐团队目标,构建企业知识库。
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -893,6 +893,19 @@
|
|||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.speaker-index {
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
background: #e2e8f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
|
|
|
|||
|
|
@ -1266,19 +1266,26 @@ const MeetingDetails = ({ user }) => {
|
|||
</div>
|
||||
|
||||
<div className="transcript-content">
|
||||
{transcript.map((item, index) => (
|
||||
<div
|
||||
key={item.segment_id}
|
||||
{transcript.map((item, index) => {
|
||||
// 计算当前发言人的序号:这是该发言人的第几条发言
|
||||
const speakerSegments = transcript.filter(seg => seg.speaker_id === item.speaker_id);
|
||||
const currentSpeakerIndex = speakerSegments.findIndex(seg => seg.segment_id === item.segment_id) + 1;
|
||||
const totalSpeakerSegments = speakerSegments.length;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.segment_id}
|
||||
ref={(el) => transcriptRefs.current[index] = el}
|
||||
className={`transcript-item ${currentHighlightIndex === index ? 'active' : ''}`}
|
||||
>
|
||||
<div className="transcript-header-item">
|
||||
<span
|
||||
<span
|
||||
className="speaker-name clickable"
|
||||
onClick={() => jumpToTime(item.start_time_ms / 1000)}
|
||||
title="跳转到此时间点播放"
|
||||
>
|
||||
{item.speaker_tag}
|
||||
<span className="speaker-index"> {currentSpeakerIndex}/{totalSpeakerSegments}</span>
|
||||
</span>
|
||||
<div className="transcript-item-actions">
|
||||
<span
|
||||
|
|
@ -1299,7 +1306,7 @@ const MeetingDetails = ({ user }) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
<div
|
||||
className="transcript-text clickable"
|
||||
onClick={() => jumpToTime(item.start_time_ms / 1000)}
|
||||
title="跳转到此时间点播放"
|
||||
|
|
@ -1307,7 +1314,8 @@ const MeetingDetails = ({ user }) => {
|
|||
{item.text_content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1574,7 +1582,7 @@ const MeetingDetails = ({ user }) => {
|
|||
<QRCodeModal
|
||||
isOpen={showQRModal}
|
||||
onClose={() => setShowQRModal(false)}
|
||||
url={`${window.location.origin}/meetings/${meeting_id}/preview`}
|
||||
url={`${window.location.origin}/meetings/preview/${meeting_id}`}
|
||||
title={meeting.title}
|
||||
description="扫描二维码查看会议总结"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import rehypeSanitize from 'rehype-sanitize';
|
|||
import './MeetingPreview.css';
|
||||
|
||||
const MeetingPreview = () => {
|
||||
const { meetingId } = useParams();
|
||||
const { meeting_id } = useParams();
|
||||
const [meetingData, setMeetingData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
|
@ -15,13 +15,13 @@ const MeetingPreview = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchMeetingPreviewData();
|
||||
}, [meetingId]);
|
||||
}, [meeting_id]);
|
||||
|
||||
const fetchMeetingPreviewData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
||||
const response = await fetch(`${apiUrl}/api/meetings/${meetingId}/preview-data`);
|
||||
const response = await fetch(`${apiUrl}/api/meetings/${meeting_id}/preview-data`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === "200") {
|
||||
|
|
|
|||
243
yarn.lock
243
yarn.lock
|
|
@ -201,7 +201,7 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.27.1"
|
||||
|
||||
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.21.5", "@babel/runtime@^7.22.5", "@babel/runtime@^7.22.6", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0":
|
||||
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.21.5", "@babel/runtime@^7.22.5", "@babel/runtime@^7.22.6", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz"
|
||||
integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
|
||||
|
|
@ -246,6 +246,135 @@
|
|||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
|
||||
"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.7.1":
|
||||
version "6.19.1"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.19.1.tgz#355e49c9fd275b42a6e16e9ea0cf4361f67a3ec4"
|
||||
integrity sha512-q6NenYkEy2fn9+JyjIxMWcNjzTL/IhwqfzOut1/G3PrIFkrbl4AL7Wkse5tLrQUUyqGoAKU5+Pi5jnnXxH5HGw==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.17.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0":
|
||||
version "6.10.0"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.10.0.tgz#b3206984fec8443c4d910565eb2e9d591c7d80b2"
|
||||
integrity sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.4.0"
|
||||
"@codemirror/view" "^6.27.0"
|
||||
"@lezer/common" "^1.1.0"
|
||||
|
||||
"@codemirror/lang-css@^6.0.0":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.1.tgz#763ca41aee81bb2431be55e3cfcc7cc8e91421a3"
|
||||
integrity sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@lezer/common" "^1.0.2"
|
||||
"@lezer/css" "^1.1.7"
|
||||
|
||||
"@codemirror/lang-html@^6.0.0":
|
||||
version "6.4.11"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.11.tgz#c46ba46ae642fd567cf05c4129005d2913ac248d"
|
||||
integrity sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/lang-css" "^6.0.0"
|
||||
"@codemirror/lang-javascript" "^6.0.0"
|
||||
"@codemirror/language" "^6.4.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.17.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
"@lezer/css" "^1.1.0"
|
||||
"@lezer/html" "^1.3.12"
|
||||
|
||||
"@codemirror/lang-javascript@^6.0.0":
|
||||
version "6.2.4"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz#eef2227d1892aae762f3a0f212f72bec868a02c5"
|
||||
integrity sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/language" "^6.6.0"
|
||||
"@codemirror/lint" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.17.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
"@lezer/javascript" "^1.0.0"
|
||||
|
||||
"@codemirror/lang-markdown@^6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz#29df87310a555b007beba8e12893363956a26e8e"
|
||||
integrity sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.7.1"
|
||||
"@codemirror/lang-html" "^6.0.0"
|
||||
"@codemirror/language" "^6.3.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
"@lezer/common" "^1.2.1"
|
||||
"@lezer/markdown" "^1.0.0"
|
||||
|
||||
"@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0", "@codemirror/language@^6.4.0", "@codemirror/language@^6.6.0":
|
||||
version "6.11.3"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.3.tgz#8e6632df566a7ed13a1bd307f9837765bb1abfdd"
|
||||
integrity sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.23.0"
|
||||
"@lezer/common" "^1.1.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
"@lezer/lr" "^1.0.0"
|
||||
style-mod "^4.0.0"
|
||||
|
||||
"@codemirror/lint@^6.0.0":
|
||||
version "6.9.2"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.9.2.tgz#09ed0aedec13381c9e36e1ac5d126027740c3ef4"
|
||||
integrity sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.35.0"
|
||||
crelt "^1.0.5"
|
||||
|
||||
"@codemirror/search@^6.0.0":
|
||||
version "6.5.11"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/search/-/search-6.5.11.tgz#a324ffee36e032b7f67aa31c4fb9f3e6f9f3ed63"
|
||||
integrity sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
crelt "^1.0.5"
|
||||
|
||||
"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.4.0", "@codemirror/state@^6.5.0", "@codemirror/state@^6.5.2":
|
||||
version "6.5.2"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz#8eca3a64212a83367dc85475b7d78d5c9b7076c6"
|
||||
integrity sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==
|
||||
dependencies:
|
||||
"@marijn/find-cluster-break" "^1.0.0"
|
||||
|
||||
"@codemirror/theme-one-dark@^6.0.0":
|
||||
version "6.1.3"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz#1dbb73f6e73c53c12ad2aed9f48c263c4e63ea37"
|
||||
integrity sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
|
||||
"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.35.0", "@codemirror/view@^6.38.6":
|
||||
version "6.38.6"
|
||||
resolved "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.6.tgz#25d9df071393801196c311025d2caa7a5523c26c"
|
||||
integrity sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.5.0"
|
||||
crelt "^1.0.6"
|
||||
style-mod "^4.1.0"
|
||||
w3c-keyname "^2.2.4"
|
||||
|
||||
"@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz"
|
||||
|
|
@ -513,6 +642,65 @@
|
|||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@lezer/common@^1.0.0", "@lezer/common@^1.0.2", "@lezer/common@^1.1.0", "@lezer/common@^1.2.0", "@lezer/common@^1.2.1", "@lezer/common@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmmirror.com/@lezer/common/-/common-1.3.0.tgz#123427ec4c53c2c8367415b4441e555b4f85c696"
|
||||
integrity sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==
|
||||
|
||||
"@lezer/css@^1.1.0", "@lezer/css@^1.1.7":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmmirror.com/@lezer/css/-/css-1.3.0.tgz#296f298814782c2fad42a936f3510042cdcd2034"
|
||||
integrity sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.2.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
"@lezer/lr" "^1.3.0"
|
||||
|
||||
"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.1.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.3.tgz#a20f324b71148a2ea9ba6ff42e58bbfaec702857"
|
||||
integrity sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.3.0"
|
||||
|
||||
"@lezer/html@^1.3.12":
|
||||
version "1.3.12"
|
||||
resolved "https://registry.npmmirror.com/@lezer/html/-/html-1.3.12.tgz#a438e2d04f4c863d49cad27efe714cde8cf3ff1b"
|
||||
integrity sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.2.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
"@lezer/lr" "^1.0.0"
|
||||
|
||||
"@lezer/javascript@^1.0.0":
|
||||
version "1.5.4"
|
||||
resolved "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.4.tgz#11746955f957d33c0933f17d7594db54a8b4beea"
|
||||
integrity sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.2.0"
|
||||
"@lezer/highlight" "^1.1.3"
|
||||
"@lezer/lr" "^1.3.0"
|
||||
|
||||
"@lezer/lr@^1.0.0", "@lezer/lr@^1.3.0":
|
||||
version "1.4.3"
|
||||
resolved "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.3.tgz#51b252ff8ff9fea863819de7f4b6501ccf69d403"
|
||||
integrity sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@lezer/markdown@^1.0.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmmirror.com/@lezer/markdown/-/markdown-1.6.0.tgz#9db3356d52955391b021cc3ead9c79f87186840f"
|
||||
integrity sha512-AXb98u3M6BEzTnreBnGtQaF7xFTiMA92Dsy5tqEjpacbjRxDSFdN4bKJo9uvU4cEEOS7D2B9MT7kvDgOEIzJSw==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.0.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
|
||||
"@marijn/find-cluster-break@^1.0.0":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8"
|
||||
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
|
||||
|
||||
"@rc-component/async-validator@^5.0.3":
|
||||
version "5.0.4"
|
||||
resolved "https://registry.npmmirror.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz"
|
||||
|
|
@ -825,11 +1013,36 @@
|
|||
resolved "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz"
|
||||
integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
|
||||
|
||||
"@uiw/codemirror-extensions-basic-setup@4.25.3":
|
||||
version "4.25.3"
|
||||
resolved "https://registry.npmmirror.com/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.3.tgz#6fb28745e7012bfcad0dc5103119487e40a744bc"
|
||||
integrity sha512-F1doRyD50CWScwGHG2bBUtUpwnOv/zqSnzkZqJcX5YAHQx6Z1CuX8jdnFMH6qktRrPU1tfpNYftTWu3QIoHiMA==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/commands" "^6.0.0"
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/lint" "^6.0.0"
|
||||
"@codemirror/search" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
|
||||
"@uiw/copy-to-clipboard@~1.0.12":
|
||||
version "1.0.17"
|
||||
resolved "https://registry.npmmirror.com/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.17.tgz"
|
||||
integrity sha512-O2GUHV90Iw2VrSLVLK0OmNIMdZ5fgEg4NhvtwINsX+eZ/Wf6DWD0TdsK9xwV7dNRnK/UI2mQtl0a2/kRgm1m1A==
|
||||
|
||||
"@uiw/react-codemirror@^4.25.3":
|
||||
version "4.25.3"
|
||||
resolved "https://registry.npmmirror.com/@uiw/react-codemirror/-/react-codemirror-4.25.3.tgz#dd61549051d4398068f087858b39f3fc988c7537"
|
||||
integrity sha512-1wtBZTXPIp8u6F/xjHvsUAYlEeF5Dic4xZBnqJyLzv7o7GjGYEUfSz9Z7bo9aK9GAx2uojG/AuBMfhA4uhvIVQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.6"
|
||||
"@codemirror/commands" "^6.1.0"
|
||||
"@codemirror/state" "^6.1.1"
|
||||
"@codemirror/theme-one-dark" "^6.0.0"
|
||||
"@uiw/codemirror-extensions-basic-setup" "4.25.3"
|
||||
codemirror "^6.0.0"
|
||||
|
||||
"@uiw/react-markdown-preview@^5.0.6":
|
||||
version "5.1.5"
|
||||
resolved "https://registry.npmmirror.com/@uiw/react-markdown-preview/-/react-markdown-preview-5.1.5.tgz"
|
||||
|
|
@ -1137,6 +1350,19 @@ classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classna
|
|||
resolved "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz"
|
||||
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
||||
|
||||
codemirror@^6.0.0:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz#4d3fea1ad60b6753f97ca835f2f48c6936a8946e"
|
||||
integrity sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/commands" "^6.0.0"
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/lint" "^6.0.0"
|
||||
"@codemirror/search" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz"
|
||||
|
|
@ -1203,6 +1429,11 @@ core-js@^3.6.0, core-js@^3.8.3:
|
|||
resolved "https://registry.npmmirror.com/core-js/-/core-js-3.45.1.tgz"
|
||||
integrity sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==
|
||||
|
||||
crelt@^1.0.5, crelt@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmmirror.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
|
||||
integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==
|
||||
|
||||
cross-spawn@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz"
|
||||
|
|
@ -3879,6 +4110,11 @@ strip-json-comments@^3.1.1:
|
|||
resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
style-mod@^4.0.0, style-mod@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.3.tgz#6e9012255bb799bdac37e288f7671b5d71bf9f73"
|
||||
integrity sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==
|
||||
|
||||
style-to-js@^1.0.0:
|
||||
version "1.1.17"
|
||||
resolved "https://registry.npmmirror.com/style-to-js/-/style-to-js-1.1.17.tgz"
|
||||
|
|
@ -4082,6 +4318,11 @@ vite@^7.0.4:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
w3c-keyname@^2.2.4:
|
||||
version "2.2.8"
|
||||
resolved "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
|
||||
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
|
||||
|
||||
web-namespaces@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/web-namespaces/-/web-namespaces-2.0.1.tgz"
|
||||
|
|
|
|||
Loading…
Reference in New Issue