feat: customized font & font size
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
> [!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/drive/folders/14kR-3V-lIbvGxri4AHc8TpiA1fqsw7SK?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)
|
||||||
|
|||||||
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."
|
||||||
|
|||||||
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:
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -207,6 +214,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;
|
||||||
}
|
}
|
||||||
@@ -275,4 +283,13 @@
|
|||||||
.tooltip.svelte-p2nen8.svelte-p2nen8 {
|
.tooltip.svelte-p2nen8.svelte-p2nen8 {
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elem_fontsize,
|
||||||
|
#elem_top_p,
|
||||||
|
#elem_temperature,
|
||||||
|
#elem_max_length_sl,
|
||||||
|
#elem_prompt {
|
||||||
|
/* 左右为0;顶部为0,底部为2px */
|
||||||
|
padding: 0 0 4px 0;
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
137
themes/init.js
137
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();
|
||||||
@@ -13,7 +136,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
|||||||
ButtonWithDropdown_init();
|
ButtonWithDropdown_init();
|
||||||
update_conversation_metadata();
|
update_conversation_metadata();
|
||||||
window.addEventListener("gptac_restore_chat_from_local_storage", restore_chat_from_local_storage);
|
window.addEventListener("gptac_restore_chat_from_local_storage", restore_chat_from_local_storage);
|
||||||
|
|
||||||
// 加载欢迎页面
|
// 加载欢迎页面
|
||||||
const welcomeMessage = new WelcomeMessage();
|
const welcomeMessage = new WelcomeMessage();
|
||||||
welcomeMessage.begin_render();
|
welcomeMessage.begin_render();
|
||||||
@@ -23,7 +146,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
|
|||||||
welcomeMessage.update();
|
welcomeMessage.update();
|
||||||
});
|
});
|
||||||
chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
|
chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
|
||||||
|
|
||||||
if (layout === "LEFT-RIGHT") { chatbotAutoHeight(); }
|
if (layout === "LEFT-RIGHT") { chatbotAutoHeight(); }
|
||||||
if (layout === "LEFT-RIGHT") { limit_scroll_position(); }
|
if (layout === "LEFT-RIGHT") { limit_scroll_position(); }
|
||||||
|
|
||||||
@@ -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,37 @@ 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 +270,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 +281,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 +304,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组件 <-> 升级多合一主提交键"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user