Show more recent chats
| @@ -6,6 +6,10 @@ | |||
| 6 | width: 100%; | 6 | width: 100%; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | .welcomePanel:has(.showMoreChats) { | ||
| 10 | padding-bottom: 5px; | ||
| 11 | } | ||
| 12 | |||
| 9 | body.bubblechat .welcomePanel { | 13 | body.bubblechat .welcomePanel { |
| 10 | border-radius: 10px; | 14 | border-radius: 10px; |
| 11 | background-color: var(--SmartThemeBotMesBlurTintColor); | 15 | background-color: var(--SmartThemeBotMesBlurTintColor); |
| @@ -22,7 +26,7 @@ body.bubblechat .welcomePanel { | |||
| 22 | 26 | ||
| 23 | .welcomePanel .recentChatsTitle { | 27 | .welcomePanel .recentChatsTitle { |
| 24 | flex-grow: 1; | 28 | flex-grow: 1; |
| 25 | font-size: calc(var(--mainFontSize) * 1.1); | 29 | font-size: calc(var(--mainFontSize) * 1.15); |
| 26 | font-weight: 600; | 30 | font-weight: 600; |
| 27 | } | 31 | } |
| 28 | 32 | ||
| @@ -36,7 +40,7 @@ body.bubblechat .welcomePanel { | |||
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | .welcomePanel .welcomeHeaderVersionDisplay { | 42 | .welcomePanel .welcomeHeaderVersionDisplay { |
| 39 | font-size: calc(var(--mainFontSize) * 1.2); | 43 | font-size: calc(var(--mainFontSize) * 1.3); |
| 40 | font-weight: 600; | 44 | font-weight: 600; |
| 41 | } | 45 | } |
| 42 | 46 | ||
| @@ -174,6 +178,14 @@ body.big-avatars .welcomeRecent .recentChatList .recentChat .chatMessageContaine | |||
| 174 | font-size: calc(var(--mainFontSize) * 0.95); | 178 | font-size: calc(var(--mainFontSize) * 0.95); |
| 175 | } | 179 | } |
| 176 | 180 | ||
| 181 | .welcomeRecent .recentChatList .recentChat.hidden { | ||
| 182 | display: none; | ||
| 183 | } | ||
| 184 | |||
| 185 | .welcomeRecent .recentChatList .showMoreChats { | ||
| 186 | align-self: center; | ||
| 187 | } | ||
| 188 | |||
| 177 | @media screen and (max-width: 1000px) { | 189 | @media screen and (max-width: 1000px) { |
| 178 | .welcomePanel .welcomeShortcuts a span { | 190 | .welcomePanel .welcomeShortcuts a span { |
| 179 | display: none; | 191 | display: none; |
| @@ -37,7 +37,7 @@ | |||
| 37 | {{/if}} | 37 | {{/if}} |
| 38 | {{#each chats}} | 38 | {{#each chats}} |
| 39 | {{#with this}} | 39 | {{#with this}} |
| 40 | <div class="recentChat" data-file="{{chat_name}}" data-avatar="{{avatar}}"> | 40 | <div class="recentChat {{#if hidden}}hidden{{/if}}" data-file="{{chat_name}}" data-avatar="{{avatar}}"> |
| 41 | <div class="avatar" title="{{char_name}}"> | 41 | <div class="avatar" title="{{char_name}}"> |
| 42 | <img src="{{char_thumbnail}}" alt="{{char_name}}"> | 42 | <img src="{{char_thumbnail}}" alt="{{char_name}}"> |
| 43 | </div> | 43 | </div> |
| @@ -66,6 +66,13 @@ | |||
| 66 | </div> | 66 | </div> |
| 67 | {{/with}} | 67 | {{/with}} |
| 68 | {{/each}} | 68 | {{/each}} |
| 69 | {{#if more}} | ||
| 70 | <button class="menu_button menu_button_icon showMoreChats"> | ||
| 71 | <small data-i18n="Show more"> | ||
| 72 | Show more | ||
| 73 | </small> | ||
| 74 | </button> | ||
| 75 | {{/if}} | ||
| 69 | </div> | 76 | </div> |
| 70 | </div> | 77 | </div> |
| 71 | </div> | 78 | </div> |
| @@ -62,6 +62,7 @@ async function sendWelcomePanel() { | |||
| 62 | chats, | 62 | chats, |
| 63 | empty: !chats.length, | 63 | empty: !chats.length, |
| 64 | version: displayVersion, | 64 | version: displayVersion, |
| 65 | more: chats.some(chat => chat.hidden), | ||
| 65 | }; | 66 | }; |
| 66 | const template = await renderTemplateAsync('welcomePanel', templateData); | 67 | const template = await renderTemplateAsync('welcomePanel', templateData); |
| 67 | const fragment = document.createRange().createContextualFragment(template); | 68 | const fragment = document.createRange().createContextualFragment(template); |
| @@ -74,9 +75,20 @@ async function sendWelcomePanel() { | |||
| 74 | } | 75 | } |
| 75 | }); | 76 | }); |
| 76 | }); | 77 | }); |
| 77 | fragment.querySelector('button.openTemporaryChat').addEventListener('click', () => { | 78 | const hiddenChats = fragment.querySelectorAll('.recentChat.hidden'); |
| 79 | fragment.querySelectorAll('button.showMoreChats').forEach((button) => { | ||
| 80 | button.addEventListener('click', () => { | ||
| 81 | hiddenChats.forEach((chatItem) => { | ||
| 82 | chatItem.classList.remove('hidden'); | ||
| 83 | }); | ||
| 84 | button.remove(); | ||
| 85 | }); | ||
| 86 | }); | ||
| 87 | fragment.querySelectorAll('button.openTemporaryChat').forEach((button) => { | ||
| 88 | button.addEventListener('click', () => { | ||
| 78 | void newAssistantChat({ temporary: true }); | 89 | void newAssistantChat({ temporary: true }); |
| 79 | }); | 90 | }); |
| 91 | }); | ||
| 80 | chatElement.append(fragment.firstChild); | 92 | chatElement.append(fragment.firstChild); |
| 81 | } catch (error) { | 93 | } catch (error) { |
| 82 | console.error('Welcome screen error:', error); | 94 | console.error('Welcome screen error:', error); |
| @@ -125,6 +137,7 @@ async function openRecentChat(avatarId, fileName) { | |||
| 125 | * @property {string} char_name Character name | 137 | * @property {string} char_name Character name |
| 126 | * @property {string} date_short Date in short format | 138 | * @property {string} date_short Date in short format |
| 127 | * @property {string} date_long Date in long format | 139 | * @property {string} date_long Date in long format |
| 140 | * @property {boolean} hidden Chat will be hidden by default | ||
| 128 | */ | 141 | */ |
| 129 | async function getRecentChats() { | 142 | async function getRecentChats() { |
| 130 | const response = await fetch('/api/characters/recent', { | 143 | const response = await fetch('/api/characters/recent', { |
| @@ -141,13 +154,15 @@ async function getRecentChats() { | |||
| 141 | data.sort((a, b) => b.last_mes - a.last_mes) | 154 | data.sort((a, b) => b.last_mes - a.last_mes) |
| 142 | .map(chat => ({ chat, character: characters.find(x => x.avatar === chat.avatar) })) | 155 | .map(chat => ({ chat, character: characters.find(x => x.avatar === chat.avatar) })) |
| 143 | .filter(t => t.character) | 156 | .filter(t => t.character) |
| 144 | .forEach(({ chat, character }) => { | 157 | .forEach(({ chat, character }, index) => { |
| 158 | const DEFAULT_DISPLAYED = 5; | ||
| 145 | const chatTimestamp = timestampToMoment(chat.last_mes); | 159 | const chatTimestamp = timestampToMoment(chat.last_mes); |
| 146 | chat.char_name = character.name; | 160 | chat.char_name = character.name; |
| 147 | chat.date_short = chatTimestamp.format('l'); | 161 | chat.date_short = chatTimestamp.format('l'); |
| 148 | chat.date_long = chatTimestamp.format('LL LT'); | 162 | chat.date_long = chatTimestamp.format('LL LT'); |
| 149 | chat.chat_name = chat.file_name.replace('.jsonl', ''); | 163 | chat.chat_name = chat.file_name.replace('.jsonl', ''); |
| 150 | chat.char_thumbnail = getThumbnailUrl('avatar', character.avatar); | 164 | chat.char_thumbnail = getThumbnailUrl('avatar', character.avatar); |
| 165 | chat.hidden = index >= DEFAULT_DISPLAYED; | ||
| 151 | }); | 166 | }); |
| 152 | 167 | ||
| 153 | return data; | 168 | return data; |
| @@ -1312,7 +1312,7 @@ router.post('/recent', async function (request, response) { | |||
| 1312 | } | 1312 | } |
| 1313 | } | 1313 | } |
| 1314 | 1314 | ||
| 1315 | const recentChats = allChatFiles.sort((a, b) => b.mtime - a.mtime).slice(0, 5); | 1315 | const recentChats = allChatFiles.sort((a, b) => b.mtime - a.mtime).slice(0, 15); |
| 1316 | const jsonFilesPromise = recentChats.map((file) => { | 1316 | const jsonFilesPromise = recentChats.map((file) => { |
| 1317 | return getChatInfo(file.filePath, { avatar: file.pngFile }); | 1317 | return getChatInfo(file.filePath, { avatar: file.pngFile }); |
| 1318 | }); | 1318 | }); |