feat: swipe picker expand/collapse, copy button, and left-align text (#5380)

* Initial plan

* feat: add expand/collapse, copy button, and left-align text in swipe picker

Addresses user feedback for swipe picker (added in 1.17.0):
1. Left-align swipe text (was inheriting centered alignment)
2. Add expand/collapse toggle using hidden checkbox + CSS :has
3. Add copy-to-clipboard button using shared copyText utility

Agent-Logs-Url: https://github.com/SillyTavern/SillyTavern/sessions/90892f1c-3c75-404b-a93b-2abe672cc0e3

Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

* revert unintended package-lock.json changes from npm install

Agent-Logs-Url: https://github.com/SillyTavern/SillyTavern/sessions/90892f1c-3c75-404b-a93b-2abe672cc0e3

Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

* fix: address review feedback - hide checkbox, use chevron icons, align buttons with fa-fw

- Fix checkbox visibility by using !important to override input[type=checkbox] grid display
- Change expand icon to fa-chevron-down and collapse to fa-chevron-up
- Add fa-fw class to all action buttons (copy, expand/collapse, branch, delete) for even sizing
- Add align-items: baseline to the actions container

Agent-Logs-Url: https://github.com/SillyTavern/SillyTavern/sessions/c8ca349f-3579-442a-baa1-fc138ba1d7a6

Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

* revert unintended package-lock.json changes

Agent-Logs-Url: https://github.com/SillyTavern/SillyTavern/sessions/c8ca349f-3579-442a-baa1-fc138ba1d7a6

Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

* fix: update class names for chat action elements in swipe picker

* fix: prevent chevron layout shift and swap button order

- Use single label element as icon (fa-chevron-down) instead of two
  child <i> elements toggling visibility, preventing layout shift
- Override ::before content to chevron-up when expanded via CSS
- Swap chevron and copy button order (chevron first, then copy)

Agent-Logs-Url: https://github.com/SillyTavern/SillyTavern/sessions/284283b1-6c7f-4371-bc4a-94d841513879

Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

* style: add comment for FA unicode value in CSS

Agent-Logs-Url: https://github.com/SillyTavern/SillyTavern/sessions/284283b1-6c7f-4371-bc4a-94d841513879

Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>

* Rotate the chevron instead of changing content

* Make the ui full height

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
Copilot
2026-03-31 00:41:03 +03:00
committed by GitHub
parent c918f4f36d
commit 63feac9423
3 changed files with 80 additions and 5 deletions
+1 -1
View File
@@ -6733,7 +6733,7 @@
<small class="chat_file_size select_chat_block_filename_item"></small>
<small class="chat_messages_num select_chat_block_filename_item"></small>
</div>
<div class="flex-container gap10px">
<div class="select_chat_actions flex-container gap10px">
<div title="Export JSONL chat file" data-format="jsonl" class="exportRawChatButton opacity50p hoverglow fa-solid fa-file-export" data-i18n="[title]Export JSONL chat file"></div>
<div title="Download chat as plain text document" data-format="txt" class="exportChatButton opacity50p hoverglow fa-solid fa-file-lines" data-i18n="[title]Download chat as plain text document"></div>
<div title="Delete chat file" file_name="" class="PastChat_cross opacity50p hoverglow fa-solid fa-skull" data-i18n="[title]Delete chat file"></div>
+37 -4
View File
@@ -4,7 +4,7 @@ import { t } from './i18n.js';
import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
import { power_user } from './power-user.js';
import { getTokenCountAsync } from './tokenizers.js';
import { clamp, timestampToMoment } from './utils.js';
import { clamp, copyText, timestampToMoment } from './utils.js';
import { chat, deleteSwipe, ensureSwipes, isMessageSwipeable, isSwipingAllowed, swipe, syncMesToSwipe } from '/script.js';
/**
@@ -132,6 +132,7 @@ async function openSwipePicker(messageId) {
const template = $('#past_chat_template .select_chat_block_wrapper').clone();
const block = template.find('.select_chat_block');
block.removeClass('select_chat_block').addClass('swipe_picker_block');
block.find('.select_chat_actions').removeClass('gap10px');
const branchButton = template.find('.exportRawChatButton');
const deleteButton = template.find('.PastChat_cross');
const swipeInfo = Array.isArray(message.swipe_info) ? message.swipe_info[index] : null;
@@ -162,7 +163,7 @@ async function openSwipePicker(messageId) {
'data-i18n': '[title]Create Branch',
})
.removeClass('exportRawChatButton fa-solid fa-file-export')
.addClass('swipe_picker_branch mes_button fa-regular fa-code-branch')
.addClass('swipe_picker_branch mes_button fa-fw fa-regular fa-code-branch')
.on('click', async (event) => {
event.preventDefault();
event.stopPropagation();
@@ -174,7 +175,7 @@ async function openSwipePicker(messageId) {
.removeAttr('file_name')
.attr('aria-disabled', String(!canDeleteSwipe))
.removeClass('fa-skull')
.addClass('swipe_picker_delete fa-trash-can')
.addClass('swipe_picker_delete fa-fw fa-trash-can')
.toggleClass('hoverglow', canDeleteSwipe)
.toggleClass('disabled', !canDeleteSwipe)
.each(function () {
@@ -229,11 +230,42 @@ async function openSwipePicker(messageId) {
await renderSwipeList();
});
// Add expand/collapse toggle
const expandCheckboxId = `swipe_picker_expand_${messageId}_${index}`;
const expandCheckbox = document.createElement('input');
expandCheckbox.type = 'checkbox';
expandCheckbox.id = expandCheckboxId;
expandCheckbox.classList.add('swipe_picker_expand_toggle');
block[0].prepend(expandCheckbox);
const expandLabel = document.createElement('label');
expandLabel.htmlFor = expandCheckboxId;
expandLabel.classList.add('swipe_picker_expand_label', 'fa-solid', 'fa-fw', 'fa-chevron-down');
expandLabel.title = t`Expand/Collapse`;
expandLabel.setAttribute('data-i18n', '[title]Expand/Collapse');
expandLabel.addEventListener('click', (event) => event.stopPropagation());
// Add copy button
const copyButton = document.createElement('div');
copyButton.classList.add('swipe_picker_copy', 'fa-solid', 'fa-fw', 'fa-copy');
copyButton.title = t`Copy`;
copyButton.setAttribute('data-i18n', '[title]Copy');
copyButton.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
await copyText(swipeText);
toastr.info(t`Copied!`, '', { timeOut: 2000 });
});
// Insert new buttons before the branch button
branchButton.before(expandLabel, copyButton);
template.find('.select_chat_block_filename').text(`#${index + 1}${index === Number(message.swipe_id ?? 0) ? ` ${t`[Current]`}` : ''}`);
template.find('.chat_messages_date').text(sendDate);
template.find('.chat_file_size').text(swipeDetails.length ? `(${swipeDetails[0]}${swipeDetails.length > 1 ? ',' : ')'}` : '');
template.find('.chat_messages_num').text(swipeDetails.length > 1 ? `${swipeDetails.slice(1).join(', ')})` : '');
template.find('.select_chat_block_mes').text(previewText || t`(empty swipe)`);
template.find('.select_chat_block_mes').text(previewText ? swipeText : t`(empty swipe)`);
block.on('click', () => setSelectedSwipe(index));
block.on('dblclick', async () => {
@@ -269,6 +301,7 @@ async function openSwipePicker(messageId) {
defaultState: String(selectedSwipeId + 1),
tooltip: `1-${message.swipes.length}`,
}],
large: true,
wider: true,
allowVerticalScrolling: true,
onOpen: function () {
+42
View File
@@ -4788,6 +4788,48 @@ h5 {
user-select: none;
}
.swipe_picker_block .select_chat_block_mes {
text-align: left;
}
.swipe_picker_block .select_chat_actions {
align-items: baseline;
}
.swipe_picker_expand_toggle {
display: none !important;
}
.swipe_picker_block:has(.swipe_picker_expand_toggle:checked) .select_chat_block_mes {
display: block;
-webkit-line-clamp: unset;
line-clamp: unset;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.swipe_picker_expand_label {
cursor: pointer;
opacity: 0.5;
}
.swipe_picker_expand_label:hover {
opacity: 1;
}
.swipe_picker_copy {
cursor: pointer;
opacity: 0.5;
}
.swipe_picker_copy:hover {
opacity: 1;
}
.swipe_picker_block:has(.swipe_picker_expand_toggle:checked) .swipe_picker_expand_label {
transform: rotate(180deg);
}
.select_chat_block .avatar {
grid-row: span 2;
}