Initial commit: Markdown editor with file management and regex tools
项目特性: - 完整的Markdown编辑器,支持实时预览 - 文件管理功能,支持保存/加载/删除文件 - 正则表达式工具,支持批量文本替换 - 前后端分离架构 - 响应式设计 技术栈: - 前端:React + TypeScript + Vite - 后端:Python Flask - Markdown解析:Python-Markdown 包含组件: - WorkingMarkdownEditor: 基础功能版本 - FullMarkdownEditor: 完整功能版本 - SimpleMarkdownEditor: 简化版本
This commit is contained in:
111
frontend/src/utils/markdown.ts
Normal file
111
frontend/src/utils/markdown.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Markdown工具模块
|
||||
* 提供Markdown相关的辅助函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 提取Markdown中的标题用于目录
|
||||
*/
|
||||
export function extractHeadings(content: string): Array<{
|
||||
level: number;
|
||||
text: string;
|
||||
id: string;
|
||||
}> {
|
||||
const headingRegex = /^(#{1,6})\s+(.+)$/gm;
|
||||
const headings: Array<{ level: number; text: string; id: string }> = [];
|
||||
let match;
|
||||
|
||||
while ((match = headingRegex.exec(content)) !== null) {
|
||||
const level = match[1].length;
|
||||
const text = match[2].trim();
|
||||
const id = text.toLowerCase().replace(/[^\w\u4e00-\u9fa5]+/g, '-');
|
||||
|
||||
headings.push({ level, text, id });
|
||||
}
|
||||
|
||||
return headings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算阅读时间
|
||||
*/
|
||||
export function calculateReadingTime(content: string): number {
|
||||
const chineseChars = content.match(/[\u4e00-\u9fa5]/g) || [];
|
||||
const englishWords = content.match(/\b\w+\b/g) || [];
|
||||
|
||||
const chineseTime = chineseChars.length / 500;
|
||||
const englishTime = englishWords.length / 200;
|
||||
|
||||
return Math.max(1, Math.ceil(chineseTime + englishTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算字数统计
|
||||
*/
|
||||
export function calculateWordCount(content: string): {
|
||||
characters: number;
|
||||
words: number;
|
||||
lines: number;
|
||||
} {
|
||||
const chineseChars = content.match(/[\u4e00-\u9fa5]/g) || [];
|
||||
const englishWords = content.match(/\b\w+\b/g) || [];
|
||||
const lines = content.split('\n').length;
|
||||
|
||||
return {
|
||||
characters: chineseChars.length,
|
||||
words: englishWords.length,
|
||||
lines,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动保存到本地存储
|
||||
*/
|
||||
export function saveToLocalStorage(key: string, content: string): void {
|
||||
try {
|
||||
localStorage.setItem(`markdown-editor-${key}`, content);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save to localStorage:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从本地存储加载内容
|
||||
*/
|
||||
export function loadFromLocalStorage(key: string): string | null {
|
||||
try {
|
||||
return localStorage.getItem(`markdown-editor-${key}`);
|
||||
} catch (error) {
|
||||
console.warn('Failed to load from localStorage:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
*/
|
||||
export function formatFileSize(bytes: number): string {
|
||||
if (bytes === 0) return '0 B';
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间戳
|
||||
*/
|
||||
export function formatDate(timestamp: number): string {
|
||||
const date = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - date.getTime();
|
||||
|
||||
if (diff < 60000) return '刚刚';
|
||||
if (diff < 3600000) return `${Math.floor(diff / 60000)}分钟前`;
|
||||
if (diff < 86400000) return `${Math.floor(diff / 3600000)}小时前`;
|
||||
if (diff < 604800000) return `${Math.floor(diff / 86400000)}天前`;
|
||||
|
||||
return date.toLocaleDateString('zh-CN');
|
||||
}
|
Reference in New Issue
Block a user