@flamingo-stack/openframe-frontend-core 0.0.275 → 0.0.276
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-5LOLBUZG.cjs → chunk-2GLHJ7ZY.cjs} +12 -12
- package/dist/{chunk-5LOLBUZG.cjs.map → chunk-2GLHJ7ZY.cjs.map} +1 -1
- package/dist/{chunk-EINXNG5D.js → chunk-62U6DFIT.js} +3 -3
- package/dist/{chunk-OKDCUJOZ.cjs → chunk-6IJWWLC3.cjs} +10 -10
- package/dist/{chunk-OKDCUJOZ.cjs.map → chunk-6IJWWLC3.cjs.map} +1 -1
- package/dist/{chunk-DBJZF2OV.cjs → chunk-DPMW5E6Q.cjs} +15 -15
- package/dist/{chunk-DBJZF2OV.cjs.map → chunk-DPMW5E6Q.cjs.map} +1 -1
- package/dist/{chunk-DEAWHNYW.cjs → chunk-IAB7QACX.cjs} +677 -640
- package/dist/chunk-IAB7QACX.cjs.map +1 -0
- package/dist/{chunk-XCPBD2PG.cjs → chunk-LH7R56RR.cjs} +2 -2
- package/dist/chunk-LH7R56RR.cjs.map +1 -0
- package/dist/{chunk-E6RCVOQG.cjs → chunk-MYJKC56A.cjs} +38 -38
- package/dist/{chunk-E6RCVOQG.cjs.map → chunk-MYJKC56A.cjs.map} +1 -1
- package/dist/{chunk-T4XXYAYY.js → chunk-NITJSVUM.js} +3 -3
- package/dist/{chunk-CTBL56EC.js → chunk-NPKMH446.js} +3 -3
- package/dist/{chunk-NY7OG2OJ.js → chunk-QNWFTSAG.js} +3 -3
- package/dist/{chunk-SA4ZTD3G.cjs → chunk-SITWWJWS.cjs} +27 -27
- package/dist/{chunk-SA4ZTD3G.cjs.map → chunk-SITWWJWS.cjs.map} +1 -1
- package/dist/{chunk-FDHKJJPK.js → chunk-UCX75VEM.js} +2 -2
- package/dist/{chunk-EKWPIEHH.js → chunk-VAIPAZPJ.js} +43 -6
- package/dist/{chunk-EKWPIEHH.js.map → chunk-VAIPAZPJ.js.map} +1 -1
- package/dist/{chunk-GHWOQUB4.js → chunk-ZS4XNIZK.js} +2 -2
- package/dist/chunk-ZS4XNIZK.js.map +1 -0
- package/dist/components/chat/index.cjs +3 -3
- package/dist/components/chat/index.js +2 -2
- package/dist/components/chat/utils/history-merge.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/features/board/board.d.ts.map +1 -1
- package/dist/components/features/board/ticket-card.d.ts +2 -1
- package/dist/components/features/board/ticket-card.d.ts.map +1 -1
- package/dist/components/features/board/types.d.ts +1 -0
- package/dist/components/features/board/types.d.ts.map +1 -1
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/index.cjs +85 -85
- package/dist/components/index.js +6 -6
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/onboarding-guides/index.cjs +22 -22
- package/dist/components/onboarding-guides/index.js +2 -2
- package/dist/components/related-content/index.cjs +4 -4
- package/dist/components/related-content/index.js +3 -3
- package/dist/components/tickets/index.cjs +61 -61
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/actions-menu.d.ts +6 -0
- package/dist/components/ui/actions-menu.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/index.cjs +3 -3
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/src/components/chat/utils/history-merge.ts +27 -0
- package/src/components/features/board/board-column.tsx +1 -0
- package/src/components/features/board/board.tsx +6 -1
- package/src/components/features/board/ticket-card.tsx +23 -6
- package/src/components/features/board/types.ts +1 -0
- package/src/components/ui/actions-menu.tsx +11 -1
- package/dist/chunk-DEAWHNYW.cjs.map +0 -1
- package/dist/chunk-GHWOQUB4.js.map +0 -1
- package/dist/chunk-XCPBD2PG.cjs.map +0 -1
- /package/dist/{chunk-EINXNG5D.js.map → chunk-62U6DFIT.js.map} +0 -0
- /package/dist/{chunk-T4XXYAYY.js.map → chunk-NITJSVUM.js.map} +0 -0
- /package/dist/{chunk-CTBL56EC.js.map → chunk-NPKMH446.js.map} +0 -0
- /package/dist/{chunk-NY7OG2OJ.js.map → chunk-QNWFTSAG.js.map} +0 -0
- /package/dist/{chunk-FDHKJJPK.js.map → chunk-UCX75VEM.js.map} +0 -0
|
@@ -41,6 +41,20 @@ function isSyntheticRealtimeId(id: string): boolean {
|
|
|
41
41
|
return SYNTHETIC_REALTIME_ID_PREFIXES.some((prefix) => id.startsWith(prefix))
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** Rendered answer text of an assistant message (TEXT segments, or a plain string). Used to
|
|
45
|
+
* recognise a replayed synthetic as the twin of an already-persisted turn when the stream-seq
|
|
46
|
+
* signal can't prove it (see the trailing-turn fallback below). Empty for tool/approval-only
|
|
47
|
+
* turns that carry no answer text — callers must treat "" as "no signal", never as a match. */
|
|
48
|
+
function assistantAnswerText(content: MessageContent): string {
|
|
49
|
+
if (typeof content === 'string') return content.trim()
|
|
50
|
+
if (!Array.isArray(content)) return ''
|
|
51
|
+
const parts: string[] = []
|
|
52
|
+
for (const seg of content) {
|
|
53
|
+
if (seg.type === 'text') parts.push(seg.text)
|
|
54
|
+
}
|
|
55
|
+
return parts.join('\n').trim()
|
|
56
|
+
}
|
|
57
|
+
|
|
44
58
|
/** Flattens DESC-sorted message pages (newest page first, newest message
|
|
45
59
|
* first within a page) into one chronological list. */
|
|
46
60
|
export function flattenMessagePagesChronological<T>(pages: readonly { messages: readonly T[] }[] | undefined): T[] {
|
|
@@ -170,6 +184,14 @@ export function mergeHistoryWithRealtime<M extends MergeableChatMessage>(input:
|
|
|
170
184
|
}
|
|
171
185
|
}
|
|
172
186
|
|
|
187
|
+
// Content-equality fallback for the trailing turn. The backend stamps `lastChunkStreamSeq`
|
|
188
|
+
// asynchronously, so a just-finished assistant can land in history with a seq BELOW the replay's
|
|
189
|
+
// terminal chunk seq; seq coverage then reads "not covered" and the replayed synthetic survives
|
|
190
|
+
// next to its persisted twin. Recognise the twin by its rendered answer text.
|
|
191
|
+
const lastProcessed = processedToUse[processedToUse.length - 1]
|
|
192
|
+
const trailingAssistantText =
|
|
193
|
+
lastProcessed && lastProcessed.role === 'assistant' ? assistantAnswerText(lastProcessed.content) : ''
|
|
194
|
+
|
|
173
195
|
const realtimeMessages = existingMessages.filter((m) => {
|
|
174
196
|
// Pin wins over everything: the twin may carry a persisted Mongo id (the
|
|
175
197
|
// chunk processors ADOPT an in-progress trailing assistant after a prior
|
|
@@ -196,6 +218,11 @@ export function mergeHistoryWithRealtime<M extends MergeableChatMessage>(input:
|
|
|
196
218
|
// (JetStream resumes after the highest seq this client has consumed).
|
|
197
219
|
// Decided by seq coverage when known, wall-clock otherwise.
|
|
198
220
|
if (isSyntheticRealtimeId(m.id) && m.id !== streamingMessageId) {
|
|
221
|
+
// A non-streaming synthetic that re-renders the trailing persisted assistant verbatim is its
|
|
222
|
+
// twin no matter what the seq signal says — drop it (covers the persistence-lag gap).
|
|
223
|
+
if (m.role === 'assistant' && trailingAssistantText && assistantAnswerText(m.content) === trailingAssistantText) {
|
|
224
|
+
return false
|
|
225
|
+
}
|
|
199
226
|
const covered =
|
|
200
227
|
historyCoversRealtime !== null ? historyCoversRealtime : (m.timestamp?.getTime() ?? 0) <= historyFetchedAt
|
|
201
228
|
if (covered) return false
|
|
@@ -147,6 +147,7 @@ function ColumnBody({ column, getTicketHref, renderAssignSlot, onLoadMore, loadM
|
|
|
147
147
|
key={t.id}
|
|
148
148
|
ticket={t}
|
|
149
149
|
columnId={column.id}
|
|
150
|
+
columnColor={column.color}
|
|
150
151
|
href={getTicketHref?.(t.id)}
|
|
151
152
|
renderAssignSlot={renderAssignSlot}
|
|
152
153
|
dragDisabled={column.dragDisabled}
|
|
@@ -302,7 +302,12 @@ export function Board({
|
|
|
302
302
|
</div>
|
|
303
303
|
<DragOverlay dropAnimation={null}>
|
|
304
304
|
{activeTicket ? (
|
|
305
|
-
<TicketCard
|
|
305
|
+
<TicketCard
|
|
306
|
+
ticket={activeTicket.ticket}
|
|
307
|
+
columnId={activeTicket.columnId}
|
|
308
|
+
columnColor={items.find(c => c.id === activeTicket.columnId)?.color}
|
|
309
|
+
isOverlay
|
|
310
|
+
/>
|
|
306
311
|
) : null}
|
|
307
312
|
</DragOverlay>
|
|
308
313
|
</DndContext>
|
|
@@ -4,11 +4,12 @@ import { useSortable } from '@dnd-kit/sortable'
|
|
|
4
4
|
import { CSS } from '@dnd-kit/utilities'
|
|
5
5
|
import Link from '../../../embed-shims/next-link'
|
|
6
6
|
import * as React from 'react'
|
|
7
|
-
import { LaptopIcon, Flag02Icon } from '../../icons-v2-generated'
|
|
7
|
+
import { LaptopIcon, Flag02Icon, MessagesIcon } from '../../icons-v2-generated'
|
|
8
8
|
import { SquareAvatar } from '../../ui/square-avatar'
|
|
9
9
|
import { Tag } from '../../ui/tag'
|
|
10
10
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip'
|
|
11
11
|
import { cn } from '../../../utils/cn'
|
|
12
|
+
import { getReadableTextColor } from '../../../utils/ods-color-utils'
|
|
12
13
|
import { formatTicketRelativeTime, formatTicketFullTimestamp } from '../../../utils/date-utils'
|
|
13
14
|
import type { BoardPriority, BoardTicket } from './types'
|
|
14
15
|
|
|
@@ -25,6 +26,7 @@ const MAX_VISIBLE_ASSIGNEES = 3
|
|
|
25
26
|
export interface TicketCardProps {
|
|
26
27
|
ticket: BoardTicket
|
|
27
28
|
columnId: string
|
|
29
|
+
columnColor?: string
|
|
28
30
|
href?: string
|
|
29
31
|
isOverlay?: boolean
|
|
30
32
|
dragDisabled?: boolean
|
|
@@ -34,6 +36,7 @@ export interface TicketCardProps {
|
|
|
34
36
|
export function TicketCard({
|
|
35
37
|
ticket,
|
|
36
38
|
columnId,
|
|
39
|
+
columnColor,
|
|
37
40
|
href,
|
|
38
41
|
isOverlay = false,
|
|
39
42
|
dragDisabled,
|
|
@@ -49,10 +52,16 @@ export function TicketCard({
|
|
|
49
52
|
disabled: dragDisabled,
|
|
50
53
|
})
|
|
51
54
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
const showNewMessage = !!ticket.hasNewMessage && !!columnColor
|
|
56
|
+
const newMessageTextColor = columnColor ? getReadableTextColor(columnColor) : undefined
|
|
57
|
+
|
|
58
|
+
const style: React.CSSProperties = isOverlay
|
|
59
|
+
? {}
|
|
60
|
+
: {
|
|
61
|
+
transform: CSS.Transform.toString(sortable.transform),
|
|
62
|
+
transition: sortable.transition,
|
|
63
|
+
}
|
|
64
|
+
if (showNewMessage) style.borderColor = columnColor
|
|
56
65
|
|
|
57
66
|
const showDeviceRow = !!(ticket.deviceHostnames?.length || ticket.organizationName)
|
|
58
67
|
const deviceText = [
|
|
@@ -126,6 +135,14 @@ export function TicketCard({
|
|
|
126
135
|
</Tooltip>
|
|
127
136
|
</TooltipProvider>
|
|
128
137
|
)}
|
|
138
|
+
{showNewMessage && (
|
|
139
|
+
<Tag
|
|
140
|
+
label="New Message"
|
|
141
|
+
icon={<MessagesIcon size={16} color={newMessageTextColor} />}
|
|
142
|
+
className="w-fit shrink-0"
|
|
143
|
+
style={{ backgroundColor: columnColor, color: newMessageTextColor }}
|
|
144
|
+
/>
|
|
145
|
+
)}
|
|
129
146
|
</>
|
|
130
147
|
)
|
|
131
148
|
|
|
@@ -138,7 +155,7 @@ export function TicketCard({
|
|
|
138
155
|
|
|
139
156
|
const outerProps = {
|
|
140
157
|
ref: isOverlay ? undefined : sortable.setNodeRef,
|
|
141
|
-
style
|
|
158
|
+
style,
|
|
142
159
|
className: cardClasses,
|
|
143
160
|
...(isOverlay ? {} : sortable.attributes),
|
|
144
161
|
...(isOverlay ? {} : sortable.listeners),
|
|
@@ -31,6 +31,12 @@ export interface ActionsMenuItem {
|
|
|
31
31
|
disabled?: boolean;
|
|
32
32
|
type?: "item" | "checkbox" | "submenu" | "separator";
|
|
33
33
|
checked?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Keep the dropdown open after this item is clicked instead of closing it
|
|
36
|
+
* (e.g. multi-select add). Only affects ActionsMenuDropdown; `checkbox` and
|
|
37
|
+
* `submenu` items always keep the menu open regardless. Defaults to closing.
|
|
38
|
+
*/
|
|
39
|
+
closeOnSelect?: boolean;
|
|
34
40
|
submenu?: ActionsMenuItem[];
|
|
35
41
|
/** Render the row in the error/destructive color (label + icon). */
|
|
36
42
|
danger?: boolean;
|
|
@@ -373,7 +379,11 @@ export const ActionsMenuDropdown: React.FC<ActionsMenuDropdownProps> = ({
|
|
|
373
379
|
const handleItemClick = useCallback(
|
|
374
380
|
(item: ActionsMenuItem) => {
|
|
375
381
|
onItemClick?.(item);
|
|
376
|
-
if (
|
|
382
|
+
if (
|
|
383
|
+
item.type !== "checkbox" &&
|
|
384
|
+
item.type !== "submenu" &&
|
|
385
|
+
item.closeOnSelect !== false
|
|
386
|
+
) {
|
|
377
387
|
setOpen(false);
|
|
378
388
|
}
|
|
379
389
|
},
|