From 9b36a08bbb55fccf260207e4a7548ad9b4f27c75 Mon Sep 17 00:00:00 2001 From: tianyaxiang Date: Wed, 29 Jan 2025 22:39:42 +0800 Subject: [PATCH] clear code --- package.json | 4 +- pnpm-lock.yaml | 4 +- src/app/layout.tsx | 3 - src/components/editor/Editor.tsx | 10 +- src/components/editor/StyleConfigDialog.tsx | 114 ++++++++---------- .../editor/components/EditorToolbar.tsx | 2 +- src/components/template/TemplateManager.tsx | 62 +++------- src/components/template/WechatStylePicker.tsx | 10 +- src/components/theme/ThemeProvider.tsx | 3 +- src/lib/markdown.ts | 26 ++-- 10 files changed, 102 insertions(+), 136 deletions(-) diff --git a/package.json b/package.json index cd6acde..49bd30a 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "@radix-ui/react-toast": "^1.2.5", "@radix-ui/react-toggle": "^1.0.3", "@radix-ui/react-tooltip": "^1.1.7", - "@tiptap/extension-image": "^2.2.4", - "@tiptap/extension-link": "^2.2.4", + "@tiptap/extension-image": "^2.11.3", + "@tiptap/extension-link": "^2.11.3", "@tiptap/pm": "^2.2.4", "@tiptap/react": "^2.2.4", "@tiptap/starter-kit": "^2.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d86d4a..bf1a238 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,10 +42,10 @@ importers: specifier: ^1.1.7 version: 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tiptap/extension-image': - specifier: ^2.2.4 + specifier: ^2.11.3 version: 2.11.3(@tiptap/core@2.11.3(@tiptap/pm@2.11.3)) '@tiptap/extension-link': - specifier: ^2.2.4 + specifier: ^2.11.3 version: 2.11.3(@tiptap/core@2.11.3(@tiptap/pm@2.11.3))(@tiptap/pm@2.11.3) '@tiptap/pm': specifier: ^2.2.4 diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d0f217f..5b6ebf9 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,9 @@ import type { Metadata, Viewport } from "next"; -import { Inter } from "next/font/google"; import "./globals.css"; import { cn } from "@/lib/utils"; import { Toaster } from "@/components/ui/toaster"; import { ThemeProvider } from "@/components/theme/ThemeProvider"; -const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "NeuraPress", @@ -38,7 +36,6 @@ export default function RootLayout({ { const [preview, setPreview] = useState(false) const editor = useEditor({ - extensions: [StarterKit], + extensions: [ + StarterKit, + Link.configure({ + openOnClick: false, + }), + Image, + ], content: '

开始编辑...

', editorProps: { attributes: { diff --git a/src/components/editor/StyleConfigDialog.tsx b/src/components/editor/StyleConfigDialog.tsx index bce915f..bb5697d 100644 --- a/src/components/editor/StyleConfigDialog.tsx +++ b/src/components/editor/StyleConfigDialog.tsx @@ -13,50 +13,44 @@ const stylePresets = { default: { name: '默认样式', options: { - fontSize: { - h1: '24px', - h2: '20px', - h3: '18px', - paragraph: '15px', - code: '14px' + base: { + primaryColor: '#333333', + textAlign: 'left', + lineHeight: '1.75' }, - colors: { - text: '#333333', - heading: '#1a1a1a', - link: '#576b95', - code: '#333333', - quote: '#666666' + block: { + h1: { fontSize: '24px', color: '#1a1a1a' }, + h2: { fontSize: '20px', color: '#1a1a1a' }, + h3: { fontSize: '18px', color: '#1a1a1a' }, + p: { fontSize: '15px', color: '#333333' }, + code_pre: { fontSize: '14px', color: '#333333' } }, - spacing: { - paragraph: '20px', - heading: '30px', - list: '20px', - quote: '20px' + inline: { + link: { color: '#576b95' }, + codespan: { color: '#333333' }, + em: { color: '#666666' } } } }, modern: { name: '现代简约', options: { - fontSize: { - h1: '28px', - h2: '24px', - h3: '20px', - paragraph: '16px', - code: '15px' + base: { + primaryColor: '#2d3748', + textAlign: 'left', + lineHeight: '1.8' }, - colors: { - text: '#2d3748', - heading: '#1a202c', - link: '#4299e1', - code: '#2d3748', - quote: '#718096' + block: { + h1: { fontSize: '28px', color: '#1a202c' }, + h2: { fontSize: '24px', color: '#1a202c' }, + h3: { fontSize: '20px', color: '#1a202c' }, + p: { fontSize: '16px', color: '#2d3748' }, + code_pre: { fontSize: '15px', color: '#2d3748' } }, - spacing: { - paragraph: '24px', - heading: '36px', - list: '24px', - quote: '24px' + inline: { + link: { color: '#4299e1' }, + codespan: { color: '#2d3748' }, + em: { color: '#718096' } } } } @@ -64,16 +58,16 @@ const stylePresets = { interface StyleConfigDialogProps { value: RendererOptions - onChange: (options: RendererOptions) => void + onChangeAction: (options: RendererOptions) => void } -export function StyleConfigDialog({ value, onChange }: StyleConfigDialogProps) { +export function StyleConfigDialog({ value, onChangeAction }: StyleConfigDialogProps) { const [currentOptions, setCurrentOptions] = useState(value) const handlePresetChange = (preset: keyof typeof stylePresets) => { const newOptions = stylePresets[preset].options setCurrentOptions(newOptions) - onChange(newOptions) + onChangeAction(newOptions) } const handleOptionChange = ( @@ -89,7 +83,7 @@ export function StyleConfigDialog({ value, onChange }: StyleConfigDialogProps) { } } setCurrentOptions(newOptions) - onChange(newOptions) + onChangeAction(newOptions) } return ( @@ -107,9 +101,9 @@ export function StyleConfigDialog({ value, onChange }: StyleConfigDialogProps) { 预设样式 - 字体 - 颜色 - 间距 + 基础 + 块级元素 + 行内元素 @@ -122,10 +116,10 @@ export function StyleConfigDialog({ value, onChange }: StyleConfigDialogProps) { >

{preset.name}

-

+

正文示例

-

+

标题示例

@@ -134,50 +128,42 @@ export function StyleConfigDialog({ value, onChange }: StyleConfigDialogProps) {
- +
- {Object.entries(currentOptions.fontSize || {}).map(([key, value]) => ( + {currentOptions.base && Object.entries(currentOptions.base).map(([key, value]) => (
handleOptionChange('fontSize', key, e.target.value)} + onChange={(e) => handleOptionChange('base', key, e.target.value)} />
))}
- +
- {Object.entries(currentOptions.colors || {}).map(([key, value]) => ( + {currentOptions.block && Object.entries(currentOptions.block).map(([key, value]) => (
-
- handleOptionChange('colors', key, e.target.value)} - /> - handleOptionChange('colors', key, e.target.value)} - /> -
+ handleOptionChange('block', key, e.target.value)} + />
))}
- +
- {Object.entries(currentOptions.spacing || {}).map(([key, value]) => ( + {currentOptions.inline && Object.entries(currentOptions.inline).map(([key, value]) => (
handleOptionChange('spacing', key, e.target.value)} + value={JSON.stringify(value)} + onChange={(e) => handleOptionChange('inline', key, e.target.value)} />
))} diff --git a/src/components/editor/components/EditorToolbar.tsx b/src/components/editor/components/EditorToolbar.tsx index 2e27c7f..1527365 100644 --- a/src/components/editor/components/EditorToolbar.tsx +++ b/src/components/editor/components/EditorToolbar.tsx @@ -67,7 +67,7 @@ export function EditorToolbar({
diff --git a/src/components/theme/ThemeProvider.tsx b/src/components/theme/ThemeProvider.tsx index 220a1f8..51c643f 100644 --- a/src/components/theme/ThemeProvider.tsx +++ b/src/components/theme/ThemeProvider.tsx @@ -1,8 +1,7 @@ "use client" import * as React from "react" -import { ThemeProvider as NextThemesProvider } from "next-themes" -import { type ThemeProviderProps } from "next-themes/dist/types" +import { ThemeProvider as NextThemesProvider, type ThemeProviderProps } from "next-themes" export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children} diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index e38aa1d..6330b11 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -4,9 +4,7 @@ import type { CSSProperties } from 'react' // 配置 marked 选项 marked.setOptions({ gfm: true, - breaks: true, - headerIds: false, - mangle: false, + breaks: true }) // 将 React CSSProperties 转换为 CSS 字符串 @@ -48,12 +46,11 @@ function baseStylesToString(base: RendererOptions['base'] = {}): string { export function convertToWechat(markdown: string, options: RendererOptions = {}): string { const renderer = new marked.Renderer() // 标题渲染 - renderer.heading = ({ tokens, depth }: { tokens: Tokens.Generic[]; depth: number }) => { - const style = options.block?.[`h${depth}` as keyof RendererOptions['block']] + renderer.heading = ({ text, depth }: Tokens.Heading) => { + const style = options.block?.[`h${depth}` as keyof typeof options.block] const styleStr = cssPropertiesToString(style) - const content = tokens.map(token => token.text).join('') - return `${content}` - } + return `${text}` + } // 段落渲染 renderer.paragraph = (text) => { @@ -102,17 +99,18 @@ export function convertToWechat(markdown: string, options: RendererOptions = {}) } // 链接渲染 - renderer.link = (href, title, text) => { + renderer.link = ({ href, title, tokens }: Tokens.Link) => { const style = options.inline?.link const styleStr = cssPropertiesToString(style) + const text = tokens?.map(t => 'text' in t ? t.text : '').join('') || '' return `${text}` } // 图片渲染 - renderer.image = (href, title, text) => { + renderer.image = ({ href, title, text }: Tokens.Image) => { const style = options.block?.image const styleStr = cssPropertiesToString(style) - return `${text}` + return `${text}` } // 列表渲染 @@ -145,7 +143,7 @@ export function convertToXiaohongshu(markdown: string): string { // 配置小红书特定的样式 const xiaohongshuRenderer = new marked.Renderer() - xiaohongshuRenderer.heading = (text, level) => { + xiaohongshuRenderer.heading = ({ text, depth }: Tokens.Heading) => { const fontSize = { 1: '20px', 2: '18px', @@ -153,9 +151,9 @@ export function convertToXiaohongshu(markdown: string): string { 4: '15px', 5: '14px', 6: '14px' - }[level] + }[depth] - return `${text}` + return `${text}` } xiaohongshuRenderer.paragraph = (text) => {