diff --git a/css/main.css b/css/main.css index b7135077..25ae15b1 100644 --- a/css/main.css +++ b/css/main.css @@ -1022,6 +1022,49 @@ audio { padding-right: 0.5rem; } +#new-chat-wrapper { + display: contents; +} + +.new-chat-arrow { + cursor: pointer; + position: relative; + padding: 0; + margin-right: -15px; + height: 39.594px; + display: flex; + align-items: center; +} + +.new-chat-menu { + display: none; + position: absolute; + top: 0; + left: 0; + padding-top: 1.2em; + z-index: var(--layer-top); + white-space: nowrap; +} + +.new-chat-arrow:hover .new-chat-menu { + display: block; +} + +.new-chat-menu-item { + cursor: pointer; + padding: var(--size-2); + background: var(--background-fill-primary); + box-shadow: var(--shadow-drop-lg); + border-radius: var(--container-radius); + color: var(--body-text-color); + font-size: var(--text-md); + font-weight: var(--button-large-text-weight); +} + +.new-chat-menu-item:hover { + background: var(--background-fill-secondary); +} + #past-chats-row, #chat-controls { width: 260px; diff --git a/js/main.js b/js/main.js index 23db59d5..f05f93c6 100644 --- a/js/main.js +++ b/js/main.js @@ -552,6 +552,38 @@ document.querySelectorAll(".focus-on-chat-input").forEach(element => { }); }); +//------------------------------------------------ +// "New chat" hover menu with incognito option +//------------------------------------------------ + +(function() { + const newChatBtn = document.getElementById("new-chat-btn"); + + const wrapper = document.createElement("div"); + wrapper.id = "new-chat-wrapper"; + newChatBtn.replaceWith(wrapper); + wrapper.appendChild(newChatBtn); + + const arrow = document.createElement("span"); + arrow.className = "new-chat-arrow"; + arrow.textContent = "\u25BE"; + + const menu = document.createElement("div"); + menu.className = "new-chat-menu"; + const option = document.createElement("div"); + option.className = "new-chat-menu-item"; + option.textContent = "Incognito chat"; + menu.appendChild(option); + + arrow.appendChild(menu); + wrapper.appendChild(arrow); + + option.addEventListener("click", function(e) { + e.stopPropagation(); + document.querySelector("#incognito-chat-btn").click(); + }); +})(); + //------------------------------------------------ // Fix a border around the "past chats" menu //------------------------------------------------ diff --git a/modules/chat.py b/modules/chat.py index bcb548fd..e4fcaabe 100644 --- a/modules/chat.py +++ b/modules/chat.py @@ -1528,7 +1528,7 @@ def redraw_html(history, name1, name2, mode, style, character, reset_cache=False return chat_html_wrapper(history, name1, name2, mode, style, character, reset_cache=reset_cache) -def start_new_chat(state): +def start_new_chat(state, unique_id=None): mode = state['mode'] # Initialize with empty metadata dictionary history = {'internal': [], 'visible': [], 'metadata': {}} @@ -1542,7 +1542,9 @@ def start_new_chat(state): # Add timestamp for assistant's greeting update_message_metadata(history['metadata'], "assistant", 0, timestamp=get_current_timestamp()) - unique_id = datetime.now().strftime('%Y%m%d-%H-%M-%S') + if unique_id is None: + unique_id = datetime.now().strftime('%Y%m%d-%H-%M-%S') + save_history(history, unique_id, state['character_menu'], state['mode']) return history @@ -1561,6 +1563,9 @@ def save_history(history, unique_id, character, mode): if shared.args.multi_user: return + if unique_id and unique_id.startswith('incognito-'): + return + p = get_history_file_path(unique_id, character, mode) if not p.parent.is_dir(): p.parent.mkdir(parents=True) @@ -1750,6 +1755,9 @@ def save_last_chat_state(character, mode, unique_id): if shared.args.multi_user: return + if unique_id and unique_id.startswith('incognito-'): + return + state = load_last_chat_state() key = get_chat_state_key(character, mode) state["last_chats"][key] = unique_id @@ -2290,11 +2298,29 @@ def handle_start_new_chat_click(state): return [history, html, past_chats_update] -def handle_delete_chat_confirm_click(state): +def handle_start_incognito_chat_click(state): import gradio as gr + unique_id = 'incognito-' + datetime.now().strftime('%Y%m%d-%H-%M-%S') + history = start_new_chat(state, unique_id=unique_id) + html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu']) + + convert_to_markdown.cache_clear() + + histories = find_all_histories_with_first_prompts(state) + past_chats_update = gr.update(choices=histories, value=unique_id) + + return [history, html, past_chats_update] + + +def handle_delete_chat_confirm_click(state): filtered_histories = find_all_histories_with_first_prompts(state) filtered_ids = [h[1] for h in filtered_histories] - index = str(filtered_ids.index(state['unique_id'])) + + if state['unique_id'] not in filtered_ids: + # Incognito or unknown chat — just load the most recent saved chat + index = '0' + else: + index = str(filtered_ids.index(state['unique_id'])) delete_history(state['unique_id'], state['character_menu'], state['mode']) history, unique_id = load_history_after_deletion(state, index) @@ -2302,13 +2328,7 @@ def handle_delete_chat_confirm_click(state): convert_to_markdown.cache_clear() - return [ - history, - html, - unique_id, - gr.update(visible=False), - gr.update(visible=True), - ] + return [history, html, unique_id] def handle_branch_chat_click(state): @@ -2324,7 +2344,8 @@ def handle_branch_chat_click(state): if 'metadata' in history: history['metadata'] = {k: v for k, v in history['metadata'].items() if int(k.split('_')[-1]) <= branch_from_index} - new_unique_id = datetime.now().strftime('%Y%m%d-%H-%M-%S') + prefix = 'incognito-' if state['unique_id'] and state['unique_id'].startswith('incognito-') else '' + new_unique_id = prefix + datetime.now().strftime('%Y%m%d-%H-%M-%S') save_history(history, new_unique_id, state['character_menu'], state['mode']) histories = find_all_histories_with_first_prompts(state) @@ -2446,6 +2467,13 @@ def handle_rename_chat_click(): def handle_rename_chat_confirm(rename_to, state): import gradio as gr + + if state['unique_id'] and state['unique_id'].startswith('incognito-'): + return [ + gr.update(), + gr.update(visible=False), + ] + rename_history(state['unique_id'], rename_to, state['character_menu'], state['mode']) histories = find_all_histories_with_first_prompts(state) diff --git a/modules/ui_chat.py b/modules/ui_chat.py index 0acf9c04..d2a515b8 100644 --- a/modules/ui_chat.py +++ b/modules/ui_chat.py @@ -28,7 +28,8 @@ def create_ui(): shared.gradio['branch_chat'] = gr.Button('Branch', elem_classes=['refresh-button', 'refresh-button-medium'], elem_id='Branch', interactive=not mu) shared.gradio['rename_chat'] = gr.Button('Rename', elem_classes=['refresh-button', 'refresh-button-medium'], interactive=not mu) shared.gradio['delete_chat'] = gr.Button('🗑️', visible=False, elem_classes='refresh-button', interactive=not mu, elem_id='delete_chat') - shared.gradio['Start new chat'] = gr.Button('New chat', elem_classes=['refresh-button', 'refresh-button-medium', 'focus-on-chat-input']) + shared.gradio['Start new chat'] = gr.Button('New chat', elem_classes=['refresh-button', 'refresh-button-medium', 'focus-on-chat-input'], elem_id='new-chat-btn') + shared.gradio['Start incognito chat'] = gr.Button('Incognito chat', visible=False, elem_id='incognito-chat-btn') shared.gradio['branch_index'] = gr.Number(value=-1, precision=0, visible=False, elem_id="Branch-index", interactive=True) shared.gradio['search_chat'] = gr.Textbox(placeholder='Search chats...', max_lines=1, elem_id='search_chat') @@ -290,6 +291,10 @@ def create_event_handlers(): ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then( chat.handle_start_new_chat_click, gradio('interface_state'), gradio('history', 'display', 'unique_id'), show_progress=False) + shared.gradio['Start incognito chat'].click( + ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then( + chat.handle_start_incognito_chat_click, gradio('interface_state'), gradio('history', 'display', 'unique_id'), show_progress=False) + shared.gradio['delete_chat-confirm'].click( ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then( chat.handle_delete_chat_confirm_click, gradio('interface_state'), gradio('history', 'display', 'unique_id'), show_progress=False)