Keldos commited on
Commit
9186425
1 Parent(s): 5429d8b

BREAKING (refactor): 完全重构整理css和js

Browse files

- 统一CSS class 与 id 变量使用 - 连接符命名
- 完全重写引入 js 与 css 方法,参考sd-webUI
- 分离 js 和 css 到不同文件

fix in merging

ChuanhuChatbot.py CHANGED
@@ -12,6 +12,7 @@ from modules.config import *
12
  from modules.utils import *
13
  from modules.presets import *
14
  from modules.overwrites import *
 
15
  from modules.models.models import get_model
16
 
17
  logging.getLogger("httpx").setLevel(logging.WARNING)
@@ -19,13 +20,13 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
19
  gr.Chatbot._postprocess_chat_messages = postprocess_chat_messages
20
  gr.Chatbot.postprocess = postprocess
21
 
22
- with open("assets/custom.css", "r", encoding="utf-8") as f:
23
- customCSS = f.read()
24
 
25
  def create_new_model():
26
  return get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
27
 
28
- with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
29
  user_name = gr.State("")
30
  promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
31
  user_question = gr.State("")
@@ -36,10 +37,10 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
36
  topic = gr.State(i18n("未命名对话历史记录"))
37
 
38
  with gr.Row():
39
- gr.HTML(CHUANHU_TITLE, elem_id="app_title")
40
- status_display = gr.Markdown(get_geoip(), elem_id="status_display")
41
- with gr.Row(elem_id="float_display"):
42
- user_info = gr.Markdown(value="getting user info...", elem_id="user_info")
43
  update_info = gr.HTML(get_html("update.html").format(
44
  current_version=repo_html(),
45
  version_time=version_time(),
@@ -52,20 +53,20 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
52
  with gr.Row(equal_height=True):
53
  with gr.Column(scale=5):
54
  with gr.Row():
55
- chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu_chatbot", latex_delimiters=latex_delimiters_set, height=700)
56
  with gr.Row():
57
  with gr.Column(min_width=225, scale=12):
58
  user_input = gr.Textbox(
59
- elem_id="user_input_tb",
60
  show_label=False, placeholder=i18n("在这里输入"),
61
  container=False
62
  )
63
  with gr.Column(min_width=42, scale=1):
64
- submitBtn = gr.Button(value="", variant="primary", elem_id="submit_btn")
65
- cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="cancel_btn")
66
  with gr.Row():
67
  emptyBtn = gr.Button(
68
- i18n("🧹 新的对话"), elem_id="empty_btn"
69
  )
70
  retryBtn = gr.Button(i18n("🔄 重新生成"))
71
  delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
@@ -88,9 +89,9 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
88
  label="API-Key",
89
  )
90
  if multi_api_key:
91
- usageTxt = gr.Markdown(i18n("多账号模式已开启,无需输入key,可直接开始对话"), elem_id="usage_display", elem_classes="insert_block", visible=show_api_billing)
92
  else:
93
- usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="usage_display", elem_classes="insert_block", visible=show_api_billing)
94
  model_select_dropdown = gr.Dropdown(
95
  label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
96
  )
@@ -98,8 +99,8 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
98
  label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
99
  )
100
  with gr.Row():
101
- single_turn_checkbox = gr.Checkbox(label=i18n("单��对话"), value=False, elem_classes="switch_checkbox")
102
- use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False, elem_classes="switch_checkbox")
103
  language_select_dropdown = gr.Dropdown(
104
  label=i18n("选择回复语言(针对搜索&索引功能)"),
105
  choices=REPLY_LANGUAGES,
@@ -179,12 +180,12 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
179
  downloadFile = gr.File(interactive=True)
180
 
181
  with gr.Tab(label=i18n("高级")):
182
- gr.HTML(get_html("appearance_switcher.html").format(label=i18n("切换亮暗色主题")), elem_classes="insert_block")
183
  use_streaming_checkbox = gr.Checkbox(
184
- label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION, elem_classes="switch_checkbox"
185
  )
186
  checkUpdateBtn = gr.Button(i18n("🔄 检查更新..."), visible=check_update)
187
- gr.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️"), elem_id="advanced_warning")
188
  with gr.Accordion(i18n("参数"), open=False):
189
  temperature_slider = gr.Slider(
190
  minimum=-0,
@@ -265,7 +266,7 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
265
  )
266
 
267
  with gr.Accordion(i18n("网络参数"), open=False):
268
- gr.Markdown(i18n("---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置"), elem_id="netsetting_warning")
269
  default_btn = gr.Button(i18n("🔙 恢复默认网络设置"))
270
  # 网络代理
271
  proxyTxt = gr.Textbox(
@@ -276,7 +277,7 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
276
  lines=1,
277
  interactive=False,
278
  container=False,
279
- elem_classes="view_only_textbox",
280
  )
281
  # changeProxyBtn = gr.Button(i18n("🔄 设置代理地址"))
282
 
@@ -289,11 +290,10 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
289
  lines=1,
290
  interactive=False,
291
  # container=False,
292
- elem_classes="view_only_textbox no-container",
293
  )
294
  # changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
295
-
296
- updateChuanhuBtn = gr.Button(visible=False, elem_classes="invisible_btn", elem_id="update_chuanhu_btn")
297
 
298
  gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
299
  gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
@@ -519,6 +519,6 @@ if __name__ == "__main__":
519
  server_port=server_port,
520
  share=share,
521
  auth=auth_from_conf if authflag else None,
522
- favicon_path="./assets/favicon.ico",
523
  inbrowser=not dockerflag, # 禁止在docker下开启inbrowser
524
  )
 
12
  from modules.utils import *
13
  from modules.presets import *
14
  from modules.overwrites import *
15
+ from modules.webui import *
16
  from modules.models.models import get_model
17
 
18
  logging.getLogger("httpx").setLevel(logging.WARNING)
 
20
  gr.Chatbot._postprocess_chat_messages = postprocess_chat_messages
21
  gr.Chatbot.postprocess = postprocess
22
 
23
+ # with open("web_assets/css/ChuanhuChat.css", "r", encoding="utf-8") as f:
24
+ # ChuanhuChatCSS = f.read()
25
 
26
  def create_new_model():
27
  return get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
28
 
29
+ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
30
  user_name = gr.State("")
31
  promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
32
  user_question = gr.State("")
 
37
  topic = gr.State(i18n("未命名对话历史记录"))
38
 
39
  with gr.Row():
40
+ gr.HTML(CHUANHU_TITLE, elem_id="app-title")
41
+ status_display = gr.Markdown(get_geoip(), elem_id="status-display")
42
+ with gr.Row(elem_id="float-display"):
43
+ user_info = gr.Markdown(value="getting user info...", elem_id="user-info")
44
  update_info = gr.HTML(get_html("update.html").format(
45
  current_version=repo_html(),
46
  version_time=version_time(),
 
53
  with gr.Row(equal_height=True):
54
  with gr.Column(scale=5):
55
  with gr.Row():
56
+ chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu-chatbot", latex_delimiters=latex_delimiters_set, height=700)
57
  with gr.Row():
58
  with gr.Column(min_width=225, scale=12):
59
  user_input = gr.Textbox(
60
+ elem_id="user-input-tb",
61
  show_label=False, placeholder=i18n("在这里输入"),
62
  container=False
63
  )
64
  with gr.Column(min_width=42, scale=1):
65
+ submitBtn = gr.Button(value="", variant="primary", elem_id="submit-btn")
66
+ cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="cancel-btn")
67
  with gr.Row():
68
  emptyBtn = gr.Button(
69
+ i18n("🧹 新的对话"), elem_id="empty-btn"
70
  )
71
  retryBtn = gr.Button(i18n("🔄 重新生成"))
72
  delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
 
89
  label="API-Key",
90
  )
91
  if multi_api_key:
92
+ usageTxt = gr.Markdown(i18n("多账号模式已开启,无需输入key,可直接开始对话"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
93
  else:
94
+ usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
95
  model_select_dropdown = gr.Dropdown(
96
  label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
97
  )
 
99
  label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
100
  )
101
  with gr.Row():
102
+ single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False, elem_classes="switch-checkbox")
103
+ use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False, elem_classes="switch-checkbox")
104
  language_select_dropdown = gr.Dropdown(
105
  label=i18n("选择回复语言(针对搜索&索引功能)"),
106
  choices=REPLY_LANGUAGES,
 
180
  downloadFile = gr.File(interactive=True)
181
 
182
  with gr.Tab(label=i18n("高级")):
183
+ gr.HTML(get_html("appearance_switcher.html").format(label=i18n("切换亮暗色主题")), elem_classes="insert-block")
184
  use_streaming_checkbox = gr.Checkbox(
185
+ label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION, elem_classes="switch-checkbox"
186
  )
187
  checkUpdateBtn = gr.Button(i18n("🔄 检查更新..."), visible=check_update)
188
+ gr.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️"), elem_id="advanced-warning")
189
  with gr.Accordion(i18n("参数"), open=False):
190
  temperature_slider = gr.Slider(
191
  minimum=-0,
 
266
  )
267
 
268
  with gr.Accordion(i18n("网络参数"), open=False):
269
+ gr.Markdown(i18n("---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置"), elem_id="netsetting-warning")
270
  default_btn = gr.Button(i18n("🔙 恢复默认网络设置"))
271
  # 网络代理
272
  proxyTxt = gr.Textbox(
 
277
  lines=1,
278
  interactive=False,
279
  container=False,
280
+ elem_classes="view-only-textbox",
281
  )
282
  # changeProxyBtn = gr.Button(i18n("🔄 设置代理地址"))
283
 
 
290
  lines=1,
291
  interactive=False,
292
  # container=False,
293
+ elem_classes="view-only-textbox no-container",
294
  )
295
  # changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
296
+ updateChuanhuBtn = gr.Button(visible=False, elem_classes="invisible-btn", elem_id="update-chuanhu-btn")
 
297
 
298
  gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
299
  gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
 
519
  server_port=server_port,
520
  share=share,
521
  auth=auth_from_conf if authflag else None,
522
+ favicon_path="./web_assets/favicon.ico",
523
  inbrowser=not dockerflag, # 禁止在docker下开启inbrowser
524
  )
assets/custom.css DELETED
@@ -1,699 +0,0 @@
1
- :root {
2
- --chatbot-color-light: #000000;
3
- --chatbot-color-dark: #FFFFFF;
4
- --chatbot-background-color-light: #F3F3F3;
5
- --chatbot-background-color-dark: #121111;
6
- --message-user-background-color-light: #95EC69;
7
- --message-user-background-color-dark: #26B561;
8
- --message-bot-background-color-light: #FFFFFF;
9
- --message-bot-background-color-dark: #2C2C2C;
10
- --switch-checkbox-color-light: #e5e7eb;
11
- --switch-checkbox-color-dark: #515151;
12
- }
13
-
14
-
15
- #app_title {
16
- font-weight: var(--prose-header-text-weight);
17
- font-size: var(--text-xxl);
18
- line-height: 1.3;
19
- text-align: left;
20
- margin-top: 6px;
21
- white-space: nowrap;
22
- }
23
- #description {
24
- text-align: center;
25
- margin: 32px 0 4px 0;
26
- }
27
-
28
- /* 解决container=False时的错误填充 */
29
- div.form {
30
- background: none !important;
31
- }
32
-
33
- /* 高级页面 */
34
- #advanced_warning {
35
- display: flex;
36
- flex-wrap: wrap;
37
- flex-direction: column;
38
- align-content: center;
39
- }
40
-
41
- #netsetting_warning hr {
42
- margin-bottom: 1em
43
- }
44
-
45
- .view_only_textbox textarea {
46
- -webkit-text-fill-color: #b2b2b2 !important
47
- }
48
-
49
- /* gradio的页脚信息 */
50
- footer {
51
- /* display: none !important; */
52
- margin-top: .2em !important;
53
- font-size: 85%;
54
- }
55
- #footer {
56
- text-align: center;
57
- }
58
- #footer div {
59
- display: inline-block;
60
- }
61
- #footer .versions{
62
- font-size: 85%;
63
- opacity: 0.60;
64
- }
65
-
66
- #float_display {
67
- position: absolute;
68
- max-height: 30px;
69
- }
70
- #toast-update {
71
- position: absolute;
72
- display: flex;
73
- top: -500px;
74
- width: 100%;
75
- justify-content: center;
76
- z-index: var(--layer-top);
77
- transition: top 0.3s ease-out;
78
- }
79
- #check-chuanhu-update {
80
- position: absolute;
81
- align-items: center;
82
- display: flex;
83
- flex-direction: column;
84
- justify-content: center;
85
- margin: var(--size-6) var(--size-4);
86
- box-shadow: var(--shadow-drop-lg);
87
- border: 1px solid var(--block-label-border-color);
88
- border-radius: var(--container-radius);
89
- background: var(--background-fill-primary);
90
- padding: var(--size-4) var(--size-6);
91
- min-width: 360px;
92
- max-width: 480px;
93
- overflow: hidden;
94
- pointer-events: auto;
95
- }
96
- #version-info-title {
97
- font-size: 1.2em;
98
- font-weight: bold;
99
- text-align: start;
100
- width: 100%;
101
- }
102
- #release-note-wrap {
103
- width: 100%;
104
- max-width: 400px;
105
- height: 120px;
106
- border: solid 1px var(--border-color-primary);
107
- overflow: auto;
108
- padding: 0 8px;
109
- }
110
- #release-note-wrap.hideK {
111
- display: none;
112
- }
113
- .btn-update-group {
114
- display: flex;
115
- justify-content: space-evenly;
116
- align-items: center;
117
- width: 100%;
118
- padding-top: 10px;
119
- }
120
- .btn-update-group.hideK {
121
- display: none;
122
- }
123
- span.hideK {
124
- display: none;
125
- }
126
- #updating-info.hideK {
127
- display: none;
128
- }
129
- #updating-info {
130
- margin: 16px 0px 24px;
131
- text-align: start;
132
- width: 100%;
133
- }
134
-
135
- /* user_info */
136
- #user_info.block {
137
- white-space: nowrap;
138
- position: absolute; left: 8em; top: .8em;
139
- z-index: var(--layer-2);
140
- box-shadow: var(--block-shadow);
141
- border: none!important; border-radius: var(--block-label-radius);
142
- background: var(--color-accent);
143
- padding: var(--block-label-padding);
144
- font-size: var(--block-label-text-size); line-height: var(--line-sm);
145
- width: auto; max-height: 30px!important;
146
- opacity: 1;
147
- transition: opacity 0.3s ease-in-out;
148
- }
149
- #user_info.block .wrap {
150
- opacity: 0;
151
- }
152
- #user_info p {
153
- color: white;
154
- font-weight: var(--block-label-text-weight);
155
- }
156
- #user_info.hideK {
157
- opacity: 0;
158
- transition: opacity 1s ease-in-out;
159
- }
160
-
161
- div.no-container {
162
- padding: 10px 0 0 0 !important;
163
- }
164
-
165
- /* status_display */
166
- #status_display {
167
- display: flex;
168
- min-height: 2em;
169
- align-items: flex-end;
170
- justify-content: flex-end;
171
- }
172
- #status_display p {
173
- font-size: .85em;
174
- font-family: ui-monospace, "SF Mono", "SFMono-Regular", "Menlo", "Consolas", "Liberation Mono", "Microsoft Yahei UI", "Microsoft Yahei", monospace;
175
- /* Windows下中文的monospace会fallback为新宋体,实在太丑,这里折中使用微软雅黑 */
176
- color: var(--body-text-color-subdued);
177
- }
178
-
179
- #status_display {
180
- transition: all 0.6s;
181
- }
182
- #chuanhu_chatbot {
183
- transition: height 0.3s ease;
184
- }
185
-
186
- /* usage_display */
187
- .insert_block {
188
- position: relative;
189
- margin: 0;
190
- padding: 8px 12px;
191
- box-shadow: var(--block-shadow);
192
- border-width: var(--block-border-width);
193
- border-color: var(--block-border-color);
194
- border-radius: var(--block-radius);
195
- background: var(--block-background-fill);
196
- width: 100%;
197
- line-height: var(--line-sm);
198
- min-height: 2em;
199
- }
200
- #usage_display p, #usage_display span {
201
- margin: 0;
202
- font-size: .85em;
203
- color: var(--body-text-color-subdued);
204
- }
205
- .progress-bar {
206
- background-color: var(--input-background-fill);;
207
- margin: .5em 0 !important;
208
- height: 20px;
209
- border-radius: 10px;
210
- overflow: hidden;
211
- }
212
- .progress {
213
- background-color: var(--block-title-background-fill);
214
- height: 100%;
215
- border-radius: 10px;
216
- text-align: right;
217
- transition: width 0.5s ease-in-out;
218
- }
219
- .progress-text {
220
- /* color: white; */
221
- color: var(--color-accent) !important;
222
- font-size: 1em !important;
223
- font-weight: bold;
224
- padding-right: 10px;
225
- line-height: 20px;
226
- }
227
-
228
- /* 亮暗色模式切换 */
229
- #apSwitch input[type="checkbox"] {
230
- margin: 0 !important;
231
- }
232
- #apSwitch label.apSwitch {
233
- display: flex;
234
- align-items: center;
235
- cursor: pointer;
236
- color: var(--body-text-color);
237
- font-weight: var(--checkbox-label-text-weight);
238
- font-size: var(--checkbox-label-text-size);
239
- line-height: var(--line-md);
240
- margin: 2px 0 !important;
241
- }
242
- input[type="checkbox"]#apSwitch_checkbox::before {
243
- background: none !important;
244
- content: '🌞';
245
- border: none !important;
246
- box-shadow: none !important;
247
- font-size: 22px;
248
- top: -4.4px;
249
- left: -1px;
250
- }
251
- input:checked[type="checkbox"]#apSwitch_checkbox::before {
252
- content: '🌚';
253
- left: 16px;
254
- }
255
-
256
- /* .apSwitch {
257
- top: 2px;
258
- display: inline-block;
259
- height: 22px;
260
- position: relative;
261
- width: 40px;
262
- border-radius: 11px;
263
- box-shadow: inset 0 0 1px 0 rgba(0,0,0,0.05), inset 0 0 2px 0 rgba(0,0,0,0.08) !important;
264
- }
265
- .apSwitch input {
266
- display: none !important;
267
- }
268
- .apSlider {
269
- background-color: var(--neutral-200);
270
- bottom: 0;
271
- cursor: pointer;
272
- left: 0;
273
- position: absolute;
274
- right: 0;
275
- top: 0;
276
- transition: .4s;
277
- font-size: 22px;
278
- border-radius: 11px;
279
- }
280
- .apSlider::before {
281
- transform: scale(0.9);
282
- position: absolute;
283
- transition: .4s;
284
- content: "🌞";
285
- }
286
- input:checked + .apSlider {
287
- background-color: var(--primary-600);
288
- }
289
- input:checked + .apSlider::before {
290
- transform: translateX(18px);
291
- content:"🌚";
292
- } */
293
-
294
- .switch_checkbox label {
295
- flex-direction: row-reverse;
296
- justify-content: space-between;
297
- }
298
- .switch_checkbox input[type="checkbox"] + span {
299
- margin-left: 0 !important;
300
- }
301
-
302
- .switch_checkbox input[type="checkbox"] {
303
- -moz-appearance: none;
304
- appearance: none;
305
- -webkit-appearance: none;
306
- outline: none;
307
- }
308
-
309
- .switch_checkbox input[type="checkbox"] {
310
- display: inline-block !important;
311
- position: relative !important;
312
- border: none !important;
313
- outline: none;
314
- width: 40px !important;
315
- height: 22px !important;
316
- border-radius: 11px !important;
317
- background-image: none !important;
318
- box-shadow: inset 0 0 1px 0 rgba(0,0,0,0.05), inset 0 0 2px 0 rgba(0,0,0,0.08) !important;
319
- background-image: none !important;
320
- background-color: var(--switch-checkbox-color-light) !important;
321
- transition: .2s ease background-color;
322
- }
323
- .dark .switch_checkbox input[type="checkbox"] {
324
- background-color: var(--switch-checkbox-color-dark) !important;
325
- }
326
- .switch_checkbox input[type="checkbox"]::before {
327
- content: "";
328
- position: absolute;
329
- width: 22px;
330
- height: 22px;
331
- top: 0;
332
- left: 0;
333
- background: #FFFFFF;
334
- border: 0.5px solid rgba(0,0,0,0.02);
335
- box-shadow: 0 0 0 0 rgba(0,0,0,0.15), 0 1px 0 0 rgba(0,0,0,0.05);
336
- transform: scale(0.9);
337
- border-radius: 11px !important;
338
- transition: .4s ease all;
339
- box-shadow: var(--input-shadow);
340
- }
341
- .switch_checkbox input:checked[type="checkbox"] {
342
- background-color: var(--primary-600) !important;
343
- }
344
- .switch_checkbox input:checked[type="checkbox"]::before {
345
- background-color: #fff;
346
- left: 18px;
347
- }
348
-
349
- /* Override Slider Styles (for webkit browsers like Safari and Chrome)
350
- * 好希望这份提案能早日实现 https://github.com/w3c/csswg-drafts/issues/4410
351
- * 进度滑块在各个平台还是太不统一了
352
- */
353
- input[type="range"] {
354
- -webkit-appearance: none;
355
- height: 4px;
356
- background: var(--input-background-fill);
357
- border-radius: 5px;
358
- background-image: linear-gradient(var(--primary-500),var(--primary-500));
359
- background-size: 0% 100%;
360
- background-repeat: no-repeat;
361
- }
362
- input[type="range"]::-webkit-slider-thumb {
363
- -webkit-appearance: none;
364
- height: 20px;
365
- width: 20px;
366
- border-radius: 50%;
367
- border: solid 0.5px #ddd;
368
- background-color: white;
369
- cursor: ew-resize;
370
- box-shadow: var(--input-shadow);
371
- transition: background-color .1s ease;
372
- }
373
- input[type="range"]::-webkit-slider-thumb:hover {
374
- background: var(--neutral-50);
375
- }
376
- input[type=range]::-webkit-slider-runnable-track {
377
- -webkit-appearance: none;
378
- box-shadow: none;
379
- border: none;
380
- background: transparent;
381
- }
382
-
383
- hr.append-display {
384
- margin: 8px 0;
385
- border: none;
386
- height: 1px;
387
- border-top-width: 0;
388
- background-image: linear-gradient(to right, rgba(50,50,50, 0.1), rgba(150, 150, 150, 0.8), rgba(50,50,50, 0.1));
389
- }
390
- .source-a {
391
- font-size: 0.8em;
392
- max-width: 100%;
393
- margin: 0;
394
- display: flex;
395
- flex-direction: row;
396
- flex-wrap: wrap;
397
- align-items: center;
398
- /* background-color: #dddddd88; */
399
- border-radius: 1.5rem;
400
- padding: 0.2em;
401
- }
402
- .source-a a {
403
- display: inline-block;
404
- background-color: #aaaaaa50;
405
- border-radius: 1rem;
406
- padding: 0.5em;
407
- text-align: center;
408
- text-overflow: ellipsis;
409
- overflow: hidden;
410
- min-width: 20%;
411
- white-space: nowrap;
412
- margin: 0.2rem 0.1rem;
413
- text-decoration: none !important;
414
- flex: 1;
415
- transition: flex 0.5s;
416
- }
417
- .source-a a:hover {
418
- background-color: #aaaaaa20;
419
- flex: 2;
420
- }
421
-
422
- #submit_btn, #cancel_btn {
423
- height: 40px !important;
424
- }
425
- #submit_btn::before {
426
- content: url("data:image/svg+xml, %3Csvg width='21px' height='20px' viewBox='0 0 21 20' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg id='page' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E %3Cg id='send' transform='translate(0.435849, 0.088463)' fill='%23FFFFFF' fill-rule='nonzero'%3E %3Cpath d='M0.579148261,0.0428666046 C0.301105539,-0.0961547561 -0.036517765,0.122307382 0.0032026237,0.420210298 L1.4927172,18.1553639 C1.5125774,18.4334066 1.79062012,18.5922882 2.04880264,18.4929872 L8.24518329,15.8913017 L11.6412765,19.7441794 C11.8597387,19.9825018 12.2370824,19.8832008 12.3165231,19.5852979 L13.9450591,13.4882182 L19.7839562,11.0255541 C20.0619989,10.8865327 20.0818591,10.4694687 19.7839562,10.3105871 L0.579148261,0.0428666046 Z M11.6138902,17.0883151 L9.85385903,14.7195502 L0.718169621,0.618812241 L12.69945,12.9346347 L11.6138902,17.0883151 Z' id='shape'%3E%3C/path%3E %3C/g%3E %3C/g%3E %3C/svg%3E");
427
- height: 21px;
428
- }
429
- #cancel_btn::before {
430
- content: url("data:image/svg+xml,%3Csvg width='21px' height='21px' viewBox='0 0 21 21' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg id='pg' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E %3Cpath d='M10.2072007,20.088463 C11.5727865,20.088463 12.8594566,19.8259823 14.067211,19.3010209 C15.2749653,18.7760595 16.3386126,18.0538087 17.2581528,17.1342685 C18.177693,16.2147282 18.8982283,15.1527965 19.4197586,13.9484733 C19.9412889,12.7441501 20.202054,11.4557644 20.202054,10.0833163 C20.202054,8.71773046 19.9395733,7.43106036 19.4146119,6.22330603 C18.8896505,5.01555169 18.1673997,3.95018885 17.2478595,3.0272175 C16.3283192,2.10424615 15.2646719,1.3837109 14.0569176,0.865611739 C12.8491633,0.34751258 11.5624932,0.088463 10.1969073,0.088463 C8.83132146,0.088463 7.54636692,0.34751258 6.34204371,0.865611739 C5.1377205,1.3837109 4.07407321,2.10424615 3.15110186,3.0272175 C2.22813051,3.95018885 1.5058797,5.01555169 0.984349419,6.22330603 C0.46281914,7.43106036 0.202054,8.71773046 0.202054,10.0833163 C0.202054,11.4557644 0.4645347,12.7441501 0.9894961,13.9484733 C1.5144575,15.1527965 2.23670831,16.2147282 3.15624854,17.1342685 C4.07578877,18.0538087 5.1377205,18.7760595 6.34204371,19.3010209 C7.54636692,19.8259823 8.83475258,20.088463 10.2072007,20.088463 Z M10.2072007,18.2562448 C9.07493099,18.2562448 8.01471483,18.0452309 7.0265522,17.6232031 C6.03838956,17.2011753 5.17031614,16.6161693 4.42233192,15.8681851 C3.6743477,15.1202009 3.09105726,14.2521274 2.67246059,13.2639648 C2.25386392,12.2758022 2.04456558,11.215586 2.04456558,10.0833163 C2.04456558,8.95104663 2.25386392,7.89083047 2.67246059,6.90266784 C3.09105726,5.9145052 3.6743477,5.04643178 4.42233192,4.29844756 C5.17031614,3.55046334 6.036674,2.9671729 7.02140552,2.54857623 C8.00613703,2.12997956 9.06463763,1.92068122 10.1969073,1.92068122 C11.329177,1.92068122 12.3911087,2.12997956 13.3827025,2.54857623 C14.3742962,2.9671729 15.2440852,3.55046334 15.9920694,4.29844756 C16.7400537,5.04643178 17.3233441,5.9145052 17.7419408,6.90266784 C18.1605374,7.89083047 18.3698358,8.95104663 18.3698358,10.0833163 C18.3698358,11.215586 18.1605374,12.2758022 17.7419408,13.2639648 C17.3233441,14.2521274 16.7400537,15.1202009 15.9920694,15.8681851 C15.2440852,16.6161693 14.3760118,17.2011753 13.3878492,17.6232031 C12.3996865,18.0452309 11.3394704,18.2562448 10.2072007,18.2562448 Z M7.65444721,13.6242324 L12.7496608,13.6242324 C13.0584616,13.6242324 13.3003556,13.5384544 13.4753427,13.3668984 C13.6503299,13.1953424 13.7378234,12.9585951 13.7378234,12.6566565 L13.7378234,7.49968276 C13.7378234,7.19774418 13.6503299,6.96099688 13.4753427,6.78944087 C13.3003556,6.61788486 13.0584616,6.53210685 12.7496608,6.53210685 L7.65444721,6.53210685 C7.33878414,6.53210685 7.09345904,6.61788486 6.91847191,6.78944087 C6.74348478,6.96099688 6.65599121,7.19774418 6.65599121,7.49968276 L6.65599121,12.6566565 C6.65599121,12.9585951 6.74348478,13.1953424 6.91847191,13.3668984 C7.09345904,13.5384544 7.33878414,13.6242324 7.65444721,13.6242324 Z' id='shape' fill='%23FF3B30' fill-rule='nonzero'%3E%3C/path%3E %3C/g%3E %3C/svg%3E");
431
- height: 21px;
432
- }
433
- /* list */
434
- ol:not(.options), ul:not(.options) {
435
- padding-inline-start: 2em !important;
436
- }
437
-
438
- /* 亮色(默认) */
439
- #chuanhu_chatbot {
440
- background-color: var(--chatbot-background-color-light) !important;
441
- color: var(--chatbot-color-light) !important;
442
- }
443
- [data-testid = "bot"] {
444
- background-color: var(--message-bot-background-color-light) !important;
445
- }
446
- [data-testid = "user"] {
447
- background-color: var(--message-user-background-color-light) !important;
448
- }
449
- /* 暗色 */
450
- .dark #chuanhu_chatbot {
451
- background-color: var(--chatbot-background-color-dark) !important;
452
- color: var(--chatbot-color-dark) !important;
453
- }
454
- .dark [data-testid = "bot"] {
455
- background-color: var(--message-bot-background-color-dark) !important;
456
- }
457
- .dark [data-testid = "user"] {
458
- background-color: var(--message-user-background-color-dark) !important;
459
- }
460
-
461
- /* 屏幕宽度大于等于500px的设备 */
462
- /* update on 2023.4.8: 高度的细致调整已写入JavaScript */
463
- @media screen and (min-width: 500px) {
464
- #chuanhu_chatbot {
465
- height: calc(100vh - 200px);
466
- }
467
- #chuanhu_chatbot>.wrapper>.wrap {
468
- max-height: calc(100vh - 200px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
469
- }
470
- }
471
- /* 屏幕宽度小于500px的设备 */
472
- @media screen and (max-width: 499px) {
473
- #chuanhu_chatbot {
474
- height: calc(100vh - 140px);
475
- }
476
- #chuanhu_chatbot>.wrapper>.wrap {
477
- max-height: calc(100vh - 140px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
478
- }
479
- [data-testid = "bot"] {
480
- max-width: 95% !important;
481
- }
482
- #app_title h1{
483
- letter-spacing: -1px; font-size: 22px;
484
- }
485
- }
486
- #chuanhu_chatbot>.wrapper>.wrap {
487
- overflow-x: hidden;
488
- }
489
- /* 对话气泡 */
490
- .message {
491
- border-radius: var(--radius-xl) !important;
492
- border: none;
493
- padding: var(--spacing-xl) !important;
494
- font-size: var(--text-md) !important;
495
- line-height: var(--line-md) !important;
496
- min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
497
- min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
498
- }
499
- [data-testid = "bot"] {
500
- max-width: 85%;
501
- border-bottom-left-radius: 0 !important;
502
- }
503
- [data-testid = "user"] {
504
- max-width: 85%;
505
- width: auto !important;
506
- border-bottom-right-radius: 0 !important;
507
- }
508
-
509
- .message.user p {
510
- white-space: pre-wrap;
511
- }
512
- .message .user-message {
513
- display: block;
514
- padding: 0 !important;
515
- white-space: pre-wrap;
516
- }
517
-
518
- .message .md-message p {
519
- margin-top: 0.6em !important;
520
- margin-bottom: 0.6em !important;
521
- }
522
- .message .md-message p:first-child { margin-top: 0 !important; }
523
- .message .md-message p:last-of-type { margin-bottom: 0 !important; }
524
-
525
- .message .md-message {
526
- display: block;
527
- padding: 0 !important;
528
- }
529
- .message .raw-message p {
530
- margin:0 !important;
531
- }
532
- .message .raw-message {
533
- display: block;
534
- padding: 0 !important;
535
- white-space: pre-wrap;
536
- }
537
- .message .hideM {
538
- display: none;
539
- }
540
-
541
- .message div.icon-button > button[title="copy"] {
542
- display: none;
543
- }
544
-
545
- /* 川虎助理 */
546
- .agent-prefix {
547
- font-size: smaller;
548
- opacity: 0.6;
549
- padding: 6px 0 4px;
550
- }
551
- .agent-prefix::before {
552
- content: '🐯';
553
- filter: grayscale();
554
- padding: 0 4px;
555
- }
556
-
557
- /* custom buttons */
558
- .chuanhu-btn {
559
- border-radius: 5px;
560
- /* background-color: #E6E6E6 !important; */
561
- color: rgba(120, 120, 120, 0.64) !important;
562
- padding: 4px !important;
563
- position: absolute;
564
- right: -22px;
565
- cursor: pointer !important;
566
- transition: color .2s ease, background-color .2s ease;
567
- }
568
- .chuanhu-btn:hover {
569
- background-color: rgba(167, 167, 167, 0.25) !important;
570
- color: unset !important;
571
- }
572
- .chuanhu-btn:active {
573
- background-color: rgba(167, 167, 167, 0.5) !important;
574
- }
575
- .chuanhu-btn:focus {
576
- outline: none;
577
- }
578
- .copy-bot-btn {
579
- /* top: 18px; */
580
- bottom: 0;
581
- }
582
- .toggle-md-btn {
583
- /* top: 0; */
584
- bottom: 20px;
585
- }
586
- .copy-code-btn {
587
- position: relative;
588
- float: right;
589
- font-size: 1em;
590
- cursor: pointer;
591
- }
592
-
593
- .message-wrap>div img{
594
- border-radius: 10px !important;
595
- }
596
-
597
- /* history message */
598
- .wrapper>.wrap>.history-message {
599
- padding: 10px !important;
600
- }
601
- .history-message {
602
- /* padding: 0 !important; */
603
- opacity: 80%;
604
- display: flex;
605
- flex-direction: column;
606
- }
607
- .history-message>.history-message {
608
- padding: 0 !important;
609
- }
610
- .history-message>.message-wrap {
611
- padding: 0 !important;
612
- margin-bottom: 16px;
613
- }
614
- .history-message>.message {
615
- margin-bottom: 16px;
616
- }
617
- .wrapper>.wrap>.history-message::after {
618
- content: "";
619
- display: block;
620
- height: 2px;
621
- background-color: var(--body-text-color-subdued);
622
- margin-bottom: 10px;
623
- margin-top: -10px;
624
- clear: both;
625
- }
626
- .wrapper>.wrap>.history-message>:last-child::after {
627
- content: "仅供查看";
628
- display: block;
629
- text-align: center;
630
- color: var(--body-text-color-subdued);
631
- font-size: 0.8em;
632
- }
633
-
634
- /* 表格 */
635
- table {
636
- margin: 1em 0;
637
- border-collapse: collapse;
638
- empty-cells: show;
639
- }
640
- td,th {
641
- border: 1.2px solid var(--border-color-primary) !important;
642
- padding: 0.2em;
643
- }
644
- thead {
645
- background-color: rgba(175,184,193,0.2);
646
- }
647
- thead th {
648
- padding: .5em .2em;
649
- }
650
- /* 行内代码 */
651
- .message :not(pre) code {
652
- display: inline;
653
- white-space: break-spaces;
654
- font-family: var(--font-mono);
655
- border-radius: 6px;
656
- margin: 0 2px 0 2px;
657
- padding: .2em .4em .1em .4em;
658
- background-color: rgba(175,184,193,0.2);
659
- }
660
- /* 代码块 */
661
- .message pre,
662
- .message pre[class*=language-] {
663
- color: #fff;
664
- overflow-x: auto;
665
- overflow-y: hidden;
666
- margin: .8em 1em 1em 0em !important;
667
- padding: var(--spacing-xl) 1.2em !important;
668
- border-radius: var(--radius-lg) !important;
669
- }
670
- .message pre code,
671
- .message pre code[class*=language-] {
672
- color: #fff;
673
- padding: 0;
674
- margin: 0;
675
- background-color: unset;
676
- text-shadow: none;
677
- font-family: var(--font-mono);
678
- }
679
- /* 覆盖 gradio 丑陋的复制按钮样式 */
680
- pre button[title="copy"] {
681
- border-radius: 5px;
682
- transition: background-color .2s ease;
683
- }
684
- pre button[title="copy"]:hover {
685
- background-color: #333232;
686
- }
687
- pre button .check {
688
- color: #fff !important;
689
- background: var(--neutral-950) !important;
690
- }
691
-
692
- /* 覆盖prism.css */
693
- .language-css .token.string,
694
- .style .token.string,
695
- .token.entity,
696
- .token.operator,
697
- .token.url {
698
- background: none !important;
699
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/html/appearance_switcher.html DELETED
@@ -1,6 +0,0 @@
1
- <div class="switch_checkbox" id="apSwitch">
2
- <label class="apSwitch">
3
- <input type="checkbox" id="apSwitch_checkbox" data-testid="checkbox" />
4
- <span class="apSwitch_span">{label}</span>
5
- </label>
6
- </div>
 
 
 
 
 
 
 
modules/overwrites.py CHANGED
@@ -70,24 +70,3 @@ def postprocess_chat_messages(
70
  return chat_message
71
  else:
72
  raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
73
-
74
- with open("./assets/custom.js", "r", encoding="utf-8") as f, \
75
- open("./assets/external-scripts.js", "r", encoding="utf-8") as f1:
76
- customJS = f.read()
77
- externalScripts = f1.read()
78
-
79
-
80
- def reload_javascript():
81
- print("Reloading javascript...")
82
- js = f'<script>{customJS}</script><script async>{externalScripts}</script>'
83
- js += '<script async src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>'
84
- js += '<script async type="module" src="http://spin.js.org/spin.umd.js"></script><link type="text/css" href="https://spin.js.org/spin.css" rel="stylesheet" />'
85
- def template_response(*args, **kwargs):
86
- res = GradioTemplateResponseOriginal(*args, **kwargs)
87
- res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
88
- res.init_headers()
89
- return res
90
-
91
- gr.routes.templates.TemplateResponse = template_response
92
-
93
- GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
 
70
  return chat_message
71
  else:
72
  raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/shared.py CHANGED
@@ -62,3 +62,4 @@ state = State()
62
 
63
  modules_path = os.path.dirname(os.path.realpath(__file__))
64
  chuanhu_path = os.path.dirname(modules_path)
 
 
62
 
63
  modules_path = os.path.dirname(os.path.realpath(__file__))
64
  chuanhu_path = os.path.dirname(modules_path)
65
+ assets_path = os.path.join(chuanhu_path, "web_assets")
modules/utils.py CHANGED
@@ -624,12 +624,6 @@ def update_chuanhu():
624
  status = '<span id="update-status" class="hideK">failure</span>'
625
  return gr.Markdown.update(value=i18n("更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)")+status)
626
 
627
- def get_html(filename):
628
- path = os.path.join(shared.chuanhu_path, "assets", "html", filename)
629
- if os.path.exists(path):
630
- with open(path, encoding="utf8") as file:
631
- return file.read()
632
- return ""
633
 
634
  def add_source_numbers(lst, source_name = "Source", use_source = True):
635
  if use_source:
 
624
  status = '<span id="update-status" class="hideK">failure</span>'
625
  return gr.Markdown.update(value=i18n("更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)")+status)
626
 
 
 
 
 
 
 
627
 
628
  def add_source_numbers(lst, source_name = "Source", use_source = True):
629
  if use_source:
modules/webui.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from collections import namedtuple
3
+ import os
4
+ import gradio as gr
5
+
6
+ from . import shared
7
+
8
+ # with open("./assets/ChuanhuChat.js", "r", encoding="utf-8") as f, \
9
+ # open("./assets/external-scripts.js", "r", encoding="utf-8") as f1:
10
+ # customJS = f.read()
11
+ # externalScripts = f1.read()
12
+
13
+
14
+ def get_html(filename):
15
+ path = os.path.join(shared.chuanhu_path, "web_assets", "html", filename)
16
+ if os.path.exists(path):
17
+ with open(path, encoding="utf8") as file:
18
+ return file.read()
19
+ return ""
20
+
21
+ def webpath(fn):
22
+ if fn.startswith(shared.assets_path):
23
+ web_path = os.path.relpath(fn, shared.chuanhu_path).replace('\\', '/')
24
+ else:
25
+ web_path = os.path.abspath(fn)
26
+ return f'file={web_path}?{os.path.getmtime(fn)}'
27
+
28
+ ScriptFile = namedtuple("ScriptFile", ["basedir", "filename", "path"])
29
+
30
+ def javascript_html():
31
+ head = ""
32
+ for script in list_scripts("javascript", ".js"):
33
+ head += f'<script type="text/javascript" src="{webpath(script.path)}"></script>\n'
34
+ for script in list_scripts("javascript", ".mjs"):
35
+ head += f'<script type="module" src="{webpath(script.path)}"></script>\n'
36
+ return head
37
+
38
+ def css_html():
39
+ head = ""
40
+ for cssfile in list_scripts("stylesheet", ".css"):
41
+ head += f'<link rel="stylesheet" property="stylesheet" href="{webpath(cssfile.path)}">'
42
+ return head
43
+
44
+ def list_scripts(scriptdirname, extension):
45
+ scripts_list = []
46
+ scripts_dir = os.path.join(shared.chuanhu_path, "web_assets", scriptdirname)
47
+ if os.path.exists(scripts_dir):
48
+ for filename in sorted(os.listdir(scripts_dir)):
49
+ scripts_list.append(ScriptFile(shared.assets_path, filename, os.path.join(scripts_dir, filename)))
50
+ scripts_list = [x for x in scripts_list if os.path.splitext(x.path)[1].lower() == extension and os.path.isfile(x.path)]
51
+ return scripts_list
52
+
53
+
54
+ def reload_javascript():
55
+ js = javascript_html()
56
+ js += '<script async type="module" src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>'
57
+ js += '<script async type="module" src="http://spin.js.org/spin.umd.js"></script><link type="text/css" href="https://spin.js.org/spin.css" rel="stylesheet" />'
58
+
59
+ css = css_html()
60
+
61
+ def template_response(*args, **kwargs):
62
+ res = GradioTemplateResponseOriginal(*args, **kwargs)
63
+ res.body = res.body.replace(b'</head>', f'{js}</head>'.encode("utf8"))
64
+ res.body = res.body.replace(b'</body>', f'{css}</body>'.encode("utf8"))
65
+ res.init_headers()
66
+ return res
67
+
68
+ gr.routes.templates.TemplateResponse = template_response
69
+
70
+ GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
{assets → web_assets}/favicon.ico RENAMED
File without changes
web_assets/html/appearance_switcher.html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <div class="switch-checkbox" id="apSwitch">
2
+ <label class="apSwitch">
3
+ <input type="checkbox" id="apSwitch-checkbox" data-testid="checkbox" />
4
+ <span class="apSwitch-span">{label}</span>
5
+ </label>
6
+ </div>
{assets → web_assets}/html/billing_info.html RENAMED
File without changes
{assets → web_assets}/html/footer.html RENAMED
File without changes
{assets → web_assets}/html/update.html RENAMED
@@ -22,8 +22,8 @@
22
  <button class="btn-update lg primary svelte-cmf5ev" id="update-button" onclick="bgUpdateChuanhu()">{update_btn}</button>
23
  </div>
24
  <div id="close-update-btn" class="btn-update-group hideK">
25
- <button class="btn-update lg secondary svelte-cmf5ev" id="update-button" onclick="getUpdateInfo()">{seenew_btn}</button>
26
- <button class="btn-update lg primary svelte-cmf5ev" id="cancel-button" onclick="cancelUpdate()">{ok_btn}</button>
27
  </div>
28
  </div>
29
  </div>
 
22
  <button class="btn-update lg primary svelte-cmf5ev" id="update-button" onclick="bgUpdateChuanhu()">{update_btn}</button>
23
  </div>
24
  <div id="close-update-btn" class="btn-update-group hideK">
25
+ <button class="btn-update lg secondary svelte-cmf5ev" id="seenew-button" onclick="getUpdateInfo()">{seenew_btn}</button>
26
+ <button class="btn-update lg primary svelte-cmf5ev" id="ok-button" onclick="cancelUpdate()">{ok_btn}</button>
27
  </div>
28
  </div>
29
  </div>
assets/custom.js → web_assets/javascript/ChuanhuChat.js RENAMED
@@ -1,5 +1,5 @@
1
 
2
- // custom javascript here
3
 
4
  const MAX_HISTORY_LENGTH = 32;
5
 
@@ -25,75 +25,10 @@ var sliders = null;
25
  var updateChuanhuBtn = null;
26
  var statusDisplay = null;
27
 
28
- var userLogged = false;
29
- var usernameGotten = false;
30
- var historyLoaded = false;
31
- var updateInfoGotten = false;
32
- var isLatestVersion = localStorage.getItem('isLatestVersion') || false;
33
 
34
- var ga = document.getElementsByTagName("gradio-app");
35
- var targetNode = ga[0];
36
  var isInIframe = (window.self !== window.top);
37
- var language = navigator.language.slice(0,2);
38
  var currentTime = new Date().getTime();
39
 
40
- // i18n
41
- const forView_i18n = {
42
- 'zh': "仅供查看",
43
- 'en': "For viewing only",
44
- 'ja': "閲覧専用",
45
- 'ko': "읽기 전용",
46
- 'fr': "Pour consultation seulement",
47
- 'es': "Solo para visualización",
48
- 'sv': "Endast för visning",
49
- };
50
-
51
- const deleteConfirm_i18n_pref = {
52
- 'zh': "你真的要删除 ",
53
- 'en': "Are you sure you want to delete ",
54
- 'ja': "本当に ",
55
- 'ko': "정말로 ",
56
- 'sv': "Är du säker på att du vill ta bort "
57
- };
58
- const deleteConfirm_i18n_suff = {
59
- 'zh': " 吗?",
60
- 'en': " ?",
61
- 'ja': " を削除してもよろしいですか?",
62
- 'ko': " 을(를) 삭제하시겠습니까?",
63
- 'sv': " ?"
64
- };
65
- var deleteConfirm_msg_pref = "Are you sure you want to delete ";
66
- var deleteConfirm_msg_suff = " ?";
67
-
68
- const usingLatest_i18n = {
69
- 'zh': "您使用的就是最新版!",
70
- 'en': "You are using the latest version!",
71
- 'ja': "最新バージョンを使用しています!",
72
- 'ko': "최신 버전을 사용하고 있습니다!",
73
- 'sv': "Du använder den senaste versionen!"
74
- };
75
-
76
- const updatingMsg_i18n = {
77
- 'zh': "正在尝试更新...",
78
- 'en': "Trying to update...",
79
- 'ja': "更新を試みています...",
80
- 'ko': "업데이트를 시도 중...",
81
- 'sv': "Försöker uppdatera..."
82
- }
83
- const updateSuccess_i18n = {
84
- 'zh': "更新成功,请重启本程序。",
85
- 'en': "Updated successfully, please restart this program.",
86
- 'ja': "更新が成功しました、このプログラムを再起動してください。",
87
- 'ko': "업데이트 성공, 이 프로그램을 재시작 해주세요.",
88
- 'sv': "Uppdaterat framgångsrikt, starta om programmet."
89
- }
90
- const updateFailure_i18n = {
91
- 'zh': '更新失败,请尝试<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手动更新</a>。',
92
- 'en': 'Update failed, please try <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">manually updating</a>.',
93
- 'ja': '更新に失敗しました、<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手動での更新</a>をお試しください。',
94
- 'ko': '업데이트 실패, <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">수동 업데이트</a>를 시도하십시오.',
95
- 'sv': 'Uppdateringen misslyckades, prova att <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">uppdatera manuellt</a>.'
96
- }
97
 
98
  // gradio 页面加载好了么??? 我能动你的元素了么??
99
  function gradioLoaded(mutations) {
@@ -101,18 +36,18 @@ function gradioLoaded(mutations) {
101
  if (mutations[i].addedNodes.length) {
102
  loginUserForm = document.querySelector(".gradio-container > .main > .wrap > .panel > .form")
103
  gradioContainer = document.querySelector(".gradio-container");
104
- user_input_tb = document.getElementById('user_input_tb');
105
- userInfoDiv = document.getElementById("user_info");
106
- appTitleDiv = document.getElementById("app_title");
107
- chatbot = document.querySelector('#chuanhu_chatbot');
108
- chatbotWrap = document.querySelector('#chuanhu_chatbot > .wrapper > .wrap');
109
  apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
110
  updateToast = document.querySelector("#toast-update");
111
- sendBtn = document.getElementById("submit_btn");
112
- cancelBtn = document.getElementById("cancel_btn");
113
  sliders = document.querySelectorAll('input[type="range"]');
114
- updateChuanhuBtn = document.getElementById("update_chuanhu_btn");
115
- statusDisplay = document.querySelector('#status_display');
116
 
117
  if (loginUserForm) {
118
  localStorage.setItem("userLogged", true);
@@ -161,23 +96,9 @@ function gradioLoaded(mutations) {
161
  }
162
  }
163
 
164
- function webLocale() {
165
- // console.log("webLocale", language);
166
- if (forView_i18n.hasOwnProperty(language)) {
167
- var forView = forView_i18n[language];
168
- var forViewStyle = document.createElement('style');
169
- forViewStyle.innerHTML = '.wrapper>.wrap>.history-message>:last-child::after { content: "' + forView + '"!important; }';
170
- document.head.appendChild(forViewStyle);
171
- }
172
- if (deleteConfirm_i18n_pref.hasOwnProperty(language)) {
173
- deleteConfirm_msg_pref = deleteConfirm_i18n_pref[language];
174
- deleteConfirm_msg_suff = deleteConfirm_i18n_suff[language];
175
- }
176
- }
177
-
178
  function showConfirmationDialog(a, file, c) {
179
  if (file != "") {
180
- var result = confirm(deleteConfirm_msg_pref + file + deleteConfirm_msg_suff);
181
  if (result) {
182
  return [a, file, c];
183
  }
@@ -241,118 +162,24 @@ function disableSendBtn() {
241
  });
242
  }
243
 
244
- var username = null;
245
- function getUserInfo() {
246
- if (usernameGotten) {
247
- return;
248
- }
249
- userLogged = localStorage.getItem('userLogged');
250
- if (userLogged) {
251
- username = userInfoDiv.innerText;
252
- if (username) {
253
- if (username.includes("getting user info…")) {
254
- setTimeout(getUserInfo, 500);
255
- return;
256
- } else if (username === " ") {
257
- localStorage.removeItem("username");
258
- localStorage.removeItem("userLogged")
259
- userLogged = false;
260
- usernameGotten = true;
261
- return;
262
- } else {
263
- username = username.match(/User:\s*(.*)/)[1] || username;
264
- localStorage.setItem("username", username);
265
- usernameGotten = true;
266
- clearHistoryHtml();
267
- }
268
- }
269
- }
270
- }
271
-
272
- function toggleUserInfoVisibility(shouldHide) {
273
- if (userInfoDiv) {
274
- if (shouldHide) {
275
- userInfoDiv.classList.add("hideK");
276
  } else {
277
- userInfoDiv.classList.remove("hideK");
 
278
  }
279
  }
280
- }
281
- function showOrHideUserInfo() {
282
- // Bind mouse/touch events to show/hide user info
283
- appTitleDiv.addEventListener("mouseenter", function () {
284
- toggleUserInfoVisibility(false);
285
- });
286
- userInfoDiv.addEventListener("mouseenter", function () {
287
- toggleUserInfoVisibility(false);
288
- });
289
- sendBtn.addEventListener("mouseenter", function () {
290
- toggleUserInfoVisibility(false);
291
- });
292
-
293
- appTitleDiv.addEventListener("mouseleave", function () {
294
- toggleUserInfoVisibility(true);
295
- });
296
- userInfoDiv.addEventListener("mouseleave", function () {
297
- toggleUserInfoVisibility(true);
298
- });
299
- sendBtn.addEventListener("mouseleave", function () {
300
- toggleUserInfoVisibility(true);
301
- });
302
-
303
- appTitleDiv.ontouchstart = function () {
304
- toggleUserInfoVisibility(false);
305
- };
306
- userInfoDiv.ontouchstart = function () {
307
- toggleUserInfoVisibility(false);
308
- };
309
- sendBtn.ontouchstart = function () {
310
- toggleUserInfoVisibility(false);
311
- };
312
-
313
- appTitleDiv.ontouchend = function () {
314
- setTimeout(function () {
315
- toggleUserInfoVisibility(true);
316
- }, 3000);
317
- };
318
- userInfoDiv.ontouchend = function () {
319
- setTimeout(function () {
320
- toggleUserInfoVisibility(true);
321
- }, 3000);
322
- };
323
- sendBtn.ontouchend = function () {
324
- setTimeout(function () {
325
- toggleUserInfoVisibility(true);
326
- }, 3000); // Delay 1 second to hide user info
327
- };
328
-
329
- // Hide user info after 2 second
330
- setTimeout(function () {
331
- toggleUserInfoVisibility(true);
332
- }, 2000);
333
- }
334
-
335
- function toggleDarkMode(isEnabled) {
336
- if (isEnabled) {
337
- document.body.classList.add("dark");
338
- document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
339
- } else {
340
- document.body.classList.remove("dark");
341
- document.body.style.backgroundColor = "";
342
- }
343
- }
344
- function adjustDarkMode() {
345
  const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
346
-
347
- // 根据当前颜色模式设置初始状态
348
  apSwitch.checked = darkModeQuery.matches;
349
  toggleDarkMode(darkModeQuery.matches);
350
- // 监听颜色模式变化
351
  darkModeQuery.addEventListener("change", (e) => {
352
  apSwitch.checked = e.matches;
353
  toggleDarkMode(e.matches);
354
  });
355
- // apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
356
  apSwitch.addEventListener("change", (e) => {
357
  toggleDarkMode(e.target.checked);
358
  });
@@ -360,7 +187,7 @@ function adjustDarkMode() {
360
 
361
  function setChatbotHeight() {
362
  const screenWidth = window.innerWidth;
363
- const statusDisplay = document.querySelector('#status_display');
364
  const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
365
  const vh = window.innerHeight * 0.01;
366
  document.documentElement.style.setProperty('--vh', `${vh}px`);
@@ -384,385 +211,27 @@ function setChatbotScroll() {
384
  var scrollHeight = chatbotWrap.scrollHeight;
385
  chatbotWrap.scrollTo(0,scrollHeight)
386
  }
387
- var rangeInputs = null;
388
- var numberInputs = null;
389
- function setSlider() {
390
- rangeInputs = document.querySelectorAll('input[type="range"]');
391
- numberInputs = document.querySelectorAll('input[type="number"]')
392
- setSliderRange();
393
- rangeInputs.forEach(rangeInput => {
394
- rangeInput.addEventListener('input', setSliderRange);
395
- });
396
- numberInputs.forEach(numberInput => {
397
- numberInput.addEventListener('input', setSliderRange);
398
- })
399
- }
400
- function setSliderRange() {
401
- var range = document.querySelectorAll('input[type="range"]');
402
- range.forEach(range => {
403
- range.style.backgroundSize = (range.value - range.min) / (range.max - range.min) * 100 + '% 100%';
404
- });
405
- }
406
-
407
- function addChuanhuButton(botElement) {
408
- var rawMessage = botElement.querySelector('.raw-message');
409
- var mdMessage = botElement.querySelector('.md-message');
410
- // var gradioCopyMsgBtn = botElement.querySelector('div.icon-button>button[title="copy"]'); // 获取 gradio 的 copy button,它可以读取真正的原始 message
411
- if (!rawMessage) {
412
- var buttons = botElement.querySelectorAll('button.chuanhu-btn');
413
- for (var i = 0; i < buttons.length; i++) {
414
- buttons[i].parentNode.removeChild(buttons[i]);
415
- }
416
- return;
417
- }
418
- var oldCopyButton = null;
419
- var oldToggleButton = null;
420
- oldCopyButton = botElement.querySelector('button.copy-bot-btn');
421
- oldToggleButton = botElement.querySelector('button.toggle-md-btn');
422
- if (oldCopyButton) oldCopyButton.remove();
423
- if (oldToggleButton) oldToggleButton.remove();
424
-
425
- // Copy bot button
426
- var copyButton = document.createElement('button');
427
- copyButton.classList.add('chuanhu-btn');
428
- copyButton.classList.add('copy-bot-btn');
429
- copyButton.setAttribute('aria-label', 'Copy');
430
- copyButton.innerHTML = copyIcon;
431
-
432
- copyButton.addEventListener('click', async () => {
433
- const textToCopy = rawMessage.innerText;
434
- try {
435
- if ("clipboard" in navigator) {
436
- await navigator.clipboard.writeText(textToCopy);
437
- copyButton.innerHTML = copiedIcon;
438
- setTimeout(() => {
439
- copyButton.innerHTML = copyIcon;
440
- }, 1500);
441
- } else {
442
- const textArea = document.createElement("textarea");
443
- textArea.value = textToCopy;
444
- document.body.appendChild(textArea);
445
- textArea.select();
446
- try {
447
- document.execCommand('copy');
448
- copyButton.innerHTML = copiedIcon;
449
- setTimeout(() => {
450
- copyButton.innerHTML = copyIcon;
451
- }, 1500);
452
- } catch (error) {
453
- console.error("Copy failed: ", error);
454
- }
455
- document.body.removeChild(textArea);
456
- }
457
- } catch (error) {
458
- console.error("Copy failed: ", error);
459
- }
460
- });
461
- botElement.appendChild(copyButton);
462
-
463
- // Toggle button
464
- var toggleButton = document.createElement('button');
465
- toggleButton.classList.add('chuanhu-btn');
466
- toggleButton.classList.add('toggle-md-btn');
467
- toggleButton.setAttribute('aria-label', 'Toggle');
468
- var renderMarkdown = mdMessage.classList.contains('hideM');
469
- toggleButton.innerHTML = renderMarkdown ? mdIcon : rawIcon;
470
- toggleButton.addEventListener('click', () => {
471
- renderMarkdown = mdMessage.classList.contains('hideM');
472
- if (renderMarkdown){
473
- renderMarkdownText(botElement);
474
- toggleButton.innerHTML=rawIcon;
475
- } else {
476
- removeMarkdownText(botElement);
477
- toggleButton.innerHTML=mdIcon;
478
- }
479
- });
480
- botElement.insertBefore(toggleButton, copyButton);
481
- }
482
-
483
- function renderMarkdownText(message) {
484
- var mdDiv = message.querySelector('.md-message');
485
- if (mdDiv) mdDiv.classList.remove('hideM');
486
- var rawDiv = message.querySelector('.raw-message');
487
- if (rawDiv) rawDiv.classList.add('hideM');
488
- }
489
- function removeMarkdownText(message) {
490
- var rawDiv = message.querySelector('.raw-message');
491
- if (rawDiv) {
492
- rawPre = rawDiv.querySelector('pre');
493
- if (rawPre) rawDiv.innerHTML = rawPre.innerHTML;
494
- rawDiv.classList.remove('hideM');
495
- }
496
- var mdDiv = message.querySelector('.md-message');
497
- if (mdDiv) mdDiv.classList.add('hideM');
498
- }
499
 
500
- let timeoutId;
501
- let isThrottled = false;
502
- var mmutation
503
- // 监听chatWrap元素的变化,为 bot 消息添加复制按钮。
504
- var mObserver = new MutationObserver(function (mutationsList) {
505
- for (mmutation of mutationsList) {
506
- if (mmutation.type === 'childList') {
507
- for (var node of mmutation.addedNodes) {
508
- if (node.nodeType === 1 && node.classList.contains('message')) {
509
- saveHistoryHtml();
510
- disableSendBtn();
511
- document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
512
- }
513
- }
514
- for (var node of mmutation.removedNodes) {
515
- if (node.nodeType === 1 && node.classList.contains('message')) {
516
- saveHistoryHtml();
517
- disableSendBtn();
518
- document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
519
- }
520
- }
521
- } else if (mmutation.type === 'attributes') {
522
- if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
523
- isThrottled = true;
524
- clearTimeout(timeoutId);
525
- timeoutId = setTimeout(() => {
526
- isThrottled = false;
527
- document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
528
- saveHistoryHtml();
529
- disableSendBtn();
530
- }, 1500);
531
- }
532
- }
533
- });
534
- // mObserver.observe(targetNode, { attributes: true, childList: true, subtree: true, characterData: true});
535
 
536
  var submitObserver = new MutationObserver(function (mutationsList) {
537
- document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
538
  saveHistoryHtml();
539
  });
540
 
541
- var statusObserver = new MutationObserver(function (mutationsList) {
542
- for (const mutation of mutationsList) {
543
- if (mutation.type === 'attributes' || mutation.type === 'childList') {
544
- if (statusDisplay.innerHTML.includes('<span id="update-status"')) {
545
- if (getUpdateStatus() === "success") {
546
- updatingInfoElement.innerText = updateSuccess_i18n.hasOwnProperty(language) ? updateSuccess_i18n[language] : updateSuccess_i18n['en'];
547
- noUpdateHtml();
548
- localStorage.setItem('isLatestVersion', 'true');
549
- isLatestVersion = true;
550
- } else if (getUpdateStatus() === "failure") {
551
- updatingInfoElement.innerText = updateFailure_i18n.hasOwnProperty(language) ? updateFailure_i18n[language] : updateFailure_i18n['en'];
552
- } else if (getUpdateStatus() != "") {
553
- updatingInfoElement.innerText = getUpdateStatus();
554
- }
555
- updateStatus.parentNode.removeChild(updateStatus);
556
- enableUpdateBtns();
557
- updateSpinner.stop();
558
- }
559
- }
560
- }
561
- });
562
-
563
- var loadhistorytime = 0; // for debugging
564
- function saveHistoryHtml() {
565
- var historyHtml = document.querySelector('#chuanhu_chatbot>.wrapper>.wrap');
566
- if (!historyHtml) return; // no history, do nothing
567
- localStorage.setItem('chatHistory', historyHtml.innerHTML);
568
- // console.log("History Saved")
569
- historyLoaded = false;
570
- }
571
- function loadHistoryHtml() {
572
- var historyHtml = localStorage.getItem('chatHistory');
573
- if (!historyHtml) {
574
- historyLoaded = true;
575
- return; // no history, do nothing
576
- }
577
- userLogged = localStorage.getItem('userLogged');
578
- if (userLogged){
579
- historyLoaded = true;
580
- return; // logged in, do nothing
581
- }
582
- if (!historyLoaded) {
583
- var tempDiv = document.createElement('div');
584
- tempDiv.innerHTML = historyHtml;
585
- var buttons = tempDiv.querySelectorAll('button.chuanhu-btn');
586
- var gradioCopyButtons = tempDiv.querySelectorAll('button.copy_code_button');
587
- for (var i = 0; i < buttons.length; i++) {
588
- buttons[i].parentNode.removeChild(buttons[i]);
589
- }
590
- for (var i = 0; i < gradioCopyButtons.length; i++) {
591
- gradioCopyButtons[i].parentNode.removeChild(gradioCopyButtons[i]);
592
- }
593
- var fakeHistory = document.createElement('div');
594
- fakeHistory.classList.add('history-message');
595
- fakeHistory.innerHTML = tempDiv.innerHTML;
596
- webLocale();
597
- chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
598
- // var fakeHistory = document.createElement('div');
599
- // fakeHistory.classList.add('history-message');
600
- // fakeHistory.innerHTML = historyHtml;
601
- // chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
602
- historyLoaded = true;
603
- console.log("History Loaded");
604
- loadhistorytime += 1; // for debugging
605
- } else {
606
- historyLoaded = false;
607
- }
608
- }
609
- function clearHistoryHtml() {
610
- localStorage.removeItem("chatHistory");
611
- historyMessages = chatbotWrap.querySelector('.history-message');
612
- if (historyMessages) {
613
- chatbotWrap.removeChild(historyMessages);
614
- console.log("History Cleared");
615
- }
616
- }
617
-
618
- var showingUpdateInfo = false;
619
- async function getLatestRelease() {
620
- try {
621
- const response = await fetch('https://api.github.com/repos/gaizhenbiao/chuanhuchatgpt/releases/latest');
622
- if (!response.ok) {
623
- console.log(`Error: ${response.status} - ${response.statusText}`);
624
- updateInfoGotten = true;
625
- return null;
626
- }
627
- const data = await response.json();
628
- updateInfoGotten = true;
629
- return data;
630
- } catch (error) {
631
- console.log(`Error: ${error}`);
632
- updateInfoGotten = true;
633
- return null;
634
- }
635
- }
636
-
637
- var releaseNoteElement = document.getElementById('release-note-content');
638
- var updatingInfoElement = document.getElementById('updating-info');
639
- async function updateLatestVersion() {
640
- const currentVersionElement = document.getElementById('current-version');
641
- const latestVersionElement = document.getElementById('latest-version-title');
642
- releaseNoteElement = document.getElementById('release-note-content');
643
- updatingInfoElement = document.getElementById('updating-info');
644
- const currentVersion = currentVersionElement.textContent;
645
- const versionTime = document.getElementById('version-time').innerText;
646
- const localVersionTime = versionTime !== "unknown" ? (new Date(versionTime)).getTime() : 0;
647
- updateInfoGotten = true; //无论成功与否都只执行一次,否则容易api超限...
648
- try {
649
- const data = await getLatestRelease();
650
- const releaseNote = data.body;
651
- if (releaseNote) {
652
- releaseNoteElement.innerHTML = marked.parse(releaseNote, {mangle: false, headerIds: false});
653
- }
654
- const latestVersion = data.tag_name;
655
- const latestVersionTime = (new Date(data.created_at)).getTime();
656
- if (latestVersionTime) {
657
- if (localVersionTime < latestVersionTime) {
658
- latestVersionElement.textContent = latestVersion;
659
- console.log(`New version ${latestVersion} found!`);
660
- if (!isInIframe) {openUpdateToast();}
661
- } else {
662
- noUpdate();
663
- }
664
- currentTime = new Date().getTime();
665
- localStorage.setItem('lastCheckTime', currentTime);
666
- }
667
- } catch (error) {
668
- console.error(error);
669
- }
670
- }
671
- function getUpdateInfo() {
672
- window.open('https://github.com/gaizhenbiao/chuanhuchatgpt/releases/latest', '_blank');
673
- closeUpdateToast();
674
- }
675
- var updateSpinner = null;
676
- function bgUpdateChuanhu() {
677
- updateChuanhuBtn.click();
678
- updatingInfoElement.innerText = updatingMsg_i18n.hasOwnProperty(language) ? updatingMsg_i18n[language] : updatingMsg_i18n['en'];
679
- var updatingSpinner = document.getElementById('updating-spinner');
680
- updateSpinner = new Spin.Spinner({color:'#06AE56',top:'45%',lines:9}).spin(updatingSpinner);
681
- updatingInfoElement.classList.remove('hideK');
682
- disableUpdateBtns();
683
- const releaseNoteWrap = document.getElementById('release-note-wrap');
684
- releaseNoteWrap.style.setProperty('display', 'none');
685
- statusObserver.observe(statusDisplay, { childList: true, subtree: true, characterData: true});
686
- }
687
- function cancelUpdate() {
688
- closeUpdateToast();
689
- }
690
- function openUpdateToast() {
691
- showingUpdateInfo = true;
692
- setUpdateWindowHeight();
693
- }
694
- function closeUpdateToast() {
695
- updateToast.style.setProperty('top', '-500px');
696
- showingUpdateInfo = false;
697
- if (updatingInfoElement.classList.contains('hideK') === false) {
698
- updatingInfoElement.classList.add('hideK');
699
- }
700
- }
701
- function manualCheckUpdate() {
702
- openUpdateToast();
703
- updateLatestVersion();
704
- currentTime = new Date().getTime();
705
- localStorage.setItem('lastCheckTime', currentTime);
706
- }
707
- function noUpdate() {
708
- localStorage.setItem('isLatestVersion', 'true');
709
- isLatestVersion = true;
710
- noUpdateHtml();
711
- }
712
- function noUpdateHtml() {
713
- const versionInfoElement = document.getElementById('version-info-title');
714
- const gotoUpdateBtn = document.getElementById('goto-update-btn');
715
- const closeUpdateBtn = document.getElementById('close-update-btn');
716
- const releaseNoteWrap = document.getElementById('release-note-wrap');
717
- releaseNoteWrap.style.setProperty('display', 'none');
718
- versionInfoElement.textContent = usingLatest_i18n.hasOwnProperty(language) ? usingLatest_i18n[language] : usingLatest_i18n['en'];
719
- gotoUpdateBtn.classList.add('hideK');
720
- closeUpdateBtn.classList.remove('hideK');
721
- }
722
- var updateStatus = null;
723
- function getUpdateStatus() {
724
- updateStatus = statusDisplay.querySelector("#update-status");
725
- if (updateStatus) {
726
- return updateStatus.innerText;
727
- } else {
728
- return "unknown";
729
- }
730
- }
731
-
732
- function disableUpdateBtns() {
733
- const updatesButtons = document.querySelectorAll('.btn-update');
734
- updatesButtons.forEach( function (btn) {
735
- btn.disabled = true;
736
- });
737
- }
738
- function enableUpdateBtns() {
739
- const updatesButtons = document.querySelectorAll('.btn-update');
740
- updatesButtons.forEach( function (btn) {
741
- btn.disabled = false;
742
- });
743
- }
744
-
745
- function setUpdateWindowHeight() {
746
- if (!showingUpdateInfo) {return;}
747
- const scrollPosition = window.scrollY;
748
- // const originalTop = updateToast.style.getPropertyValue('top');
749
- const resultTop = scrollPosition - 20 + 'px';
750
- updateToast.style.setProperty('top', resultTop);
751
- }
752
-
753
  // 监视页面内部 DOM 变动
754
  var observer = new MutationObserver(function (mutations) {
755
  gradioLoaded(mutations);
756
  });
757
- observer.observe(targetNode, { childList: true, subtree: true });
758
 
759
  // 监视页面变化
760
  window.addEventListener("DOMContentLoaded", function () {
 
 
761
  isInIframe = (window.self !== window.top);
762
  historyLoaded = false;
763
  });
764
  window.addEventListener('resize', setChatbotHeight);
765
- window.addEventListener('scroll', function(){setChatbotHeight();setUpdateWindowHeight();});
766
  window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);
767
 
768
  // console suprise
 
1
 
2
+ // ChuanhuChat core javascript
3
 
4
  const MAX_HISTORY_LENGTH = 32;
5
 
 
25
  var updateChuanhuBtn = null;
26
  var statusDisplay = null;
27
 
 
 
 
 
 
28
 
 
 
29
  var isInIframe = (window.self !== window.top);
 
30
  var currentTime = new Date().getTime();
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  // gradio 页面加载好了么??? 我能动你的元素了么??
34
  function gradioLoaded(mutations) {
 
36
  if (mutations[i].addedNodes.length) {
37
  loginUserForm = document.querySelector(".gradio-container > .main > .wrap > .panel > .form")
38
  gradioContainer = document.querySelector(".gradio-container");
39
+ user_input_tb = document.getElementById('user-input-tb');
40
+ userInfoDiv = document.getElementById("user-info");
41
+ appTitleDiv = document.getElementById("app-title");
42
+ chatbot = document.querySelector('#chuanhu-chatbot');
43
+ chatbotWrap = document.querySelector('#chuanhu-chatbot > .wrapper > .wrap');
44
  apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
45
  updateToast = document.querySelector("#toast-update");
46
+ sendBtn = document.getElementById("submit-btn");
47
+ cancelBtn = document.getElementById("cancel-btn");
48
  sliders = document.querySelectorAll('input[type="range"]');
49
+ updateChuanhuBtn = document.getElementById("update-chuanhu-btn");
50
+ statusDisplay = document.querySelector('#status-display');
51
 
52
  if (loginUserForm) {
53
  localStorage.setItem("userLogged", true);
 
96
  }
97
  }
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  function showConfirmationDialog(a, file, c) {
100
  if (file != "") {
101
+ var result = confirm(i18n(deleteConfirm_i18n_pref) + file + i18n(deleteConfirm_i18n_suff));
102
  if (result) {
103
  return [a, file, c];
104
  }
 
162
  });
163
  }
164
 
165
+ function adjustDarkMode() {
166
+ function toggleDarkMode(isEnabled) {
167
+ if (isEnabled) {
168
+ document.body.classList.add("dark");
169
+ document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  } else {
171
+ document.body.classList.remove("dark");
172
+ document.body.style.backgroundColor = "";
173
  }
174
  }
175
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
 
 
177
  apSwitch.checked = darkModeQuery.matches;
178
  toggleDarkMode(darkModeQuery.matches);
 
179
  darkModeQuery.addEventListener("change", (e) => {
180
  apSwitch.checked = e.matches;
181
  toggleDarkMode(e.matches);
182
  });
 
183
  apSwitch.addEventListener("change", (e) => {
184
  toggleDarkMode(e.target.checked);
185
  });
 
187
 
188
  function setChatbotHeight() {
189
  const screenWidth = window.innerWidth;
190
+ const statusDisplay = document.querySelector('#status-display');
191
  const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
192
  const vh = window.innerHeight * 0.01;
193
  document.documentElement.style.setProperty('--vh', `${vh}px`);
 
211
  var scrollHeight = chatbotWrap.scrollHeight;
212
  chatbotWrap.scrollTo(0,scrollHeight)
213
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
  var submitObserver = new MutationObserver(function (mutationsList) {
217
+ document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
218
  saveHistoryHtml();
219
  });
220
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  // 监视页面内部 DOM 变动
222
  var observer = new MutationObserver(function (mutations) {
223
  gradioLoaded(mutations);
224
  });
 
225
 
226
  // 监视页面变化
227
  window.addEventListener("DOMContentLoaded", function () {
228
+ const ga = document.getElementsByTagName("gradio-app");
229
+ observer.observe(ga[0], { childList: true, subtree: true });
230
  isInIframe = (window.self !== window.top);
231
  historyLoaded = false;
232
  });
233
  window.addEventListener('resize', setChatbotHeight);
234
+ window.addEventListener('scroll', function(){setChatbotHeight(); setUpdateWindowHeight();});
235
  window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);
236
 
237
  // console suprise
web_assets/javascript/chat-history.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ var historyLoaded = false;
3
+ var loadhistorytime = 0; // for debugging
4
+
5
+
6
+ function saveHistoryHtml() {
7
+ var historyHtml = document.querySelector('#chuanhu-chatbot>.wrapper>.wrap');
8
+ if (!historyHtml) return; // no history, do nothing
9
+ localStorage.setItem('chatHistory', historyHtml.innerHTML);
10
+ // console.log("History Saved")
11
+ historyLoaded = false;
12
+ }
13
+
14
+ function loadHistoryHtml() {
15
+ var historyHtml = localStorage.getItem('chatHistory');
16
+ if (!historyHtml) {
17
+ historyLoaded = true;
18
+ return; // no history, do nothing
19
+ }
20
+ userLogged = localStorage.getItem('userLogged');
21
+ if (userLogged){
22
+ historyLoaded = true;
23
+ return; // logged in, do nothing
24
+ }
25
+ if (!historyLoaded) {
26
+ var tempDiv = document.createElement('div');
27
+ tempDiv.innerHTML = historyHtml;
28
+ var buttons = tempDiv.querySelectorAll('button.chuanhu-btn');
29
+ var gradioCopyButtons = tempDiv.querySelectorAll('button.copy_code_button');
30
+ for (var i = 0; i < buttons.length; i++) {
31
+ buttons[i].parentNode.removeChild(buttons[i]);
32
+ }
33
+ for (var i = 0; i < gradioCopyButtons.length; i++) {
34
+ gradioCopyButtons[i].parentNode.removeChild(gradioCopyButtons[i]);
35
+ }
36
+ var fakeHistory = document.createElement('div');
37
+ fakeHistory.classList.add('history-message');
38
+ fakeHistory.innerHTML = tempDiv.innerHTML;
39
+ const forViewStyle = document.createElement('style');
40
+ forViewStyle.innerHTML = '.wrapper>.wrap>.history-message>:last-child::after { content: "' + i18n(forView_i18n) + '"!important; }';
41
+ document.head.appendChild(forViewStyle);
42
+ chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
43
+ // var fakeHistory = document.createElement('div');
44
+ // fakeHistory.classList.add('history-message');
45
+ // fakeHistory.innerHTML = historyHtml;
46
+ // chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
47
+ historyLoaded = true;
48
+ console.log("History Loaded");
49
+ loadhistorytime += 1; // for debugging
50
+ } else {
51
+ historyLoaded = false;
52
+ }
53
+ }
54
+
55
+ function clearHistoryHtml() {
56
+ localStorage.removeItem("chatHistory");
57
+ historyMessages = chatbotWrap.querySelector('.history-message');
58
+ if (historyMessages) {
59
+ chatbotWrap.removeChild(historyMessages);
60
+ console.log("History Cleared");
61
+ }
62
+ }
{assets → web_assets/javascript}/external-scripts.js RENAMED
File without changes
web_assets/javascript/localization.js ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // i18n
3
+
4
+ const language = navigator.language.slice(0,2);
5
+
6
+ const forView_i18n = {
7
+ 'zh': "仅供查看",
8
+ 'en': "For viewing only",
9
+ 'ja': "閲覧専用",
10
+ 'ko': "읽기 전용",
11
+ 'fr': "Pour consultation seulement",
12
+ 'es': "Solo para visualización",
13
+ 'sv': "Endast för visning",
14
+ };
15
+
16
+ const deleteConfirm_i18n_pref = {
17
+ 'zh': "你真的要删除 ",
18
+ 'en': "Are you sure you want to delete ",
19
+ 'ja': "本当に ",
20
+ 'ko': "정말로 ",
21
+ 'sv': "Är du säker på att du vill ta bort "
22
+ };
23
+
24
+ const deleteConfirm_i18n_suff = {
25
+ 'zh': " 吗?",
26
+ 'en': " ?",
27
+ 'ja': " を削除してもよろしいですか?",
28
+ 'ko': " 을(를) 삭제하시겠습니까?",
29
+ 'sv': " ?"
30
+ };
31
+
32
+ const usingLatest_i18n = {
33
+ 'zh': "您使用的就是最新版!",
34
+ 'en': "You are using the latest version!",
35
+ 'ja': "最新バージョンを使用しています!",
36
+ 'ko': "최신 버전을 사용하고 있습니다!",
37
+ 'sv': "Du använder den senaste versionen!"
38
+ };
39
+
40
+ const updatingMsg_i18n = {
41
+ 'zh': "正在尝试更新...",
42
+ 'en': "Trying to update...",
43
+ 'ja': "更新を試みています...",
44
+ 'ko': "업데이트를 시도 중...",
45
+ 'sv': "Försöker uppdatera..."
46
+ }
47
+
48
+ const updateSuccess_i18n = {
49
+ 'zh': "更新成功,请重启本程序。",
50
+ 'en': "Updated successfully, please restart this program.",
51
+ 'ja': "更新が成功しました、このプログラムを再起動してください。",
52
+ 'ko': "업데이트 성공, 이 프로그램을 재시작 해주세요.",
53
+ 'sv': "Uppdaterat framgångsrikt, starta om programmet."
54
+ }
55
+
56
+ const updateFailure_i18n = {
57
+ 'zh': '更新失败,请尝试<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手动更新</a>。',
58
+ 'en': 'Update failed, please try <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">manually updating</a>.',
59
+ 'ja': '更新に失敗しました、<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手動での更新</a>をお試しください。',
60
+ 'ko': '업데이트 실패, <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">수동 업데이트</a>를 시도하십시오.',
61
+ 'sv': 'Uppdateringen misslyckades, prova att <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">uppdatera manuellt</a>.'
62
+ }
63
+
64
+
65
+ function i18n(msg) {
66
+ return msg.hasOwnProperty(language) ? msg[language] : msg['en'];
67
+ }
web_assets/javascript/message-button.js ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // 为 bot 消息添加复制与切换显示按钮
3
+
4
+ function addChuanhuButton(botElement) {
5
+ var rawMessage = botElement.querySelector('.raw-message');
6
+ var mdMessage = botElement.querySelector('.md-message');
7
+ // var gradioCopyMsgBtn = botElement.querySelector('div.icon-button>button[title="copy"]'); // 获取 gradio 的 copy button,它可以读取真正的原始 message
8
+ if (!rawMessage) {
9
+ var buttons = botElement.querySelectorAll('button.chuanhu-btn');
10
+ for (var i = 0; i < buttons.length; i++) {
11
+ buttons[i].parentNode.removeChild(buttons[i]);
12
+ }
13
+ return;
14
+ }
15
+ var oldCopyButton = null;
16
+ var oldToggleButton = null;
17
+ oldCopyButton = botElement.querySelector('button.copy-bot-btn');
18
+ oldToggleButton = botElement.querySelector('button.toggle-md-btn');
19
+ if (oldCopyButton) oldCopyButton.remove();
20
+ if (oldToggleButton) oldToggleButton.remove();
21
+
22
+ // Copy bot button
23
+ var copyButton = document.createElement('button');
24
+ copyButton.classList.add('chuanhu-btn');
25
+ copyButton.classList.add('copy-bot-btn');
26
+ copyButton.setAttribute('aria-label', 'Copy');
27
+ copyButton.innerHTML = copyIcon;
28
+
29
+ copyButton.addEventListener('click', async () => {
30
+ const textToCopy = rawMessage.innerText;
31
+ try {
32
+ if ("clipboard" in navigator) {
33
+ await navigator.clipboard.writeText(textToCopy);
34
+ copyButton.innerHTML = copiedIcon;
35
+ setTimeout(() => {
36
+ copyButton.innerHTML = copyIcon;
37
+ }, 1500);
38
+ } else {
39
+ const textArea = document.createElement("textarea");
40
+ textArea.value = textToCopy;
41
+ document.body.appendChild(textArea);
42
+ textArea.select();
43
+ try {
44
+ document.execCommand('copy');
45
+ copyButton.innerHTML = copiedIcon;
46
+ setTimeout(() => {
47
+ copyButton.innerHTML = copyIcon;
48
+ }, 1500);
49
+ } catch (error) {
50
+ console.error("Copy failed: ", error);
51
+ }
52
+ document.body.removeChild(textArea);
53
+ }
54
+ } catch (error) {
55
+ console.error("Copy failed: ", error);
56
+ }
57
+ });
58
+ botElement.appendChild(copyButton);
59
+
60
+ // Toggle button
61
+ var toggleButton = document.createElement('button');
62
+ toggleButton.classList.add('chuanhu-btn');
63
+ toggleButton.classList.add('toggle-md-btn');
64
+ toggleButton.setAttribute('aria-label', 'Toggle');
65
+ var renderMarkdown = mdMessage.classList.contains('hideM');
66
+ toggleButton.innerHTML = renderMarkdown ? mdIcon : rawIcon;
67
+ toggleButton.addEventListener('click', () => {
68
+ renderMarkdown = mdMessage.classList.contains('hideM');
69
+ if (renderMarkdown){
70
+ renderMarkdownText(botElement);
71
+ toggleButton.innerHTML=rawIcon;
72
+ } else {
73
+ removeMarkdownText(botElement);
74
+ toggleButton.innerHTML=mdIcon;
75
+ }
76
+ });
77
+ botElement.insertBefore(toggleButton, copyButton);
78
+
79
+ function renderMarkdownText(message) {
80
+ var mdDiv = message.querySelector('.md-message');
81
+ if (mdDiv) mdDiv.classList.remove('hideM');
82
+ var rawDiv = message.querySelector('.raw-message');
83
+ if (rawDiv) rawDiv.classList.add('hideM');
84
+ }
85
+ function removeMarkdownText(message) {
86
+ var rawDiv = message.querySelector('.raw-message');
87
+ if (rawDiv) {
88
+ rawPre = rawDiv.querySelector('pre');
89
+ if (rawPre) rawDiv.innerHTML = rawPre.innerHTML;
90
+ rawDiv.classList.remove('hideM');
91
+ }
92
+ var mdDiv = message.querySelector('.md-message');
93
+ if (mdDiv) mdDiv.classList.add('hideM');
94
+ }
95
+ }
96
+
97
+
98
+ let timeoutId;
99
+ let isThrottled = false;
100
+ // 监听chatWrap元素的变化,为 bot 消息添加复制按钮。
101
+ var mObserver = new MutationObserver(function (mutationsList) {
102
+ for (const mmutation of mutationsList) {
103
+ if (mmutation.type === 'childList') {
104
+ for (var node of mmutation.addedNodes) {
105
+ if (node.nodeType === 1 && node.classList.contains('message')) {
106
+ saveHistoryHtml();
107
+ disableSendBtn();
108
+ document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
109
+ }
110
+ }
111
+ for (var node of mmutation.removedNodes) {
112
+ if (node.nodeType === 1 && node.classList.contains('message')) {
113
+ saveHistoryHtml();
114
+ disableSendBtn();
115
+ document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
116
+ }
117
+ }
118
+ } else if (mmutation.type === 'attributes') {
119
+ if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
120
+ isThrottled = true;
121
+ clearTimeout(timeoutId);
122
+ timeoutId = setTimeout(() => {
123
+ isThrottled = false;
124
+ document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
125
+ saveHistoryHtml();
126
+ disableSendBtn();
127
+ }, 1500);
128
+ }
129
+ }
130
+ });
web_assets/javascript/sliders.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ var rangeInputs = null;
3
+ var numberInputs = null;
4
+
5
+
6
+ function setSlider() {
7
+ function setSliderRange() {
8
+ var range = document.querySelectorAll('input[type="range"]');
9
+ range.forEach(range => {
10
+ range.style.backgroundSize = (range.value - range.min) / (range.max - range.min) * 100 + '% 100%';
11
+ });
12
+ }
13
+ rangeInputs = document.querySelectorAll('input[type="range"]');
14
+ numberInputs = document.querySelectorAll('input[type="number"]')
15
+ setSliderRange();
16
+ rangeInputs.forEach(rangeInput => {
17
+ rangeInput.addEventListener('input', setSliderRange);
18
+ });
19
+ numberInputs.forEach(numberInput => {
20
+ numberInput.addEventListener('input', setSliderRange);
21
+ })
22
+ }
web_assets/javascript/updater.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ var updateInfoGotten = false;
3
+ var isLatestVersion = localStorage.getItem('isLatestVersion') || false;
4
+
5
+
6
+ var statusObserver = new MutationObserver(function (mutationsList) {
7
+ for (const mutation of mutationsList) {
8
+ if (mutation.type === 'attributes' || mutation.type === 'childList') {
9
+ if (statusDisplay.innerHTML.includes('<span id="update-status"')) {
10
+ if (getUpdateStatus() === "success") {
11
+ updatingInfoElement.innerText = i18n(updateSuccess_i18n);
12
+ noUpdateHtml();
13
+ localStorage.setItem('isLatestVersion', 'true');
14
+ isLatestVersion = true;
15
+ enableUpdateBtns();
16
+ } else if (getUpdateStatus() === "failure") {
17
+ updatingInfoElement.innerHTML = i18n(updateFailure_i18n);
18
+ document.querySelector('#update-button.btn-update').disabled = true;
19
+ document.querySelector('#cancel-button.btn-update').disabled = false;
20
+ } else if (getUpdateStatus() != "") {
21
+ updatingInfoElement.innerText = getUpdateStatus();
22
+ enableUpdateBtns();
23
+ }
24
+ updateStatus.parentNode.removeChild(updateStatus);
25
+ if (updateSpinner) updateSpinner.stop();
26
+ }
27
+ }
28
+ }
29
+ });
30
+
31
+ var showingUpdateInfo = false;
32
+ async function getLatestRelease() {
33
+ try {
34
+ const response = await fetch('https://api.github.com/repos/gaizhenbiao/chuanhuchatgpt/releases/latest');
35
+ if (!response.ok) {
36
+ console.log(`Error: ${response.status} - ${response.statusText}`);
37
+ updateInfoGotten = true;
38
+ return null;
39
+ }
40
+ const data = await response.json();
41
+ updateInfoGotten = true;
42
+ return data;
43
+ } catch (error) {
44
+ console.log(`Error: ${error}`);
45
+ updateInfoGotten = true;
46
+ return null;
47
+ }
48
+ }
49
+
50
+ var releaseNoteElement = document.getElementById('release-note-content');
51
+ var updatingInfoElement = document.getElementById('updating-info');
52
+ async function updateLatestVersion() {
53
+ const currentVersionElement = document.getElementById('current-version');
54
+ const latestVersionElement = document.getElementById('latest-version-title');
55
+ releaseNoteElement = document.getElementById('release-note-content');
56
+ updatingInfoElement = document.getElementById('updating-info');
57
+ const currentVersion = currentVersionElement.textContent;
58
+ const versionTime = document.getElementById('version-time').innerText;
59
+ const localVersionTime = versionTime !== "unknown" ? (new Date(versionTime)).getTime() : 0;
60
+ updateInfoGotten = true; //无论成功与否都只执行一次,否则容易api超限...
61
+ try {
62
+ const data = await getLatestRelease();
63
+ const releaseNote = data.body;
64
+ if (releaseNote) {
65
+ releaseNoteElement.innerHTML = marked.parse(releaseNote, {mangle: false, headerIds: false});
66
+ }
67
+ const latestVersion = data.tag_name;
68
+ const latestVersionTime = (new Date(data.created_at)).getTime();
69
+ if (latestVersionTime) {
70
+ if (localVersionTime < latestVersionTime) {
71
+ latestVersionElement.textContent = latestVersion;
72
+ console.log(`New version ${latestVersion} found!`);
73
+ if (!isInIframe) {openUpdateToast();}
74
+ } else {
75
+ noUpdate();
76
+ }
77
+ currentTime = new Date().getTime();
78
+ localStorage.setItem('lastCheckTime', currentTime);
79
+ }
80
+ } catch (error) {
81
+ console.error(error);
82
+ }
83
+ }
84
+
85
+ function getUpdateInfo() {
86
+ window.open('https://github.com/gaizhenbiao/chuanhuchatgpt/releases/latest', '_blank');
87
+ closeUpdateToast();
88
+ }
89
+
90
+ var updateSpinner = null;
91
+
92
+ function bgUpdateChuanhu() {
93
+ updateChuanhuBtn.click();
94
+ updatingInfoElement.innerText = i18n(updatingMsg_i18n);
95
+ var updatingSpinner = document.getElementById('updating-spinner');
96
+ try {
97
+ updateSpinner = new Spin.Spinner({color:'#06AE56',top:'45%',lines:9}).spin(updatingSpinner);
98
+ } catch (error) {
99
+ console.error("Can't create spinner")
100
+ }
101
+ updatingInfoElement.classList.remove('hideK');
102
+ disableUpdateBtns();
103
+ const releaseNoteWrap = document.getElementById('release-note-wrap');
104
+ releaseNoteWrap.style.setProperty('display', 'none');
105
+ statusObserver.observe(statusDisplay, { childList: true, subtree: true, characterData: true});
106
+ }
107
+ function cancelUpdate() {
108
+ closeUpdateToast();
109
+ }
110
+ function openUpdateToast() {
111
+ showingUpdateInfo = true;
112
+ setUpdateWindowHeight();
113
+ }
114
+ function closeUpdateToast() {
115
+ updateToast.style.setProperty('top', '-500px');
116
+ showingUpdateInfo = false;
117
+ if (updatingInfoElement.classList.contains('hideK') === false) {
118
+ updatingInfoElement.classList.add('hideK');
119
+ }
120
+ }
121
+ function manualCheckUpdate() {
122
+ openUpdateToast();
123
+ updateLatestVersion();
124
+ currentTime = new Date().getTime();
125
+ localStorage.setItem('lastCheckTime', currentTime);
126
+ }
127
+ function noUpdate() {
128
+ localStorage.setItem('isLatestVersion', 'true');
129
+ isLatestVersion = true;
130
+ noUpdateHtml();
131
+ }
132
+ function noUpdateHtml() {
133
+ const versionInfoElement = document.getElementById('version-info-title');
134
+ const gotoUpdateBtn = document.getElementById('goto-update-btn');
135
+ const closeUpdateBtn = document.getElementById('close-update-btn');
136
+ const releaseNoteWrap = document.getElementById('release-note-wrap');
137
+ releaseNoteWrap.style.setProperty('display', 'none');
138
+ versionInfoElement.textContent = i18n(usingLatest_i18n)
139
+ gotoUpdateBtn.classList.add('hideK');
140
+ closeUpdateBtn.classList.remove('hideK');
141
+ }
142
+
143
+ var updateStatus = null;
144
+ function getUpdateStatus() {
145
+ updateStatus = statusDisplay.querySelector("#update-status");
146
+ if (updateStatus) {
147
+ return updateStatus.innerText;
148
+ } else {
149
+ return "unknown";
150
+ }
151
+ }
152
+
153
+ function disableUpdateBtns() {
154
+ const updatesButtons = document.querySelectorAll('.btn-update');
155
+ updatesButtons.forEach( function (btn) {
156
+ btn.disabled = true;
157
+ });
158
+ }
159
+ function enableUpdateBtns() {
160
+ const updatesButtons = document.querySelectorAll('.btn-update');
161
+ updatesButtons.forEach( function (btn) {
162
+ btn.disabled = false;
163
+ });
164
+ }
165
+
166
+ function setUpdateWindowHeight() {
167
+ if (!showingUpdateInfo) {return;}
168
+ const scrollPosition = window.scrollY;
169
+ // const originalTop = updateToast.style.getPropertyValue('top');
170
+ const resultTop = scrollPosition - 20 + 'px';
171
+ updateToast.style.setProperty('top', resultTop);
172
+ }
web_assets/javascript/user-info.js ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ var userLogged = false;
3
+ var usernameGotten = false;
4
+ var username = null;
5
+
6
+
7
+ function getUserInfo() {
8
+ if (usernameGotten) {
9
+ return;
10
+ }
11
+ userLogged = localStorage.getItem('userLogged');
12
+ if (userLogged) {
13
+ username = userInfoDiv.innerText;
14
+ if (username) {
15
+ if (username.includes("getting user info…")) {
16
+ setTimeout(getUserInfo, 500);
17
+ return;
18
+ } else if (username === " ") {
19
+ localStorage.removeItem("username");
20
+ localStorage.removeItem("userLogged")
21
+ userLogged = false;
22
+ usernameGotten = true;
23
+ return;
24
+ } else {
25
+ username = username.match(/User:\s*(.*)/)[1] || username;
26
+ localStorage.setItem("username", username);
27
+ usernameGotten = true;
28
+ clearHistoryHtml();
29
+ }
30
+ }
31
+ }
32
+ }
33
+
34
+ function showOrHideUserInfo() {
35
+ function toggleUserInfoVisibility(shouldHide) {
36
+ if (userInfoDiv) {
37
+ if (shouldHide) {
38
+ userInfoDiv.classList.add("info-transparent");
39
+ } else {
40
+ userInfoDiv.classList.remove("info-transparent");
41
+ }
42
+ }
43
+ }
44
+
45
+ // When webpage loaded, hide user info after 2 second
46
+ setTimeout(function () {
47
+ toggleUserInfoVisibility(true);
48
+ }, 2000);
49
+
50
+ let triggerElements = {appTitleDiv, userInfoDiv, sendBtn};
51
+ for (let elem in triggerElements) {
52
+ triggerElements[elem].addEventListener("mouseenter", function () {
53
+ toggleUserInfoVisibility(false);
54
+ });
55
+ triggerElements[elem].addEventListener("mouseleave", function () {
56
+ toggleUserInfoVisibility(true);
57
+ });
58
+ triggerElements[elem].ontouchstart = function () {
59
+ toggleUserInfoVisibility(false);
60
+ };
61
+ triggerElements[elem].ontouchend = function () {
62
+ setTimeout(function () {
63
+ toggleUserInfoVisibility(true);
64
+ }, 3000);
65
+ };
66
+ }
67
+ }
web_assets/stylesheet/ChuanhuChat.css ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --chatbot-color-light: #000000;
3
+ --chatbot-color-dark: #FFFFFF;
4
+ --chatbot-background-color-light: #F3F3F3;
5
+ --chatbot-background-color-dark: #121111;
6
+ --message-user-background-color-light: #95EC69;
7
+ --message-user-background-color-dark: #26B561;
8
+ --message-bot-background-color-light: #FFFFFF;
9
+ --message-bot-background-color-dark: #2C2C2C;
10
+ --switch-checkbox-color-light: #e5e7eb;
11
+ --switch-checkbox-color-dark: #515151;
12
+ }
13
+
14
+ .hideK {
15
+ display: none;
16
+ }
17
+
18
+ #app-title {
19
+ font-weight: var(--prose-header-text-weight);
20
+ font-size: var(--text-xxl);
21
+ line-height: 1.3;
22
+ text-align: left;
23
+ margin-top: 6px;
24
+ white-space: nowrap;
25
+ }
26
+ #description {
27
+ text-align: center;
28
+ margin: 32px 0 4px 0;
29
+ }
30
+
31
+ /* 高级页面 */
32
+ #advanced-warning {
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ flex-direction: column;
36
+ align-content: center;
37
+ }
38
+
39
+ #netsetting-warning hr {
40
+ margin-bottom: 1em
41
+ }
42
+
43
+ .view-only-textbox textarea {
44
+ -webkit-text-fill-color: #b2b2b2 !important
45
+ }
46
+
47
+ #footer {
48
+ text-align: center;
49
+ }
50
+ #footer div {
51
+ display: inline-block;
52
+ }
53
+ #footer .versions{
54
+ font-size: 85%;
55
+ opacity: 0.60;
56
+ }
57
+
58
+
59
+ #float-display {
60
+ position: absolute;
61
+ max-height: 30px;
62
+ }
63
+
64
+ .insert-block {
65
+ position: relative;
66
+ margin: 0;
67
+ padding: 8px 12px;
68
+ box-shadow: var(--block-shadow);
69
+ border-width: var(--block-border-width);
70
+ border-color: var(--block-border-color);
71
+ border-radius: var(--block-radius);
72
+ background: var(--block-background-fill);
73
+ width: 100%;
74
+ line-height: var(--line-sm);
75
+ min-height: 2em;
76
+ }
77
+
78
+ /* status-display */
79
+ #status-display {
80
+ display: flex;
81
+ min-height: 2em;
82
+ align-items: flex-end;
83
+ justify-content: flex-end;
84
+ transition: all 0.6s;
85
+ }
86
+ #status-display p {
87
+ font-size: .85em;
88
+ font-family: ui-monospace, "SF Mono", "SFMono-Regular", "Menlo", "Consolas", "Liberation Mono", "Microsoft Yahei UI", "Microsoft Yahei", monospace;
89
+ /* Windows下中文的monospace会fallback为新宋体,实在太丑,这里折中使用微软雅黑 */
90
+ color: var(--body-text-color-subdued);
91
+ }
92
+
93
+
94
+ #submit-btn, #cancel-btn {
95
+ height: 40px !important;
96
+ }
97
+ #submit-btn::before {
98
+ content: url("data:image/svg+xml, %3Csvg width='21px' height='20px' viewBox='0 0 21 20' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg id='page' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E %3Cg id='send' transform='translate(0.435849, 0.088463)' fill='%23FFFFFF' fill-rule='nonzero'%3E %3Cpath d='M0.579148261,0.0428666046 C0.301105539,-0.0961547561 -0.036517765,0.122307382 0.0032026237,0.420210298 L1.4927172,18.1553639 C1.5125774,18.4334066 1.79062012,18.5922882 2.04880264,18.4929872 L8.24518329,15.8913017 L11.6412765,19.7441794 C11.8597387,19.9825018 12.2370824,19.8832008 12.3165231,19.5852979 L13.9450591,13.4882182 L19.7839562,11.0255541 C20.0619989,10.8865327 20.0818591,10.4694687 19.7839562,10.3105871 L0.579148261,0.0428666046 Z M11.6138902,17.0883151 L9.85385903,14.7195502 L0.718169621,0.618812241 L12.69945,12.9346347 L11.6138902,17.0883151 Z' id='shape'%3E%3C/path%3E %3C/g%3E %3C/g%3E %3C/svg%3E");
99
+ height: 21px;
100
+ }
101
+ #cancel-btn::before {
102
+ content: url("data:image/svg+xml,%3Csvg width='21px' height='21px' viewBox='0 0 21 21' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg id='pg' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E %3Cpath d='M10.2072007,20.088463 C11.5727865,20.088463 12.8594566,19.8259823 14.067211,19.3010209 C15.2749653,18.7760595 16.3386126,18.0538087 17.2581528,17.1342685 C18.177693,16.2147282 18.8982283,15.1527965 19.4197586,13.9484733 C19.9412889,12.7441501 20.202054,11.4557644 20.202054,10.0833163 C20.202054,8.71773046 19.9395733,7.43106036 19.4146119,6.22330603 C18.8896505,5.01555169 18.1673997,3.95018885 17.2478595,3.0272175 C16.3283192,2.10424615 15.2646719,1.3837109 14.0569176,0.865611739 C12.8491633,0.34751258 11.5624932,0.088463 10.1969073,0.088463 C8.83132146,0.088463 7.54636692,0.34751258 6.34204371,0.865611739 C5.1377205,1.3837109 4.07407321,2.10424615 3.15110186,3.0272175 C2.22813051,3.95018885 1.5058797,5.01555169 0.984349419,6.22330603 C0.46281914,7.43106036 0.202054,8.71773046 0.202054,10.0833163 C0.202054,11.4557644 0.4645347,12.7441501 0.9894961,13.9484733 C1.5144575,15.1527965 2.23670831,16.2147282 3.15624854,17.1342685 C4.07578877,18.0538087 5.1377205,18.7760595 6.34204371,19.3010209 C7.54636692,19.8259823 8.83475258,20.088463 10.2072007,20.088463 Z M10.2072007,18.2562448 C9.07493099,18.2562448 8.01471483,18.0452309 7.0265522,17.6232031 C6.03838956,17.2011753 5.17031614,16.6161693 4.42233192,15.8681851 C3.6743477,15.1202009 3.09105726,14.2521274 2.67246059,13.2639648 C2.25386392,12.2758022 2.04456558,11.215586 2.04456558,10.0833163 C2.04456558,8.95104663 2.25386392,7.89083047 2.67246059,6.90266784 C3.09105726,5.9145052 3.6743477,5.04643178 4.42233192,4.29844756 C5.17031614,3.55046334 6.036674,2.9671729 7.02140552,2.54857623 C8.00613703,2.12997956 9.06463763,1.92068122 10.1969073,1.92068122 C11.329177,1.92068122 12.3911087,2.12997956 13.3827025,2.54857623 C14.3742962,2.9671729 15.2440852,3.55046334 15.9920694,4.29844756 C16.7400537,5.04643178 17.3233441,5.9145052 17.7419408,6.90266784 C18.1605374,7.89083047 18.3698358,8.95104663 18.3698358,10.0833163 C18.3698358,11.215586 18.1605374,12.2758022 17.7419408,13.2639648 C17.3233441,14.2521274 16.7400537,15.1202009 15.9920694,15.8681851 C15.2440852,16.6161693 14.3760118,17.2011753 13.3878492,17.6232031 C12.3996865,18.0452309 11.3394704,18.2562448 10.2072007,18.2562448 Z M7.65444721,13.6242324 L12.7496608,13.6242324 C13.0584616,13.6242324 13.3003556,13.5384544 13.4753427,13.3668984 C13.6503299,13.1953424 13.7378234,12.9585951 13.7378234,12.6566565 L13.7378234,7.49968276 C13.7378234,7.19774418 13.6503299,6.96099688 13.4753427,6.78944087 C13.3003556,6.61788486 13.0584616,6.53210685 12.7496608,6.53210685 L7.65444721,6.53210685 C7.33878414,6.53210685 7.09345904,6.61788486 6.91847191,6.78944087 C6.74348478,6.96099688 6.65599121,7.19774418 6.65599121,7.49968276 L6.65599121,12.6566565 C6.65599121,12.9585951 6.74348478,13.1953424 6.91847191,13.3668984 C7.09345904,13.5384544 7.33878414,13.6242324 7.65444721,13.6242324 Z' id='shape' fill='%23FF3B30' fill-rule='nonzero'%3E%3C/path%3E %3C/g%3E %3C/svg%3E");
103
+ height: 21px;
104
+ }
web_assets/stylesheet/chatbot.css ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ hr.append-display {
3
+ margin: 8px 0;
4
+ border: none;
5
+ height: 1px;
6
+ border-top-width: 0;
7
+ background-image: linear-gradient(to right, rgba(50,50,50, 0.1), rgba(150, 150, 150, 0.8), rgba(50,50,50, 0.1));
8
+ }
9
+ .source-a {
10
+ font-size: 0.8em;
11
+ max-width: 100%;
12
+ margin: 0;
13
+ display: flex;
14
+ flex-direction: row;
15
+ flex-wrap: wrap;
16
+ align-items: center;
17
+ /* background-color: #dddddd88; */
18
+ border-radius: 1.5rem;
19
+ padding: 0.2em;
20
+ }
21
+ .source-a a {
22
+ display: inline-block;
23
+ background-color: #aaaaaa50;
24
+ border-radius: 1rem;
25
+ padding: 0.5em;
26
+ text-align: center;
27
+ text-overflow: ellipsis;
28
+ overflow: hidden;
29
+ min-width: 20%;
30
+ white-space: nowrap;
31
+ margin: 0.2rem 0.1rem;
32
+ text-decoration: none !important;
33
+ flex: 1;
34
+ transition: flex 0.5s;
35
+ }
36
+ .source-a a:hover {
37
+ background-color: #aaaaaa20;
38
+ flex: 2;
39
+ }
40
+
41
+ /* 川虎助理 */
42
+ .agent-prefix {
43
+ font-size: smaller;
44
+ opacity: 0.6;
45
+ padding: 6px 0 4px;
46
+ }
47
+ .agent-prefix::before {
48
+ content: '🐯';
49
+ filter: grayscale();
50
+ padding: 0 4px;
51
+ }
52
+
53
+ /* 亮色(默认) */
54
+ #chuanhu-chatbot {
55
+ background-color: var(--chatbot-background-color-light) !important;
56
+ color: var(--chatbot-color-light) !important;
57
+ }
58
+ [data-testid = "bot"] {
59
+ background-color: var(--message-bot-background-color-light) !important;
60
+ }
61
+ [data-testid = "user"] {
62
+ background-color: var(--message-user-background-color-light) !important;
63
+ }
64
+ /* 暗色 */
65
+ .dark #chuanhu-chatbot {
66
+ background-color: var(--chatbot-background-color-dark) !important;
67
+ color: var(--chatbot-color-dark) !important;
68
+ }
69
+ .dark [data-testid = "bot"] {
70
+ background-color: var(--message-bot-background-color-dark) !important;
71
+ }
72
+ .dark [data-testid = "user"] {
73
+ background-color: var(--message-user-background-color-dark) !important;
74
+ }
75
+
76
+ /* 对话气泡 */
77
+ .message {
78
+ border-radius: var(--radius-xl) !important;
79
+ border: none;
80
+ padding: var(--spacing-xl) !important;
81
+ font-size: var(--text-md) !important;
82
+ line-height: var(--line-md) !important;
83
+ min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
84
+ min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
85
+ }
86
+ [data-testid = "bot"] {
87
+ max-width: 85%;
88
+ border-bottom-left-radius: 0 !important;
89
+ }
90
+ [data-testid = "user"] {
91
+ max-width: 85%;
92
+ width: auto !important;
93
+ border-bottom-right-radius: 0 !important;
94
+ }
95
+
96
+ /* 屏幕宽度大于等于500px的设备 */
97
+ /* update on 2023.4.8: 高度的细致调整已写入JavaScript */
98
+ @media screen and (min-width: 500px) {
99
+ #chuanhu-chatbot {
100
+ height: calc(100vh - 200px);
101
+ }
102
+ #chuanhu-chatbot>.wrapper>.wrap {
103
+ max-height: calc(100vh - 200px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
104
+ }
105
+ }
106
+ /* 屏幕宽度小于500px的设备 */
107
+ @media screen and (max-width: 499px) {
108
+ #chuanhu-chatbot {
109
+ height: calc(100vh - 140px);
110
+ }
111
+ #chuanhu-chatbot>.wrapper>.wrap {
112
+ max-height: calc(100vh - 140px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
113
+ }
114
+ [data-testid = "bot"] {
115
+ max-width: 95% !important;
116
+ }
117
+ #app-title h1{
118
+ letter-spacing: -1px; font-size: 22px;
119
+ }
120
+ }
121
+
122
+ #chuanhu-chatbot>.wrapper>.wrap {
123
+ overflow-x: hidden;
124
+ }
125
+
126
+ .message.user p {
127
+ white-space: pre-wrap;
128
+ }
129
+ .message .user-message {
130
+ display: block;
131
+ padding: 0 !important;
132
+ white-space: pre-wrap;
133
+ }
134
+
135
+ .message .md-message p {
136
+ margin-top: 0.6em !important;
137
+ margin-bottom: 0.6em !important;
138
+ }
139
+ .message .md-message p:first-child { margin-top: 0 !important; }
140
+ .message .md-message p:last-of-type { margin-bottom: 0 !important; }
141
+
142
+ .message .md-message {
143
+ display: block;
144
+ padding: 0 !important;
145
+ }
146
+ .message .raw-message p {
147
+ margin:0 !important;
148
+ }
149
+ .message .raw-message {
150
+ display: block;
151
+ padding: 0 !important;
152
+ white-space: pre-wrap;
153
+ }
154
+ .message .hideM {
155
+ display: none;
156
+ }
157
+
158
+ /* custom buttons */
159
+ .chuanhu-btn {
160
+ border-radius: 5px;
161
+ /* background-color: #E6E6E6 !important; */
162
+ color: rgba(120, 120, 120, 0.64) !important;
163
+ padding: 4px !important;
164
+ position: absolute;
165
+ right: -22px;
166
+ cursor: pointer !important;
167
+ transition: color .2s ease, background-color .2s ease;
168
+ }
169
+ .chuanhu-btn:hover {
170
+ background-color: rgba(167, 167, 167, 0.25) !important;
171
+ color: unset !important;
172
+ }
173
+ .chuanhu-btn:active {
174
+ background-color: rgba(167, 167, 167, 0.5) !important;
175
+ }
176
+ .chuanhu-btn:focus {
177
+ outline: none;
178
+ }
179
+
180
+ .copy-bot-btn {
181
+ /* top: 18px; */
182
+ bottom: 0;
183
+ }
184
+ .toggle-md-btn {
185
+ /* top: 0; */
186
+ bottom: 20px;
187
+ }
188
+
189
+ /* note: this is deprecated */
190
+ .copy-code-btn {
191
+ position: relative;
192
+ float: right;
193
+ font-size: 1em;
194
+ cursor: pointer;
195
+ }
196
+ /* note: the button below disabled in chatbot.py */
197
+ .message div.icon-button > button[title="copy"] {
198
+ display: none;
199
+ }
200
+
201
+
202
+ /* history message */
203
+ .wrapper>.wrap>.history-message {
204
+ padding: 10px !important;
205
+ }
206
+ .history-message {
207
+ /* padding: 0 !important; */
208
+ opacity: 80%;
209
+ display: flex;
210
+ flex-direction: column;
211
+ }
212
+ .history-message>.history-message {
213
+ padding: 0 !important;
214
+ }
215
+ .history-message>.message-wrap {
216
+ padding: 0 !important;
217
+ margin-bottom: 16px;
218
+ }
219
+ .history-message>.message {
220
+ margin-bottom: 16px;
221
+ }
222
+ .wrapper>.wrap>.history-message::after {
223
+ content: "";
224
+ display: block;
225
+ height: 2px;
226
+ background-color: var(--body-text-color-subdued);
227
+ margin-bottom: 10px;
228
+ margin-top: -10px;
229
+ clear: both;
230
+ }
231
+ .wrapper>.wrap>.history-message>:last-child::after {
232
+ content: "仅供查看";
233
+ display: block;
234
+ text-align: center;
235
+ color: var(--body-text-color-subdued);
236
+ font-size: 0.8em;
237
+ }
238
+
239
+ /* #chuanhu-chatbot {
240
+ transition: height 0.3s ease;
241
+ note: find it better without transition animation...;
242
+ } */
web_assets/stylesheet/custom-components.css ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* user-info */
3
+ #user-info.block {
4
+ white-space: nowrap;
5
+ position: absolute; left: 8em; top: .8em;
6
+ z-index: var(--layer-2);
7
+ box-shadow: var(--block-shadow);
8
+ border: none!important; border-radius: var(--block-label-radius);
9
+ background: var(--color-accent);
10
+ padding: var(--block-label-padding);
11
+ font-size: var(--block-label-text-size); line-height: var(--line-sm);
12
+ width: auto; max-height: 30px!important;
13
+ opacity: 1;
14
+ transition: opacity 0.3s ease-in-out;
15
+ }
16
+ #user-info.block .wrap {
17
+ opacity: 0;
18
+ }
19
+ #user-info p {
20
+ color: white;
21
+ font-weight: var(--block-label-text-weight);
22
+ }
23
+ #user-info.info-transparent {
24
+ opacity: 0;
25
+ transition: opacity 1s ease-in-out;
26
+ }
27
+
28
+
29
+ /* updater */
30
+ #toast-update {
31
+ position: absolute;
32
+ display: flex;
33
+ top: -500px;
34
+ width: 100%;
35
+ justify-content: center;
36
+ z-index: var(--layer-top);
37
+ transition: top 0.3s ease-out;
38
+ }
39
+ #check-chuanhu-update {
40
+ position: absolute;
41
+ align-items: center;
42
+ display: flex;
43
+ flex-direction: column;
44
+ justify-content: center;
45
+ margin: var(--size-6) var(--size-4);
46
+ box-shadow: var(--shadow-drop-lg);
47
+ border: 1px solid var(--block-label-border-color);
48
+ border-radius: var(--container-radius);
49
+ background: var(--background-fill-primary);
50
+ padding: var(--size-4) var(--size-6);
51
+ min-width: 360px;
52
+ max-width: 480px;
53
+ overflow: hidden;
54
+ pointer-events: auto;
55
+ }
56
+ #version-info-title {
57
+ font-size: 1.2em;
58
+ font-weight: bold;
59
+ text-align: start;
60
+ width: 100%;
61
+ }
62
+ #release-note-wrap {
63
+ width: 100%;
64
+ max-width: 400px;
65
+ height: 120px;
66
+ border: solid 1px var(--border-color-primary);
67
+ overflow: auto;
68
+ padding: 0 8px;
69
+ }
70
+ #release-note-wrap.hideK {
71
+ display: none;
72
+ }
73
+ .btn-update-group {
74
+ display: flex;
75
+ justify-content: space-evenly;
76
+ align-items: center;
77
+ width: 100%;
78
+ padding-top: 10px;
79
+ }
80
+ .btn-update-group.hideK {
81
+ display: none;
82
+ }
83
+ #updating-info {
84
+ margin: 16px 0px 24px;
85
+ text-align: start;
86
+ width: 100%;
87
+ }
88
+
89
+
90
+ #usage-display p, #usage-display span {
91
+ margin: 0;
92
+ font-size: .85em;
93
+ color: var(--body-text-color-subdued);
94
+ }
95
+ .progress-bar {
96
+ background-color: var(--input-background-fill);;
97
+ margin: .5em 0 !important;
98
+ height: 20px;
99
+ border-radius: 10px;
100
+ overflow: hidden;
101
+ }
102
+ .progress {
103
+ background-color: var(--block-title-background-fill);
104
+ height: 100%;
105
+ border-radius: 10px;
106
+ text-align: right;
107
+ transition: width 0.5s ease-in-out;
108
+ }
109
+ .progress-text {
110
+ /* color: white; */
111
+ color: var(--color-accent) !important;
112
+ font-size: 1em !important;
113
+ font-weight: bold;
114
+ padding-right: 10px;
115
+ line-height: 20px;
116
+ }
117
+
118
+
119
+ /* 亮暗色模式切换 */
120
+ #apSwitch input[type="checkbox"] {
121
+ margin: 0 !important;
122
+ }
123
+ #apSwitch label.apSwitch {
124
+ display: flex;
125
+ align-items: center;
126
+ cursor: pointer;
127
+ color: var(--body-text-color);
128
+ font-weight: var(--checkbox-label-text-weight);
129
+ font-size: var(--checkbox-label-text-size);
130
+ line-height: var(--line-md);
131
+ margin: 2px 0 !important;
132
+ }
133
+ input[type="checkbox"]#apSwitch-checkbox::before {
134
+ background: none !important;
135
+ content: '🌞';
136
+ border: none !important;
137
+ box-shadow: none !important;
138
+ font-size: 22px;
139
+ top: -4.4px;
140
+ left: -1px;
141
+ }
142
+ input:checked[type="checkbox"]#apSwitch-checkbox::before {
143
+ content: '🌚';
144
+ left: 16px;
145
+ }
146
+
147
+ /* .apSwitch {
148
+ top: 2px;
149
+ display: inline-block;
150
+ height: 22px;
151
+ position: relative;
152
+ width: 40px;
153
+ border-radius: 11px;
154
+ box-shadow: inset 0 0 1px 0 rgba(0,0,0,0.05), inset 0 0 2px 0 rgba(0,0,0,0.08) !important;
155
+ }
156
+ .apSwitch input {
157
+ display: none !important;
158
+ }
159
+ .apSlider {
160
+ background-color: var(--neutral-200);
161
+ bottom: 0;
162
+ cursor: pointer;
163
+ left: 0;
164
+ position: absolute;
165
+ right: 0;
166
+ top: 0;
167
+ transition: .4s;
168
+ font-size: 22px;
169
+ border-radius: 11px;
170
+ }
171
+ .apSlider::before {
172
+ transform: scale(0.9);
173
+ position: absolute;
174
+ transition: .4s;
175
+ content: "🌞";
176
+ }
177
+ input:checked + .apSlider {
178
+ background-color: var(--primary-600);
179
+ }
180
+ input:checked + .apSlider::before {
181
+ transform: translateX(18px);
182
+ content:"🌚";
183
+ } */
184
+
185
+ /* switch-checkbox */
186
+ .switch-checkbox label {
187
+ flex-direction: row-reverse;
188
+ justify-content: space-between;
189
+ }
190
+ .switch-checkbox input[type="checkbox"] + span {
191
+ margin-left: 0 !important;
192
+ }
193
+
194
+ .switch-checkbox input[type="checkbox"] {
195
+ -moz-appearance: none;
196
+ appearance: none;
197
+ -webkit-appearance: none;
198
+ outline: none;
199
+ }
200
+
201
+ .switch-checkbox input[type="checkbox"] {
202
+ display: inline-block !important;
203
+ position: relative !important;
204
+ border: none !important;
205
+ outline: none;
206
+ width: 40px !important;
207
+ height: 22px !important;
208
+ border-radius: 11px !important;
209
+ background-image: none !important;
210
+ box-shadow: inset 0 0 1px 0 rgba(0,0,0,0.05), inset 0 0 2px 0 rgba(0,0,0,0.08) !important;
211
+ background-image: none !important;
212
+ background-color: var(--switch-checkbox-color-light) !important;
213
+ transition: .2s ease background-color;
214
+ }
215
+ .dark .switch-checkbox input[type="checkbox"] {
216
+ background-color: var(--switch-checkbox-color-dark) !important;
217
+ }
218
+ .switch-checkbox input[type="checkbox"]::before {
219
+ content: "";
220
+ position: absolute;
221
+ width: 22px;
222
+ height: 22px;
223
+ top: 0;
224
+ left: 0;
225
+ background: #FFFFFF;
226
+ border: 0.5px solid rgba(0,0,0,0.02);
227
+ box-shadow: 0 0 0 0 rgba(0,0,0,0.15), 0 1px 0 0 rgba(0,0,0,0.05);
228
+ transform: scale(0.9);
229
+ border-radius: 11px !important;
230
+ transition: .4s ease all;
231
+ box-shadow: var(--input-shadow);
232
+ }
233
+ .switch-checkbox input:checked[type="checkbox"] {
234
+ background-color: var(--primary-600) !important;
235
+ }
236
+ .switch-checkbox input:checked[type="checkbox"]::before {
237
+ background-color: #fff;
238
+ left: 18px;
239
+ }
240
+
web_assets/stylesheet/markdown.css ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .message-wrap>div img{
3
+ border-radius: 10px !important;
4
+ }
5
+
6
+ /* 表格 */
7
+ .message table {
8
+ margin: 1em 0;
9
+ border-collapse: collapse;
10
+ empty-cells: show;
11
+ }
12
+ .message td, .message th {
13
+ border: 1.2px solid var(--border-color-primary) !important;
14
+ padding: 0.2em;
15
+ }
16
+ .message thead {
17
+ background-color: rgba(175,184,193,0.2);
18
+ }
19
+ .message thead th {
20
+ padding: .5em .2em;
21
+ }
22
+
23
+ /* 行内代码 */
24
+ .message :not(pre) code {
25
+ display: inline;
26
+ white-space: break-spaces;
27
+ font-family: var(--font-mono);
28
+ border-radius: 6px;
29
+ margin: 0 2px 0 2px;
30
+ padding: .2em .4em .1em .4em;
31
+ background-color: rgba(175,184,193,0.2);
32
+ }
33
+ /* 代码块 */
34
+ .message pre,
35
+ .message pre[class*=language-] {
36
+ color: #fff;
37
+ overflow-x: auto;
38
+ overflow-y: hidden;
39
+ margin: .8em 1em 1em 0em !important;
40
+ padding: var(--spacing-xl) 1.2em !important;
41
+ border-radius: var(--radius-lg) !important;
42
+ }
43
+ .message pre code,
44
+ .message pre code[class*=language-] {
45
+ color: #fff;
46
+ padding: 0;
47
+ margin: 0;
48
+ background-color: unset;
49
+ text-shadow: none;
50
+ font-family: var(--font-mono);
51
+ }
52
+
53
+
54
+ /* 覆盖prism.css */
55
+ .language-css .token.string,
56
+ .style .token.string,
57
+ .token.entity,
58
+ .token.operator,
59
+ .token.url {
60
+ background: none !important;
61
+ }
web_assets/stylesheet/override-gradio.css ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* 解决container=False时的错误填充 */
3
+ div.form {
4
+ background: none !important;
5
+ }
6
+ div.no-container {
7
+ padding: 10px 0 0 0 !important;
8
+ }
9
+
10
+ /* gradio的页脚信息 */
11
+ footer {
12
+ /* display: none !important; */
13
+ margin-top: .2em !important;
14
+ font-size: 85%;
15
+ }
16
+
17
+ /* 覆盖 gradio 丑陋的复制按钮样式 */
18
+ .message pre button[title="copy"] {
19
+ border-radius: 5px;
20
+ transition: background-color .2s ease;
21
+ }
22
+ .message pre button[title="copy"]:hover {
23
+ background-color: #333232;
24
+ }
25
+ .message pre button .check {
26
+ color: #fff !important;
27
+ background: var(--neutral-950) !important;
28
+ }
29
+
30
+
31
+
32
+
33
+ /* Override Slider Styles (for webkit browsers like Safari and Chrome)
34
+ * 好希望这份提案能早日实现 https://github.com/w3c/csswg-drafts/issues/4410
35
+ * 进度滑块在各个平台还是太不统一了
36
+ **/
37
+
38
+ input[type="range"] {
39
+ /* -webkit-appearance: none; */
40
+ appearance: none;
41
+ height: 4px;
42
+ background: var(--input-background-fill);
43
+ border-radius: 5px;
44
+ background-image: linear-gradient(var(--primary-500),var(--primary-500));
45
+ background-size: 0% 100%;
46
+ background-repeat: no-repeat;
47
+ }
48
+ input[type="range"]::-webkit-slider-thumb {
49
+ -webkit-appearance: none;
50
+ height: 20px;
51
+ width: 20px;
52
+ border-radius: 50%;
53
+ border: solid 0.5px #ddd;
54
+ background-color: white;
55
+ cursor: ew-resize;
56
+ box-shadow: var(--input-shadow);
57
+ transition: background-color .1s ease;
58
+ }
59
+ input[type="range"]::-webkit-slider-thumb:hover {
60
+ background: var(--neutral-50);
61
+ }
62
+ input[type=range]::-webkit-slider-runnable-track {
63
+ -webkit-appearance: none;
64
+ box-shadow: none;
65
+ border: none;
66
+ background: transparent;
67
+ }