feat: 对话国际化样式调整

This commit is contained in:
DEV_DSW
2026-04-21 11:42:40 +08:00
parent 6ac4bf1dd9
commit 0e10ae9e35
4 changed files with 168 additions and 13 deletions

View File

@@ -1,2 +1,7 @@
require('bytenode') "use strict";
require('./main.jsc') require("electron");
require("./main-Bm7LE7mJ.js");
require("electron-squirrel-startup");
require("electron-log");
require("bytenode");
require("axios");

View File

@@ -1 +1,137 @@
"use strict";const r=require("electron");var i=(e=>(e.EXTERNAL_OPEN="external-open",e.APP_SET_FRAMELESS="app:set-frameless",e.APP_LOAD_PAGE="app:load-page",e.TAB_CREATE="tab:create",e.TAB_LIST="tab:list",e.TAB_NAVIGATE="tab:navigate",e.TAB_RELOAD="tab:reload",e.TAB_BACK="tab:back",e.TAB_FORWARD="tab:forward",e.TAB_SWITCH="tab:switch",e.TAB_CLOSE="tab:close",e.LOG_TO_MAIN="log-to-main",e.READ_FILE="read-file",e.INVOKE="ipc:invoke",e.INVOKE_ASYNC="ipc:invokeAsync",e.APP_MINIMIZE="app:minimize",e.APP_MAXIMIZE="app:maximize",e.APP_QUIT="app:quit",e.FILE_READ="file:read",e.FILE_WRITE="file:write",e.GET_WINDOW_ID="get-window-id",e.CUSTOM_EVENT="custom:event",e.TIME_UPDATE="time:update",e.RENDERER_IS_READY="renderer-ready",e.SHOW_CONTEXT_MENU="show-context-menu",e.START_A_DIALOGUE="start-a-dialogue",e.OPEN_WINDOW="open-window",e.LOG_DEBUG="log-debug",e.LOG_INFO="log-info",e.LOG_WARN="log-warn",e.LOG_ERROR="log-error",e.CONFIG_UPDATED="config-updated",e.SET_CONFIG="set-config",e.GET_CONFIG="get-config",e.UPDATE_CONFIG="update-config",e.SET_THEME_MODE="set-theme-mode",e.GET_THEME_MODE="get-theme-mode",e.IS_DARK_THEME="is-dark-theme",e.THEME_MODE_UPDATED="theme-mode-updated",e.EXECUTE_SCRIPT="execute-script",e.TASK_PROGRESS="task:progress",e.TASK_STARTED="task:started",e.TASK_COMPLETED="task:completed",e.OPEN_CHANNEL="open-channel",e.SCRIPT_LIST="script:list",e.SCRIPT_GET="script:get",e.SCRIPT_SAVE="script:save",e.SCRIPT_DELETE="script:delete",e.SCRIPT_TOGGLE="script:toggle",e.SCRIPT_RUN="script:run",e.SCRIPT_RECORD_START="script:record-start",e.SCRIPT_RECORD_STOP="script:record-stop",e.SCRIPT_CODEGEN="script:codegen",e.GATEWAY_RPC="gateway:rpc",e.GATEWAY_EVENT="gateway:event",e.UPDATE_CHECK="update:check",e.UPDATE_DOWNLOAD="update:download",e.UPDATE_INSTALL="update:install",e.UPDATE_VERSION="update:version",e.UPDATE_STATUS_CHANGED="update:status-changed",e))(i||{});const d={versions:process.versions,external:{open:e=>r.ipcRenderer.invoke("external-open",e)},platform:process.platform,windowMinimize:()=>r.ipcRenderer.invoke("window:minimize"),windowMaximize:()=>r.ipcRenderer.invoke("window:maximize"),windowClose:()=>r.ipcRenderer.invoke("window:close"),windowIsMaximized:()=>r.ipcRenderer.invoke("window:isMaximized"),viewIsReady:()=>r.ipcRenderer.send(i.RENDERER_IS_READY),app:{setFrameless:e=>r.ipcRenderer.invoke(i.APP_SET_FRAMELESS,e),loadPage:e=>r.ipcRenderer.invoke(i.APP_LOAD_PAGE,e)},readFile:e=>r.ipcRenderer.invoke(i.READ_FILE,e),invoke:(e,...n)=>r.ipcRenderer.invoke(e,...n),invokeAsync:(e,...n)=>r.ipcRenderer.invoke(e,...n),on:(e,n)=>{const t=(o,...R)=>n(...R);return r.ipcRenderer.on(e,t),()=>r.ipcRenderer.removeListener(e,t)},send:(e,...n)=>r.ipcRenderer.send(e,...n),getCurrentWindowId:()=>r.ipcRenderer.sendSync(i.GET_WINDOW_ID),logger:{debug:(e,...n)=>r.ipcRenderer.send(i.LOG_DEBUG,e,...n),info:(e,...n)=>r.ipcRenderer.send(i.LOG_INFO,e,...n),warn:(e,...n)=>r.ipcRenderer.send(i.LOG_WARN,e,...n),error:(e,...n)=>r.ipcRenderer.send(i.LOG_ERROR,e,...n)},executeScript:e=>r.ipcRenderer.invoke(i.EXECUTE_SCRIPT,e),onTaskProgress:e=>{const n=(t,o)=>e(o);return r.ipcRenderer.on(i.TASK_PROGRESS,n),()=>r.ipcRenderer.removeListener(i.TASK_PROGRESS,n)},onTaskStarted:e=>{const n=(t,o)=>e(o);return r.ipcRenderer.on(i.TASK_STARTED,n),()=>r.ipcRenderer.removeListener(i.TASK_STARTED,n)},onTaskCompleted:e=>{const n=(t,o)=>e(o);return r.ipcRenderer.on(i.TASK_COMPLETED,n),()=>r.ipcRenderer.removeListener(i.TASK_COMPLETED,n)},openChannel:e=>r.ipcRenderer.invoke(i.OPEN_CHANNEL,e),scriptApi:{list:()=>r.ipcRenderer.invoke(i.SCRIPT_LIST),get:e=>r.ipcRenderer.invoke(i.SCRIPT_GET,e),save:e=>r.ipcRenderer.invoke(i.SCRIPT_SAVE,e),delete:e=>r.ipcRenderer.invoke(i.SCRIPT_DELETE,e),toggle:(e,n)=>r.ipcRenderer.invoke(i.SCRIPT_TOGGLE,e,n),run:e=>r.ipcRenderer.invoke(i.SCRIPT_RUN,e),startRecording:e=>r.ipcRenderer.invoke(i.SCRIPT_RECORD_START,e),stopRecording:()=>r.ipcRenderer.invoke(i.SCRIPT_RECORD_STOP),codegen:(e,n)=>r.ipcRenderer.invoke(i.SCRIPT_CODEGEN,e,n)}};r.contextBridge.exposeInMainWorld("api",d); "use strict";
const electron = require("electron");
var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => {
IPC_EVENTS2["EXTERNAL_OPEN"] = "external-open";
IPC_EVENTS2["APP_SET_FRAMELESS"] = "app:set-frameless";
IPC_EVENTS2["APP_LOAD_PAGE"] = "app:load-page";
IPC_EVENTS2["TAB_CREATE"] = "tab:create";
IPC_EVENTS2["TAB_LIST"] = "tab:list";
IPC_EVENTS2["TAB_NAVIGATE"] = "tab:navigate";
IPC_EVENTS2["TAB_RELOAD"] = "tab:reload";
IPC_EVENTS2["TAB_BACK"] = "tab:back";
IPC_EVENTS2["TAB_FORWARD"] = "tab:forward";
IPC_EVENTS2["TAB_SWITCH"] = "tab:switch";
IPC_EVENTS2["TAB_CLOSE"] = "tab:close";
IPC_EVENTS2["LOG_TO_MAIN"] = "log-to-main";
IPC_EVENTS2["READ_FILE"] = "read-file";
IPC_EVENTS2["INVOKE"] = "ipc:invoke";
IPC_EVENTS2["INVOKE_ASYNC"] = "ipc:invokeAsync";
IPC_EVENTS2["APP_MINIMIZE"] = "app:minimize";
IPC_EVENTS2["APP_MAXIMIZE"] = "app:maximize";
IPC_EVENTS2["APP_QUIT"] = "app:quit";
IPC_EVENTS2["FILE_READ"] = "file:read";
IPC_EVENTS2["FILE_WRITE"] = "file:write";
IPC_EVENTS2["GET_WINDOW_ID"] = "get-window-id";
IPC_EVENTS2["CUSTOM_EVENT"] = "custom:event";
IPC_EVENTS2["TIME_UPDATE"] = "time:update";
IPC_EVENTS2["RENDERER_IS_READY"] = "renderer-ready";
IPC_EVENTS2["SHOW_CONTEXT_MENU"] = "show-context-menu";
IPC_EVENTS2["START_A_DIALOGUE"] = "start-a-dialogue";
IPC_EVENTS2["OPEN_WINDOW"] = "open-window";
IPC_EVENTS2["LOG_DEBUG"] = "log-debug";
IPC_EVENTS2["LOG_INFO"] = "log-info";
IPC_EVENTS2["LOG_WARN"] = "log-warn";
IPC_EVENTS2["LOG_ERROR"] = "log-error";
IPC_EVENTS2["CONFIG_UPDATED"] = "config-updated";
IPC_EVENTS2["SET_CONFIG"] = "set-config";
IPC_EVENTS2["GET_CONFIG"] = "get-config";
IPC_EVENTS2["UPDATE_CONFIG"] = "update-config";
IPC_EVENTS2["SET_THEME_MODE"] = "set-theme-mode";
IPC_EVENTS2["GET_THEME_MODE"] = "get-theme-mode";
IPC_EVENTS2["IS_DARK_THEME"] = "is-dark-theme";
IPC_EVENTS2["THEME_MODE_UPDATED"] = "theme-mode-updated";
IPC_EVENTS2["EXECUTE_SCRIPT"] = "execute-script";
IPC_EVENTS2["TASK_PROGRESS"] = "task:progress";
IPC_EVENTS2["TASK_STARTED"] = "task:started";
IPC_EVENTS2["TASK_COMPLETED"] = "task:completed";
IPC_EVENTS2["OPEN_CHANNEL"] = "open-channel";
IPC_EVENTS2["SCRIPT_LIST"] = "script:list";
IPC_EVENTS2["SCRIPT_GET"] = "script:get";
IPC_EVENTS2["SCRIPT_SAVE"] = "script:save";
IPC_EVENTS2["SCRIPT_DELETE"] = "script:delete";
IPC_EVENTS2["SCRIPT_TOGGLE"] = "script:toggle";
IPC_EVENTS2["SCRIPT_RUN"] = "script:run";
IPC_EVENTS2["SCRIPT_RECORD_START"] = "script:record-start";
IPC_EVENTS2["SCRIPT_RECORD_STOP"] = "script:record-stop";
IPC_EVENTS2["SCRIPT_CODEGEN"] = "script:codegen";
IPC_EVENTS2["GATEWAY_RPC"] = "gateway:rpc";
IPC_EVENTS2["GATEWAY_EVENT"] = "gateway:event";
IPC_EVENTS2["UPDATE_CHECK"] = "update:check";
IPC_EVENTS2["UPDATE_DOWNLOAD"] = "update:download";
IPC_EVENTS2["UPDATE_INSTALL"] = "update:install";
IPC_EVENTS2["UPDATE_VERSION"] = "update:version";
IPC_EVENTS2["UPDATE_STATUS_CHANGED"] = "update:status-changed";
return IPC_EVENTS2;
})(IPC_EVENTS || {});
const api = {
versions: process.versions,
external: {
open: (url) => electron.ipcRenderer.invoke("external-open", url)
},
platform: process.platform,
windowMinimize: () => electron.ipcRenderer.invoke("window:minimize"),
windowMaximize: () => electron.ipcRenderer.invoke("window:maximize"),
windowClose: () => electron.ipcRenderer.invoke("window:close"),
windowIsMaximized: () => electron.ipcRenderer.invoke("window:isMaximized"),
viewIsReady: () => electron.ipcRenderer.send(IPC_EVENTS.RENDERER_IS_READY),
app: {
setFrameless: (route) => electron.ipcRenderer.invoke(IPC_EVENTS.APP_SET_FRAMELESS, route),
loadPage: (page) => electron.ipcRenderer.invoke(IPC_EVENTS.APP_LOAD_PAGE, page)
},
// 通过 IPC 调用主进程
readFile: (filePath) => electron.ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath),
// 异步调用(映射为 electron 的 invoke)
invoke: (channel, ...args) => electron.ipcRenderer.invoke(channel, ...args),
// 异步调用(为了兼容老代码)
invokeAsync: (channel, ...args) => electron.ipcRenderer.invoke(channel, ...args),
// 监听主进程消息
on: (event, callback) => {
const subscription = (_event, ...args) => callback(...args);
electron.ipcRenderer.on(event, subscription);
return () => electron.ipcRenderer.removeListener(event, subscription);
},
// 发送消息到主进程
send: (channel, ...args) => electron.ipcRenderer.send(channel, ...args),
// 获取窗口ID
getCurrentWindowId: () => electron.ipcRenderer.sendSync(IPC_EVENTS.GET_WINDOW_ID),
// 发送日志
logger: {
debug: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_DEBUG, message, ...meta),
info: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_INFO, message, ...meta),
warn: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_WARN, message, ...meta),
error: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_ERROR, message, ...meta)
},
// 执行脚本
executeScript: (params) => electron.ipcRenderer.invoke(IPC_EVENTS.EXECUTE_SCRIPT, params),
// 任务事件
onTaskProgress: (cb) => {
const subscription = (_event, payload) => cb(payload);
electron.ipcRenderer.on(IPC_EVENTS.TASK_PROGRESS, subscription);
return () => electron.ipcRenderer.removeListener(IPC_EVENTS.TASK_PROGRESS, subscription);
},
onTaskStarted: (cb) => {
const subscription = (_event, payload) => cb(payload);
electron.ipcRenderer.on(IPC_EVENTS.TASK_STARTED, subscription);
return () => electron.ipcRenderer.removeListener(IPC_EVENTS.TASK_STARTED, subscription);
},
onTaskCompleted: (cb) => {
const subscription = (_event, payload) => cb(payload);
electron.ipcRenderer.on(IPC_EVENTS.TASK_COMPLETED, subscription);
return () => electron.ipcRenderer.removeListener(IPC_EVENTS.TASK_COMPLETED, subscription);
},
// 打开渠道
openChannel: (channels) => electron.ipcRenderer.invoke(IPC_EVENTS.OPEN_CHANNEL, channels),
// 脚本管理
scriptApi: {
list: () => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_LIST),
get: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_GET, id),
save: (input) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_SAVE, input),
delete: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_DELETE, id),
toggle: (id, enabled) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_TOGGLE, id, enabled),
run: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RUN, id),
startRecording: (url) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_START, url),
stopRecording: () => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_STOP),
codegen: (id, url) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_CODEGEN, id, url)
}
};
electron.contextBridge.exposeInMainWorld("api", api);

View File

@@ -1,6 +1,7 @@
import { useRef } from 'react'; import { useRef } from 'react';
import { SendHorizontal, Square, X, Paperclip, FileText, Film, Music, FileArchive, File, Loader2, AtSign } from 'lucide-react'; import { SendHorizontal, Square, X, Paperclip, FileText, Film, Music, FileArchive, File, Loader2, AtSign } from 'lucide-react';
import type { AttachedFileMeta } from '../../shared/chat-model'; import type { AttachedFileMeta } from '../../shared/chat-model';
import { useI18n } from '../../i18n';
type ChatComposerProps = { type ChatComposerProps = {
value: string; value: string;
@@ -28,10 +29,11 @@ export default function ChatComposer({
onDismissError, onDismissError,
}: ChatComposerProps) { }: ChatComposerProps) {
const fileInputRef = useRef<HTMLInputElement | null>(null); const fileInputRef = useRef<HTMLInputElement | null>(null);
const { t } = useI18n();
return ( return (
<div className="border-t border-[#edf2f7] px-6 py-4 dark:border-[#2a2a2d]"> <div className="border-t border-[#edf2f7] p-4 dark:border-[#2a2a2d]">
<div className="rounded-[18px] border border-[#dfeaf6] bg-white p-4 shadow-[0_10px_30px_rgba(15,23,42,0.04)] dark:border-[#2a2a2d] dark:bg-[#1f1f22]"> <div className="rounded-xl border border-[#dfeaf6] bg-white p-4 shadow-[0_10px_30px_rgba(15,23,42,0.04)] dark:border-[#2a2a2d] dark:bg-[#1f1f22]">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
{error ? ( {error ? (
@@ -39,14 +41,14 @@ export default function ChatComposer({
<span className="min-w-0 flex-1">{error}</span> <span className="min-w-0 flex-1">{error}</span>
{onDismissError ? ( {onDismissError ? (
<button type="button" className="text-xs transition-colors hover:text-[#7f1d1d]" onClick={onDismissError}> <button type="button" className="text-xs transition-colors hover:text-[#7f1d1d]" onClick={onDismissError}>
{t('conversation.composer.dismissError')}
</button> </button>
) : null} ) : null}
</div> </div>
) : null} ) : null}
<textarea <textarea
className="min-h-[120px] w-full resize-none rounded-[14px] border border-[#BEDBFF] bg-[#f8fbff] px-4 py-3 text-sm text-[#171717] outline-none transition-colors placeholder:text-[#99A0AE] focus:border-[#2B7FFF] dark:border-[#2a2a2d] dark:bg-[#232327] dark:text-gray-100 dark:placeholder:text-gray-500" className="min-h-30 w-full resize-none text-sm text-[#171717] outline-none transition-colors placeholder:text-[#99A0AE] focus:border-[#2B7FFF] dark:border-[#2a2a2d] dark:bg-[#232327] dark:text-gray-100 dark:placeholder:text-gray-500"
placeholder="输入消息,按 Enter 发送Shift + Enter 换行" placeholder={t('conversation.composer.placeholder')}
value={value} value={value}
onChange={(event) => onChange(event.target.value)} onChange={(event) => onChange(event.target.value)}
onKeyDown={(event) => { onKeyDown={(event) => {
@@ -61,13 +63,13 @@ export default function ChatComposer({
}} }}
/> />
{attachments.length > 0 ? ( {attachments.length > 0 ? (
<div className="mt-3 flex flex-wrap gap-2"> <div className="w-full flex justify-between">
{attachments.map((attachment, index) => ( {attachments.map((attachment, index) => (
<div <div
key={attachment.filePath || `${attachment.fileName}-${index}`} key={attachment.filePath || `${attachment.fileName}-${index}`}
className="flex items-center gap-2 rounded-full border border-[#E5E8EE] bg-white px-3 py-1.5 text-xs text-[#525866] dark:border-[#2a2a2d] dark:bg-[#232327] dark:text-gray-300" className="flex items-center gap-2 rounded-full border border-[#E5E8EE] bg-white px-3 py-1.5 text-xs text-[#525866] dark:border-[#2a2a2d] dark:bg-[#232327] dark:text-gray-300"
> >
<span className="max-w-[180px] truncate">{attachment.fileName}</span> <span className="max-w-45 truncate">{attachment.fileName}</span>
<button <button
type="button" type="button"
className="text-[#99A0AE] transition-colors hover:text-[#ef4444]" className="text-[#99A0AE] transition-colors hover:text-[#ef4444]"
@@ -79,7 +81,7 @@ export default function ChatComposer({
))} ))}
</div> </div>
) : null} ) : null}
<div className="mt-3 flex flex-wrap gap-2"> <div className="w-full flex justify-between">
<input <input
ref={fileInputRef} ref={fileInputRef}
hidden hidden
@@ -95,14 +97,14 @@ export default function ChatComposer({
/> />
<button <button
type="button" type="button"
className="rounded-full border border-[#E5E8EE] px-3 py-1.5 text-xs text-[#525866] transition-colors hover:border-[#2B7FFF] hover:text-[#2B7FFF] dark:border-[#2a2a2d] dark:text-gray-300" className="p-3 text-xs text-[#525866] transition-colors hover:border-[#2B7FFF] hover:text-[#2B7FFF] dark:border-[#2a2a2d] dark:text-gray-300"
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
> >
<Paperclip className="h-4 w-4" /> <Paperclip className="h-4 w-4" />
</button> </button>
<button <button
type="button" type="button"
className="rounded-full border border-[#E5E8EE] px-3 py-1.5 text-xs text-[#525866] transition-colors hover:border-[#2B7FFF] hover:text-[#2B7FFF] dark:border-[#2a2a2d] dark:text-gray-300" className="rounded-lg border border-[#E5E8EE] p-3 text-xs text-[#525866] transition-colors hover:border-[#2B7FFF] hover:text-[#2B7FFF] dark:border-[#2a2a2d] dark:text-gray-300"
onClick={isSending ? onStop : onSend} onClick={isSending ? onStop : onSend}
> >
{isSending ? <Square className="h-4 w-4" /> : <SendHorizontal className="h-4 w-4" />} {isSending ? <Square className="h-4 w-4" /> : <SendHorizontal className="h-4 w-4" />}

View File

@@ -39,6 +39,10 @@ export const messages: I18nMessages = {
conversation: { conversation: {
newConversation: 'New conversation', newConversation: 'New conversation',
emptyState: 'No messages yet', emptyState: 'No messages yet',
composer: {
placeholder: 'Type a message, press Enter to send, Shift + Enter for a new line',
dismissError: 'Close',
},
}, },
sidebar: { sidebar: {
home: 'Home', home: 'Home',
@@ -519,6 +523,10 @@ export const messages: I18nMessages = {
conversation: { conversation: {
newConversation: '新建对话', newConversation: '新建对话',
emptyState: '暂无消息', emptyState: '暂无消息',
composer: {
placeholder: '输入消息,按 Enter 发送Shift + Enter 换行',
dismissError: '关闭',
},
}, },
sidebar: { sidebar: {
home: '首页', home: '首页',
@@ -999,6 +1007,10 @@ export const messages: I18nMessages = {
conversation: { conversation: {
newConversation: '新しい会話', newConversation: '新しい会話',
emptyState: 'まだメッセージがありません', emptyState: 'まだメッセージがありません',
composer: {
placeholder: 'メッセージを入力し、Enter で送信、Shift + Enter で改行',
dismissError: '閉じる',
},
}, },
sidebar: { sidebar: {
home: 'ホーム', home: 'ホーム',