diff options
| author | sigoden <sigoden@gmail.com> | 2024-05-19 10:08:44 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-19 10:08:44 +0800 |
| commit | 1b9aed6afbf16094c9ae12f5f2c3f1208bf72c73 (patch) | |
| tree | 52d37189ef7829148e88b1bb116aa2d7ad70fbeb /assets | |
| parent | 31977f9642d61885543da109dc3f7972bf3250f7 (diff) | |
| download | aichat-1b9aed6afbf16094c9ae12f5f2c3f1208bf72c73.tar.gz | |
feat: webui add toolbox(copy-bt/regenerate-btn) to message (#521)
Diffstat (limited to 'assets')
| -rw-r--r-- | assets/arena.html | 78 | ||||
| -rw-r--r-- | assets/playground.html | 79 |
2 files changed, 144 insertions, 13 deletions
diff --git a/assets/arena.html b/assets/arena.html index 35be8a6..edd19e1 100644 --- a/assets/arena.html +++ b/assets/arena.html @@ -14,6 +14,7 @@ <script src="//unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js"></script> <script src="//unpkg.com/marked@12.0.2/lib/marked.umd.js" defer></script> <script src="//unpkg.com/alpinejs@3.13.10/dist/cdn.min.js" defer></script> + <script src="//unpkg.com/clipboard@2.0.1/dist/clipboard.min.js" def></script> <style> :root { --fg-primary: #1652f1; @@ -109,9 +110,11 @@ } .chat-message-content { + position: relative; display: flex; flex-direction: column; width: 100%; + margin-top: -2px; padding-left: 0.625rem; flex-grow: 1; } @@ -164,6 +167,27 @@ width: 100%; } + .message-toolbox { + display: flex; + position: absolute; + bottom: -1.25rem; + } + + .copy-message-btn, + .regenerate-message-btn { + top: 0.7rem; + right: 0.7rem; + cursor: pointer; + font-size: 0.9rem; + padding-right: 4px; + } + + .copy-message-btn svg, + .regenerate-message-btn svg { + width: 1rem; + height: 1rem; + } + .copy-code-btn { position: absolute; top: 0.7rem; @@ -283,8 +307,9 @@ } .spinner { - width: 1.25rem; - height: 1.25rem; + width: 1.1rem; + height: 1.1rem; + margin-top: 3px; border: 2px solid #000; border-bottom-color: transparent; border-radius: 50%; @@ -299,7 +324,6 @@ left: 50%; text-align: center; transform: translate(-50%, 0); - min-width: 200px; background-color: var(--fg-default); color: var(--bg-primary); padding: 1rem; @@ -364,8 +388,8 @@ </select> </div> <div class="chat-body" :id="'chat-body-' + index" @scroll="(event) => handleScrollChatBody(event, index)"> - <template x-for="message in chat.messages" :key="message.id"> - <div class="chat-message"> + <template x-for="(message, messageIndex) in chat.messages" :key="message.id"> + <div class="chat-message" @mouseover="chat.hoveredMessageIndex = messageIndex" @mouseleave="chat.messageHoveredIndex = null"> <div class="chat-avatar" :class="message.role == 'user' ? 'chat-avatar user' : 'chat-avatar assistant'"> <template x-if="message.role == 'user'"> <svg fill="currentColor" viewBox="0 0 16 16"> @@ -384,6 +408,7 @@ </template> </div> <div class="chat-message-content"> + <!-- message --> <template x-if="message.role == 'assistant' && message.html"> <div class="markdown-body" x-html="message.html"></div> </template> @@ -410,6 +435,28 @@ x-if="message.role == 'user' && Object.prototype.toString.call(message.content) == '[object String]'"> <div class="message-text" x-text="message.content"></div> </template> + <!-- toolbox --> + <template x-if="messageIndex == chat.hoveredMessageIndex"> + <div class="message-toolbox"> + <div :id="'copy-message-btn-' + index" class="copy-message-btn" :data-clipboard-text="message.content" + x-init="copyMessageClipboard = new ClipboardJS('#copy-message-btn-' + index); copyMessageClipboard.on('success', () => {toast('Copied Message')})" title="Copy"> + <svg fill="currentColor" viewBox="0 0 16 16"> + <path fill-rule="evenodd" + d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z" /> + </svg> + </div> + <template + x-if="messageIndex == chat.messages.length - 1 && (message.state == 'succeed' || message.state == 'failed')"> + <div class="regenerate-message-btn" @click="(event) => handleRegenerateMessage(index)" title="Regenerate"> + <svg fill="currentColor" viewBox="0 0 16 16"> + <path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" /> + <path + d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466" /> + </svg> + </div> + </template> + </div> + </template> </div> </div> </template> @@ -505,6 +552,8 @@ chats: Array.from(Array(NUM)).map(_ => ({ model_id: "", messages: [], + hoveredMessageIndex: null, + copyMessageClipboard: null, askAbortController: null, shouldScrollChatBodyToBottom: true, isShowScrollToBottomBtn: false, @@ -590,6 +639,22 @@ this.images = []; }, + handleRegenerateMessage(index) { + const chat = this.chats[index]; + const lastIndex = chat.messages.length - 1; + if (lastIndex !== chat.hoveredMessageIndex) { + return + } + let lastMessage = chat.messages[lastIndex]; + lastMessage.content = ""; + lastMessage.state = "loading"; + lastMessage.error = ""; + lastMessage.html = ""; + this.asking++; + this.ask(index); + }, + + handleCancelAsk() { for (const chat of this.chats) { chat.askAbortController?.abort(); @@ -618,6 +683,7 @@ $chatBody.scrollTop = $chatBody.scrollHeight; $chatBody._prevScrollTop = $chatBody.scrollTop; chat.isShowScrollToBottomBtn = false; + chat.autoScrollChatBodyToBottom = true; }, handleNewlineInput(event) { @@ -644,7 +710,7 @@ window.getSelection().addRange(range); document.execCommand('copy'); window.getSelection().removeAllRanges(); - toast("Copied code to clipboard"); + toast("Copied Code"); } }, diff --git a/assets/playground.html b/assets/playground.html index 6160a9f..7e73297 100644 --- a/assets/playground.html +++ b/assets/playground.html @@ -14,6 +14,7 @@ <script src="//unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js"></script> <script src="//unpkg.com/marked@12.0.2/lib/marked.umd.js" defer></script> <script src="//unpkg.com/alpinejs@3.13.10/dist/cdn.min.js" defer></script> + <script src="//unpkg.com/clipboard@2.0.1/dist/clipboard.min.js" def></script> <style> :root { --fg-primary: #1652f1; @@ -168,7 +169,7 @@ .chat-body { display: flex; flex-direction: column; - padding: 1.25rem; + padding: 0.5rem; flex-grow: 1; overflow-x: hidden; overflow-y: auto; @@ -186,9 +187,11 @@ } .chat-message-content { + position: relative; display: flex; flex-direction: column; width: 100%; + margin-top: -2px; padding-left: 0.625rem; flex-grow: 1; } @@ -241,6 +244,27 @@ width: 100%; } + .message-toolbox { + display: flex; + position: absolute; + bottom: -1.25rem; + } + + .copy-message-btn, + .regenerate-message-btn { + top: 0.7rem; + right: 0.7rem; + cursor: pointer; + font-size: 0.9rem; + padding-right: 4px; + } + + .copy-message-btn svg, + .regenerate-message-btn svg { + width: 1rem; + height: 1rem; + } + .copy-code-btn { position: absolute; top: 0.7rem; @@ -362,8 +386,9 @@ } .spinner { - width: 1.25rem; - height: 1.25rem; + width: 1.1rem; + height: 1.1rem; + margin-top: 3px; border: 2px solid #000; border-bottom-color: transparent; border-radius: 50%; @@ -378,7 +403,6 @@ left: 50%; text-align: center; transform: translate(-50%, 0); - min-width: 200px; background-color: var(--fg-default); color: var(--bg-primary); padding: 1rem; @@ -527,8 +551,8 @@ </div> </div> <div class="chat-body" x-ref="chat-body" @scroll="handleScrollChatBody"> - <template x-for="message in messages" :key="message.id"> - <div class="chat-message"> + <template x-for="(message, index) in messages" :key="message.id"> + <div class="chat-message" @mouseover="hoveredMessageIndex = index" @mouseleave="messageHoveredIndex = null"> <div class="chat-avatar" :class="message.role == 'user' ? 'chat-avatar user' : 'chat-avatar assistant'"> <template x-if="message.role == 'user'"> <svg fill="currentColor" viewBox="0 0 16 16"> @@ -547,6 +571,7 @@ </template> </div> <div class="chat-message-content"> + <!-- message --> <template x-if="message.role == 'assistant' && message.html"> <div class="markdown-body" x-html="message.html"></div> </template> @@ -573,6 +598,28 @@ x-if="message.role == 'user' && Object.prototype.toString.call(message.content) == '[object String]'"> <div class="message-text" x-text="message.content"></div> </template> + <!-- toolbox --> + <template x-if="index == hoveredMessageIndex"> + <div class="message-toolbox"> + <div class="copy-message-btn" :data-clipboard-text="message.content" + x-init="copyMessageClipboard = new ClipboardJS('.copy-message-btn'); copyMessageClipboard.on('success', () => {toast('Copied Message')})" title="Copy"> + <svg fill="currentColor" viewBox="0 0 16 16"> + <path fill-rule="evenodd" + d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z" /> + </svg> + </div> + <template + x-if="index == messages.length - 1 && (message.state == 'succeed' || message.state == 'failed')"> + <div class="regenerate-message-btn" @click="handleRegenerateMessage" title="Regenerate"> + <svg fill="currentColor" viewBox="0 0 16 16"> + <path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" /> + <path + d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466" /> + </svg> + </div> + </template> + </div> + </template> </div> </div> </template> @@ -679,6 +726,8 @@ roles: [{ name: "", prompt: "" }], currentModel: {}, messages: [], + hoveredMessageIndex: null, + copyMessageClipboard: null, skipMessageId: -1, input: "", images: [], @@ -750,6 +799,21 @@ this.ask(); }, + handleRegenerateMessage() { + const lastIndex = this.messages.length - 1; + if (lastIndex !== this.hoveredMessageIndex) { + return + } + let lastMessage = this.messages[lastIndex]; + lastMessage.content = ""; + lastMessage.state = "loading"; + lastMessage.error = ""; + lastMessage.html = ""; + this.asking = true; + this.ask(); + }, + + handleCancelAsk() { this.askAbortController?.abort(); }, @@ -773,6 +837,7 @@ $chatBody.scrollTop = $chatBody.scrollHeight; $chatBody._prevScrollTop = $chatBody.scrollTop; this.isShowScrollToBottomBtn = false; + this.autoScrollChatBodyToBottom = true; }, handleShowSidebarBtnClick() { @@ -810,7 +875,7 @@ window.getSelection().addRange(range); document.execCommand('copy'); window.getSelection().removeAllRanges(); - toast("Copied code to clipboard"); + toast("Copied Code"); } }, |
