@adatechnology/conversations-ui 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-RLUNDWGA.js +51 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -43
- package/dist/participant/index.d.ts +87 -2
- package/dist/participant/index.js +1073 -398
- package/dist/styles.css +218 -0
- package/package.json +1 -1
- package/src/conversationTranscript.ts +6 -2
- package/src/participant/ConversationThread.tsx +8 -1
- package/src/participant/ParticipantAuthorAvatar.tsx +18 -0
- package/src/participant/ParticipantBubbleBody.tsx +33 -3
- package/src/participant/ParticipantConversationMenu.tsx +89 -0
- package/src/participant/ParticipantConversations.tsx +6 -1
- package/src/participant/ParticipantMenuBubble.tsx +106 -0
- package/src/participant/ParticipantMessageBubble.test.tsx +51 -0
- package/src/participant/ParticipantMessageBubble.tsx +39 -7
- package/src/participant/ParticipantMessageInfo.tsx +70 -0
- package/src/participant/ParticipantMessageMenu.tsx +168 -0
- package/src/participant/ParticipantMessageMenuContext.tsx +109 -0
- package/src/participant/ParticipantSelectionBar.tsx +64 -0
- package/src/participant/ParticipantThread.tsx +30 -3
- package/src/participant/ParticipantThreadScreen.tsx +2 -0
- package/src/participant/ParticipantTimeline.tsx +7 -1
- package/src/participant/index.ts +11 -1
- package/src/participant/participantConversationMenuDom.test.tsx +166 -0
- package/src/participant/participantLabels.ts +40 -0
- package/src/participant/participantMessageInfoModel.test.ts +68 -0
- package/src/participant/participantMessageInfoModel.ts +80 -0
- package/src/participant/participantMessageMenuDom.test.tsx +111 -0
- package/src/participant/participantMessageMenuModel.test.ts +43 -0
- package/src/participant/participantMessageMenuModel.ts +91 -0
- package/src/participant/participantMessageMoment.ts +7 -0
- package/src/participant/participantTranscriptionText.test.ts +48 -0
- package/src/participant/participantTranscriptionText.ts +28 -0
- package/src/participant/useOpenUpwards.ts +22 -0
- package/src/participant/useParticipantFailedMenu.ts +3 -0
- package/src/styles.css +41 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ParticipantAttachment } from '@adatechnology/conversation-contracts'
|
|
2
|
+
|
|
3
|
+
export type ReadyTranscription = { readonly filename: string; readonly text: string }
|
|
4
|
+
|
|
5
|
+
/** The audios of a message whose transcription is ready and says something, in attachment order. */
|
|
6
|
+
export function readyTranscriptions(attachments: readonly ParticipantAttachment[]): readonly ReadyTranscription[] {
|
|
7
|
+
return attachments.flatMap((attachment) => {
|
|
8
|
+
const transcription = attachment.kind === 'audio' ? attachment.transcription : undefined
|
|
9
|
+
const text = transcription?.status === 'ready' ? transcription.text.trim() : ''
|
|
10
|
+
return text === '' ? [] : [{ filename: attachment.filename, text }]
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** One attachment in a copy or an export: `[file]`, followed by what was said when the audio was transcribed. */
|
|
15
|
+
export function attachmentForCopy(attachment: ParticipantAttachment, transcriptionLabel: string): string {
|
|
16
|
+
const [ready] = readyTranscriptions([attachment])
|
|
17
|
+
return ready ? `[${attachment.filename}] ${transcriptionLabel}: ${ready.text}` : `[${attachment.filename}]`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The file name of one audio's transcription: the audio's name without its extension, then the label. */
|
|
21
|
+
export function transcriptionFileName(audioFilename: string, transcriptionLabel: string): string {
|
|
22
|
+
const base = audioFilename.replace(/\.[^.]+$/, '')
|
|
23
|
+
const safe = `${base} ${transcriptionLabel}`
|
|
24
|
+
.replace(/[\\/:*?"<>|]+/g, ' ')
|
|
25
|
+
.replace(/\s+/g, ' ')
|
|
26
|
+
.trim()
|
|
27
|
+
return `${safe}.txt`
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useLayoutEffect, useState, type RefObject } from 'react'
|
|
2
|
+
|
|
3
|
+
const SCROLLER_SELECTOR = '.cv-p-thread__scroll'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A panel under a bubble opens downwards; when that would cross the bottom of the thread's scrolling area (the
|
|
7
|
+
* last messages), it opens upwards instead, so it is never cut off.
|
|
8
|
+
*/
|
|
9
|
+
export function useOpenUpwards(isOpen: boolean, panelRef: RefObject<HTMLElement | null>): boolean {
|
|
10
|
+
const [isUpwards, setUpwards] = useState(false)
|
|
11
|
+
useLayoutEffect(() => {
|
|
12
|
+
const panel = panelRef.current
|
|
13
|
+
if (!isOpen || !panel) {
|
|
14
|
+
setUpwards(false)
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
const scroller = panel.closest(SCROLLER_SELECTOR)
|
|
18
|
+
if (!scroller) return
|
|
19
|
+
setUpwards(panel.getBoundingClientRect().bottom > scroller.getBoundingClientRect().bottom)
|
|
20
|
+
}, [isOpen, panelRef])
|
|
21
|
+
return isUpwards
|
|
22
|
+
}
|
|
@@ -21,6 +21,8 @@ export type ParticipantFailedMenuController = {
|
|
|
21
21
|
readonly handleMenuKeyDown: (event: KeyboardEvent<HTMLDivElement>) => void
|
|
22
22
|
readonly handleBlur: (event: FocusEvent<HTMLSpanElement>) => void
|
|
23
23
|
readonly close: () => void
|
|
24
|
+
/** Opens with the first item focused, as a right-click or a long press does. */
|
|
25
|
+
readonly open: () => void
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export function useParticipantFailedMenu(): ParticipantFailedMenuController {
|
|
@@ -56,5 +58,6 @@ export function useParticipantFailedMenu(): ParticipantFailedMenuController {
|
|
|
56
58
|
handleMenuKeyDown: (event) => handleFailedMenuKeyDown({ event, menuRef, triggerRef, dispatch }),
|
|
57
59
|
handleBlur,
|
|
58
60
|
close: () => dispatch({ type: 'close' }),
|
|
61
|
+
open: () => dispatch({ type: 'open', initialFocus: 'first' }),
|
|
59
62
|
}
|
|
60
63
|
}
|
package/src/styles.css
CHANGED
|
@@ -1302,6 +1302,7 @@
|
|
|
1302
1302
|
.cv-p-bubble__author { display: block; flex: 1 0 100%; margin-bottom: 0.15rem; font: 0.65rem 'SFMono-Regular', Consolas, monospace; text-transform: uppercase; color: var(--cv-p-i-accent); }
|
|
1303
1303
|
.cv-p-bubble > .cv-p-text { flex: 0 1 auto; min-width: 0; margin-inline-end: auto; }
|
|
1304
1304
|
.cv-p-bubble > .cv-p-attachment { flex: 1 0 100%; }
|
|
1305
|
+
.cv-p-bubble__host-actions { display: inline-flex; align-items: center; gap: 0.25rem; }
|
|
1305
1306
|
.cv-p-bubble__original { flex: 1 0 100%; margin-top: 0.2rem; }
|
|
1306
1307
|
.cv-p-bubble__original-toggle { display: inline-block; font-size: 0.7rem; color: var(--cv-p-i-text-muted); cursor: pointer; list-style: none; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; }
|
|
1307
1308
|
.cv-p-bubble__original-toggle::-webkit-details-marker { display: none; }
|
|
@@ -1327,6 +1328,45 @@
|
|
|
1327
1328
|
.cv-p-failed-menu__list--below { bottom: auto; top: calc(100% + 0.25rem); }
|
|
1328
1329
|
.cv-p-failed-menu__item--danger { color: var(--cv-p-i-danger); }
|
|
1329
1330
|
.cv-p-failed-menu__item:hover, .cv-p-failed-menu__item:focus-visible { background: var(--cv-p-i-highlight); }
|
|
1331
|
+
/* Menu da mensagem (opt-in `messageMenu`): seta discreta no canto do balão, à vista só com o ponteiro em cima, com
|
|
1332
|
+
foco ou com o menu aberto; no toque abre por toque longo e a seta não ocupa o balão. */
|
|
1333
|
+
.cv-p-message-menu { position: absolute; top: 0.2rem; right: 0.2rem; display: inline-flex; }
|
|
1334
|
+
.cv-p-message-menu__trigger { display: inline-grid; place-items: center; width: 1.5rem; height: 1.25rem; padding: 0; border: 0; border-radius: var(--cv-p-i-radius); color: var(--cv-p-i-text-muted); background: var(--cv-p-tail-fill, var(--cv-p-i-surface-raised)); box-shadow: -0.5rem 0 0.5rem var(--cv-p-tail-fill, var(--cv-p-i-surface-raised)); cursor: pointer; opacity: 0; }
|
|
1335
|
+
.cv-p-bubble:hover .cv-p-message-menu__trigger, .cv-p-message-menu__trigger:focus-visible, .cv-p-message-menu[data-open] .cv-p-message-menu__trigger { opacity: 1; }
|
|
1336
|
+
.cv-p-message-menu__trigger:hover { color: var(--cv-p-i-text); }
|
|
1337
|
+
@media (hover: none) { .cv-p-message-menu__trigger:not(:focus-visible) { pointer-events: none; } }
|
|
1338
|
+
.cv-p-message-menu__list { min-width: 11rem; }
|
|
1339
|
+
.cv-p-message-menu__list { bottom: auto; top: calc(100% + 0.25rem); }
|
|
1340
|
+
.cv-p-message-menu__list.cv-p-message-menu__list--up { top: auto; bottom: calc(100% + 0.25rem); }
|
|
1341
|
+
.cv-p-message-menu__item { display: flex; align-items: center; font: inherit; font-size: 0.85rem; text-transform: none; letter-spacing: normal; text-decoration: none; white-space: nowrap; }
|
|
1342
|
+
.cv-p-message-menu__item:disabled, .cv-p-message-menu__item[aria-disabled='true'] { color: var(--cv-p-i-text-muted); cursor: default; }
|
|
1343
|
+
.cv-p-bubble--menu-open { z-index: 3; }
|
|
1344
|
+
.cv-p-bubble:has(.cv-p-message-menu[data-open]) { z-index: 3; }
|
|
1345
|
+
.cv-p-conversation-menu { position: relative; display: inline-flex; }
|
|
1346
|
+
.cv-p-conversation-menu__trigger { display: inline-flex; align-items: center; justify-content: center; min-width: var(--cv-p-i-touch); min-height: var(--cv-p-i-touch); padding: 0; border: 0; border-radius: var(--cv-p-i-radius); color: var(--cv-p-i-text-muted); background: transparent; cursor: pointer; }
|
|
1347
|
+
.cv-p-conversation-menu__trigger:hover, .cv-p-conversation-menu__trigger:focus-visible, .cv-p-conversation-menu__trigger[aria-expanded='true'] { color: var(--cv-p-i-text); background: var(--cv-p-i-highlight); }
|
|
1348
|
+
.cv-p-conversation-menu__list { z-index: 4; }
|
|
1349
|
+
.cv-p-message-info { min-width: 14rem; padding: 0.5rem 0.75rem 0.65rem; color: var(--cv-p-i-text); font-size: 0.85rem; cursor: default; }
|
|
1350
|
+
.cv-p-message-info__head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.35rem; }
|
|
1351
|
+
.cv-p-message-info__title { font-weight: 600; }
|
|
1352
|
+
.cv-p-message-info__close { display: inline-flex; align-items: center; justify-content: center; padding: 0.2rem; border: 0; border-radius: var(--cv-p-i-radius); color: var(--cv-p-i-text-muted); background: transparent; cursor: pointer; }
|
|
1353
|
+
.cv-p-message-info__close:hover, .cv-p-message-info__close:focus-visible { color: var(--cv-p-i-text); background: var(--cv-p-i-highlight); }
|
|
1354
|
+
.cv-p-message-info__rows { display: grid; gap: 0.25rem; margin: 0; }
|
|
1355
|
+
.cv-p-message-info__row { display: flex; justify-content: space-between; gap: 1rem; }
|
|
1356
|
+
.cv-p-message-info__row dt { color: var(--cv-p-i-text-muted); }
|
|
1357
|
+
.cv-p-message-info__row dd { margin: 0; text-align: end; font-variant-numeric: tabular-nums; }
|
|
1358
|
+
/* Modo de seleção: caixa à esquerda de cada mensagem; a linha inteira marca e desmarca. */
|
|
1359
|
+
.cv-p-select-row { display: flex; align-items: center; gap: 0.6rem; width: 100%; cursor: pointer; border-radius: var(--cv-p-i-radius); }
|
|
1360
|
+
.cv-p-select-row[data-selected] { background: var(--cv-p-i-highlight); }
|
|
1361
|
+
.cv-p-select-row > .cv-p-bubble--mine { margin-inline-start: auto; }
|
|
1362
|
+
.cv-p-select-row > .cv-p-bubble-row { max-width: 92%; }
|
|
1363
|
+
.cv-p-select-row__check { flex: 0 0 auto; width: 1.1rem; height: 1.1rem; margin: 0 0 0 0.25rem; accent-color: var(--cv-p-i-accent); cursor: pointer; }
|
|
1364
|
+
.cv-p-selection-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; padding: 0.6rem 0.75rem; border-top: 1px solid var(--cv-p-i-border); background: var(--cv-p-i-surface-raised); }
|
|
1365
|
+
.cv-p-selection-bar__count { margin-inline-end: auto; font-weight: 600; color: var(--cv-p-i-text); }
|
|
1366
|
+
.cv-p-selection-bar__close, .cv-p-selection-bar__action { min-height: var(--cv-p-i-touch); padding: 0 0.75rem; border: 1px solid var(--cv-p-i-border); border-radius: var(--cv-p-i-radius); color: var(--cv-p-i-text); background: transparent; font: inherit; font-size: 0.85rem; cursor: pointer; }
|
|
1367
|
+
.cv-p-selection-bar__close { display: inline-grid; place-items: center; min-width: var(--cv-p-i-touch); padding: 0; border-color: transparent; }
|
|
1368
|
+
.cv-p-selection-bar__action:disabled { color: var(--cv-p-i-text-muted); cursor: default; }
|
|
1369
|
+
.cv-p-selection-bar__close:hover, .cv-p-selection-bar__action:hover:not(:disabled), .cv-p-selection-bar__close:focus-visible, .cv-p-selection-bar__action:focus-visible { background: var(--cv-p-i-highlight); }
|
|
1330
1370
|
.cv-p-attachment { display: flex; flex-direction: column; gap: 0.25rem; margin-top: 0.35rem; color: inherit; }
|
|
1331
1371
|
.cv-p-attachment--document { flex-direction: row; justify-content: space-between; min-height: var(--cv-p-i-touch); align-items: center; text-decoration: underline; }
|
|
1332
1372
|
.cv-p-attachment__image { display: block; max-width: 100%; max-height: 16rem; object-fit: contain; }
|
|
@@ -1389,6 +1429,7 @@
|
|
|
1389
1429
|
.cv-p-thread__latest-button { animation: cv-p-latest-in 0.16s ease-out; }
|
|
1390
1430
|
.cv-p-protocol__copy, .cv-p-search__input { transition: border-color 0.12s, background-color 0.12s; }
|
|
1391
1431
|
.cv-p-row, .cv-p-button, .cv-p-chip, .cv-p-thread__back, .cv-p-composer__attach { transition: background-color 0.12s, border-color 0.12s; }
|
|
1432
|
+
.cv-p-message-menu__trigger { transition: opacity 0.12s; }
|
|
1392
1433
|
}
|
|
1393
1434
|
|
|
1394
1435
|
/* Scrolls. Verticais: barra fina no tema. Faixas horizontais: sem barra, com esmaecimento nas bordas. O
|