feat(ui): add copy option for selected text
This commit is contained in:
@@ -7,6 +7,8 @@ import { getSessionInfo } from "../stores/sessions"
|
||||
import { messageStoreBus } from "../stores/message-v2/bus"
|
||||
import { useScrollCache } from "../lib/hooks/use-scroll-cache"
|
||||
import { useI18n } from "../lib/i18n"
|
||||
import { copyToClipboard } from "../lib/clipboard"
|
||||
import { showToastNotification } from "../lib/notifications"
|
||||
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
|
||||
|
||||
const SCROLL_SCOPE = "session"
|
||||
@@ -375,7 +377,9 @@ export default function MessageSection(props: MessageSectionProps) {
|
||||
const anchorRect = rects.length > 0 ? rects[0] : range.getBoundingClientRect()
|
||||
const shellRect = shell.getBoundingClientRect()
|
||||
const relativeTop = Math.max(anchorRect.top - shellRect.top - 40, 8)
|
||||
const maxLeft = Math.max(shell.clientWidth - 180, 8)
|
||||
// Keep the popover within the stream shell. The quote popover currently
|
||||
// renders 3 actions; keep enough horizontal room for the pill.
|
||||
const maxLeft = Math.max(shell.clientWidth - 260, 8)
|
||||
const relativeLeft = Math.min(Math.max(anchorRect.left - shellRect.left, 8), maxLeft)
|
||||
setQuoteSelection({ text: limited, top: relativeTop, left: relativeLeft })
|
||||
}
|
||||
@@ -394,6 +398,24 @@ export default function MessageSection(props: MessageSectionProps) {
|
||||
selection?.removeAllRanges()
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCopySelectionRequest() {
|
||||
const info = quoteSelection()
|
||||
if (!info) return
|
||||
|
||||
const success = await copyToClipboard(info.text)
|
||||
showToastNotification({
|
||||
message: success ? t("messageSection.quote.copied") : t("messageSection.quote.copyFailed"),
|
||||
variant: success ? "success" : "error",
|
||||
duration: success ? 2000 : 6000,
|
||||
})
|
||||
|
||||
clearQuoteSelection()
|
||||
if (typeof window !== "undefined") {
|
||||
const selection = window.getSelection()
|
||||
selection?.removeAllRanges()
|
||||
}
|
||||
}
|
||||
|
||||
function handleContentRendered() {
|
||||
if (props.loading) {
|
||||
@@ -835,6 +857,9 @@ export default function MessageSection(props: MessageSectionProps) {
|
||||
<button type="button" class="message-quote-button" onClick={() => handleQuoteSelectionRequest("code")}>
|
||||
{t("messageSection.quote.addAsCode")}
|
||||
</button>
|
||||
<button type="button" class="message-quote-button" onClick={() => void handleCopySelectionRequest()}>
|
||||
{t("messageSection.quote.copy")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -20,6 +20,9 @@ export const messagingMessages = {
|
||||
"messageSection.scroll.toLatestAriaLabel": "Scroll to latest message",
|
||||
"messageSection.quote.addAsQuote": "Add as quote",
|
||||
"messageSection.quote.addAsCode": "Add as code",
|
||||
"messageSection.quote.copy": "Copy",
|
||||
"messageSection.quote.copied": "Copied!",
|
||||
"messageSection.quote.copyFailed": "Copy failed",
|
||||
|
||||
"messageTimeline.ariaLabel": "Message timeline",
|
||||
"messageTimeline.segment.user.label": "You",
|
||||
|
||||
@@ -20,6 +20,9 @@ export const messagingMessages = {
|
||||
"messageSection.scroll.toLatestAriaLabel": "Desplazarse al último mensaje",
|
||||
"messageSection.quote.addAsQuote": "Añadir como cita",
|
||||
"messageSection.quote.addAsCode": "Añadir como código",
|
||||
"messageSection.quote.copy": "Copiar",
|
||||
"messageSection.quote.copied": "¡Copiado!",
|
||||
"messageSection.quote.copyFailed": "No se pudo copiar",
|
||||
|
||||
"messageTimeline.ariaLabel": "Línea de tiempo de mensajes",
|
||||
"messageTimeline.segment.user.label": "Tú",
|
||||
|
||||
@@ -20,6 +20,9 @@ export const messagingMessages = {
|
||||
"messageSection.scroll.toLatestAriaLabel": "Aller au dernier message",
|
||||
"messageSection.quote.addAsQuote": "Ajouter en citation",
|
||||
"messageSection.quote.addAsCode": "Ajouter en code",
|
||||
"messageSection.quote.copy": "Copier",
|
||||
"messageSection.quote.copied": "Copié !",
|
||||
"messageSection.quote.copyFailed": "Impossible de copier",
|
||||
|
||||
"messageTimeline.ariaLabel": "Chronologie des messages",
|
||||
"messageTimeline.segment.user.label": "Vous",
|
||||
|
||||
@@ -20,6 +20,9 @@ export const messagingMessages = {
|
||||
"messageSection.scroll.toLatestAriaLabel": "最新のメッセージへスクロール",
|
||||
"messageSection.quote.addAsQuote": "引用として追加",
|
||||
"messageSection.quote.addAsCode": "コードとして追加",
|
||||
"messageSection.quote.copy": "コピー",
|
||||
"messageSection.quote.copied": "コピーしました",
|
||||
"messageSection.quote.copyFailed": "コピーできませんでした",
|
||||
|
||||
"messageTimeline.ariaLabel": "メッセージタイムライン",
|
||||
"messageTimeline.segment.user.label": "あなた",
|
||||
|
||||
@@ -20,6 +20,9 @@ export const messagingMessages = {
|
||||
"messageSection.scroll.toLatestAriaLabel": "Прокрутить к последнему сообщению",
|
||||
"messageSection.quote.addAsQuote": "Добавить как цитату",
|
||||
"messageSection.quote.addAsCode": "Добавить как код",
|
||||
"messageSection.quote.copy": "Копировать",
|
||||
"messageSection.quote.copied": "Скопировано!",
|
||||
"messageSection.quote.copyFailed": "Не удалось скопировать",
|
||||
|
||||
"messageTimeline.ariaLabel": "Таймлайн сообщений",
|
||||
"messageTimeline.segment.user.label": "Вы",
|
||||
|
||||
@@ -20,6 +20,9 @@ export const messagingMessages = {
|
||||
"messageSection.scroll.toLatestAriaLabel": "滚动到最新消息",
|
||||
"messageSection.quote.addAsQuote": "作为引用添加",
|
||||
"messageSection.quote.addAsCode": "作为代码添加",
|
||||
"messageSection.quote.copy": "复制",
|
||||
"messageSection.quote.copied": "已复制!",
|
||||
"messageSection.quote.copyFailed": "无法复制",
|
||||
|
||||
"messageTimeline.ariaLabel": "消息时间线",
|
||||
"messageTimeline.segment.user.label": "你",
|
||||
|
||||
Reference in New Issue
Block a user