mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-20 05:27:59 +08:00
feat(web): support manually renaming sessions in console #2897
This commit is contained in:
@@ -244,6 +244,52 @@
|
|||||||
}
|
}
|
||||||
.dark .session-delete:hover { background: rgba(239, 68, 68, 0.15); }
|
.dark .session-delete:hover { background: rgba(239, 68, 68, 0.15); }
|
||||||
|
|
||||||
|
/* Rename button: shares the look of the delete button, sits to its left.
|
||||||
|
Negative right margin tightens the gap to the delete button only. */
|
||||||
|
.session-rename {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: -6px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #9ca3af;
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s, color 0.15s, background 0.15s;
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.session-item:hover .session-rename { opacity: 1; }
|
||||||
|
.session-rename:hover {
|
||||||
|
color: #4ABE6E;
|
||||||
|
background: rgba(74, 190, 110, 0.12);
|
||||||
|
}
|
||||||
|
.dark .session-rename:hover { background: rgba(74, 190, 110, 0.18); }
|
||||||
|
|
||||||
|
/* Inline title editor */
|
||||||
|
.session-title-input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: inherit;
|
||||||
|
color: #111827;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #4ABE6E;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.dark .session-title-input {
|
||||||
|
color: #e5e5e5;
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border-color: #4ABE6E;
|
||||||
|
}
|
||||||
|
|
||||||
/* Context Divider */
|
/* Context Divider */
|
||||||
.context-divider {
|
.context-divider {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ const I18N = {
|
|||||||
today: '今天', yesterday: '昨天', earlier: '更早',
|
today: '今天', yesterday: '昨天', earlier: '更早',
|
||||||
delete_session_confirm: '确认删除该会话?所有消息将被清除。',
|
delete_session_confirm: '确认删除该会话?所有消息将被清除。',
|
||||||
delete_session_title: '删除会话',
|
delete_session_title: '删除会话',
|
||||||
|
rename_session: '重命名',
|
||||||
delete_message_confirm: '确认删除这条消息?',
|
delete_message_confirm: '确认删除这条消息?',
|
||||||
delete_message_title: '删除消息',
|
delete_message_title: '删除消息',
|
||||||
edit_disabled_reply_active: '正在生成回复,暂时无法编辑。',
|
edit_disabled_reply_active: '正在生成回复,暂时无法编辑。',
|
||||||
@@ -460,6 +461,7 @@ const I18N = {
|
|||||||
today: 'Today', yesterday: 'Yesterday', earlier: 'Earlier',
|
today: 'Today', yesterday: 'Yesterday', earlier: 'Earlier',
|
||||||
delete_session_confirm: 'Delete this session? All messages will be removed.',
|
delete_session_confirm: 'Delete this session? All messages will be removed.',
|
||||||
delete_session_title: 'Delete Session',
|
delete_session_title: 'Delete Session',
|
||||||
|
rename_session: 'Rename',
|
||||||
delete_message_confirm: 'Delete this message?',
|
delete_message_confirm: 'Delete this message?',
|
||||||
delete_message_title: 'Delete Message',
|
delete_message_title: 'Delete Message',
|
||||||
edit_disabled_reply_active: 'Reply is being generated; editing is temporarily unavailable.',
|
edit_disabled_reply_active: 'Reply is being generated; editing is temporarily unavailable.',
|
||||||
@@ -3670,6 +3672,9 @@ function _addOptimisticSessionItem(sid) {
|
|||||||
item.innerHTML = `
|
item.innerHTML = `
|
||||||
<i class="fas fa-message session-icon"></i>
|
<i class="fas fa-message session-icon"></i>
|
||||||
<span class="session-title" title="${escapeHtml(title)}">${escapeHtml(title)}</span>
|
<span class="session-title" title="${escapeHtml(title)}">${escapeHtml(title)}</span>
|
||||||
|
<button class="session-rename" onclick="event.stopPropagation(); renameSession('${sid}')" title="${escapeHtml(t('rename_session'))}">
|
||||||
|
<i class="fas fa-pen"></i>
|
||||||
|
</button>
|
||||||
<button class="session-delete" onclick="event.stopPropagation(); deleteSession('${sid}')" title="Delete">
|
<button class="session-delete" onclick="event.stopPropagation(); deleteSession('${sid}')" title="Delete">
|
||||||
<i class="fas fa-trash-can"></i>
|
<i class="fas fa-trash-can"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -3757,6 +3762,9 @@ function _fetchSessionPage(page, clear, onDone) {
|
|||||||
item.innerHTML = `
|
item.innerHTML = `
|
||||||
<i class="fas fa-message session-icon"></i>
|
<i class="fas fa-message session-icon"></i>
|
||||||
<span class="session-title" title="${escapeHtml(title)}">${escapeHtml(title)}</span>
|
<span class="session-title" title="${escapeHtml(title)}">${escapeHtml(title)}</span>
|
||||||
|
<button class="session-rename" onclick="event.stopPropagation(); renameSession('${s.session_id}')" title="${escapeHtml(t('rename_session'))}">
|
||||||
|
<i class="fas fa-pen"></i>
|
||||||
|
</button>
|
||||||
<button class="session-delete" onclick="event.stopPropagation(); deleteSession('${s.session_id}')" title="Delete">
|
<button class="session-delete" onclick="event.stopPropagation(); deleteSession('${s.session_id}')" title="Delete">
|
||||||
<i class="fas fa-trash-can"></i>
|
<i class="fas fa-trash-can"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -3875,6 +3883,84 @@ function switchSession(newSessionId) {
|
|||||||
if (currentView !== 'chat') navigateTo('chat');
|
if (currentView !== 'chat') navigateTo('chat');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In-place rename a session title: replace the title <span> with an <input>,
|
||||||
|
// commit on Enter/blur, cancel on Escape. Persists via PUT /api/sessions/<id>.
|
||||||
|
function renameSession(sid) {
|
||||||
|
const item = document.querySelector(`.session-item[data-session-id="${sid}"]`);
|
||||||
|
if (!item) return;
|
||||||
|
const titleEl = item.querySelector('.session-title');
|
||||||
|
if (!titleEl || item.querySelector('.session-title-input')) return;
|
||||||
|
|
||||||
|
const oldTitle = titleEl.textContent;
|
||||||
|
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'text';
|
||||||
|
input.className = 'session-title-input';
|
||||||
|
input.value = oldTitle;
|
||||||
|
input.maxLength = 100;
|
||||||
|
|
||||||
|
// Avoid switching session while interacting with the input
|
||||||
|
const stop = e => e.stopPropagation();
|
||||||
|
input.addEventListener('click', stop);
|
||||||
|
input.addEventListener('mousedown', stop);
|
||||||
|
|
||||||
|
titleEl.replaceWith(input);
|
||||||
|
input.focus();
|
||||||
|
input.select();
|
||||||
|
|
||||||
|
let done = false;
|
||||||
|
|
||||||
|
const restore = (title) => {
|
||||||
|
if (done) return;
|
||||||
|
done = true;
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.className = 'session-title';
|
||||||
|
span.title = title;
|
||||||
|
span.textContent = title;
|
||||||
|
input.replaceWith(span);
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = () => {
|
||||||
|
if (done) return;
|
||||||
|
const newTitle = input.value.trim();
|
||||||
|
if (!newTitle || newTitle === oldTitle) {
|
||||||
|
restore(oldTitle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Optimistically show the new title, then persist.
|
||||||
|
restore(newTitle);
|
||||||
|
fetch(`/api/sessions/${encodeURIComponent(sid)}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ title: newTitle })
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status !== 'success') {
|
||||||
|
// Revert UI on failure
|
||||||
|
const span = item.querySelector('.session-title');
|
||||||
|
if (span) {
|
||||||
|
span.title = oldTitle;
|
||||||
|
span.textContent = oldTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
const span = item.querySelector('.session-title');
|
||||||
|
if (span) {
|
||||||
|
span.title = oldTitle;
|
||||||
|
span.textContent = oldTitle;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
input.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') { e.preventDefault(); commit(); }
|
||||||
|
else if (e.key === 'Escape') { e.preventDefault(); restore(oldTitle); }
|
||||||
|
});
|
||||||
|
input.addEventListener('blur', commit);
|
||||||
|
}
|
||||||
|
|
||||||
function deleteSession(sid) {
|
function deleteSession(sid) {
|
||||||
showConfirmModal(t('delete_session_title'), t('delete_session_confirm'), () => {
|
showConfirmModal(t('delete_session_title'), t('delete_session_confirm'), () => {
|
||||||
// Before deleting, find the next real session to fall back to when the
|
// Before deleting, find the next real session to fall back to when the
|
||||||
|
|||||||
Reference in New Issue
Block a user