Skip to content

Commit c25a381

Browse files
authored
Add a "Branch here" footer button to chat messages (#6967)
1 parent 8e10f98 commit c25a381

File tree

6 files changed

+83
-77
lines changed

6 files changed

+83
-77
lines changed

css/main.css

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,67 +1244,31 @@ div.svelte-362y77>*, div.svelte-362y77>.form>* {
12441244
position: relative;
12451245
}
12461246

1247-
.footer-button {
1247+
/* New container for the buttons */
1248+
.message-actions {
12481249
position: absolute;
1250+
bottom: -23px;
1251+
left: 0;
1252+
display: flex;
1253+
gap: 5px;
1254+
opacity: 0;
1255+
transition: opacity 0.2s;
1256+
}
1257+
1258+
.footer-button {
12491259
padding: 0;
12501260
margin: 0;
12511261
border: none;
12521262
border-radius: 3px;
12531263
cursor: pointer;
1254-
opacity: 0;
12551264
display: flex;
12561265
align-items: center;
1257-
transition: opacity 0.2s;
1258-
}
1259-
1260-
.footer-button.footer-copy-button {
1261-
bottom: -23px;
1262-
left: 0;
1263-
}
1264-
1265-
.footer-button.footer-refresh-button {
1266-
bottom: -23px;
1267-
left: 25px;
1268-
}
1269-
1270-
.footer-button.footer-continue-button {
1271-
bottom: -23px;
1272-
left: 50px;
1273-
}
1274-
1275-
.footer-button.footer-remove-button {
1276-
bottom: -23px;
1277-
left: 75px;
1278-
}
1279-
1280-
.footer-button.footer-info-button {
1281-
bottom: -23px;
1282-
}
1283-
1284-
.user-message .footer-button.footer-info-button {
1285-
left: 25px;
1286-
}
1287-
1288-
.assistant-message:not(:last-child) .footer-button.footer-info-button {
1289-
left: 25px;
1290-
}
1291-
1292-
.assistant-message:last-child .footer-button.footer-info-button {
1293-
left: 100px;
1294-
}
1295-
1296-
.message:not(:last-child) .text-bot .footer-button.footer-info-button,
1297-
.message .text-you .footer-button.footer-info-button {
1298-
left: 25px;
1299-
}
1300-
1301-
.message:last-child .text-bot .footer-button.footer-info-button {
1302-
left: 100px;
1266+
justify-content: center;
13031267
}
13041268

1305-
.message:hover .footer-button,
1306-
.user-message:hover .footer-button,
1307-
.assistant-message:hover .footer-button {
1269+
.message:hover .message-actions,
1270+
.user-message:hover .message-actions,
1271+
.assistant-message:hover .message-actions {
13081272
opacity: 1;
13091273
}
13101274

js/global_scope_js.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,37 @@ function copyToClipboard(element) {
1818
});
1919
}
2020

21+
function branchHere(element) {
22+
if (!element) return;
23+
24+
const messageElement = element.closest(".message, .user-message, .assistant-message");
25+
if (!messageElement) return;
26+
27+
const index = messageElement.getAttribute("data-index");
28+
if (!index) return;
29+
30+
const branchIndexInput = document.getElementById("Branch-index").querySelector("input");
31+
if (!branchIndexInput) {
32+
console.error("Element with ID 'Branch-index' not found.");
33+
return;
34+
}
35+
const branchButton = document.getElementById("Branch");
36+
37+
if (!branchButton) {
38+
console.error("Required element 'Branch' not found.");
39+
return;
40+
}
41+
42+
branchIndexInput.value = index;
43+
44+
// Trigger any 'change' or 'input' events Gradio might be listening for
45+
const event = new Event("input", { bubbles: true }); // 'change' might also work
46+
branchIndexInput.dispatchEvent(event);
47+
48+
branchButton.click(); // Gradio will now pick up the 'index'
49+
50+
}
51+
2152
function regenerateClick() {
2253
document.getElementById("Regenerate").click();
2354
}

modules/chat.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,13 @@ def handle_delete_chat_confirm_click(state):
12481248

12491249

12501250
def handle_branch_chat_click(state):
1251-
history = state['history']
1251+
branch_from_index = state['branch_index']
1252+
if branch_from_index == -1:
1253+
history = state['history']
1254+
else:
1255+
history = state['history']
1256+
history['visible'] = history['visible'][:branch_from_index + 1]
1257+
history['internal'] = history['internal'][:branch_from_index + 1]
12521258
new_unique_id = datetime.now().strftime('%Y%m%d-%H-%M-%S')
12531259
save_history(history, new_unique_id, state['character_menu'], state['mode'])
12541260

@@ -1259,7 +1265,7 @@ def handle_branch_chat_click(state):
12591265

12601266
past_chats_update = gr.update(choices=histories, value=new_unique_id)
12611267

1262-
return [history, html, past_chats_update]
1268+
return [history, html, past_chats_update, -1]
12631269

12641270

12651271
def handle_rename_chat_click():

modules/html_generator.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,12 @@ def get_image_cache(path):
335335
refresh_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-repeat"><path d="M4 12v-3a3 3 0 0 1 3 -3h13m-3 -3l3 3l-3 3"></path><path d="M20 12v3a3 3 0 0 1 -3 3h-13m3 3l-3 -3l3 -3"></path></svg>'''
336336
continue_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-player-play"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 4v16l13 -8z" /></svg>'''
337337
remove_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-trash"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 7l16 0" /><path d="M10 11l0 6" /><path d="M14 11l0 6" /><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" /><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" /></svg>'''
338+
branch_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-git-branch"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M7 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M17 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M7 8l0 8" /><path d="M9 18h6a2 2 0 0 0 2 -2v-5" /><path d="M14 14l3 -3l3 3" /></svg>'''
338339
info_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="thinking-icon tabler-icon tabler-icon-info-circle"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a10 10 0 0 1 0 20a10 10 0 0 1 0 -20z" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>'''
339340
info_svg_small = '''<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="thinking-icon tabler-icon tabler-icon-info-circle"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a10 10 0 0 1 0 20a10 10 0 0 1 0 -20z" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>'''
340341

341342
copy_button = f'<button class="footer-button footer-copy-button" title="Copy" onclick="copyToClipboard(this)">{copy_svg}</button>'
343+
branch_button = f'<button class="footer-button footer-branch-button" title="Branch here" onclick="branchHere(this)">{branch_svg}</button>'
342344
refresh_button = f'<button class="footer-button footer-refresh-button" title="Regenerate" onclick="regenerateClick()">{refresh_svg}</button>'
343345
continue_button = f'<button class="footer-button footer-continue-button" title="Continue" onclick="continueClick()">{continue_svg}</button>'
344346
remove_button = f'<button class="footer-button footer-remove-button" title="Remove last reply" onclick="removeLastClick()">{remove_svg}</button>'
@@ -355,6 +357,17 @@ def format_message_timestamp(history, role, index):
355357
return ""
356358

357359

360+
def actions_html(history, i, info_message=""):
361+
return (f'<div class="message-actions">'
362+
f'{copy_button}'
363+
f'{refresh_button if i == len(history["visible"]) - 1 else ""}'
364+
f'{continue_button if i == len(history["visible"]) - 1 else ""}'
365+
f'{remove_button if i == len(history["visible"]) - 1 else ""}'
366+
f'{branch_button}'
367+
f'{info_message}'
368+
f'</div>')
369+
370+
358371
def generate_instruct_html(history):
359372
output = f'<style>{instruct_css}</style><div class="chat" id="chat" data-mode="instruct"><div class="messages">'
360373

@@ -386,22 +399,18 @@ def generate_instruct_html(history):
386399
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
387400
f'<div class="text">'
388401
f'<div class="message-body">{converted_visible[0]}</div>'
389-
f'{copy_button}'
390-
f'{info_message_user}'
402+
f'<div class="message-actions">{copy_button}{info_message_user}</div>'
391403
f'</div>'
392404
f'</div>'
393405
)
394406

395407
output += (
396408
f'<div class="assistant-message" '
397-
f'data-raw="{html.escape(row_internal[1], quote=True)}">'
409+
f'data-raw="{html.escape(row_internal[1], quote=True)}"'
410+
f'data-index={i}>'
398411
f'<div class="text">'
399412
f'<div class="message-body">{converted_visible[1]}</div>'
400-
f'{copy_button}'
401-
f'{refresh_button if i == len(history["visible"]) - 1 else ""}'
402-
f'{continue_button if i == len(history["visible"]) - 1 else ""}'
403-
f'{remove_button if i == len(history["visible"]) - 1 else ""}'
404-
f'{info_message_assistant}'
413+
f'{actions_html(history, i, info_message_assistant)}'
405414
f'</div>'
406415
f'</div>'
407416
)
@@ -441,22 +450,20 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
441450
f'<div class="text">'
442451
f'<div class="username">{name1}{user_timestamp}</div>'
443452
f'<div class="message-body">{converted_visible[0]}</div>'
444-
f'{copy_button}'
453+
f'<div class="message-actions">{copy_button}</div>'
445454
f'</div>'
446455
f'</div>'
447456
)
448457

449458
output += (
450459
f'<div class="message" '
451-
f'data-raw="{html.escape(row_internal[1], quote=True)}">'
460+
f'data-raw="{html.escape(row_internal[1], quote=True)}"'
461+
f'data-index={i}>'
452462
f'<div class="circle-bot">{img_bot}</div>'
453463
f'<div class="text">'
454464
f'<div class="username">{name2}{assistant_timestamp}</div>'
455465
f'<div class="message-body">{converted_visible[1]}</div>'
456-
f'{copy_button}'
457-
f'{refresh_button if i == len(history["visible"]) - 1 else ""}'
458-
f'{continue_button if i == len(history["visible"]) - 1 else ""}'
459-
f'{remove_button if i == len(history["visible"]) - 1 else ""}'
466+
f'{actions_html(history, i)}'
460467
f'</div>'
461468
f'</div>'
462469
)
@@ -496,22 +503,18 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
496503
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
497504
f'<div class="text-you">'
498505
f'<div class="message-body">{converted_visible[0]}</div>'
499-
f'{copy_button}'
500-
f'{info_message_user}'
506+
f'<div class="message-actions">{copy_button}{info_message_user}</div>'
501507
f'</div>'
502508
f'</div>'
503509
)
504510

505511
output += (
506512
f'<div class="message" '
507-
f'data-raw="{html.escape(row_internal[1], quote=True)}">'
513+
f'data-raw="{html.escape(row_internal[1], quote=True)}"'
514+
f'data-index={i}>'
508515
f'<div class="text-bot">'
509516
f'<div class="message-body">{converted_visible[1]}</div>'
510-
f'{copy_button}'
511-
f'{refresh_button if i == len(history["visible"]) - 1 else ""}'
512-
f'{continue_button if i == len(history["visible"]) - 1 else ""}'
513-
f'{remove_button if i == len(history["visible"]) - 1 else ""}'
514-
f'{info_message_assistant}'
517+
f'{actions_html(history, i, info_message_assistant)}'
515518
f'</div>'
516519
f'</div>'
517520
)

modules/ui.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ def list_interface_input_elements():
210210
'negative_prompt',
211211
'dry_sequence_breakers',
212212
'grammar_string',
213+
'branch_index'
213214
]
214215

215216
# Chat elements

modules/ui_chat.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ def create_ui():
2424
with gr.Row(elem_id='past-chats-row', elem_classes=['pretty_scrollbar']):
2525
with gr.Column():
2626
with gr.Row(elem_id='past-chats-buttons'):
27-
shared.gradio['branch_chat'] = gr.Button('Branch', elem_classes='refresh-button', interactive=not mu)
27+
shared.gradio['branch_chat'] = gr.Button('Branch', elem_classes='refresh-button', elem_id='Branch', interactive=not mu)
28+
shared.gradio['branch_index'] = gr.Number(value=-1, precision=0, visible=False, elem_id="Branch-index", interactive=True)
2829
shared.gradio['rename_chat'] = gr.Button('Rename', elem_classes='refresh-button', interactive=not mu)
2930
shared.gradio['delete_chat'] = gr.Button('🗑️', elem_classes='refresh-button', interactive=not mu)
3031
shared.gradio['Start new chat'] = gr.Button('New chat', elem_classes=['refresh-button', 'focus-on-chat-input'])
@@ -258,7 +259,7 @@ def create_event_handlers():
258259

259260
shared.gradio['branch_chat'].click(
260261
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
261-
chat.handle_branch_chat_click, gradio('interface_state'), gradio('history', 'display', 'unique_id'), show_progress=False)
262+
chat.handle_branch_chat_click, gradio('interface_state'), gradio('history', 'display', 'unique_id', 'branch_index'), show_progress=False)
262263

263264
shared.gradio['rename_chat'].click(chat.handle_rename_chat_click, None, gradio('rename_to', 'rename-row'), show_progress=False)
264265
shared.gradio['rename_to-cancel'].click(lambda: gr.update(visible=False), None, gradio('rename-row'), show_progress=False)

0 commit comments

Comments
 (0)