diff --git a/src/renderer/src/pages/home/Messages/Messages.tsx b/src/renderer/src/pages/home/Messages/Messages.tsx index 13c989d4..3b9d801b 100644 --- a/src/renderer/src/pages/home/Messages/Messages.tsx +++ b/src/renderer/src/pages/home/Messages/Messages.tsx @@ -18,7 +18,12 @@ import { updateMessages } from '@renderer/store/messages' import type { Assistant, Message, Topic } from '@renderer/types' -import { captureScrollableDivAsBlob, captureScrollableDivAsDataURL, runAsyncFunction } from '@renderer/utils' +import { + captureScrollableDivAsBlob, + captureScrollableDivAsDataURL, + removeSpecialCharactersForFileName, + runAsyncFunction +} from '@renderer/utils' import { isEmpty, last } from 'lodash' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -154,7 +159,7 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) EventEmitter.on(EVENT_NAMES.EXPORT_TOPIC_IMAGE, async () => { const imageData = await captureScrollableDivAsDataURL(containerRef) if (imageData) { - window.api.file.saveImage(topic.name, imageData) + window.api.file.saveImage(removeSpecialCharactersForFileName(topic.name), imageData) } }), EventEmitter.on(EVENT_NAMES.NEW_CONTEXT, async () => { diff --git a/src/renderer/src/pages/home/Tabs/TopicsTab.tsx b/src/renderer/src/pages/home/Tabs/TopicsTab.tsx index 2223b99e..e30c06b0 100644 --- a/src/renderer/src/pages/home/Tabs/TopicsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/TopicsTab.tsx @@ -35,6 +35,7 @@ import { findIndex } from 'lodash' import { FC, useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' +import { removeSpecialCharactersForFileName } from '@renderer/utils' interface Props { assistant: Assistant @@ -234,7 +235,7 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic key: 'word', onClick: async () => { const markdown = await topicToMarkdown(topic) - window.api.export.toWord(markdown, topic.name) + window.api.export.toWord(markdown, removeSpecialCharactersForFileName(topic.name)) } }, { diff --git a/src/renderer/src/providers/AnthropicProvider.ts b/src/renderer/src/providers/AnthropicProvider.ts index b129a0f6..270c6c0c 100644 --- a/src/renderer/src/providers/AnthropicProvider.ts +++ b/src/renderer/src/providers/AnthropicProvider.ts @@ -13,7 +13,7 @@ import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@rende import { EVENT_NAMES } from '@renderer/services/EventService' import { filterContextMessages, filterUserRoleStartMessages } from '@renderer/services/MessagesService' import { Assistant, FileTypes, MCPToolResponse, Message, Model, Provider, Suggestion } from '@renderer/types' -import { removeSpecialCharacters } from '@renderer/utils' +import { removeSpecialCharactersForTopicName } from '@renderer/utils' import { first, flatten, sum, takeRight } from 'lodash' import OpenAI from 'openai' @@ -409,7 +409,7 @@ export default class AnthropicProvider extends BaseProvider { const content = message.content[0].type === 'text' ? message.content[0].text : '' - return removeSpecialCharacters(content) + return removeSpecialCharactersForTopicName(content) } public async generateText({ prompt, content }: { prompt: string; content: string }): Promise { diff --git a/src/renderer/src/providers/GeminiProvider.ts b/src/renderer/src/providers/GeminiProvider.ts index db1258b7..a5bccda2 100644 --- a/src/renderer/src/providers/GeminiProvider.ts +++ b/src/renderer/src/providers/GeminiProvider.ts @@ -20,7 +20,7 @@ import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@rende import { EVENT_NAMES } from '@renderer/services/EventService' import { filterContextMessages, filterUserRoleStartMessages } from '@renderer/services/MessagesService' import { Assistant, FileType, FileTypes, MCPToolResponse, Message, Model, Provider, Suggestion } from '@renderer/types' -import { removeSpecialCharacters } from '@renderer/utils' +import { removeSpecialCharactersForTopicName } from '@renderer/utils' import axios from 'axios' import { isEmpty, takeRight } from 'lodash' import OpenAI from 'openai' @@ -379,7 +379,7 @@ export default class GeminiProvider extends BaseProvider { const { response } = await chat.sendMessage(userMessage.content) - return removeSpecialCharacters(response.text()) + return removeSpecialCharactersForTopicName(response.text()) } public async generateText({ prompt, content }: { prompt: string; content: string }): Promise { diff --git a/src/renderer/src/providers/OpenAIProvider.ts b/src/renderer/src/providers/OpenAIProvider.ts index 0d893305..57ee85dd 100644 --- a/src/renderer/src/providers/OpenAIProvider.ts +++ b/src/renderer/src/providers/OpenAIProvider.ts @@ -21,7 +21,7 @@ import { Provider, Suggestion } from '@renderer/types' -import { removeSpecialCharacters } from '@renderer/utils' +import { removeSpecialCharactersForTopicName } from '@renderer/utils' import { takeRight } from 'lodash' import OpenAI, { AzureOpenAI } from 'openai' import { @@ -575,7 +575,7 @@ export default class OpenAIProvider extends BaseProvider { let content = response.choices[0].message?.content || '' content = content.replace(/^(.*?)<\/think>/s, '') - return removeSpecialCharacters(content.substring(0, 50)) + return removeSpecialCharactersForTopicName(content.substring(0, 50)) } public async generateText({ prompt, content }: { prompt: string; content: string }): Promise { diff --git a/src/renderer/src/utils/export.ts b/src/renderer/src/utils/export.ts index e99e91cb..32c0f0f5 100644 --- a/src/renderer/src/utils/export.ts +++ b/src/renderer/src/utils/export.ts @@ -5,6 +5,7 @@ import { getMessageTitle } from '@renderer/services/MessagesService' import store from '@renderer/store' import { setExportState } from '@renderer/store/runtime' import { Message, Topic } from '@renderer/types' +import { removeSpecialCharactersForFileName } from '@renderer/utils/index' export const messageToMarkdown = (message: Message) => { const roleText = message.role === 'user' ? '🧑‍💻 User' : '🤖 Assistant' @@ -30,7 +31,7 @@ export const topicToMarkdown = async (topic: Topic) => { } export const exportTopicAsMarkdown = async (topic: Topic) => { - const fileName = topic.name + '.md' + const fileName = removeSpecialCharactersForFileName(topic.name) + '.md' const markdown = await topicToMarkdown(topic) window.api.file.save(fileName, markdown) } diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index 8cc84ed8..342b3870 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -180,6 +180,17 @@ export function removeSpecialCharacters(str: string) { return str.replace(/[\n"]/g, '').replace(/[\p{M}\p{N}\p{P}\p{S}]/gu, '') } +export function removeSpecialCharactersForTopicName(str: string) { + return str.replace(/[\r\n]+/g, ' ').trim() +} + +export function removeSpecialCharactersForFileName(str: string) { + return str + .replace(/[<>:"/\\|?*.]/g, '_') + .replace(/[\r\n]+/g, ' ') + .trim() +} + export function generateColorFromChar(char: string) { // 使用字符的Unicode值作为随机种子 const seed = char.charCodeAt(0)