Phase 14.5 구현: 회의 모드 고도화 — 다중 문서 생성/편집/내보내기
PLAUD 수준의 기능 확장: - 전사 편집: 인라인 편집 + 원본 보존 + 수정됨 표시(점선 밑줄) - 다중 문서 생성: 회의록/보고서/아이디어노트/커스텀 템플릿 - MD 에디터: 렌더링/편집 토글 (react-markdown + remark-gfm) - 상세 페이지 리디자인: 풀스크린 탭 전환 구조 - 내보내기: MD, PDF, TXT, DOCX 4종 (docx 패키지) - 커스텀 프롬프트 템플릿 저장/재사용 (electron-store) - SSOT: 마크다운 파싱 유틸 추출, 후처리에서 자동 문서 생성 제거 DB: meeting_documents 테이블 + edited_transcript 컬럼 IPC: 8+4 채널, 서비스 2개(MeetingModeService 확장 + MeetingDocTemplateService 신규) UI: 8개 신규 컴포넌트 (meeting/ 디렉토리), 12개 locale i18n
This commit is contained in:
parent
6197ceb132
commit
d4928ffa60
38 changed files with 4791 additions and 438 deletions
198
src/renderer/components/meeting/MarkdownRenderer.tsx
Normal file
198
src/renderer/components/meeting/MarkdownRenderer.tsx
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
// src/renderer/components/meeting/MarkdownRenderer.tsx
|
||||
// Phase 14.5: react-markdown + remark-gfm 래퍼 — d3roPalette 기반 스타일
|
||||
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Box } from '@mui/material'
|
||||
import { d3roPalette, d3roFontMono, d3roTypo, d3roRadius } from '../../theme'
|
||||
import type { Components } from 'react-markdown'
|
||||
|
||||
interface MarkdownRendererProps {
|
||||
content: string
|
||||
}
|
||||
|
||||
const components: Components = {
|
||||
h1: ({ children }) => (
|
||||
<Box
|
||||
component="h1"
|
||||
sx={{
|
||||
fontSize: d3roTypo.value.size,
|
||||
fontWeight: 600,
|
||||
color: d3roPalette.accent.amber,
|
||||
mt: 2,
|
||||
mb: 1,
|
||||
borderBottom: `1px solid ${d3roPalette.border.default}`,
|
||||
pb: 0.5,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<Box
|
||||
component="h2"
|
||||
sx={{
|
||||
fontSize: d3roTypo.heading.size,
|
||||
fontWeight: 600,
|
||||
color: d3roPalette.text.primary,
|
||||
mt: 1.5,
|
||||
mb: 0.75,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<Box
|
||||
component="h3"
|
||||
sx={{
|
||||
fontSize: d3roTypo.body.size,
|
||||
fontWeight: 600,
|
||||
color: d3roPalette.text.secondary,
|
||||
mt: 1,
|
||||
mb: 0.5,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
p: ({ children }) => (
|
||||
<Box
|
||||
component="p"
|
||||
sx={{
|
||||
fontSize: d3roTypo.body.size,
|
||||
color: d3roPalette.text.primary,
|
||||
lineHeight: 1.7,
|
||||
mb: 0.75,
|
||||
mt: 0,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
code: ({ children, className }) => {
|
||||
const isBlock = className?.startsWith('language-') ?? false
|
||||
if (isBlock) {
|
||||
return (
|
||||
<Box
|
||||
component="pre"
|
||||
sx={{
|
||||
bgcolor: d3roPalette.bg.inset,
|
||||
borderRadius: d3roRadius.small,
|
||||
p: 1.5,
|
||||
overflowX: 'auto',
|
||||
my: 1,
|
||||
border: `1px solid ${d3roPalette.border.subtle}`,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="code"
|
||||
sx={{
|
||||
fontFamily: d3roFontMono,
|
||||
fontSize: d3roTypo.compact.size,
|
||||
color: d3roPalette.accent.amber,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
component="code"
|
||||
sx={{
|
||||
fontFamily: d3roFontMono,
|
||||
fontSize: d3roTypo.compact.size,
|
||||
color: d3roPalette.accent.amber,
|
||||
bgcolor: d3roPalette.bg.inset,
|
||||
px: 0.5,
|
||||
py: 0.25,
|
||||
borderRadius: d3roRadius.xs,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
table: ({ children }) => (
|
||||
<Box
|
||||
component="table"
|
||||
sx={{
|
||||
width: '100%',
|
||||
borderCollapse: 'collapse',
|
||||
fontSize: d3roTypo.compact.size,
|
||||
mb: 1,
|
||||
'& th, & td': {
|
||||
border: `1px solid ${d3roPalette.border.default}`,
|
||||
p: 0.75,
|
||||
textAlign: 'left',
|
||||
},
|
||||
'& th': {
|
||||
bgcolor: d3roPalette.bg.chassis,
|
||||
color: d3roPalette.text.secondary,
|
||||
fontWeight: 600,
|
||||
},
|
||||
'& tr:nth-of-type(even)': {
|
||||
bgcolor: d3roPalette.bg.inset,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
li: ({ children }) => (
|
||||
<Box
|
||||
component="li"
|
||||
sx={{
|
||||
fontSize: d3roTypo.body.size,
|
||||
color: d3roPalette.text.primary,
|
||||
lineHeight: 1.7,
|
||||
mb: 0.25,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
ul: ({ children }) => (
|
||||
<Box
|
||||
component="ul"
|
||||
sx={{ pl: 2.5, mb: 0.75, mt: 0 }}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
ol: ({ children }) => (
|
||||
<Box
|
||||
component="ol"
|
||||
sx={{ pl: 2.5, mb: 0.75, mt: 0 }}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
blockquote: ({ children }) => (
|
||||
<Box
|
||||
component="blockquote"
|
||||
sx={{
|
||||
borderLeft: `3px solid ${d3roPalette.accent.amber}`,
|
||||
pl: 1.5,
|
||||
ml: 0,
|
||||
my: 0.75,
|
||||
color: d3roPalette.text.secondary,
|
||||
fontStyle: 'italic',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
),
|
||||
}
|
||||
|
||||
export function MarkdownRenderer({ content }: MarkdownRendererProps): React.ReactElement {
|
||||
return (
|
||||
<Box sx={{ color: d3roPalette.text.primary, fontSize: d3roTypo.body.size }}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={components}>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue