js实现支持暗黑模式切换语音识别聊天记录会话、自己设置openaikey的chatgpt web网页代码
代码语言:html
所属分类:其他
代码描述:js实现支持暗黑模式切换语音识别聊天记录会话、自己设置openaikey的chatgpt web网页代码,自定设置openai key,保留包本地,不存放在服务器上,直接调用openai的官方接口,需要科学上网,自适应兼容手机端、可设置azure的语音识别key等多种语音识别方式,支持不同的角色prompt预设选择,支持打字速度设置、回答质量设置,支持key的额度查询,支持文件夹。
代码标签: js 暗黑 模式 切换 语音 识别 聊天 记录 会话 设置 openai key chatgpt
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="zh-CN"> <meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="description" content="使用ChatGPT接口,实现简单HTML网页版在线聊天" /> <head> <meta charset="utf-8"> <title>ChatGPT</title> <style> * { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji" !important; } html { width: 100%; height: 100%; } :root { --background: #edeff2; --chat-back: #fff; --main-back: #f6f6f6; --active-btn: #e0e0e0; --lighter-active: #eaeaea; --sel-btn: #d0d0d0; --btn-color: #404040; --text-color: #909090; --chat-text-color: #24292f; --response-color: #f7f7f8; --lighter-active-color: #e8e8e8; --lighter-text-color: #555; --svg-color: #808080; --lighter-svg-color: #c0c0c0; --code-color: #f0f0f0; --black-color: #000; } [data-theme="dark"] { --background: #1f1f1f; --chat-back: #3c3c3c; --main-back: #333; --active-btn: #1f1f1f; --lighter-active: #151515; --sel-btn: #1e1e1e; --btn-color: #bfbfbf; --text-color: #8f8f8f; --chat-text-color: #c9d1d9; --response-color: #2f2f2f; --lighter-active-color: #171717; --lighter-text-color: #aaa; --svg-color: #7f7f7f; --lighter-svg-color: #3f3f3f; --code-color: #101010; --black-color: #fff; } body { background-color: var(--background); width: 100%; height: 100%; margin: 0; display: flex; justify-content: center; align-items: center; } .chat_window { position: absolute; width: 100%; max-width: 1188px; height: 100%; max-height: 888px; border-radius: 8px; background-color: var(--chat-back); overflow: hidden; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15); } .overlay { width: 100%; height: 100%; position: absolute; left: 0; top: 0; background-color: rgba(0, 0, 0, .3); z-index: 90; opacity: 0; visibility: hidden; cursor: pointer; transition: all 120ms ease-in-out; } @media screen and (max-width: 1188px) and (max-height: 888px) { #toggleFull { display: none; } } @media screen and (min-width: 800px) { .chat_window { display: flex; } .mainContent { width: calc(100% - 250px); } .nav { position: relative; margin-left: -250px; transition: margin-left 120ms ease-in-out; } .show-nav .nav { margin-left: 0; } } @media screen and (max-width: 800px) { .chat_window { display: block; } .nav { position: absolute !important; left: -250px; transition: left 120ms ease-in-out; } .show-nav .nav { left: 0; box-shadow: rgba(0, 0, 0, 0.2) 0px 8px 10px -5px, rgba(0, 0, 0, 0.14) 0px 16px 24px 2px, rgba(0, 0, 0, 0.12) 0px 6px 30px 5px; } .show-nav .overlay { opacity: 1; visibility: visible; } } .full_window { max-width: none; max-height: none; } .nav { width: 250px; height: 100%; border-right: 1px solid var(--active-btn); background-color: var(--main-back); top: 0; z-index: 99; flex-shrink: 0; display: flex; flex-direction: column; } .mainContent { height: 100%; position: relative; display: flex; flex-direction: column; flex: 1; } .top_menu { background-color: var(--main-back); width: 100%; height: 50px; padding: 5px 0; } .top_menu .toggler { margin-left: 10px; width: 40px; height: 40px; float: left; padding: 5px 7px; border-radius: 4px; cursor: pointer; } .top_menu .toggler:hover { background: var(--active-btn); } .top_menu .toggler .button { width: 26px; height: 4px; border-radius: 4px; position: absolute; pointer-events: none; } .top_menu .toggler .button.close { margin-top: 3px; background-color: #99c959; } .top_menu .toggler .button.minimize { margin-top: 13px; background-color: #f8b26a; } .top_menu .toggler .button.maximize { margin-top: 23px; background-color: #e15b64; } .top_menu .title { color: var(--text-color); font-size: 20px; height: 40px; line-height: 40px; position: relative; pointer-events: none; } .title>span { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .messages { position: relative; flex: 1; overflow-x: hidden; overflow-y: auto; font-size: 16px; color: var(--chat-text-color); text-align: center; } .messages::-webkit-scrollbar, #chatlog .markdown-body>pre::-webkit-scrollbar, #setDialog::-webkit-scrollbar, .allList::-webkit-scrollbar, .sysDetail::-webkit-scrollbar { width: 10px; height: 10px; } .messages::-webkit-scrollbar-track, #chatlog .markdown-body>pre::-webkit-scrollbar-track, #setDialog::-webkit-scrollbar-track, .allList::-webkit-scrollbar-track, .sysDetail::-webkit-scrollbar-track { background-clip: padding-box; background: transparent; border: none; } .messages::-webkit-scrollbar-corner, #chatlog .markdown-body>pre::-webkit-scrollbar-corner, #setDialog::-webkit-scrollbar-corner, .allList::-webkit-scrollbar-corner, .sysDetail::-webkit-scrollbar-corner { background-color: transparent; } .messages::-webkit-scrollbar-thumb, #chatlog .markdown-body>pre::-webkit-scrollbar-thumb, #setDialog::-webkit-scrollbar-thumb, .allList::-webkit-scrollbar-thumb, .sysDetail::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.1); background-clip: padding-box; border: solid transparent; border-radius: 10px; } .messages::-webkit-scrollbar-thumb:hover, #chatlog .markdown-body>pre::-webkit-scrollbar-thumb:hover, #setDialog::-webkit-scrollbar-thumb:hover, .allList::-webkit-scrollbar-thumb:hover, .sysDetail::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.4); } .messages .message { clear: both; overflow: hidden; margin-bottom: 20px; transition: all 0.5s linear; opacity: 0; } #chatlog { word-wrap: break-word; text-align: start; } #chatlog>div { padding: 18px 30px 18px 54px; } .chatAvatar { position: absolute; top: 14px; left: 13px; width: 30px; height: 30px; border-radius: 2px; } .response>.chatAvatar { background: #10a37f; top: 18px; } .chatAvatar>img { display: block; width: 100%; height: 100%; border-radius: 2px; } .chatAvatar>svg { display: block; margin-top: 4px; margin-left: 4px; } #chatlog .request { position: relative; } .requestBody { white-space: pre-wrap; } #chatlog .response { background: var(--response-color); position: relative; } .response .markdown-body { background: var(--response-color) !important; } #chatlog .markdown-body>pre { overflow-x: auto; padding: 10px; position: relative; background: var(--code-color); } .m-mdic-copy-wrapper { position: absolute; top: 5px; right: 16px; -webkit-user-select: none; user-select: none; } .m-mdic-copy-wrapper span.u-mdic-copy-code_lang { position: absolute; top: 3px; right: calc(100% + 4px); font-family: system-ui; font-size: 12px; line-height: 18px; color: #bbb; } .m-mdic-copy-wrapper div.u-mdic-copy-notify { position: absolute; top: 0; right: 0; padding: 3px 6px; border: 0; border-radius: 3px; background: none; font-family: system-ui; font-size: 12px; line-height: 18px; color: var(--lighter-text-color); outline: none; right: 100%; padding-right: 4px; } .m-mdic-copy-wrapper button.u-mdic-copy-btn { position: relative; top: 0; right: 0; padding: 3px 6px; border: 0; border-radius: 3px; background: none; font-family: system-ui; font-size: 12px; line-height: 18px; color: #bbb; outline: none; cursor: pointer; transition: color 0.2s; } .m-mdic-copy-wrapper button.u-mdic-copy-btn:hover { color: var(--lighter-text-color); } #stopChat { display: none; margin: 0 auto; margin-top: 3px; width: 80px; height: 32px; text-align: center; line-height: 32px; color: white; background: #f8b26a; cursor: pointer; border-radius: 3px; position: sticky; bottom: 2px; justify-content: center; align-items: center; } #stopChat>svg { margin-right: 8px; } #stopChat:hover { background: #f0aa60; } .bottom_wrapper { position: relative; width: 100%; padding: 10px 10px; } .bottom_wrapper .message_input_wrapper { border: none; width: calc(100% - 143px); position: relative; text-align: left; } .bottom_wrapper .message_input_wrapper .message_input_text { border-radius: 4px; border: none; outline: none; resize: none; background: var(--main-back); color: var(--chat-text-color); height: 47px; font-size: 16px; max-height: 200px; padding: 13px 0 13px 16px; width: 100%; display: block; transition: background 0.3s; } .bottom_wrapper .message_input_wrapper .message_input_text:focus { background: var(--code-color); } .bottom_wrapper .message_input_wrapper .message_input_text::-webkit-scrollbar { display: none; width: 0; height: 0; } #sendbutton { width: 80px; height: 47px; font-size: 18px; font-weight: bold; border-radius: 3px; background-color: #b8da8b; border: none; padding: 0; color: #fff; cursor: pointer; transition: all 0.2s linear; text-align: center; float: right; position: absolute; right: 65px; bottom: 10px; cursor: not-allowed; } .activeSendBtn { background-color: #99c959 !important; cursor: pointer !important; } .activeSendBtn:hover { background-color: #90c050 !important; } .clearConv { position: absolute; right: 10px; bottom: 10px; width: 47px; height: 47px; border-radius: 3px; background: var(--text-color); border: none; color: #fff; cursor: pointer; } .clearConv>svg { margin: 0 auto; } .clearConv:hover { background: var(--svg-color); } .clearConv svg:first-child { display: none; } .clearConv svg:nth-child(2) { display: block; } .closeConv { background: var(--active-btn); } .closeConv:hover { background: var(--lighter-active-color); } .closeConv svg:first-child { display: block; } .closeConv svg:nth-child(2) { display: none; } .loaded>span { display: inline-block; } .loaded>svg { display: none; } .loading { background: var(--active-btn) !important; } .loading>span { display: none; } .loading>svg { display: block; } .switch-slide { display: inline-block; vertical-align: middle; } .switch-slide-label { display: block; width: 38px; height: 18px; background: var(--text-color); border-radius: 30px; cursor: pointer; position: relative; -webkit-transition: 0.3s ease; transition: 0.3s ease; } .switch-slide-label:after { content: ""; display: block; width: 16px; height: 16px; border-radius: 100%; background: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, .1); position: absolute; left: 1px; top: 1px; -webkit-transform: translateZ(0); transform: translateZ(0); -webkit-transition: 0.3s ease; transition: 0.3s ease; } .switch-slide input:checked+label { background: #99c959; transition: 0.3s ease; } .switch-slide input:checked+label:after { left: 21px; } .settings { margin-right: 10px; display: flex; position: absolute; right: 0; top: 5px; } .setBtn { margin-left: 2px; cursor: pointer; padding: 5px; border: none; background-color: transparent; border-radius: 4px; } .setBtn>svg { display: block; color: var(--text-color); } .setBtn:hover { background: var(--active-btn); } #setting { right: 15px; } #toggleFull { right: 56px; } #toggleLight *, #toggleFull * { pointer-events: none; } .showSetting { background: var(--lighter-svg-color) !important; } #setDialog { color: var(--btn-color); position: absolute; z-index: 2; background: var(--main-back); width: 320px; right: 6px; top: 46px; overflow-y: auto; max-height: calc(100% - 55px); -webkit-user-select: none; user-select: none; border-radius: 5px; padding: 8px 12px 8px 12px; box-shadow: 0 0 6px rgba(0, 0, 0, 0.15); } #setDialog input { width: 100%; } #setDialog .inlineTitle { display: inline-block; width: 80px; line-height: 16px; vertical-align: middle; } #convOption, #speechOption, #speechDetail, #recOption { margin-bottom: 6px; } #convOption>div, #speechOption>div, #speechDetail>div, #recOption div { margin-top: 10px; } #voiceRecSetting select, #speechDetail select { background: var(--chat-back); color: var(--chat-text-color); } .inputTextClass { outline: none; border-radius: 2px; margin-top: 2px; height: 32px; font-size: 15px; padding-left: 6px; background: var(--chat-back); color: var(--chat-text-color); border: none; } .areaTextClass { width: 100%; height: 80px; display: block; resize: none; padding: 6px; } input[type="range"] { -webkit-appearance: none; appearance: none; display: block; margin: 6px 0 3px 0; height: 8px; background: var(--text-color); border-radius: 5px; background-image: linear-gradient(#99c959, #99c959); background-size: 100% 100%; background-repeat: no-repeat; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; height: 15px; width: 15px; border-radius: 50%; background: #99c959; cursor: ew-resize; } input[type=range]::-webkit-slider-runnable-track { -webkit-appearance: none; box-shadow: none; border: none; background: transparent; } .justSetLine { display: flex; justify-content: space-between; } .presetSelect>div { display: inline-block; } .presetSelect select { outline: none; border-radius: 3px; width: 120px; border-color: rgba(0, 0, 0, .3); background: var(--chat-back); color: var(--chat-text-color); } .selectDef { display: flex; justify-content: space-between; font-size: 13px; color: var(--text-color); } #preSetSpeech { width: 100%; outline: none; height: 30px; font-size: 14px; margin-top: 5px; border-radius: 3px; border-color: rgba(0, 0, 0, .3); } .mdOption { position: absolute; right: 0px; top: 0; width: 30px; height: 100%; pointer-events: none; } .mdOption>div { pointer-events: auto; cursor: pointer; } .mdOption svg * { pointer-events: none; } .refreshReq svg:not(:first-child) { display: none; } .halfRefReq svg:not(:nth-child(2)) { display: none; } .optionItems { position: absolute; bottom: -12px; display: flex; justify-content: space-between; visibility: hidden; z-index: 1; color: var(--svg-color); } .optionItems:hover { visibility: visible; } .request:hover .optionItems, .request:hover .voiceCls, .response:hover .optionItems, .response:hover .voiceCls { visibility: visible; } .optionItem { border-radius: 10px; height: 24px; width: 32px; border: 1px solid var(--active-btn); background-color: var(--response-color); display: flex !important; justify-content: center; align-items: center; } .optionItem * { pointer-events: none; } .optionItem:hover { background: var(--active-btn); } .voiceCls { position: relative; height: 100%; visibility: hidden; display: flex; align-items: center; } .voiceCls>svg { color: var(--lighter-svg-color); display: block; margin-left: 5px; position: relative; pointer-events: none; } .voiceCls:hover>svg { color: var(--svg-color); } .showVoiceCls, .showVoiceCls .markdown-body { background: var(--active-btn) !important; } .showVoiceCls .voiceCls { visibility: visible !important; } .showEditReq { position: sticky !important; top: 0; bottom: 0; z-index: 1; } .showEditReq, .showEditReq .markdown-body { background: var(--active-btn) !important; } .readyVoice svg:not(:first-child) { display: none; } .pauseVoice svg:not(:nth-child(2)) { display: none; } .resumeVoice svg:not(:nth-child(3)) { display: none; } #voiceTypes>span { border-radius: 3px; margin-left: 4px; cursor: pointer; padding: 1px 5px; } #voiceTypes>span:hover { background: var(--active-btn); } .selVoiceType { background: var(--sel-btn) !important; } .navHeader { width: 100%; padding: 5px 10px; display: flex; justify-content: space-between; } #newChat { text-align: center; width: 80%; height: 40px; border-radius: 3px; background: var(--lighter-active-color); color: var(--btn-color); display: flex; align-items: center; justify-content: center; -webkit-user-select: none; user-select: none; cursor: pointer; flex: 1; } .navHeader>div:hover { background: var(--active-btn) !important; } #newChat>svg { margin-right: 2px; } #newFolder { height: 40px; width: 40px; margin-left: 10px; border-radius: 3px; color: var(--btn-color); cursor: pointer; position: relative; background: var(--lighter-active-color); -webkit-user-select: none; user-select: none; } #newFolder>svg { display: block; margin: 8px auto; } .extraChat { padding: 2px 10px 6px 10px; position: relative; } #searchChat { width: 100%; height: 36px; padding-left: 10px; padding-right: 16px; font-size: 16px; outline: none; border: none; color: var(--chat-text-color); background: var(--lighter-active-color); border-radius: 3px; } #searchChat:focus { background: var(--active-btn) } #searchChat:placeholder-shown+#clearSearch { display: none; } #clearSearch { position: absolute; right: 12px; top: 8px; cursor: pointer; color: var(--btn-color); } #clearSearch:hover { color: var(--black-color); } #clearSearch>svg { display: block; } .divider { width: 100%; border-top: 1px solid var(--active-btn); margin: 4px 0; } .navFooter { padding-bottom: 8px; } .navFunc { padding-top: 5px; display: flex; justify-content: space-around; } .navFunc svg { display: block; } .navFunc>div, .navFunc>label { border-radius: 20px; text-align: center; padding: 8px 8px; color: var(--btn-color); font-size: 14px; cursor: pointer; } .navFunc>div:hover, .navFunc>label:hover { background: var(--active-btn); } .links { text-align: center; } .links a { color: var(--btn-color); text-decoration: none; } .links a:hover { color: var(--chat-text-color) !important; } .links a:visited { color: var(--btn-color); } .allList { width: 100%; flex: 1; overflow-y: auto; } #chatList { min-height: 50px; } .dragingLi { filter: brightness(90%); } .dragingChat { background: var(--lighter-active-color); } .expandFolder>.headLi>svg { transform: rotate(90deg); } .expandFolder>.chatsInFolder { display: block; } .chatsInFolder { display: none; margin-left: 22px; padding-left: 2px; border-left: 1px solid var(--text-color); } .headLi, .chatLi { cursor: pointer; width: 100%; height: 50px; color: var(--text-color); display: flex; justify-content: space-between; align-items: center; position: relative; } .headLi *, .chatLi * { pointer-events: none; } .headLi>svg, .chatLi>svg { margin-left: 10px; color: var(--btn-color); pointer-events: none; } .folderOption svg, .chatOption svg { pointer-events: auto; } .headLi svg *, .chatLi svg * { pointer-events: none; } .headLi .folderInfo, .chatLi .chatInfo { position: absolute; left: 45px; height: 40px; max-width: calc(100% - 100px); } .folderInfo *, .chatInfo * { -webkit-user-select: none; user-select: none; } .chatInfo span { background: #f8b26a; } .headLi .folderName, .chatLi .chatName { color: var(--btn-color); text-overflow: ellipsis; white-space: nowrap; overflow: hidden; line-height: 20px; height: 20px; } .headLi .folderNum, .chatLi .chatPre { color: var(--btn-color); text-overflow: ellipsis; white-space: nowrap; overflow: hidden; font-size: 12px; line-height: 20px; height: 20px; } .headLi .folderOption, .chatLi .chatOption { visibility: hidden; display: flex; color: #777; margin-right: 5px; } .folderLi:hover { background: var(--lighter-active); } .chatLi:hover { background: var(--active-btn); } .headLi:hover .folderOption, .chatLi:hover .chatOption { visibility: visible !important; } .activeFolder, .activeChatLi { background: var(--sel-btn) !important; } .activeChatLi .chatOption { visibility: visible !important; } .chatOption>svg:hover { color: #444; } #activeChatEdit { position: absolute; left: 42px; font-size: 16px; border-radius: 2px; color: var(--chat-text-color); background: var(--chat-back); outline: none; border: none; pointer-events: auto; height: 24px; line-height: 24px; width: calc(100% - 75px); padding: 20px 3px; z-index: 1; } #loadMask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 100; background-color: var(--background); } #loadMask>div { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); text-align: center; } #loadMask svg { width: 160px; height: 80px; } #loadMask>div>div { font-size: 40px; color: var(--text-color); } #voiceRec { position: absolute; right: 0; top: 0; width: 47px; height: 100%; } .message_if_voice { padding-right: 47px !important; } #voiceRecIcon { width: 100%; height: 100%; text-align: center; cursor: pointer; position: relative; } #voiceRecIcon:hover>svg { color: var(--svg-color); } #voiceRecIcon>svg { width: 28px; height: 28px; color: #b0b0b0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } #voiceRecIcon>svg .animVoice { display: none; } .voiceRecing>svg { color: #99c959 !important; } .voiceRecing .animVoice { display: inline !important; transform-origin: 0 64%; animation-duration: 1.5s; animation-name: scaleVoice; animation-timing-function: ease; animation-iteration-count: infinite; } .voiceLong .animVoice { display: inline !important; transform-origin: 0 64%; animation-duration: 0.3s; animation-name: longVoice; animation-timing-function: ease-in-out; animation-iteration-count: 1; } @keyframes longVoice { 0% { transform: scaleY(0); } 100% { transform: scaleY(1); } } @keyframes scaleVoice { 0% { transform: scaleY(0.28); } 20% { transform: scaleY(0.60); } 28% { transform: scaleY(0.28); } 36% { transform: scaleY(0.45); } 44% { transform: scaleY(0.28); } 52% { transform: scaleY(0.45); } 62% { transform: scaleY(0.80); } 72% { transform: scaleY(0.80); } 90% { transform: scaleY(0.28); } 100% { transform: scaleY(0.28); } } #voiceRecSetting { display: none; position: absolute; top: -70px; left: -26px; z-index: 1; padding: 4px 4px; -webkit-user-select: none; user-select: none; border-radius: 3px; background-color: var(--main-back); box-shadow: 0 0 6px rgba(0, 0, 0, 0.15); } #voiceRecSetting select { width: 102px; outline: none; height: 28px; border-radius: 3px; border-color: rgba(0, 0, 0, .3); } .presetModelCls label { margin-right: 8px; } .presetModelCls select { height: 30px; margin-top: 2px; font-size: 15px; } .setSwitch { display: flex; } .setSwitch>div { border-radius: 3px; width: calc(100% / 3); height: 32px; line-height: 32px; text-align: center; cursor: pointer; } .setSwitch>div:hover { background-color: var(--active-btn); } .activeSwitch { background-color: var(--sel-btn) !important; } #checkVoiceLoad { height: 32px; border-radius: 3px; line-height: 32px; background: var(--sel-btn); text-align: center; display: flex; justify-content: center; cursor: pointer; } #checkVoiceLoad:hover { background: var(--lighter-svg-color); } .voiceChecking { background-color: var(--lighter-svg-color) !important; } .voiceChecking>svg { display: inline !important; } #checkVoiceLoad>svg { display: none; margin-right: 8px; height: 32px; width: 64px; } #preSetSystem { height: 20px; line-height: 20px; vertical-align: top; } #sysDialog { display: none; position: fixed; z-index: 200; top: 0; left: 0; bottom: 0; right: 0; cursor: pointer; justify-content: center; align-items: center; background: rgba(0, 0, 0, .4); } #sysContent { position: relative; background: var(--chat-back); color: var(--btn-color); cursor: auto; max-height: 100%; width: 88%; max-width: 400px; display: flex; flex-direction: column; border-radius: 4px; padding: 12px 20px 16px 20px; } .sysTitle { font-size: 20px; font-weight: bold; } .sysDetail { overflow-y: auto; flex: 1; margin-top: 4px; } #closeSet { position: absolute; right: 0px; top: 0px; cursor: pointer; padding: 10px 14px; } #closeSet:hover { color: var(--black-color); } .setContent { margin-top: 8px; } .setTitle { margin-bottom: 6px; font-weight: bold; } .setDetail { margin: 0 10px; } .dataDetail { display: flex; } .dataDetail svg { margin-right: 4px; } .dataDetail>div, .dataDetail>label { border-radius: 3px; text-align: center; padding: 6px 8px; margin-right: 12px; color: var(--btn-color); font-size: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between; } .dataDetail>div:hover, .dataDetail>label:hover { background: var(--lighter-active-color); } .avatarDetail { display: flex; margin-top: 2px; } .avatarDetail img { border-radius: 2px; width: 32px; height: 32px; flex-shrink: 0; margin-right: 10px; margin-top: 2px; } .inputDetail input { outline: none; border-radius: 3px; padding-left: 8px; font-size: 15px; width: 100%; height: 34px; border: 1px solid rgba(0, 0, 0, .3); background: var(--chat-back); color: var(--chat-text-color); } .apiDetail { display: flex; } .apiDetail input { flex: 1; } .apiDetail>div { margin-left: 6px; background: var(--lighter-active-color); border-radius: 3px; width: 108px; text-align: center; cursor: pointer; margin-top: 2px; height: 34px; line-height: 34px; font-size: 15px; } .apiDetail>div:hover { background: var(--active-btn); } .themeDetail { display: flex; width: 180px; justify-content: space-between; pointer-events: none; } .themeDetail svg { display: block; } .themeDetail>div { pointer-events: auto; border-radius: 20px; text-align: center; padding: 8px 8px; color: var(--btn-color); font-size: 14px; cursor: pointer; } .themeDetail>div * { pointer-events: none; } .themeDetail>div:hover { background: var(--lighter-active-color); } .darkTheme>div:first-child { background: var(--sel-btn); } .lightTheme>div:nth-child(2) { background: var(--sel-btn); } .autoTheme>div:nth-child(3) { background: var(--sel-btn); } #customAutoSet input { width: 100px; height: 30px; line-height: 30px; font-size: 15px; outline: none; border: 1px solid rgba(0, 0, 0, .3); text-align: center; border-radius: 3px; background: var(--chat-back); color: var(--chat-text-color); } #customAutoSet label { margin-right: 8px; } .progressBar { position: relative; width: 100%; height: 12px; border-radius: 6px; background: var(--active-btn); overflow: hidden; } .expiredBar { background: #e15b64; } .nowProgress { position: absolute; left: 0; top: 0; height: 12px; min-width: 1px; border-radius: 6px; background: #99c959; } .progressDetail { display: flex; justify-content: space-between; font-size: 15px; } </style> </head> <body> <div style="display: none"> <svg> <symbol viewBox="0 0 24 24" id="optionIcon"> <path fill="currentColor" d="M12 3c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2zm0 14c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2zm0-7c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="refreshIcon"> <path fill="currentColor" d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="halfRefIcon"> <path fill="currentColor" d="M 4.009 12.163 C 4.012 12.206 2.02 12.329 2 12.098 C 2 6.575 6.477 2 12 2 C 17.523 2 22 6.477 22 12 C 22 14.136 21.33 16.116 20.19 17.74 L 17 12 L 20 12 C 19.999 5.842 13.333 1.993 7.999 5.073 C 3.211 8.343 4.374 12.389 4.009 12.163 Z" /> </symbol> <symbol viewBox="-2 -2 20 20" id="copyIcon"> <path fill="currentColor" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"> </path> <path fill="currentColor" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="delIcon"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 7v0a3 3 0 0 1 3-3v0a3 3 0 0 1 3 3v0M9 7h6M9 7H6m9 0h3m2 0h-2M4 7h2m0 0v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7"> </path> </symbol> <symbol viewBox="0 0 24 24" id="readyVoiceIcon"> <path fill="currentColor" d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"> </path> </symbol> <symbol viewBox="0 0 16 16" id="pauseVoiceIcon"> <path stroke="currentColor" stroke-width="2" d="M5 2v12M11 2v12"></path> </symbol> <symbol viewBox="0 0 16 16" id="resumeVoiceIcon"> <path fill="currentColor" d="M4 2L4 14L12 8Z"></path> </symbol> <symbol viewBox="0 0 24 24" id="stopResIcon"> <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10zm0-2a8 8 0 1 0 0-16a8 8 0 0 0 0 16zM9 9h6v6H9V9z"> </path> </symbol> <symbol viewBox="0 0 128 128" id="downAudioIcon"> <path d="M 64.662 1.549 C 56.549 4.524, 46.998 14.179, 45.523 20.895 C 45.041 23.089, 44.073 23.833, 40.433 24.807 C 34.752 26.326, 27.956 32.929, 25.527 39.289 C 24.273 42.574, 23.884 45.715, 24.196 50.034 C 24.620 55.897, 24.528 56.193, 21.836 57.585 C 17.142 60.012, 16 63.617, 16 76 C 16 88.463, 17.137 91.985, 21.967 94.483 C 28.244 97.729, 36.120 95.350, 38.579 89.466 C 39.387 87.532, 40 82.764, 40 78.415 C 40 70.971, 40.060 70.783, 42.250 71.370 C 43.487 71.701, 48.888 71.979, 54.250 71.986 L 64 72 64 76 L 64 80 57.122 80 C 49.420 80, 48.614 80.543, 47.547 86.453 C 46.552 91.964, 43.550 97.473, 40.273 99.803 C 33 104.974, 23.120 105.042, 16.118 99.971 C 11.407 96.558, 9.048 92.484, 8.145 86.205 C 6.963 77.979, 0.794 77.729, 0.191 85.883 C -0.196 91.111, 3.323 99.170, 8.062 103.908 C 11.290 107.136, 20.073 111.969, 22.750 111.990 C 23.540 111.996, 24 113.472, 24 116 C 24 119.740, 23.813 120, 21.122 120 C 17.674 120, 15.727 122.044, 16.173 125.195 C 16.492 127.441, 16.781 127.500, 27.391 127.500 C 36.676 127.500, 38.445 127.242, 39.386 125.750 C 40.993 123.203, 38.986 120.568, 35.149 120.187 C 32.206 119.894, 32 119.617, 32 115.956 C 32 112.509, 32.330 111.959, 34.750 111.377 C 42.181 109.591, 52.157 101.208, 53.575 95.559 C 53.928 94.152, 54.514 93, 54.878 93 C 55.242 93, 59.797 97.275, 65 102.500 C 70.762 108.286, 75.256 112, 76.495 112 C 77.769 112, 83.287 107.231, 91.264 99.236 C 101.113 89.366, 104 85.876, 104 83.843 C 104 80.580, 102.553 80, 94.418 80 L 88 80 88 76.105 L 88 72.211 99.750 71.815 C 113.117 71.364, 117.595 69.741, 122.762 63.473 C 128.159 56.925, 129.673 45.269, 126.134 37.500 C 123.787 32.346, 117.218 26.445, 112.132 24.921 C 108.617 23.868, 107.767 22.968, 105.028 17.405 C 99.364 5.901, 89.280 -0.062, 75.712 0.070 C 71.746 0.109, 66.773 0.774, 64.662 1.549 M 67.885 9.380 C 60.093 12.164, 55.057 17.704, 52.527 26.276 C 51.174 30.856, 50.220 31.617, 44.729 32.496 C 37.017 33.729, 30.917 42.446, 32.374 50.154 C 34.239 60.026, 40.582 63.944, 54.750 63.978 L 64 64 64 57.122 C 64 52.457, 64.449 49.872, 65.396 49.086 C 66.310 48.328, 70.370 48.027, 77.146 48.214 L 87.500 48.500 87.794 56.359 L 88.088 64.218 98.989 63.845 C 108.043 63.535, 110.356 63.125, 112.634 61.424 C 119.736 56.122, 121.911 47.667, 118.097 40.190 C 115.870 35.824, 110.154 32.014, 105.790 31.985 C 102.250 31.961, 101.126 30.787, 99.532 25.443 C 95.580 12.197, 80.880 4.736, 67.885 9.380 M 72 70.800 C 72 80.978, 71.625 85.975, 70.800 86.800 C 70.140 87.460, 67.781 88, 65.559 88 L 61.517 88 68.759 95.241 L 76 102.483 83.241 95.241 L 90.483 88 86.441 88 C 84.219 88, 81.860 87.460, 81.200 86.800 C 80.375 85.975, 80 80.978, 80 70.800 L 80 56 76 56 L 72 56 72 70.800 M 25.200 65.200 C 23.566 66.834, 23.566 85.166, 25.200 86.800 C 27.002 88.602, 29.798 88.246, 30.965 86.066 C 31.534 85.002, 32 80.472, 32 76 C 32 71.528, 31.534 66.998, 30.965 65.934 C 29.798 63.754, 27.002 63.398, 25.200 65.200" stroke="none" fill="currentColor" fill-rule="evenodd" /> </symbol> <symbol viewBox="0 0 24 24" id="chatIcon"> <path fill="currentColor" d="m18 21l-1.4-1.4l1.575-1.6H14v-2h4.175L16.6 14.4L18 13l4 4l-4 4ZM3 21V6q0-.825.588-1.413T5 4h12q.825 0 1.413.588T19 6v5.075q-.25-.05-.5-.063T18 11q-.25 0-.5.013t-.5.062V6H5v10h7.075q-.05.25-.063.5T12 17q0 .25.013.5t.062.5H6l-3 3Zm4-11h8V8H7v2Zm0 4h5v-2H7v2Zm-2 2V6v10Z" /> </symbol> <symbol viewBox="0 0 24 24" id="chatEditIcon"> <path fill="currentColor" d="M5 19h1.4l8.625-8.625l-1.4-1.4L5 17.6V19ZM19.3 8.925l-4.25-4.2l1.4-1.4q.575-.575 1.413-.575t1.412.575l1.4 1.4q.575.575.6 1.388t-.55 1.387L19.3 8.925ZM17.85 10.4L7.25 21H3v-4.25l10.6-10.6l4.25 4.25Zm-3.525-.725l-.7-.7l1.4 1.4l-.7-.7Z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="deleteIcon"> <path fill="currentColor" d="M8 20v-5h2v5h9v-7H5v7h3zm-4-9h16V8h-6V4h-4v4H4v3zM3 21v-8H2V7a1 1 0 0 1 1-1h5V3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v3h5a1 1 0 0 1 1 1v6h-1v8a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="addIcon" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="12" y1="5" x2="12" y2="19"></line> <line x1="5" y1="12" x2="19" y2="12"></line> </symbol> <symbol viewBox="0 0 200 100" preserveAspectRatio="xMidYMid" id="loadingIcon"> <g transform="translate(50 50)"> <circle cx="0" cy="0" r="15" fill="#e15b64"> <animateTransform attributeName="transform" type="scale" begin="-0.3333333333333333s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform> </circle> </g> <g transform="translate(100 50)"> <circle cx="0" cy="0" r="15" fill="#f8b26a"> <animateTransform attributeName="transform" type="scale" begin="-0.16666666666666666s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform> </circle> </g> <g transform="translate(150 50)"> <circle cx="0" cy="0" r="15" fill="#99c959"> <animateTransform attributeName="transform" type="scale" begin="0s" calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform> </circle> </g> </symbol> <symbol viewBox="0 0 24 24" id="exportIcon"> <path fill="currentColor" d="m17.86 18l1.04 1c-1.4 1.2-3.96 2-6.9 2c-4.41 0-8-1.79-8-4V7c0-2.21 3.58-4 8-4c2.95 0 5.5.8 6.9 2l-1.04 1l-.36.4C16.65 5.77 14.78 5 12 5C8.13 5 6 6.5 6 7s2.13 2 6 2c1.37 0 2.5-.19 3.42-.46l.96.96H13.5v1.42c-.5.05-1 .08-1.5.08c-2.39 0-4.53-.53-6-1.36v2.81C7.3 13.4 9.58 14 12 14c.5 0 1-.03 1.5-.08v.58h2.88l-1 1l.12.11c-1.09.25-2.26.39-3.5.39c-2.28 0-4.39-.45-6-1.23V17c0 .5 2.13 2 6 2c2.78 0 4.65-.77 5.5-1.39l.36.39m1.06-10.92L17.5 8.5L20 11h-5v2h5l-2.5 2.5l1.42 1.42L23.84 12l-4.92-4.92Z" /> <symbol viewBox="0 0 24 24" id="importIcon"> <path fill="currentColor" d="m8.84 12l-4.92 4.92L2.5 15.5L5 13H0v-2h5L2.5 8.5l1.42-1.42L8.84 12M12 3C8.59 3 5.68 4.07 4.53 5.57L5 6l1.03 1.07C6 7.05 6 7 6 7c0-.5 2.13-2 6-2s6 1.5 6 2s-2.13 2-6 2c-2.62 0-4.42-.69-5.32-1.28l3.12 3.12c.7.1 1.44.16 2.2.16c2.39 0 4.53-.53 6-1.36v2.81c-1.3.95-3.58 1.55-6 1.55c-.96 0-1.9-.1-2.76-.27l-1.65 1.64c1.32.4 2.82.63 4.41.63c2.28 0 4.39-.45 6-1.23V17c0 .5-2.13 2-6 2s-6-1.5-6-2v-.04L5 18l-.46.43C5.69 19.93 8.6 21 12 21c4.41 0 8-1.79 8-4V7c0-2.21-3.58-4-8-4Z" /> </symbol> <symbol viewBox="0 0 24 24" id="clearAllIcon"> <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10zm0-2a8 8 0 1 0 0-16a8 8 0 0 0 0 16zm0-9.414l2.828-2.829l1.415 1.415L13.414 12l2.829 2.828l-1.415 1.415L12 13.414l-2.828 2.829l-1.415-1.415L10.586 12L7.757 9.172l1.415-1.415L12 10.586z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="collapseFullIcon"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m20 20l-5-5m0 0v4m0-4h4M4 20l5-5m0 0v4m0-4H5M20 4l-5 5m0 0V5m0 4h4M4 4l5 5m0 0V5m0 4H5" /> </symbol> <symbol viewBox="0 0 24 24" id="expandFullIcon"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 9L4 4m0 0v4m0-4h4m7 5l5-5m0 0v4m0-4h-4M9 15l-5 5m0 0v-4m0 4h4m7-5l5 5m0 0v-4m0 4h-4" /> </symbol> <symbol viewBox="0 0 24 24" id="darkThemeIcon"> <path fill="currentColor" d="M20.742 13.045a8.088 8.088 0 0 1-2.077.271c-2.135 0-4.14-.83-5.646-2.336a8.025 8.025 0 0 1-2.064-7.723A1 1 0 0 0 9.73 2.034a10.014 10.014 0 0 0-4.489 2.582c-3.898 3.898-3.898 10.243 0 14.143a9.937 9.937 0 0 0 7.072 2.93 9.93 9.93 0 0 0 7.07-2.929 10.007 10.007 0 0 0 2.583-4.491 1.001 1.001 0 0 0-1.224-1.224zm-2.772 4.301a7.947 7.947 0 0 1-5.656 2.343 7.953 7.953 0 0 1-5.658-2.344c-3.118-3.119-3.118-8.195 0-11.314a7.923 7.923 0 0 1 2.06-1.483 10.027 10.027 0 0 0 2.89 7.848 9.972 9.972 0 0 0 7.848 2.891 8.036 8.036 0 0 1-1.484 2.059z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="lightThemeIcon"> <path fill="currentColor" d="M6.993 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007S14.761 6.993 12 6.993 6.993 9.239 6.993 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19h2v3h-2zm0-17h2v3h-2zm-9 9h3v2h-3zm17 0h3v2h-3zM4.219 18.363l2.12-2.122 1.415 1.414-2.12 2.122zM16.24 6.344l2.122-2.122 1.414 1.414-2.122 2.122zM6.342 7.759 4.22 5.637l1.415-1.414 2.12 2.122zm13.434 10.605-1.414 1.414-2.122-2.122 1.414-1.414z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="autoThemeIcon"> <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"> <path d="M9.173 14.83a4 4 0 1 1 5.657-5.657" /> <path d="m11.294 12.707l.174.247a7.5 7.5 0 0 0 8.845 2.492A9 9 0 0 1 5.642 18.36M3 12h1m8-9v1M5.6 5.6l.7.7M3 21L21 3" /> </g> </symbol> <symbol viewBox="0 0 24 24" id="newFolderIcon"> <path fill="currentColor" d="M14 16h2v-2h2v-2h-2v-2h-2v2h-2v2h2v2ZM2 20V4h8l2 2h10v14H2Zm2-2h16V8h-8.825l-2-2H4v12Zm0 0V6v12Z" /> </symbol> <symbol viewBox="0 0 20 20" id="expandFolderIcon"> <path fill="currentColor" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="closeIcon"> <path fill="currentColor" d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z" /> </symbol> <symbol viewBox="0 0 24 24" id="settingIcon"> <path fill="currentColor" d="M13.85 22.25h-3.7c-.74 0-1.36-.54-1.45-1.27l-.27-1.89c-.27-.14-.53-.29-.79-.46l-1.8.72c-.7.26-1.47-.03-1.81-.65L2.2 15.53c-.35-.66-.2-1.44.36-1.88l1.53-1.19c-.01-.15-.02-.3-.02-.46 0-.15.01-.31.02-.46l-1.52-1.19c-.59-.45-.74-1.26-.37-1.88l1.85-3.19c.34-.62 1.11-.9 1.79-.63l1.81.73c.26-.17.52-.32.78-.46l.27-1.91c.09-.7.71-1.25 1.44-1.25h3.7c.74 0 1.36.54 1.45 1.27l.27 1.89c.27.14.53.29.79.46l1.8-.72c.71-.26 1.48.03 1.82.65l1.84 3.18c.36.66.2 1.44-.36 1.88l-1.52 1.19c.01.15.02.3.02.46s-.01.31-.02.46l1.52 1.19c.56.45.72 1.23.37 1.86l-1.86 3.22c-.34.62-1.11.9-1.8.63l-1.8-.72c-.26.17-.52.32-.78.46l-.27 1.91c-.1.68-.72 1.22-1.46 1.22zm-3.23-2h2.76l.37-2.55.53-.22c.44-.18.88-.44 1.34-.78l.45-.34 2.38.96 1.38-2.4-2.03-1.58.07-.56c.03-.26.06-.51.06-.78s-.03-.53-.06-.78l-.07-.56 2.03-1.58-1.39-2.4-2.39.96-.45-.35c-.42-.32-.87-.58-1.33-.77l-.52-.22-.37-2.55h-2.76l-.37 2.55-.53.21c-.44.19-.88.44-1.34.79l-.45.33-2.38-.95-1.39 2.39 2.03 1.58-.07.56a7 7 0 0 0-.06.79c0 .26.02.53.06.78l.07.56-2.03 1.58 1.38 2.4 2.39-.96.45.35c.43.33.86.58 1.33.77l.53.22.38 2.55z"> </path> <circle fill="currentColor" cx="12" cy="12" r="3.5"></circle> </symbol> <symbol viewBox="0 0 41 41" id="aiIcon"> <path fill="white" stroke-width="1.5" d="M37.5324 16.8707C37.9808 15.5241 38.1363 14.0974 37.9886 12.6859C37.8409 11.2744 37.3934 9.91076 36.676 8.68622C35.6126 6.83404 33.9882 5.3676 32.0373 4.4985C30.0864 3.62941 27.9098 3.40259 25.8215 3.85078C24.8796 2.7893 23.7219 1.94125 22.4257 1.36341C21.1295 0.785575 19.7249 0.491269 18.3058 0.500197C16.1708 0.495044 14.0893 1.16803 12.3614 2.42214C10.6335 3.67624 9.34853 5.44666 8.6917 7.47815C7.30085 7.76286 5.98686 8.3414 4.8377 9.17505C3.68854 10.0087 2.73073 11.0782 2.02839 12.312C0.956464 14.1591 0.498905 16.2988 0.721698 18.4228C0.944492 20.5467 1.83612 22.5449 3.268 24.1293C2.81966 25.4759 2.66413 26.9026 2.81182 28.3141C2.95951 29.7256 3.40701 31.0892 4.12437 32.3138C5.18791 34.1659 6.8123 35.6322 8.76321 36.5013C10.7141 37.3704 12.8907 37.5973 14.9789 37.1492C15.9208 38.2107 17.0786 39.0587 18.3747 39.6366C19.6709 40.2144 21.0755 40.5087 22.4946 40.4998C24.6307 40.5054 26.7133 39.8321 28.4418 38.5772C30.1704 37.3223 31.4556 35.5506 32.1119 33.5179C33.5027 33.2332 34.8167 32.6547 35.9659 31.821C37.115 30.9874 38.0728 29.9178 38.7752 28.684C39.8458 26.8371 40.3023 24.6979 40.0789 22.5748C39.8556 20.4517 38.9639 18.4544 37.5324 16.8707ZM22.4978 37.8849C20.7443 37.8874 19.0459 37.2733 17.6994 36.1501C17.7601 36.117 17.8666 36.0586 17.936 36.0161L25.9004 31.4156C26.1003 31.3019 26.2663 31.137 26.3813 30.9378C26.4964 30.7386 26.5563 30.5124 26.5549 30.2825V19.0542L29.9213 20.998C29.9389 21.0068 29.9541 21.0198 29.9656 21.0359C29.977 21.052 29.9842 21.0707 29.9867 21.0902V30.3889C29.9842 32.375 29.1946 34.2791 27.7909 35.6841C26.3872 37.0892 24.4838 37.8806 22.4978 37.8849ZM6.39227 31.0064C5.51397 29.4888 5.19742 27.7107 5.49804 25.9832C5.55718 26.0187 5.66048 26.0818 5.73461 26.1244L13.699 30.7248C13.8975 30.8408 14.1233 30.902 14.3532 30.902C14.583 30.902 14.8088 30.8408 15.0073 30.7248L24.731 25.1103V28.9979C24.7321 29.0177 24.7283 29.0376 24.7199 29.0556C24.7115 29.0736 24.6988 29.0893 24.6829 29.1012L16.6317 33.7497C14.9096 34.7416 12.8643 35.0097 10.9447 34.4954C9.02506 33.9811 7.38785 32.7263 6.39227 31.0064ZM4.29707 13.6194C5.17156 12.0998 6.55279 10.9364 8.19885 10.3327C8.19885 10.4013 8.19491 10.5228 8.19491 10.6071V19.808C8.19351 20.0378 8.25334 20.2638 8.36823 20.4629C8.48312 20.6619 8.64893 20.8267 8.84863 20.9404L18.5723 26.5542L15.206 28.4979C15.1894 28.5089 15.1703 28.5155 15.1505 28.5173C15.1307 28.5191 15.1107 28.516 15.0924 28.5082L7.04046 23.8557C5.32135 22.8601 4.06716 21.2235 3.55289 19.3046C3.03862 17.3858 3.30624 15.3413 4.29707 13.6194ZM31.955 20.0556L22.2312 14.4411L25.5976 12.4981C25.6142 12.4872 25.6333 12.4805 25.6531 12.4787C25.6729 12.4769 25.6928 12.4801 25.7111 12.4879L33.7631 17.1364C34.9967 17.849 36.0017 18.8982 36.6606 20.1613C37.3194 21.4244 37.6047 22.849 37.4832 24.2684C37.3617 25.6878 36.8382 27.0432 35.9743 28.1759C35.1103 29.3086 33.9415 30.1717 32.6047 30.6641C32.6047 30.5947 32.6047 30.4733 32.6047 30.3889V21.188C32.6066 20.9586 32.5474 20.7328 32.4332 20.5338C32.319 20.3348 32.154 20.1698 31.955 20.0556ZM35.3055 15.0128C35.2464 14.9765 35.1431 14.9142 35.069 14.8717L27.1045 10.2712C26.906 10.1554 26.6803 10.0943 26.4504 10.0943C26.2206 10.0943 25.9948 10.1554 25.7963 10.2712L16.0726 15.8858V11.9982C16.0715 11.9783 16.0753 11.9585 16.0837 11.9405C16.0921 11.9225 16.1048 11.9068 16.1207 11.8949L24.1719 7.25025C25.4053 6.53903 26.8158 6.19376 28.2383 6.25482C29.6608 6.31589 31.0364 6.78077 32.2044 7.59508C33.3723 8.40939 34.2842 9.53945 34.8334 10.8531C35.3826 12.1667 35.5464 13.6095 35.3055 15.0128ZM14.2424 21.9419L10.8752 19.9981C10.8576 19.9893 10.8423 19.9763 10.8309 19.9602C10.8195 19.9441 10.8122 19.9254 10.8098 19.9058V10.6071C10.8107 9.18295 11.2173 7.78848 11.9819 6.58696C12.7466 5.38544 13.8377 4.42659 15.1275 3.82264C16.4173 3.21869 17.8524 2.99464 19.2649 3.1767C20.6775 3.35876 22.0089 3.93941 23.1034 4.85067C23.0427 4.88379 22.937 4.94215 22.8668 4.98473L14.9024 9.58517C14.7025 9.69878 14.5366 9.86356 14.4215 10.0626C14.3065 10.2616 14.2466 10.4877 14.2479 10.7175L14.2424 21.9419ZM16.071 17.9991L20.4018 15.4978L24.7325 17.9975V22.9985L20.4018 25.4983L16.071 22.9985V17.9991Z"> </path> </symbol> <symbol viewBox="0 0 24 24" id="importSetIcon"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 21l-8-4.5v-9L12 3l8 4.5V12m-8 0l8-4.5M12 12v9m0-9L4 7.5M22 18h-7m3-3l-3 3l3 3" /> </symbol> <symbol viewBox="0 0 24 24" id="exportSetIcon"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 21l-8-4.5v-9L12 3l8 4.5V12m-8 0l8-4.5M12 12v9m0-9L4 7.5M15 18h7m-3-3l3 3l-3 3" /> </symbol> </svg> </div> <div id="loadMask"> <div> <div>ChatGPT</div> <svg> <use xlink:href="#loadingIcon" /> </svg> </div> </div> <div class="chat_window"> <div class="overlay"></div> <nav class="nav"> <div class="navHeader"> <div id="newChat"> <svg width="24" height="24"> <use xlink:href="#addIcon" /> </svg> <span>新建会话</span> </div> <div id="newFolder" title="新建文件夹"> <svg width="24" height="24"> <use xlink:href="#newFolderIcon" /> </svg> </div> </div> <div class="extraChat"> <input type="text" id="searchChat" placeholder="搜索" /> <div id="clearSearch"> <svg width="24" height="24"> <use xlink:href="#closeIcon" /> </svg> </div> </div> <div class="allList"> <div id="folderList"></div> <div id="chatList"></div> </div> <div class="navFooter"> <div class="navFunc"> <div id="clearChat" title="清空"> <svg width="24" height="24"> <use xlink:href="#clearAllIcon" /> </svg> </div> <div id="toggleLight" title="浅色"> <svg width="24" height="24"> <use xlink:href="#lightThemeIcon" /> </svg> </div> <div id="sysSetting" title="设置"> <svg width="24" height="24"> <use xlink:href="#settingIcon" /> </svg> </div> </div> <div class="divider"></div> </div> </nav> <div class="mainContent"> <div class="top_menu"> <div class="toggler"> <div class="button close"></div> <div class="button minimize"></div> <div class="button maximize"></div> </div> <div class="title"><span>ChatGPT</span></div> <div class="settings"> <button class="setBtn" id="toggleFull" title="全屏"> <svg width="30" height="30"> <use xlink:href="#expandFullIcon" /> </svg> </button> <button class="setBtn" id="setting"> <svg viewBox="0 0 100 100" width="30" height="30"> <title>设置</title> <circle cx="50" cy="20" r="10" fill="#e15b64" /> <circle cx="50" cy="50" r="10" fill="#f8b26a" /> <circle cx="50" cy="80" r="10" fill="#99c959" /> </svg> </button> </div> <div id="setDialog" style="display:none;"> <div class="setSwitch"> <div data-id="convOption" class="activeSwitch">对话</div> <div data-id="speechOption">语音合成</div> <div data-id="recOption">语音识别</div> </div> <div id="convOption"> <div class="presetSelect presetModelCls"> <label for="preSetModel">GPT模型</label> <select id="preSetModel"> <option value="gpt-3.5-turbo">gpt-3.5</option> <option value="gpt-4">gpt-4</option> <option value="gpt-4-32k">gpt-4-32k</option> </select> </div> <div> <div>用户头像</div> <div class="avatarDetail"> <img id="setAvatarPre" src="" /> <input class="inputTextClass" type="text" id="setAvatar" /> </div> </div> <div> <div class="justSetLine presetSelect"> <div>系统角色</div> <div> <label for="preSetSystem">预设角色</label> <select id="preSetSystem"> <option value="">默认</option> <option value="normal">助手</option> <option value="cat">猫娘</option> <option value="emoji">表情</option> <option value="image">有图</option> </select> </div> </div> <textarea class="inputTextClass areaTextClass" placeholder="你是一个乐于助人的助手,尽量简明扼要地回答" id="systemInput"></textarea> </div> <div> <span>角色性格</span> <input type="range" id="top_p" min="0" max="1" value="0.7" step="0.05" /> <div class="selectDef"> <span>准确严谨</span> <span>灵活创新</span> </div> </div> <div> <span>回答质量</span> <input type="range" id="temp" min="0" max="2" value="1" step="0.05" /> <div class="selectDef"> <span>重复保守</span> <span>胡言乱语</span> </div> </div> <div> <span>打字机速度</span> <input type="range" id="textSpeed" min="0" max="100" value="88" step="1" /> <div class="selectDef"> <span>慢</span> <span>快</span> </div> </div> <div> <span class="inlineTitle">连续对话</span> <label class="switch-slide"> <input type="checkbox" id="enableCont" checked="true" hidden /> <label for="enableCont" class="switch-slide-label"></label> </label> </div> <div> <span class="inlineTitle">长回复</span> <label class="switch-slide"> <input type="checkbox" id="enableLongReply" hidden /> <label for="enableLongReply" class="switch-slide-label"></label> </label> </div> </div> <div id="speechOption" style="display: none;"> <div class="presetSelect presetModelCls"> <label for="preSetService">语音合成服务</label> <select id="preSetService"> <option value="3">Azure语音</option> <option selected value="2">Edge语音</option> <option value="1">系统语音</option> </select> </div> <div class="presetSelect presetModelCls"> <label for="preSetAzureRegion">Azure 区域</label> <select id="preSetAzureRegion"> </select> </div> <div> <div>Azure Access Key</div> <input class="inputTextClass" type="password" placeholder="Azure Key" id="azureKeyInput" style="-webkit-text-security: disc;" /> </div> <div id="checkVoiceLoad" style="display: none;"> <svg> <use xlink:href="#loadingIcon" /> </svg> <span>加载语音</span> </div> <div id="speechDetail"> <div> <div class="justSetLine"> <div>选择语音</div> <div id="voiceTypes"> <span data-type="0">提问语音</span> <span data-type="1" class="selVoiceType">回答语音</span> </div> </div> <select id="preSetSpeech"> </select> </div> <div class="justSetLine presetSelect" id="azureExtra" style="display:none;"> <div class="presetModelCls"> <label for="preSetVoiceStyle">风格</label> <select id="preSetVoiceStyle"> </select> </div> <div class="presetModelCls"> <label for="preSetVoiceRole">角色</label> <select id="preSetVoiceRole"> </select> </div> </div> <div> <span>音量</span> <input type="range" id="voiceVolume" min="0" max="1" value="1" step="0.1" /> <div class="selectDef"> <span>低</span> <span>高</span> </div> </div> <div> <span>语速</span> <input type="range" id="voiceRate" min="0.1" max="2" value="1" step="0.1" /> <div class="selectDef"> <span>慢</span> <span>快</span> </div> </div> <div> <span>音调</span> <input type="range" id="voicePitch" min="0" max="2" value="1" step="0.1" /> <div class="selectDef"> <span>平淡</span> <span>起伏</span> </div> </div> <div> <span class="inlineTitle">连续朗读</span> <label class="switch-slide"> <input type="checkbox" id="enableContVoice" checked="true" hidden /> <label for="enableContVoice" class="switch-slide-label"></label> </label> </div> <div> <span class="inlineTitle">自动朗读</span> <label class="switch-slide"> <input type="checkbox" id="enableAutoVoice" hidden /> <label for="enableAutoVoice" class="switch-slide-label"></label> </label> </div> </div> </div> <div id="recOption" style="display: none;"> <div id="noRecTip" style="display: block;">当前环境不支持语音识别,请尝试使用chrome内核浏览器或重新部署https页面。</div> <div id="yesRec" style="display: none;"> <div class="presetSelect presetModelCls"> <label for="selectLangOption">语言</label> <select id="selectLangOption"> </select> </div> <div class="presetSelect presetModelCls"> <label for="selectDiaOption">方言</label> <select id="selectDiaOption"> </select> </div> </div> </div> </div> </div> <div class="messages"> <div id="chatlog"></div> <div id="stopChat"><svg width="24" height="24"> <use xlink:href="#stopResIcon" /> </svg>停止</div> </div> <div class="bottom_wrapper clearfix"> <div class="message_input_wrapper"> <textarea class="message_input_text" spellcheck="false" placeholder="来问点什么吧" id="chatinput"></textarea> <div id="voiceRec" style="display:none;"> <div id="voiceRecIcon"> <svg viewBox="0 0 48 48" id="voiceInputIcon"> <g fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="4"> <rect fill="none" width="14" height="27" x="17" y="4" rx="7" /> <rect class="animVoice" x="18" y="4" width="12" height="27" stroke="none" fill="currentColor"></rect> <path stroke-linecap="round" d="M9 23c0 8.284 6.716 15 15 15c8.284 0 15-6.716 15-15M24 38v6" /> </g> </svg> </div> <div id="voiceRecSetting"> <select id="select_language" style="margin-bottom: 4px;"></select> <select id="select_dialect"></select> </div> </div> </div> <button class="loaded" id="sendbutton"> <span>发送</span> <svg style="margin:0 auto;height:40px;width:100%;"> <use xlink:href="#loadingIcon" /> </svg> </button> <button class="clearConv" title="清空会话"> <svg style="color: #e15b64;" width="29" height="29"> <use xlink:href="#closeIcon" /> </svg> <svg width="21" height="21"> <use xlink:href="#deleteIcon" /> </svg> </button> </div> </div> </div> <div id="sysDialog"> <div id="sysContent"> <div id="closeSet"> <svg width="24" height="24"> <use xlink:href="#closeIcon" /> </svg> </div> <div class="sysTitle">设置</div> <div class="sysDetail"> <div class="setContent"> <div class="setTitle">OpenAI接口</div> <div class="setDetail inputDetail"> <input class="inputTextClass" placeholder="https://api.openai.com/" id="apiHostInput" value="https://api.openai.com/" /> </div> </div> <div class="setContent"> <div class="setTitle">API密钥</div> <div class="setDetail inputDetail apiDetail"> <input class="inputTextClass" type="password" placeholder="sk-xxxxxx" id="keyInput" style="-webkit-text-security: disc;" /> <div id="checkBillBtn" class="loaded"> <span>检查API额度</span> <svg style="margin:0 auto;height:34px;width:100%;"> <use xlink:href="#loadingIcon"></use> </svg> </div> </div> </div> <div class="setContent" id="quotaContent" style="display: none;"> <div class="setTitle" id="quotaTitle">免费额度</div> <div class="setDetail"> <div class="progressBar"> <div class="nowProgress" id="usedQuotaBar"></div> </div> <div class="progressDetail"> <div><span>已用 </span><span id="usedQuota"></span></div> <div><span>可用 </span><span id="availableQuota"></span></div> </div> </div> </div> <div class="setContent"> <div class="setTitle">主题</div> <div class="setDetail themeDetail lightTheme" id="setLight"> <div title="深色"> <svg width="24" height="24"> <use xlink:href="#darkThemeIcon"></use> </svg> </div> <div title="浅色"> <svg width="24" height="24"> <use xlink:href="#lightThemeIcon"></use> </svg> </div> <div title="自动"> <svg width="24" height="24"> <use xlink:href="#autoThemeIcon"></use> </svg> </div> </div> </div> <div class="setContent" id="autoDetail" style="display: none;font-size: 15px;"> <div class="setDetail"> <div> <input type="radio" id="autoTheme1" name="autoLight" value="1" checked /> <label for="autoTheme1">跟随系统</label> </div> <div style="margin-top: 8px;"> <input type="radio" id="autoTheme0" name="autoLight" value="0" /> <label for="autoTheme0">自定义深色主题时间</label> </div> <div id="customAutoSet" style="display: none; margin-top: 10px;"> <div> <label for="customStart">开始时间</label> <input type="time" id="customStart" required> </div> <div style="margin-top: 10px;"> <label for="customEnd">结束时间</label> <input type="time" id="customEnd" required> </div> </div> </div> </div> <div class="setContent"> <div class="setTitle">会话</div> <div class="setDetail dataDetail"> <div id="exportChat"> <svg width="24" height="24"> <use xlink:href="#exportIcon" /> </svg> <span>导出</span> </div> <label id="importChat" for="importChatInput"> <svg width="24" height="24"> <use xlink:href="#importIcon" /> </svg> <span>导入</span> </label> <input type="file" style="display: none;" id="importChatInput" accept="application/json" /> <div id="clearChatSet"> <svg width="24" height="24"> <use xlink:href="#clearAllIcon" /> </svg> <span>清空</span> </div> </div> </div> <div class="setContent"> <div class="setTitle">设置</div> <div class="setDetail dataDetail"> <div id="exportSet"> <svg width="24" height="24"> <use xlink:href="#exportSetIcon" /> </svg> <span>导出</span> </div> <label id="importSet" for="importSetInput"> <svg width="24" height="24"> <use xlink:href="#importSetIcon" /> </svg> <span>导入</span> </label> <input type="file" style="display: none;" id="importSetInput" accept="application/json" /> <div id="resetSet"> <svg width="22" height="22" style="transform: scaleX(-1)"> <use xlink:href="#refreshIcon" /> </svg> <span>重置</span> </div> </div> </div> <div class="setContent"> <div class="setTitle">本地存储</div> <div class="setDetail"> <div class="progressBar"> <div class="nowProgress" id="usedStorageBar"></div> </div> <div class="progressDetail"> <div><span>已用 </span><span id="usedStorage"></span></div> <div><span>可用 </span><span id="availableStorage"></span></div> </div> </div> </div> </div> </div> </div> <link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/github-markdown-light.min.css"> <link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/notyf.css"> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/notyf.js"></script> <script> const isMobile = navigator.userAgent.match(/iPhone|iPad|iPod|Android|BlackBerry|webOS/); if (isMobile) { const script = document.createElement("script"); script.src = "//repo.bfw.wiki/bfwrepo/js/mobile-drag-drop.js"; script.defer = true; script.onload = () => { MobileDragDrop.polyfill(); } document.body.appendChild(script); const link = document.createElement("link"); link.rel = "stylesheet"; link.href = "//repo.bfw.wiki/bfwrepo/css/mobile-drag-drop.css"; document.body.appendChild(link); } </script> <script> const notyf = new Notyf({ position: {x: "center", y: "top"}, types: [ { type: "success", background: "#99c959", duration: 2000, }, { type: "error", background: "#e15b64", duration: 3000, } ] }); const windowEle = document.getElementsByClassName("chat_window")[0]; const messagsEle = document.getElementsByClassName("messages")[0]; const chatlog = document.getElementById("chatlog"); const stopEle = document.getElementById("stopChat"); const sendBtnEle = document.getElementById("sendbutton"); const clearEle = document.getElementsByClassName("clearConv")[0]; const textarea = document.getElementById("chatinput"); const settingEle = document.getElementById("setting"); const dialogEle = document.getElementById("setDialog"); const lightEle = document.getElementById("toggleLight"); const setLightEle = document.getElementById("setLight"); const autoThemeEle = document.getElementById("autoDetail"); const systemEle = document.getElementById("systemInput"); const speechServiceEle = document.getElementById("preSetService"); const newChatEle = document.getElementById("newChat"); const folderListEle = document.getElementById("folderList"); const chatListEle = document.getElementById("chatList"); const searchChatEle = document.getElementById("searchChat"); const voiceRecEle = document.getElementById("voiceRecIcon"); const voiceRecSetEle = document.getElementById("voiceRecSetting"); let voiceType = 1; // 设置 0: 提问语音,1:回答语音 let voiceRole = []; // 语音 let voiceVolume = []; //音量 let voiceRate = []; // 语速 let voicePitch = []; // 音调 let enableContVoice; // 连续朗读 let enableAutoVoice; // 自动朗读 let existVoice = 2; // 3:Azure语音 2:使用edge在线语音, 1:使用本地语音, 0:不支持语音 let azureToken; let azureTokenTimer; let azureRegion; let azureKey; let azureRole = []; let azureStyle = []; let isSafeEnv = location.hostname.match(/127.|localhost/) || location.protocol.match(/https:|file:/); // https或本地安全环境 let supportRec = !!window.webkitSpeechRecognition && isSafeEnv; // 是否支持语音识别输入 let recing = false; let toggleRecEv; const dayMs = 8.64e7; const noLoading = () => { return !loading && (!currentResEle || currentResEle.dataset.loading !== "true") } textarea.focus(); const textInputEvent = () => { if (noLoading()) { if (textarea.value.trim().length) { sendBtnEle.classList.add("activeSendBtn"); } else { sendBtnEle.classList.remove("activeSendBtn"); } } textarea.style.height = "47px"; textarea.style.height = textarea.scrollHeight + "px"; }; textarea.oninput = textInputEvent; document.body.addEventListener("click", event => { if (event.target.className === "toggler") { document.body.classList.toggle("show-nav"); if (window.innerWidth > 800) { localStorage.setItem("pinNav", document.body.classList.contains("show-nav")) } } else if (event.target.className === "overlay") { document.body.classList.remove("show-nav"); } else if (event.target === document.body) { if (window.innerWidth <= 800) { document.body.classList.remove("show-nav"); } } }); const endSetEvent = (ev) => { if (!document.getElementById("sysContent").contains(ev.target)) { ev.preventDefault(); ev.stopPropagation(); endSet(); } } const endSet = () => { document.getElementById("sysDialog").style.display = "none"; document.body.removeEventListener("click", endSetEvent, true); } document.getElementById("closeSet").onclick = endSet; document.getElementById("sysSetting").onclick = () => { document.getElementById("sysDialog").style.display = "flex"; checkStorage(); document.body.addEventListener("click", endSetEvent, true); }; const initRecSetting = () => { if (supportRec) { noRecTip.style.display = "none"; yesRec.style.display = "block"; document.getElementById("voiceRec").style.display = "block"; textarea.classList.add("message_if_voice"); let langs = [ // from https://www.google.com/intl/en/chrome/demos/speech.html ['中文', ['cmn-Hans-CN', '普通话 (大陆)'], ['cmn-Hans-HK', '普通话 (香港)'], ['cmn-Hant-TW', '中文 (台灣)'], ['yue-Hant-HK', '粵語 (香港)']], ['English', ['en-US', 'United States'], ['en-GB', 'United Kingdom'], ['en-AU', 'Australia'], ['en-CA', 'Canada'], ['en-IN', 'India'], ['en-KE', 'Kenya'], ['en-TZ', 'Tanzania'], ['en-GH', 'Ghana'], ['en-NZ', 'New Zealand'], ['en-NG', 'Nigeria'], ['en-ZA', 'South Africa'], ['en-PH', 'Philippines']] ]; langs.forEach((lang, i) => { select_language.options.add(new Option(lang[0], i)); selectLangOption.options.add(new Option(lang[0], i)) }); const updateCountry = function () { selectLangOption.selectedIndex = select_language.selectedIndex = this.selectedIndex; select_dialect.innerHTML = ""; selectDiaOption.innerHTML = ""; let list = langs[select_language.selectedIndex]; for (let i = 1; i < list.length; i++) { select_dialect.options.add(new Option(list[i][1], list[i][0])); selectDiaOption.options.add(new Option(list[i][1], list[i][0])); } select_dialect.style.visibility = list[1].length == 1 ? "hidden" : "visible"; selectDiaOption.parentElement.style.visibility = list[1].length == 1 ? "hidden" : "visible"; localStorage.setItem("voiceRecLang", select_dialect.value); }; let localLangIdx = 0; let localDiaIdx = 0; let localRecLang = localStorage.getItem("voiceRecLang") || "cmn-Hans-CN"; if (localRecLang) { let localIndex = langs.findIndex(item => { let diaIdx = item.findIndex(lang => {return lang instanceof Array && lang[0] === localRecLang}); if (diaIdx !== -1) { localDiaIdx = diaIdx - 1; return true; } return false; }); if (localIndex !== -1) localLangIdx = localIndex; } selectLangOption.onchange = updateCountry; select_language.onchange = updateCountry; selectDiaOption.onchange = select_dialect.onchange = function () { selectDiaOption.selectedIndex = select_dialect.selectedIndex = this.selectedIndex; localStorage.setItem("voiceRecLang", select_dialect.value); } selectLangOption.selectedIndex = select_language.selectedIndex = localLangIdx; select_language.dispatchEvent(new Event("change")); selectDiaOption.selectedIndex = select_dialect.selectedIndex = localDiaIdx; select_dialect.dispatchEvent(new Event("change")); let recIns = new webkitSpeechRecognition(); // prevent some Android bug recIns.continuous = !(/\bAndroid\b/i.test(navigator.userAgent)); recIns.interimResults = true; recIns.maxAlternatives = 1; let recRes = tempRes = ""; let preRes, affRes; const resEvent = (event) => { if (typeof (event.results) === "undefined") { toggleRecEvent(); return; } let isFinal; for (let i = event.resultIndex; i < event.results.length; ++i) { isFinal = event.results[i].isFinal; if (isFinal) {recRes += event.results[i][0].transcript} else {tempRes = recRes + event.results[i][0].transcript} } textarea.value = preRes + (isFinal ? recRes : tempRes) + affRes; textInputEvent(); textarea.focus(); textarea.selectionEnd = textarea.value.length - affRes.length; }; const endEvent = (event) => { if (!(event && event.type === "end")) recIns.stop(); recIns.onresult = null; recIns.onerror = null; recIns.onend = null; recRes = tempRes = ""; voiceRecEle.classList.remove("voiceRecing"); recing = false; }; const errorEvent = (ev) => { if (event.error === "no-speech") { notyf.error("未识别到语音,请调整设备后重试!") } if (event.error === "audio-capture") { notyf.error("未找到麦克风,请确保已安装麦克风!") } if (event.error === "not-allowed") { notyf.error("未允许使用麦克风的权限!") } endEvent(); } const closeEvent = (ev) => { if (voiceRecSetEle.contains(ev.target)) return; if (!voiceRecSetEle.contains(ev.target)) { voiceRecSetEle.style.display = "none"; document.removeEventListener("mousedown", closeEvent, true); voiceRecEle.classList.remove("voiceLong"); } } const longEvent = () => { voiceRecSetEle.style.display = "block"; document.addEventListener("mousedown", closeEvent, true); } const toggleRecEvent = () => { voiceRecEle.classList.toggle("voiceRecing"); if (voiceRecEle.classList.contains("voiceRecing")) { try { preRes = textarea.value.slice(0, textarea.selectionStart); affRes = textarea.value.slice(textarea.selectionEnd); recIns.lang = select_dialect.value; recIns.start(); recIns.onresult = resEvent; recIns.onerror = errorEvent; recIns.onend = endEvent; recing = true; } catch (e) { endEvent(); } } else { endEvent(); } }; toggleRecEv = toggleRecEvent; let timer; const voiceDownEvent = (ev) => { ev.preventDefault(); let i = 0; voiceRecEle.classList.add("voiceLong"); timer = setInterval(() => { i += 1; if (i >= 3) { clearInterval(timer); timer = void 0; longEvent(); } }, 100) }; const voiceUpEvent = (ev) => { ev.preventDefault(); if (timer !== void 0) { toggleRecEvent(); clearInterval(timer); timer = void 0; voiceRecEle.classList.remove("voiceLong"); } } voiceRecEle.onmousedown = voiceDownEvent; voiceRecEle.ontouchstart = voiceDownEvent; voiceRecEle.onmouseup = voiceUpEvent; voiceRecEle.ontouchend = voiceUpEvent; }; }; initRecSetting(); document.getElementsByClassName("setSwitch")[0].onclick = function (ev) { let activeEle = this.getElementsByClassName("activeSwitch")[0]; if (ev.target !== activeEle) { activeEle.classList.remove("activeSwitch"); ev.target.classList.add("activeSwitch"); document.getElementById(ev.target.dataset.id).style.display = "block"; document.getElementById(activeEle.dataset.id).style.display = "none"; } }; if (!(window.speechSynthesis && window.SpeechSynthesisUtterance)) { speechServiceEle.remove(2); } const initVoiceVal = () => { let localVoiceType = localStorage.getItem("existVoice"); speechServiceEle.value = existVoice = parseInt(localVoiceType || "2"); } initVoiceVal(); const clearAzureVoice = () => { azureKey = void 0; azureRegion = void 0; azureRole = []; azureStyle = []; document.getElementById("azureExtra").style.display = "none"; azureKeyInput.parentElement.style.display = "none"; preSetAzureRegion.parentElement.style.display = "none"; if (azureTokenTimer) { clearInterval(azureTokenTimer); azureTokenTimer = void 0; } } speechServiceEle.onchange = () => { existVoice = parseInt(speechServiceEle.value); localStorage.setItem("existVoice", existVoice); toggleVoiceCheck(true); if (checkAzureAbort && !checkAzureAbort.signal.aborted) { checkAzureAbort.abort(); checkAzureAbort = void 0; } if (checkEdgeAbort && !checkEdgeAbort.signal.aborted) { checkEdgeAbort.abort(); checkEdgeAbort = void 0; } if (existVoice === 3) { azureKeyInput.parentElement.style.display = "block"; preSetAzureRegion.parentElement.style.display = "block"; loadAzureVoice(); } else if (existVoice === 2) { clearAzureVoice(); loadEdgeVoice(); } else if (existVoice === 1) { toggleVoiceCheck(false); clearAzureVoice(); loadLocalVoice(); } } let azureVoiceData, edgeVoiceData, systemVoiceData, checkAzureAbort, checkEdgeAbort; const toggleVoiceCheck = (bool) => { checkVoiceLoad.style.display = bool ? "flex" : "none"; speechDetail.style.display = bool ? "none" : "block"; } const loadAzureVoice = () => { let checking = false; const checkAzureFunc = () => { if (checking) return; if (azureKey) { checking = true; checkVoiceLoad.classList.add("voiceChecking"); if (azureTokenTimer) { clearInterval(azureTokenTimer); } checkAzureAbort = new AbortController(); setTimeout(() => { if (checkAzureAbort && !checkAzureAbort.signal.aborted) { checkAzureAbort.abort(); checkAzureAbort = void 0; } }, 15000); Promise.all([getAzureToken(checkAzureAbort.signal), getVoiceList(checkAzureAbort.signal)]).then(() => { azureTokenTimer = setInterval(() => { getAzureToken(); }, 540000); toggleVoiceCheck(false); }).catch(e => { }).finally(() => { checkVoiceLoad.classList.remove("voiceChecking"); checking = false; }) } }; checkVoiceLoad.onclick = checkAzureFunc; const getAzureToken = (signal) => { return new Promise((res, rej) => { fetch("https://" + azureRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken", { signal, method: "POST", headers: { "Ocp-Apim-Subscription-Key": azureKey } }).then(response => { response.text().then(text => { try { let json = JSON.parse(text); notyf.error("由于订阅密钥无效或 API 端点错误,访问被拒绝!"); rej(); } catch (e) { azureToken = text; res(); } }); }).catch(e => { rej(); }) }) }; const getVoiceList = (signal) => { return new Promise((res, rej) => { if (azureVoiceData) { initVoiceSetting(azureVoiceData); res(); } else { let localAzureVoiceData = localStorage.getItem(azureRegion + "voiceData"); if (localAzureVoiceData) { azureVoiceData = JSON.parse(localAzureVoiceData); initVoiceSetting(azureVoiceData); res(); } else { fetch("https://" + azureRegion + ".tts.speech.microsoft.com/cognitiveservices/voices/list", { signal, headers: { "Ocp-Apim-Subscription-Key": azureKey } }).then(response => { response.json().then(json => { azureVoiceData = json; localStorage.setItem(azureRegion + "voiceData", JSON.stringify(json)); initVoiceSetting(json); res(); }).catch(e => { notyf.error("由于订阅密钥无效或 API 端点错误,访问被拒绝!"); rej(); }) }).catch(e => { rej(); }) } } }) }; let azureRegionEle = document.getElementById("preSetAzureRegion"); if (!azureRegionEle.options.length) { const azureRegions = ['southafricanorth', 'eastasia', 'southeastasia', 'australiaeast', 'centralindia', 'japaneast', 'japanwest', 'koreacentral', 'canadacentral', 'northeurope', 'westeurope', 'francecentral', 'germanywestcentral', 'norwayeast', 'switzerlandnorth', 'switzerlandwest', 'uksouth', 'uaenorth', 'brazilsouth', 'centralus', 'eastus', 'eastus2', 'northcentralus', 'southcentralus', 'westcentralus', 'westus', 'westus2', 'westus3']; azureRegions.forEach((region, i) => { let option = document.createElement("option"); option.value = region; option.text = region; azureRegionEle.options.add(option); }); } let localAzureRegion = localStorage.getItem("azureRegion"); if (localAzureRegion) { azureRegion = localAzureRegion; azureRegionEle.value = localAzureRegion; } azureRegionEle.onchange = () => { azureRegion = azureRegionEle.value; localStorage.setItem("azureRegion", azureRegion); toggleVoiceCheck(true); } azureRegionEle.dispatchEvent(new Event("change")); let azureKeyEle = document.getElementById("azureKeyInput"); let localAzureKey = localStorage.getItem("azureKey"); if (localAzureKey) { azureKey = localAzureKey; azureKeyEle.value = localAzureKey; } azureKeyEle.onchange = () => { azureKey = azureKeyEle.value; localStorage.setItem("azureKey", azureKey); toggleVoiceCheck(true); } azureKeyEle.dispatchEvent(new Event("change")); if (azureKey) { checkAzureFunc(); } } const loadEdgeVoice = () => { let checking = false; const endCheck = () => { checkVoiceLoad.classList.remove("voiceChecking"); checking = false; }; const checkEdgeFunc = () => { if (checking) return; checking = true; checkVoiceLoad.classList.add("voiceChecking"); if (edgeVoiceData) { initVoiceSetting(edgeVoiceData); toggleVoiceCheck(false); endCheck(); } else { checkEdgeAbort = new AbortController(); setTimeout(() => { if (checkEdgeAbort && !checkEdgeAbort.signal.aborted) { checkEdgeAbort.abort(); checkEdgeAbort = void 0; } }, 10000); fetch("https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4", {signal: checkEdgeAbort.signal}).then(response => { response.json().then(json => { edgeVoiceData = json; toggleVoiceCheck(false); initVoiceSetting(json); endCheck(); }); }).catch(err => { endCheck(); }) } }; checkEdgeFunc(); checkVoiceLoad.onclick = checkEdgeFunc; }; const loadLocalVoice = () => { if (systemVoiceData) { initVoiceSetting(systemVoiceData); } else { let initedVoice = false; const getLocalVoice = () => { let voices = speechSynthesis.getVoices(); if (voices.length) { if (!initedVoice) { initedVoice = true; systemVoiceData = voices; initVoiceSetting(voices); } return true; } else { return false; } } let syncExist = getLocalVoice(); if (!syncExist) { if ("onvoiceschanged" in speechSynthesis) { speechSynthesis.onvoiceschanged = () => { getLocalVoice(); } } else if (speechSynthesis.addEventListener) { speechSynthesis.addEventListener("voiceschanged", () => { getLocalVoice(); }) } let timeout = 0; let timer = setInterval(() => { if (getLocalVoice() || timeout > 1000) { if (timeout > 1000) { existVoice = 0; } clearInterval(timer); timer = null; } timeout += 300; }, 300) } } }; const initVoiceSetting = (voices) => { let isOnline = existVoice >= 2; let voicesEle = document.getElementById("preSetSpeech"); // 支持中文和英文 voices = isOnline ? voices.filter(item => item.Locale.match(/^(zh-|en-)/)) : voices.filter(item => item.lang.match(/^(zh-|en-)/)); if (isOnline) { voices.map(item => { item.name = item.FriendlyName || (`${item.DisplayName} Online (${item.VoiceType}) - ${item.LocaleName}`); item.lang = item.Locale; }) } voices.sort((a, b) => { if (a.lang.slice(0, 2) === b.lang.slice(0, 2)) return 0; return (a.lang < b.lang) ? 1 : -1; // 中文在前"z" }); voices.map(item => { if (item.name.match(/^(Google |Microsoft )/)) { item.displayName = item.name.replace(/^.*? /, ""); } else { item.displayName = item.name; } }); voicesEle.innerHTML = ""; voices.forEach((voice, i) => { let option = document.createElement("option"); option.value = i; option.text = voice.displayName; voicesEle.options.add(option); }); voicesEle.onchange = () => { voiceRole[voiceType] = voices[voicesEle.value]; localStorage.setItem("voice" + voiceType, voiceRole[voiceType].name); if (voiceRole[voiceType].StyleList || voiceRole[voiceType].RolePlayList) { document.getElementById("azureExtra").style.display = "block"; let voiceStyles = voiceRole[voiceType].StyleList; let voiceRoles = voiceRole[voiceType].RolePlayList; if (voiceRoles) { preSetVoiceRole.innerHTML = ""; voiceRoles.forEach((role, i) => { let option = document.createElement("option"); option.value = role; option.text = role; preSetVoiceRole.options.add(option); }); let localRole = localStorage.getItem("azureRole" + voiceType); if (localRole && voiceRoles.indexOf(localRole) !== -1) { preSetVoiceRole.value = localRole; azureRole[voiceType] = localRole; } else { preSetVoiceRole.selectedIndex = 0; azureRole[voiceType] = voiceRole[0]; } preSetVoiceRole.onchange = () => { azureRole[voiceType] = preSetVoiceRole.value; localStorage.setItem("azureRole" + voiceType, preSetVoiceRole.value); } preSetVoiceRole.dispatchEvent(new Event("change")); } else { azureRole[voiceType] = void 0; localStorage.removeItem("azureRole" + voiceType); } preSetVoiceRole.style.display = voiceRoles ? "block" : "none"; preSetVoiceRole.previousElementSibling.style.display = voiceRoles ? "block" : "none"; if (voiceStyles) { preSetVoiceStyle.innerHTML = ""; voiceStyles.forEach((style, i) => { let option = document.createElement("option"); option.value = style; option.text = style; preSetVoiceStyle.options.add(option); }); let localStyle = localStorage.getItem("azureStyle" + voiceType); if (localStyle && voiceStyles.indexOf(localStyle) !== -1) { preSetVoiceStyle.valu.........完整代码请登录后点击上方下载按钮下载查看
网友评论0