镜像自地址
https://github.com/binary-husky/gpt_academic.git
已同步 2025-12-06 14:36:48 +00:00
Merge branch 'master' into frontier
这个提交包含在:
@@ -1,9 +1,9 @@
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
|
> `master主分支`最新动态(2025.1.28): 增加字体自定义功能
|
||||||
> `frontier开发分支`最新动态(2024.12.9): 更新对话时间线功能,优化xelatex论文翻译
|
> `frontier开发分支`最新动态(2024.12.9): 更新对话时间线功能,优化xelatex论文翻译
|
||||||
> `wiki文档`最新动态(2024.12.5): 更新ollama接入指南
|
> `wiki文档`最新动态(2024.12.5): 更新ollama接入指南
|
||||||
> `master主分支`最新动态(2024.12.19): 更新3.91版本,更新release页一键安装脚本
|
|
||||||
>
|
>
|
||||||
> 2024.10.10: 突发停电,紧急恢复了提供[whl包](https://drive.google.com/file/d/19U_hsLoMrjOlQSzYS3pzWX9fTzyusArP/view?usp=sharing)的文件服务器
|
> 2024.10.10: 突发停电,紧急恢复了提供[whl包](https://drive.google.com/drive/folders/14kR-3V-lIbvGxri4AHc8TpiA1fqsw7SK?usp=sharing)的文件服务器
|
||||||
> 2024.10.8: 版本3.90加入对llama-index的初步支持,版本3.80加入插件二级菜单功能(详见wiki)
|
> 2024.10.8: 版本3.90加入对llama-index的初步支持,版本3.80加入插件二级菜单功能(详见wiki)
|
||||||
> 2024.5.1: 加入Doc2x翻译PDF论文的功能,[查看详情](https://github.com/binary-husky/gpt_academic/wiki/Doc2x)
|
> 2024.5.1: 加入Doc2x翻译PDF论文的功能,[查看详情](https://github.com/binary-husky/gpt_academic/wiki/Doc2x)
|
||||||
> 2024.3.11: 全力支持Qwen、GLM、DeepseekCoder等中文大语言模型! SoVits语音克隆模块,[查看详情](https://www.bilibili.com/video/BV1Rp421S7tF/)
|
> 2024.3.11: 全力支持Qwen、GLM、DeepseekCoder等中文大语言模型! SoVits语音克隆模块,[查看详情](https://www.bilibili.com/video/BV1Rp421S7tF/)
|
||||||
|
|||||||
24
config.py
24
config.py
@@ -85,6 +85,30 @@ DEFAULT_WORKER_NUM = 3
|
|||||||
THEME = "Default"
|
THEME = "Default"
|
||||||
AVAIL_THEMES = ["Default", "Chuanhu-Small-and-Beautiful", "High-Contrast", "Gstaff/Xkcd", "NoCrypt/Miku"]
|
AVAIL_THEMES = ["Default", "Chuanhu-Small-and-Beautiful", "High-Contrast", "Gstaff/Xkcd", "NoCrypt/Miku"]
|
||||||
|
|
||||||
|
FONT = "Theme-Default-Font"
|
||||||
|
AVAIL_FONTS = [
|
||||||
|
"默认值(Theme-Default-Font)",
|
||||||
|
"宋体(SimSun)",
|
||||||
|
"黑体(SimHei)",
|
||||||
|
"楷体(KaiTi)",
|
||||||
|
"仿宋(FangSong)",
|
||||||
|
"华文细黑(STHeiti Light)",
|
||||||
|
"华文楷体(STKaiti)",
|
||||||
|
"华文仿宋(STFangsong)",
|
||||||
|
"华文宋体(STSong)",
|
||||||
|
"华文中宋(STZhongsong)",
|
||||||
|
"华文新魏(STXinwei)",
|
||||||
|
"华文隶书(STLiti)",
|
||||||
|
"思源宋体(Source Han Serif CN VF@https://chinese-fonts-cdn.deno.dev/packages/syst/dist/SourceHanSerifCN/result.css)",
|
||||||
|
"月星楷(Moon Stars Kai HW@https://chinese-fonts-cdn.deno.dev/packages/moon-stars-kai/dist/MoonStarsKaiHW-Regular/result.css)",
|
||||||
|
"珠圆体(MaokenZhuyuanTi@https://chinese-fonts-cdn.deno.dev/packages/mkzyt/dist/猫啃珠圆体/result.css)",
|
||||||
|
"平方萌萌哒(PING FANG MENG MNEG DA@https://chinese-fonts-cdn.deno.dev/packages/pfmmd/dist/平方萌萌哒/result.css)",
|
||||||
|
"Helvetica",
|
||||||
|
"ui-sans-serif",
|
||||||
|
"sans-serif",
|
||||||
|
"system-ui"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# 默认的系统提示词(system prompt)
|
# 默认的系统提示词(system prompt)
|
||||||
INIT_SYS_PROMPT = "Serve me as a writing and programming assistant."
|
INIT_SYS_PROMPT = "Serve me as a writing and programming assistant."
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ def 编译Latex(chatbot, history, main_file_original, main_file_modified, work_f
|
|||||||
# 根据编译器类型返回编译命令
|
# 根据编译器类型返回编译命令
|
||||||
def get_compile_command(compiler, filename):
|
def get_compile_command(compiler, filename):
|
||||||
compile_command = f'{compiler} -interaction=batchmode -file-line-error {filename}.tex'
|
compile_command = f'{compiler} -interaction=batchmode -file-line-error {filename}.tex'
|
||||||
logger.info('Latex 编译指令: ', compile_command)
|
logger.info('Latex 编译指令: ' + compile_command)
|
||||||
return compile_command
|
return compile_command
|
||||||
|
|
||||||
# 确定使用的编译器
|
# 确定使用的编译器
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ FROM fuqingxu/11.3.1-runtime-ubuntu20.04-with-texlive:latest
|
|||||||
|
|
||||||
# edge-tts需要的依赖,某些pip包所需的依赖
|
# edge-tts需要的依赖,某些pip包所需的依赖
|
||||||
RUN apt update && apt install ffmpeg build-essential -y
|
RUN apt update && apt install ffmpeg build-essential -y
|
||||||
|
RUN apt-get install -y fontconfig
|
||||||
|
RUN ln -s /usr/local/texlive/2023/texmf-dist/fonts/truetype /usr/share/fonts/truetype/texlive
|
||||||
|
RUN fc-cache -fv
|
||||||
RUN apt-get clean
|
RUN apt-get clean
|
||||||
|
|
||||||
# use python3 as the system default python
|
# use python3 as the system default python
|
||||||
|
|||||||
7
main.py
7
main.py
@@ -49,7 +49,7 @@ def main():
|
|||||||
# 读取配置
|
# 读取配置
|
||||||
proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION = get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION')
|
proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION = get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION')
|
||||||
CHATBOT_HEIGHT, LAYOUT, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = get_conf('CHATBOT_HEIGHT', 'LAYOUT', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT')
|
CHATBOT_HEIGHT, LAYOUT, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = get_conf('CHATBOT_HEIGHT', 'LAYOUT', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT')
|
||||||
ENABLE_AUDIO, AUTO_CLEAR_TXT, PATH_LOGGING, AVAIL_THEMES, THEME, ADD_WAIFU = get_conf('ENABLE_AUDIO', 'AUTO_CLEAR_TXT', 'PATH_LOGGING', 'AVAIL_THEMES', 'THEME', 'ADD_WAIFU')
|
ENABLE_AUDIO, AUTO_CLEAR_TXT, AVAIL_FONTS, AVAIL_THEMES, THEME, ADD_WAIFU = get_conf('ENABLE_AUDIO', 'AUTO_CLEAR_TXT', 'AVAIL_FONTS', 'AVAIL_THEMES', 'THEME', 'ADD_WAIFU')
|
||||||
NUM_CUSTOM_BASIC_BTN, SSL_KEYFILE, SSL_CERTFILE = get_conf('NUM_CUSTOM_BASIC_BTN', 'SSL_KEYFILE', 'SSL_CERTFILE')
|
NUM_CUSTOM_BASIC_BTN, SSL_KEYFILE, SSL_CERTFILE = get_conf('NUM_CUSTOM_BASIC_BTN', 'SSL_KEYFILE', 'SSL_CERTFILE')
|
||||||
DARK_MODE, INIT_SYS_PROMPT, ADD_WAIFU, TTS_TYPE = get_conf('DARK_MODE', 'INIT_SYS_PROMPT', 'ADD_WAIFU', 'TTS_TYPE')
|
DARK_MODE, INIT_SYS_PROMPT, ADD_WAIFU, TTS_TYPE = get_conf('DARK_MODE', 'INIT_SYS_PROMPT', 'ADD_WAIFU', 'TTS_TYPE')
|
||||||
if LLM_MODEL not in AVAIL_LLM_MODELS: AVAIL_LLM_MODELS += [LLM_MODEL]
|
if LLM_MODEL not in AVAIL_LLM_MODELS: AVAIL_LLM_MODELS += [LLM_MODEL]
|
||||||
@@ -178,7 +178,7 @@ def main():
|
|||||||
# 左上角工具栏定义
|
# 左上角工具栏定义
|
||||||
from themes.gui_toolbar import define_gui_toolbar
|
from themes.gui_toolbar import define_gui_toolbar
|
||||||
checkboxes, checkboxes_2, max_length_sl, theme_dropdown, system_prompt, file_upload_2, md_dropdown, top_p, temperature = \
|
checkboxes, checkboxes_2, max_length_sl, theme_dropdown, system_prompt, file_upload_2, md_dropdown, top_p, temperature = \
|
||||||
define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode)
|
define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, AVAIL_FONTS, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode)
|
||||||
|
|
||||||
# 浮动菜单定义
|
# 浮动菜单定义
|
||||||
from themes.gui_floating_menu import define_gui_floating_menu
|
from themes.gui_floating_menu import define_gui_floating_menu
|
||||||
@@ -228,9 +228,6 @@ def main():
|
|||||||
cancel_handles.append(submit_btn.click(**predict_args))
|
cancel_handles.append(submit_btn.click(**predict_args))
|
||||||
resetBtn.click(None, None, [chatbot, history, status], _js= """clear_conversation""") # 先在前端快速清除chatbot&status
|
resetBtn.click(None, None, [chatbot, history, status], _js= """clear_conversation""") # 先在前端快速清除chatbot&status
|
||||||
resetBtn2.click(None, None, [chatbot, history, status], _js="""clear_conversation""") # 先在前端快速清除chatbot&status
|
resetBtn2.click(None, None, [chatbot, history, status], _js="""clear_conversation""") # 先在前端快速清除chatbot&status
|
||||||
# reset_server_side_args = (lambda history: ([], [], "已重置"), [history], [chatbot, history, status])
|
|
||||||
# resetBtn.click(*reset_server_side_args) # 再在后端清除history
|
|
||||||
# resetBtn2.click(*reset_server_side_args) # 再在后端清除history
|
|
||||||
clearBtn.click(None, None, [txt, txt2], _js=js_code_clear)
|
clearBtn.click(None, None, [txt, txt2], _js=js_code_clear)
|
||||||
clearBtn2.click(None, None, [txt, txt2], _js=js_code_clear)
|
clearBtn2.click(None, None, [txt, txt2], _js=js_code_clear)
|
||||||
if AUTO_CLEAR_TXT:
|
if AUTO_CLEAR_TXT:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ scipdf_parser>=0.52
|
|||||||
spacy==3.7.4
|
spacy==3.7.4
|
||||||
anthropic>=0.18.1
|
anthropic>=0.18.1
|
||||||
python-markdown-math
|
python-markdown-math
|
||||||
pymdown-extensions
|
pymdown-extensions>=10.14
|
||||||
websocket-client
|
websocket-client
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
prompt_toolkit
|
prompt_toolkit
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import markdown
|
|||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
|
import html
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
@@ -384,6 +385,24 @@ def markdown_convertion(txt):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def code_block_title_replace_format(match):
|
||||||
|
lang = match.group(1)
|
||||||
|
filename = match.group(2)
|
||||||
|
return f"```{lang} {{title=\"{filename}\"}}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_backticks_indent(text):
|
||||||
|
# 从后向前查找最后一个 ```
|
||||||
|
lines = text.splitlines()
|
||||||
|
for line in reversed(lines):
|
||||||
|
if '```' in line:
|
||||||
|
# 计算前面的空格数量
|
||||||
|
indent = len(line) - len(line.lstrip())
|
||||||
|
return indent
|
||||||
|
return 0 # 如果没找到返回0
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=16) # 使用lru缓存
|
||||||
def close_up_code_segment_during_stream(gpt_reply):
|
def close_up_code_segment_during_stream(gpt_reply):
|
||||||
"""
|
"""
|
||||||
在gpt输出代码的中途(输出了前面的```,但还没输出完后面的```),补上后面的```
|
在gpt输出代码的中途(输出了前面的```,但还没输出完后面的```),补上后面的```
|
||||||
@@ -397,6 +416,12 @@ def close_up_code_segment_during_stream(gpt_reply):
|
|||||||
"""
|
"""
|
||||||
if "```" not in gpt_reply:
|
if "```" not in gpt_reply:
|
||||||
return gpt_reply
|
return gpt_reply
|
||||||
|
|
||||||
|
# replace [```python:warp.py] to [```python {title="warp.py"}]
|
||||||
|
pattern = re.compile(r"```([a-z]{1,12}):([^:\n]{1,35}\.([a-zA-Z^:\n]{1,3}))\n")
|
||||||
|
if pattern.search(gpt_reply):
|
||||||
|
gpt_reply = pattern.sub(code_block_title_replace_format, gpt_reply)
|
||||||
|
|
||||||
if gpt_reply.endswith("```"):
|
if gpt_reply.endswith("```"):
|
||||||
return gpt_reply
|
return gpt_reply
|
||||||
|
|
||||||
@@ -404,7 +429,11 @@ def close_up_code_segment_during_stream(gpt_reply):
|
|||||||
segments = gpt_reply.split("```")
|
segments = gpt_reply.split("```")
|
||||||
n_mark = len(segments) - 1
|
n_mark = len(segments) - 1
|
||||||
if n_mark % 2 == 1:
|
if n_mark % 2 == 1:
|
||||||
return gpt_reply + "\n```" # 输出代码片段中!
|
try:
|
||||||
|
num_padding = get_last_backticks_indent(gpt_reply)
|
||||||
|
except:
|
||||||
|
num_padding = 0
|
||||||
|
return gpt_reply + "\n" + " "*num_padding + "```" # 输出代码片段中!
|
||||||
else:
|
else:
|
||||||
return gpt_reply
|
return gpt_reply
|
||||||
|
|
||||||
@@ -421,6 +450,19 @@ def special_render_issues_for_mermaid(text):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def contain_html_tag(text):
|
||||||
|
"""
|
||||||
|
判断文本中是否包含HTML标签。
|
||||||
|
"""
|
||||||
|
pattern = r'</?([a-zA-Z0-9_]{3,16})>|<script\s+[^>]*src=["\']([^"\']+)["\'][^>]*>'
|
||||||
|
return re.search(pattern, text) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def contain_image(text):
|
||||||
|
pattern = r'<br/><br/><div align="center"><img src="file=(.*?)" base64="(.*?)"></div>'
|
||||||
|
return re.search(pattern, text) is not None
|
||||||
|
|
||||||
|
|
||||||
def compat_non_markdown_input(text):
|
def compat_non_markdown_input(text):
|
||||||
"""
|
"""
|
||||||
改善非markdown输入的显示效果,例如将空格转换为 ,将换行符转换为</br>等。
|
改善非markdown输入的显示效果,例如将空格转换为 ,将换行符转换为</br>等。
|
||||||
@@ -429,9 +471,13 @@ def compat_non_markdown_input(text):
|
|||||||
# careful input:markdown输入
|
# careful input:markdown输入
|
||||||
text = special_render_issues_for_mermaid(text) # 处理特殊的渲染问题
|
text = special_render_issues_for_mermaid(text) # 处理特殊的渲染问题
|
||||||
return text
|
return text
|
||||||
elif "</div>" in text:
|
elif ("<" in text) and (">" in text) and contain_html_tag(text):
|
||||||
# careful input:html输入
|
# careful input:html输入
|
||||||
return text
|
if contain_image(text):
|
||||||
|
return text
|
||||||
|
else:
|
||||||
|
escaped_text = html.escape(text)
|
||||||
|
return escaped_text
|
||||||
else:
|
else:
|
||||||
# whatever input:非markdown输入
|
# whatever input:非markdown输入
|
||||||
lines = text.split("\n")
|
lines = text.split("\n")
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Replace 'Tex/' with the actual directory path where your files are located befor
|
|||||||
md = """
|
md = """
|
||||||
Following code including wrapper
|
Following code including wrapper
|
||||||
|
|
||||||
```mermaid
|
```python:wrapper.py
|
||||||
graph TD
|
graph TD
|
||||||
A[Enter Chart Definition] --> B(Preview)
|
A[Enter Chart Definition] --> B(Preview)
|
||||||
B --> C{decide}
|
B --> C{decide}
|
||||||
@@ -41,6 +41,33 @@ Any folded content here. It requires an empty line just above it.
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
md ="""
|
||||||
|
|
||||||
|
在这种场景中,您希望机器 B 能够通过轮询机制来间接地“请求”机器 A,而实际上机器 A 只能主动向机器 B 发出请求。这是一种典型的客户端-服务器轮询模式。下面是如何实现这种机制的详细步骤:
|
||||||
|
|
||||||
|
### 机器 B 的实现
|
||||||
|
|
||||||
|
1. **安装 FastAPI 和必要的依赖库**:
|
||||||
|
```bash
|
||||||
|
pip install fastapi uvicorn
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **创建 FastAPI 服务**:
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from uuid import uuid4
|
||||||
|
from threading import Lock
|
||||||
|
import time
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# 字典用于存储请求和状态
|
||||||
|
requests = {}
|
||||||
|
process_lock = Lock()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def validate_path():
|
def validate_path():
|
||||||
import os, sys
|
import os, sys
|
||||||
@@ -53,10 +80,12 @@ def validate_path():
|
|||||||
|
|
||||||
validate_path() # validate path so you can run from base directory
|
validate_path() # validate path so you can run from base directory
|
||||||
from toolbox import markdown_convertion
|
from toolbox import markdown_convertion
|
||||||
from shared_utils.advanced_markdown_format import markdown_convertion_for_file
|
# from shared_utils.advanced_markdown_format import markdown_convertion_for_file
|
||||||
|
from shared_utils.advanced_markdown_format import close_up_code_segment_during_stream
|
||||||
# with open("gpt_log/default_user/shared/2024-04-22-01-27-43.zip.extract/translated_markdown.md", "r", encoding="utf-8") as f:
|
# with open("gpt_log/default_user/shared/2024-04-22-01-27-43.zip.extract/translated_markdown.md", "r", encoding="utf-8") as f:
|
||||||
# md = f.read()
|
# md = f.read()
|
||||||
html = markdown_convertion_for_file(md)
|
md = close_up_code_segment_during_stream(md)
|
||||||
|
html = markdown_convertion(md)
|
||||||
# print(html)
|
# print(html)
|
||||||
with open("test.html", "w", encoding="utf-8") as f:
|
with open("test.html", "w", encoding="utf-8") as f:
|
||||||
f.write(html)
|
f.write(html)
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
|
:root {
|
||||||
|
--gpt-academic-message-font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
font-size: var(--gpt-academic-message-font-size) !important;
|
||||||
|
}
|
||||||
|
|
||||||
#plugin_arg_menu {
|
#plugin_arg_menu {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
border: dashed;
|
border: dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* hide remove all button */
|
/* hide remove all button */
|
||||||
.remove-all.svelte-aqlk7e.svelte-aqlk7e.svelte-aqlk7e {
|
.remove-all.svelte-aqlk7e.svelte-aqlk7e.svelte-aqlk7e {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
@@ -25,7 +32,6 @@
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* height of the upload box */
|
/* height of the upload box */
|
||||||
.wrap.svelte-xwlu1w {
|
.wrap.svelte-xwlu1w {
|
||||||
min-height: var(--size-32);
|
min-height: var(--size-32);
|
||||||
@@ -97,13 +103,9 @@
|
|||||||
min-width: min(80px, 100%);
|
min-width: min(80px, 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cbs,
|
||||||
#cbs {
|
|
||||||
background-color: var(--block-background-fill) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cbsc {
|
#cbsc {
|
||||||
background-color: var(--block-background-fill) !important;
|
background-color: rgba(var(--block-background-fill), 0.5) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#interact-panel .form {
|
#interact-panel .form {
|
||||||
@@ -207,6 +209,7 @@
|
|||||||
.welcome-content {
|
.welcome-content {
|
||||||
text-wrap: balance;
|
text-wrap: balance;
|
||||||
height: 55px;
|
height: 55px;
|
||||||
|
font-size: 13px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -276,3 +279,35 @@
|
|||||||
box-shadow: 10px 10px 15px rgba(0, 0, 0, 0.5);
|
box-shadow: 10px 10px 15px rgba(0, 0, 0, 0.5);
|
||||||
left: 10px;
|
left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tooltip .hidden {
|
||||||
|
/* display: none; */
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip .visible {
|
||||||
|
/* display: block; */
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elem_fontsize,
|
||||||
|
#elem_top_p,
|
||||||
|
#elem_temperature,
|
||||||
|
#elem_max_length_sl,
|
||||||
|
#elem_prompt {
|
||||||
|
/* 左右为0;顶部为0,底部为2px */
|
||||||
|
padding: 0 0 4px 0;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
background-color: rgba(var(--block-background-fill), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#tooltip #cbs,
|
||||||
|
#tooltip #cbsc,
|
||||||
|
#tooltip .svelte-b6y5bg,
|
||||||
|
#tooltip .tabitem {
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
background-color: rgba(var(--block-background-fill), 0.5);
|
||||||
|
}
|
||||||
|
|||||||
@@ -750,10 +750,24 @@ function minor_ui_adjustment() {
|
|||||||
var bar_btn_width = [];
|
var bar_btn_width = [];
|
||||||
// 自动隐藏超出范围的toolbar按钮
|
// 自动隐藏超出范围的toolbar按钮
|
||||||
function auto_hide_toolbar() {
|
function auto_hide_toolbar() {
|
||||||
var qq = document.getElementById('tooltip');
|
// if chatbot hit upper page boarder, hide all
|
||||||
var tab_nav = qq.getElementsByClassName('tab-nav');
|
const elem_chatbot = document.getElementById('gpt-chatbot');
|
||||||
|
const chatbot_top = elem_chatbot.getBoundingClientRect().top;
|
||||||
|
var tooltip = document.getElementById('tooltip');
|
||||||
|
var tab_nav = tooltip.getElementsByClassName('tab-nav')[0];
|
||||||
|
|
||||||
|
// 20 px 大概是一个字的高度
|
||||||
|
if (chatbot_top < 20) {
|
||||||
|
// tab_nav.style.display = 'none';
|
||||||
|
if (tab_nav.classList.contains('visible')) {tab_nav.classList.remove('visible');}
|
||||||
|
if (!tab_nav.classList.contains('hidden')) {tab_nav.classList.add('hidden');}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tab_nav.classList.contains('hidden')) {tab_nav.classList.remove('hidden');}
|
||||||
|
if (!tab_nav.classList.contains('visible')) {tab_nav.classList.add('visible');}
|
||||||
|
// tab_nav.style.display = '';
|
||||||
if (tab_nav.length == 0) { return; }
|
if (tab_nav.length == 0) { return; }
|
||||||
var btn_list = tab_nav[0].getElementsByTagName('button')
|
var btn_list = tab_nav.getElementsByTagName('button')
|
||||||
if (btn_list.length == 0) { return; }
|
if (btn_list.length == 0) { return; }
|
||||||
// 获取页面宽度
|
// 获取页面宽度
|
||||||
var page_width = document.documentElement.clientWidth;
|
var page_width = document.documentElement.clientWidth;
|
||||||
@@ -1034,7 +1048,7 @@ async function save_conversation_history() {
|
|||||||
return timeB - timeA;
|
return timeB - timeA;
|
||||||
});
|
});
|
||||||
|
|
||||||
const max_chat_preserve = 4;
|
const max_chat_preserve = 10;
|
||||||
|
|
||||||
if (conversation_history.length >= max_chat_preserve + 1) {
|
if (conversation_history.length >= max_chat_preserve + 1) {
|
||||||
toast_push('对话时间线记录已满,正在移除最早的对话记录。您也可以点击左侧的记录点进行手动清理。', 3000);
|
toast_push('对话时间线记录已满,正在移除最早的对话记录。您也可以点击左侧的记录点进行手动清理。', 3000);
|
||||||
|
|||||||
@@ -567,7 +567,6 @@ ul:not(.options) {
|
|||||||
border-radius: var(--radius-xl) !important;
|
border-radius: var(--radius-xl) !important;
|
||||||
border: none;
|
border: none;
|
||||||
padding: var(--spacing-xl) !important;
|
padding: var(--spacing-xl) !important;
|
||||||
font-size: 15px !important;
|
|
||||||
line-height: var(--line-md) !important;
|
line-height: var(--line-md) !important;
|
||||||
min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
|
min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
|
||||||
min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
|
min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ theme_dir = os.path.dirname(__file__)
|
|||||||
def adjust_theme():
|
def adjust_theme():
|
||||||
try:
|
try:
|
||||||
set_theme = gr.themes.Soft(
|
set_theme = gr.themes.Soft(
|
||||||
|
font=[
|
||||||
|
"Helvetica",
|
||||||
|
"Microsoft YaHei",
|
||||||
|
"ui-sans-serif",
|
||||||
|
"sans-serif",
|
||||||
|
"system-ui",
|
||||||
|
],
|
||||||
|
font_mono=["ui-monospace", "Consolas", "monospace"],
|
||||||
primary_hue=gr.themes.Color(
|
primary_hue=gr.themes.Color(
|
||||||
c50="#EBFAF2",
|
c50="#EBFAF2",
|
||||||
c100="#CFF3E1",
|
c100="#CFF3E1",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import gradio as gr
|
import gradio as gr
|
||||||
|
from toolbox import get_conf
|
||||||
|
|
||||||
def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode):
|
def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, AVAIL_FONTS, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode):
|
||||||
with gr.Floating(init_x="0%", init_y="0%", visible=True, width=None, drag="forbidden", elem_id="tooltip"):
|
with gr.Floating(init_x="0%", init_y="0%", visible=True, width=None, drag="forbidden", elem_id="tooltip"):
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
with gr.Tab("上传文件", elem_id="interact-panel"):
|
with gr.Tab("上传文件", elem_id="interact-panel"):
|
||||||
@@ -9,12 +10,12 @@ def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAI
|
|||||||
|
|
||||||
with gr.Tab("更换模型", elem_id="interact-panel"):
|
with gr.Tab("更换模型", elem_id="interact-panel"):
|
||||||
md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, elem_id="elem_model_sel", label="更换LLM模型/请求源").style(container=False)
|
md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, elem_id="elem_model_sel", label="更换LLM模型/请求源").style(container=False)
|
||||||
top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)",)
|
top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)", elem_id="elem_top_p")
|
||||||
temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature", elem_id="elem_temperature")
|
temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature", elem_id="elem_temperature")
|
||||||
max_length_sl = gr.Slider(minimum=256, maximum=1024*32, value=4096, step=128, interactive=True, label="Local LLM MaxLength",)
|
max_length_sl = gr.Slider(minimum=256, maximum=1024*32, value=4096, step=128, interactive=True, label="Local LLM MaxLength", elem_id="elem_max_length_sl")
|
||||||
system_prompt = gr.Textbox(show_label=True, lines=2, placeholder=f"System Prompt", label="System prompt", value=INIT_SYS_PROMPT, elem_id="elem_prompt")
|
system_prompt = gr.Textbox(show_label=True, lines=2, placeholder=f"System Prompt", label="System prompt", value=INIT_SYS_PROMPT, elem_id="elem_prompt")
|
||||||
temperature.change(None, inputs=[temperature], outputs=None,
|
temperature.change(None, inputs=[temperature], outputs=None,
|
||||||
_js="""(temperature)=>gpt_academic_gradio_saveload("save", "elem_prompt", "js_temperature_cookie", temperature)""")
|
_js="""(temperature)=>gpt_academic_gradio_saveload("save", "elem_temperature", "js_temperature_cookie", temperature)""")
|
||||||
system_prompt.change(None, inputs=[system_prompt], outputs=None,
|
system_prompt.change(None, inputs=[system_prompt], outputs=None,
|
||||||
_js="""(system_prompt)=>gpt_academic_gradio_saveload("save", "elem_prompt", "js_system_prompt_cookie", system_prompt)""")
|
_js="""(system_prompt)=>gpt_academic_gradio_saveload("save", "elem_prompt", "js_system_prompt_cookie", system_prompt)""")
|
||||||
md_dropdown.change(None, inputs=[md_dropdown], outputs=None,
|
md_dropdown.change(None, inputs=[md_dropdown], outputs=None,
|
||||||
@@ -22,6 +23,8 @@ def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAI
|
|||||||
|
|
||||||
with gr.Tab("界面外观", elem_id="interact-panel"):
|
with gr.Tab("界面外观", elem_id="interact-panel"):
|
||||||
theme_dropdown = gr.Dropdown(AVAIL_THEMES, value=THEME, label="更换UI主题").style(container=False)
|
theme_dropdown = gr.Dropdown(AVAIL_THEMES, value=THEME, label="更换UI主题").style(container=False)
|
||||||
|
fontfamily_dropdown = gr.Dropdown(AVAIL_FONTS, value=get_conf("FONT"), elem_id="elem_fontfamily", label="更换字体类型").style(container=False)
|
||||||
|
fontsize_slider = gr.Slider(minimum=5, maximum=25, value=15, step=1, interactive=True, label="字体大小(默认15)", elem_id="elem_fontsize")
|
||||||
checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "浮动输入区", "输入清除键", "插件参数区"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区", elem_id='cbs').style(container=False)
|
checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "浮动输入区", "输入清除键", "插件参数区"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区", elem_id='cbs').style(container=False)
|
||||||
opt = ["自定义菜单"]
|
opt = ["自定义菜单"]
|
||||||
value=[]
|
value=[]
|
||||||
@@ -31,7 +34,10 @@ def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAI
|
|||||||
dark_mode_btn.click(None, None, None, _js=js_code_for_toggle_darkmode)
|
dark_mode_btn.click(None, None, None, _js=js_code_for_toggle_darkmode)
|
||||||
open_new_tab = gr.Button("打开新对话", variant="secondary").style(size="sm")
|
open_new_tab = gr.Button("打开新对话", variant="secondary").style(size="sm")
|
||||||
open_new_tab.click(None, None, None, _js=f"""()=>duplicate_in_new_window()""")
|
open_new_tab.click(None, None, None, _js=f"""()=>duplicate_in_new_window()""")
|
||||||
|
fontfamily_dropdown.select(None, inputs=[fontfamily_dropdown], outputs=None,
|
||||||
|
_js="""(fontfamily)=>{gpt_academic_gradio_saveload("save", "elem_fontfamily", "js_fontfamily", fontfamily); gpt_academic_change_chatbot_font(fontfamily, null, null);}""")
|
||||||
|
fontsize_slider.change(None, inputs=[fontsize_slider], outputs=None,
|
||||||
|
_js="""(fontsize)=>{gpt_academic_gradio_saveload("save", "elem_fontsize", "js_fontsize", fontsize); gpt_academic_change_chatbot_font(null, fontsize, null);}""")
|
||||||
|
|
||||||
with gr.Tab("帮助", elem_id="interact-panel"):
|
with gr.Tab("帮助", elem_id="interact-panel"):
|
||||||
gr.Markdown(help_menu_description)
|
gr.Markdown(help_menu_description)
|
||||||
|
|||||||
133
themes/init.js
133
themes/init.js
@@ -5,6 +5,129 @@ function remove_legacy_cookie() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function processFontFamily(fontfamily) {
|
||||||
|
// 检查是否包含括号
|
||||||
|
if (fontfamily.includes('(')) {
|
||||||
|
// 分割字符串
|
||||||
|
const parts = fontfamily.split('(');
|
||||||
|
const fontNamePart = parts[1].split(')')[0].trim(); // 获取括号内的部分
|
||||||
|
|
||||||
|
// 检查是否包含 @
|
||||||
|
if (fontNamePart.includes('@')) {
|
||||||
|
const [fontName, fontUrl] = fontNamePart.split('@').map(part => part.trim());
|
||||||
|
return { fontName, fontUrl };
|
||||||
|
} else {
|
||||||
|
return { fontName: fontNamePart, fontUrl: null };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { fontName: fontfamily, fontUrl: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查字体是否存在
|
||||||
|
function checkFontAvailability(fontfamily) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// 设置两个不同的字体进行比较
|
||||||
|
const testText = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
context.font = `16px ${fontfamily}, sans-serif`;
|
||||||
|
const widthWithFont = context.measureText(testText).width;
|
||||||
|
|
||||||
|
context.font = '16px sans-serif';
|
||||||
|
const widthWithFallback = context.measureText(testText).width;
|
||||||
|
|
||||||
|
// 如果宽度相同,说明字体不存在
|
||||||
|
resolve(widthWithFont !== widthWithFallback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function checkFontAvailabilityV2(fontfamily) {
|
||||||
|
fontName = fontfamily;
|
||||||
|
console.log('Checking font availability:', fontName);
|
||||||
|
if ('queryLocalFonts' in window) {
|
||||||
|
try {
|
||||||
|
const fonts = await window.queryLocalFonts();
|
||||||
|
const fontExists = fonts.some(font => font.family === fontName);
|
||||||
|
console.log(`Local Font "${fontName}" exists:`, fontExists);
|
||||||
|
return fontExists;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error querying local fonts:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('queryLocalFonts is not supported in this browser.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 动态加载字体
|
||||||
|
function loadFont(fontfamily, fontUrl) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 使用 Google Fonts 或其他字体来源
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.href = fontUrl;
|
||||||
|
link.onload = () => {
|
||||||
|
toast_push(`字体 "${fontfamily}" 已成功加载`, 3000);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
link.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
document.head.appendChild(link);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function gpt_academic_change_chatbot_font(fontfamily, fontsize, fontcolor) {
|
||||||
|
const chatbot = document.querySelector('#gpt-chatbot');
|
||||||
|
// 检查元素是否存在
|
||||||
|
if (chatbot) {
|
||||||
|
if (fontfamily != null) {
|
||||||
|
// 更改字体
|
||||||
|
const result = processFontFamily(fontfamily);
|
||||||
|
if (result.fontName == "Theme-Default-Font") {
|
||||||
|
chatbot.style.fontFamily = result.fontName;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 检查字体是否存在
|
||||||
|
checkFontAvailability(result.fontName).then((isAvailable) => {
|
||||||
|
if (isAvailable) {
|
||||||
|
// 如果字体存在,直接应用
|
||||||
|
chatbot.style.fontFamily = result.fontName;
|
||||||
|
} else {
|
||||||
|
if (result.fontUrl == null) {
|
||||||
|
// toast_push('无法加载字体,本地字体不存在,且URL未提供', 3000);
|
||||||
|
// 直接把失效的字体放上去,让系统自动fallback
|
||||||
|
chatbot.style.fontFamily = result.fontName;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
toast_push('正在下载字体', 3000);
|
||||||
|
// 如果字体不存在,尝试加载字体
|
||||||
|
loadFont(result.fontName, result.fontUrl).then(() => {
|
||||||
|
chatbot.style.fontFamily = result.fontName;
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(`无法加载字体 "${result.fontName}":`, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
if (fontsize != null) {
|
||||||
|
// 修改字体大小
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
'--gpt-academic-message-font-size',
|
||||||
|
`${fontsize}px`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (fontcolor != null) {
|
||||||
|
// 更改字体颜色
|
||||||
|
chatbot.style.color = fontcolor;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('#gpt-chatbot is missing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
||||||
// 第一部分,布局初始化
|
// 第一部分,布局初始化
|
||||||
remove_legacy_cookie();
|
remove_legacy_cookie();
|
||||||
@@ -46,7 +169,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 自动朗读
|
// 自动朗读
|
||||||
if (tts != "DISABLE"){
|
if (tts != "DISABLE") {
|
||||||
enable_tts = true;
|
enable_tts = true;
|
||||||
if (getCookie("js_auto_read_cookie")) {
|
if (getCookie("js_auto_read_cookie")) {
|
||||||
auto_read_tts = getCookie("js_auto_read_cookie")
|
auto_read_tts = getCookie("js_auto_read_cookie")
|
||||||
@@ -56,7 +179,11 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 字体
|
||||||
|
gpt_academic_gradio_saveload("load", "elem_fontfamily", "js_fontfamily", null, "str");
|
||||||
|
gpt_academic_change_chatbot_font(getCookie("js_fontfamily"), null, null);
|
||||||
|
gpt_academic_gradio_saveload("load", "elem_fontsize", "js_fontsize", null, "str");
|
||||||
|
gpt_academic_change_chatbot_font(null, getCookie("js_fontsize"), null);
|
||||||
// SysPrompt 系统静默提示词
|
// SysPrompt 系统静默提示词
|
||||||
gpt_academic_gradio_saveload("load", "elem_prompt", "js_system_prompt_cookie", null, "str");
|
gpt_academic_gradio_saveload("load", "elem_prompt", "js_system_prompt_cookie", null, "str");
|
||||||
// Temperature 大模型温度参数
|
// Temperature 大模型温度参数
|
||||||
@@ -66,7 +193,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
|||||||
const cached_model = getCookie("js_md_dropdown_cookie");
|
const cached_model = getCookie("js_md_dropdown_cookie");
|
||||||
var model_sel = await get_gradio_component("elem_model_sel");
|
var model_sel = await get_gradio_component("elem_model_sel");
|
||||||
// determine whether the cached model is in the choices
|
// determine whether the cached model is in the choices
|
||||||
if (model_sel.props.choices.includes(cached_model)){
|
if (model_sel.props.choices.includes(cached_model)) {
|
||||||
// change dropdown
|
// change dropdown
|
||||||
gpt_academic_gradio_saveload("load", "elem_model_sel", "js_md_dropdown_cookie", null, "str");
|
gpt_academic_gradio_saveload("load", "elem_model_sel", "js_md_dropdown_cookie", null, "str");
|
||||||
// 连锁修改chatbot的label
|
// 连锁修改chatbot的label
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class WelcomeMessage {
|
|||||||
this.card_array = [];
|
this.card_array = [];
|
||||||
this.static_welcome_message_previous = [];
|
this.static_welcome_message_previous = [];
|
||||||
this.reflesh_time_interval = 15 * 1000;
|
this.reflesh_time_interval = 15 * 1000;
|
||||||
|
this.major_title = "欢迎使用GPT-Academic";
|
||||||
|
|
||||||
const reflesh_render_status = () => {
|
const reflesh_render_status = () => {
|
||||||
for (let index = 0; index < this.card_array.length; index++) {
|
for (let index = 0; index < this.card_array.length; index++) {
|
||||||
@@ -99,6 +99,8 @@ class WelcomeMessage {
|
|||||||
|
|
||||||
// call update when page size change, call this.update when page size change
|
// call update when page size change, call this.update when page size change
|
||||||
window.addEventListener('resize', this.update.bind(this));
|
window.addEventListener('resize', this.update.bind(this));
|
||||||
|
// add a loop to reflesh cards
|
||||||
|
this.startRefleshCards();
|
||||||
}
|
}
|
||||||
|
|
||||||
begin_render() {
|
begin_render() {
|
||||||
@@ -106,9 +108,12 @@ class WelcomeMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async startRefleshCards() {
|
async startRefleshCards() {
|
||||||
|
// sleep certain time
|
||||||
await new Promise(r => setTimeout(r, this.reflesh_time_interval));
|
await new Promise(r => setTimeout(r, this.reflesh_time_interval));
|
||||||
await this.reflesh_cards();
|
// checkout visible status
|
||||||
if (this.visible) {
|
if (this.visible) {
|
||||||
|
// if visible, then reflesh cards
|
||||||
|
await this.reflesh_cards();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.startRefleshCards.call(this);
|
this.startRefleshCards.call(this);
|
||||||
}, 1);
|
}, 1);
|
||||||
@@ -194,35 +199,38 @@ class WelcomeMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update() {
|
async update() {
|
||||||
// console.log('update')
|
// update the card visibility
|
||||||
const elem_chatbot = document.getElementById('gpt-chatbot');
|
const elem_chatbot = document.getElementById('gpt-chatbot');
|
||||||
const chatbot_top = elem_chatbot.getBoundingClientRect().top;
|
const chatbot_top = elem_chatbot.getBoundingClientRect().top;
|
||||||
const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0];
|
const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0];
|
||||||
|
|
||||||
|
// detect if welcome card overflow
|
||||||
let welcome_card_overflow = false;
|
let welcome_card_overflow = false;
|
||||||
if (welcome_card_container) {
|
if (welcome_card_container) {
|
||||||
const welcome_card_top = welcome_card_container.getBoundingClientRect().top;
|
const welcome_card_top = welcome_card_container.getBoundingClientRect().top;
|
||||||
if (welcome_card_top < chatbot_top) {
|
if (welcome_card_top < chatbot_top) {
|
||||||
welcome_card_overflow = true;
|
welcome_card_overflow = true;
|
||||||
// console.log("welcome_card_overflow");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var page_width = document.documentElement.clientWidth;
|
var page_width = document.documentElement.clientWidth;
|
||||||
const width_to_hide_welcome = 1200;
|
const width_to_hide_welcome = 1200;
|
||||||
if (!await this.isChatbotEmpty() || page_width < width_to_hide_welcome || welcome_card_overflow) {
|
if (!await this.isChatbotEmpty() || page_width < width_to_hide_welcome || welcome_card_overflow) {
|
||||||
|
// overflow !
|
||||||
if (this.visible) {
|
if (this.visible) {
|
||||||
console.log("remove welcome");
|
// console.log("remove welcome");
|
||||||
this.removeWelcome(); this.visible = false; // this two lines must always be together
|
this.removeWelcome();
|
||||||
this.card_array = [];
|
this.card_array = [];
|
||||||
this.static_welcome_message_previous = [];
|
this.static_welcome_message_previous = [];
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.visible) {
|
if (this.visible) {
|
||||||
|
// console.log("already visible");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("show welcome");
|
// not overflow, not yet shown, then create and display welcome card
|
||||||
this.showWelcome(); this.visible = true; // this two lines must always be together
|
// console.log("show welcome");
|
||||||
this.startRefleshCards();
|
this.showWelcome();
|
||||||
}
|
}
|
||||||
|
|
||||||
showCard(message) {
|
showCard(message) {
|
||||||
@@ -263,7 +271,7 @@ class WelcomeMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async showWelcome() {
|
async showWelcome() {
|
||||||
|
this.visible = true;
|
||||||
// 首先,找到想要添加子元素的父元素
|
// 首先,找到想要添加子元素的父元素
|
||||||
const elem_chatbot = document.getElementById('gpt-chatbot');
|
const elem_chatbot = document.getElementById('gpt-chatbot');
|
||||||
|
|
||||||
@@ -274,7 +282,7 @@ class WelcomeMessage {
|
|||||||
// 创建主标题
|
// 创建主标题
|
||||||
const major_title = document.createElement('div');
|
const major_title = document.createElement('div');
|
||||||
major_title.classList.add('welcome-title');
|
major_title.classList.add('welcome-title');
|
||||||
major_title.textContent = "欢迎使用GPT-Academic";
|
major_title.textContent = this.major_title;
|
||||||
welcome_card_container.appendChild(major_title)
|
welcome_card_container.appendChild(major_title)
|
||||||
|
|
||||||
// 创建卡片
|
// 创建卡片
|
||||||
@@ -297,16 +305,17 @@ class WelcomeMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeWelcome() {
|
async removeWelcome() {
|
||||||
|
this.visible = false;
|
||||||
// remove welcome-card-container
|
// remove welcome-card-container
|
||||||
const elem_chatbot = document.getElementById('gpt-chatbot');
|
const elem_chatbot = document.getElementById('gpt-chatbot');
|
||||||
const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0];
|
const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0];
|
||||||
// 添加隐藏动画
|
// begin hide animation
|
||||||
welcome_card_container.classList.add('hide');
|
welcome_card_container.classList.add('hide');
|
||||||
// 等待动画结束后再移除元素
|
|
||||||
welcome_card_container.addEventListener('transitionend', () => {
|
welcome_card_container.addEventListener('transitionend', () => {
|
||||||
elem_chatbot.removeChild(welcome_card_container);
|
elem_chatbot.removeChild(welcome_card_container);
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
const timeout = 600; // 与CSS中transition的时间保持一致(1s)
|
// add a fail safe timeout
|
||||||
|
const timeout = 600; // 与 CSS 中 transition 的时间保持一致(1s)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (welcome_card_container.parentNode) {
|
if (welcome_card_container.parentNode) {
|
||||||
elem_chatbot.removeChild(welcome_card_container);
|
elem_chatbot.removeChild(welcome_card_container);
|
||||||
|
|||||||
4
version
4
version
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": 3.91,
|
"version": 3.92,
|
||||||
"show_feature": true,
|
"show_feature": true,
|
||||||
"new_feature": "优化前端并修复TTS的BUG <-> 添加时间线回溯功能 <-> 支持chatgpt-4o-latest <-> 增加RAG组件 <-> 升级多合一主提交键"
|
"new_feature": "字体和字体大小自定义 <-> 优化前端并修复TTS的BUG <-> 添加时间线回溯功能 <-> 支持chatgpt-4o-latest <-> 增加RAG组件 <-> 升级多合一主提交键"
|
||||||
}
|
}
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户