@flamingo-stack/openframe-frontend-core 0.0.231 → 0.0.232-snapshot.20260608164341
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-BAQ6SS32.js → chunk-4ZUR4RJ7.js} +2 -2
- package/dist/{chunk-3C4GHSLI.js → chunk-55IS2VJD.js} +813 -771
- package/dist/chunk-55IS2VJD.js.map +1 -0
- package/dist/{chunk-J3F3LZZI.cjs → chunk-7CCNTX6H.cjs} +166 -124
- package/dist/chunk-7CCNTX6H.cjs.map +1 -0
- package/dist/{chunk-RL6VQEFT.cjs → chunk-7RR7C26P.cjs} +7 -7
- package/dist/{chunk-RL6VQEFT.cjs.map → chunk-7RR7C26P.cjs.map} +1 -1
- package/dist/{chunk-DNRO2BXS.cjs → chunk-L7AKGXCD.cjs} +10 -10
- package/dist/{chunk-DNRO2BXS.cjs.map → chunk-L7AKGXCD.cjs.map} +1 -1
- package/dist/{chunk-USACX2O4.js → chunk-N3CB52DX.js} +2 -2
- package/dist/{chunk-NFZLQOD7.cjs → chunk-QZFUVNYV.cjs} +25 -25
- package/dist/{chunk-NFZLQOD7.cjs.map → chunk-QZFUVNYV.cjs.map} +1 -1
- package/dist/{chunk-R46QCZLS.js → chunk-T7REIQSK.js} +2 -2
- package/dist/{chunk-BVLU6HLN.cjs → chunk-VENRDZ74.cjs} +9 -9
- package/dist/{chunk-BVLU6HLN.cjs.map → chunk-VENRDZ74.cjs.map} +1 -1
- package/dist/{chunk-U2OLWICZ.js → chunk-VHPEQK2V.js} +2 -2
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/types/api.types.d.ts +14 -0
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/features/entity-tag-badges.d.ts +14 -6
- package/dist/components/features/entity-tag-badges.d.ts.map +1 -1
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/index.cjs +77 -77
- package/dist/components/index.js +4 -4
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +26 -26
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/tickets/index.cjs +64 -64
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/components/ui/search-input.d.ts.map +1 -1
- package/dist/components/ui/tag.d.ts +1 -1
- package/dist/components/ui/tag.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/chat/hooks/use-realtime-chunk-processor.ts +28 -0
- package/src/components/chat/types/api.types.ts +14 -0
- package/src/components/features/entity-tag-badges.tsx +47 -16
- package/src/components/ui/search-input.tsx +5 -2
- package/src/components/ui/tag.tsx +7 -0
- package/dist/chunk-3C4GHSLI.js.map +0 -1
- package/dist/chunk-J3F3LZZI.cjs.map +0 -1
- /package/dist/{chunk-BAQ6SS32.js.map → chunk-4ZUR4RJ7.js.map} +0 -0
- /package/dist/{chunk-USACX2O4.js.map → chunk-N3CB52DX.js.map} +0 -0
- /package/dist/{chunk-R46QCZLS.js.map → chunk-T7REIQSK.js.map} +0 -0
- /package/dist/{chunk-U2OLWICZ.js.map → chunk-VHPEQK2V.js.map} +0 -0
|
@@ -15,6 +15,11 @@ import { MESSAGE_TYPE } from '../types'
|
|
|
15
15
|
import type { UseRealtimeChunkProcessorReturn, UseRealtimeChunkProcessorOptions, ChatApprovalStatus, PendingToolCallData } from '../types'
|
|
16
16
|
import { getCommandText } from '../utils/tool-call-helpers'
|
|
17
17
|
|
|
18
|
+
// Actions allowed through once the dialog is in direct mode. Everything else is
|
|
19
|
+
// an AI-assistant chunk and gets dropped — an allowlist so any future assistant
|
|
20
|
+
// action is blocked by default.
|
|
21
|
+
const DIRECT_MODE_ALLOWED = new Set(['direct_message', 'message_request', 'system', 'dialog_closed'])
|
|
22
|
+
|
|
18
23
|
/**
|
|
19
24
|
* Hook for processing real-time NATS chunks into message segments
|
|
20
25
|
*/
|
|
@@ -31,6 +36,7 @@ export function useRealtimeChunkProcessor(
|
|
|
31
36
|
// tickets view). Default ON so consumers that haven't wired the flag yet
|
|
32
37
|
// get the new batch UI; pass `false` explicitly to fall back to legacy.
|
|
33
38
|
batchApprovalsEnabled = true,
|
|
39
|
+
isDirectMode = false,
|
|
34
40
|
} = options
|
|
35
41
|
|
|
36
42
|
const accumulatorRef = useRef<MessageSegmentAccumulator>(
|
|
@@ -72,6 +78,14 @@ export function useRealtimeChunkProcessor(
|
|
|
72
78
|
// resumed-dialog initializeWithState.
|
|
73
79
|
const hasEverStreamedRef = useRef(false)
|
|
74
80
|
|
|
81
|
+
// Direct-mode barrier: once engaged, AI-assistant chunks are dropped. Engaged
|
|
82
|
+
// by the host flag (optimistic) or by an in-order DIRECT_MESSAGE chunk.
|
|
83
|
+
const directModeFlagRef = useRef(isDirectMode)
|
|
84
|
+
const sawDirectMessageRef = useRef(false)
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
directModeFlagRef.current = isDirectMode
|
|
87
|
+
}, [isDirectMode])
|
|
88
|
+
|
|
75
89
|
// Track pending escalated approvals (single or batch)
|
|
76
90
|
const pendingEscalatedRef = useRef<
|
|
77
91
|
Map<string, { command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }>
|
|
@@ -93,6 +107,19 @@ export function useRealtimeChunkProcessor(
|
|
|
93
107
|
|
|
94
108
|
const accumulator = accumulatorRef.current
|
|
95
109
|
|
|
110
|
+
if (action.action === 'direct_message') sawDirectMessageRef.current = true
|
|
111
|
+
|
|
112
|
+
if ((directModeFlagRef.current || sawDirectMessageRef.current) && !DIRECT_MODE_ALLOWED.has(action.action)) {
|
|
113
|
+
// Hard stop: tear down an open AI stream exactly once so the typing
|
|
114
|
+
// indicator and half-rendered bubble clear, then drop the chunk.
|
|
115
|
+
if (isInStreamRef.current) {
|
|
116
|
+
isInStreamRef.current = false
|
|
117
|
+
callbacks.onStreamEnd?.()
|
|
118
|
+
accumulator.resetSegments()
|
|
119
|
+
}
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
96
123
|
switch (action.action) {
|
|
97
124
|
case 'message_start':
|
|
98
125
|
isInStreamRef.current = true
|
|
@@ -362,6 +389,7 @@ export function useRealtimeChunkProcessor(
|
|
|
362
389
|
accumulatorRef.current.reset()
|
|
363
390
|
pendingEscalatedRef.current.clear()
|
|
364
391
|
hasInitializedWithData.current = false
|
|
392
|
+
sawDirectMessageRef.current = false
|
|
365
393
|
}, [])
|
|
366
394
|
|
|
367
395
|
const updateApprovalStatus = useCallback(
|
|
@@ -243,6 +243,20 @@ export interface UseRealtimeChunkProcessorOptions {
|
|
|
243
243
|
* dropped from the UI in the unfolded mode.
|
|
244
244
|
*/
|
|
245
245
|
batchApprovalsEnabled?: boolean
|
|
246
|
+
/**
|
|
247
|
+
* Engages the direct-mode barrier optimistically: once true, all AI-assistant
|
|
248
|
+
* chunks (text, thinking, tool executions, approvals, metadata, errors, etc.)
|
|
249
|
+
* are dropped and any open stream is torn down, leaving only human/control
|
|
250
|
+
* messages. A `DIRECT_MESSAGE` chunk engages the same barrier automatically
|
|
251
|
+
* regardless of this flag. Pass this from a host that already knows the dialog
|
|
252
|
+
* switched to direct mode (e.g. an optimistic mode toggle) to close the race
|
|
253
|
+
* window before the server emits the first `DIRECT_MESSAGE`.
|
|
254
|
+
*
|
|
255
|
+
* Do NOT set this when the host reconstructs history by replaying chunks
|
|
256
|
+
* through `processChunk` — a static flag would drop legitimate pre-switch AI
|
|
257
|
+
* history. Rely on the in-order `DIRECT_MESSAGE` detection instead.
|
|
258
|
+
*/
|
|
259
|
+
isDirectMode?: boolean
|
|
246
260
|
}
|
|
247
261
|
|
|
248
262
|
export interface UseRealtimeChunkProcessorReturn {
|
|
@@ -1,32 +1,63 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
1
2
|
import { StatusBadge } from '../ui/status-badge'
|
|
2
3
|
import type { TagAssoc } from '../../types/blog'
|
|
3
4
|
|
|
4
5
|
interface EntityTagBadgesProps {
|
|
5
6
|
/** Flat `<entity>_tags[]` association array (from entity_tags hydration). */
|
|
6
7
|
tags?: TagAssoc[] | null
|
|
8
|
+
/**
|
|
9
|
+
* When set, each badge links to `${basePath}?tags=${slug}` (clickable, SPA nav
|
|
10
|
+
* via next/link). Omit for a non-interactive display (badges with no slug also
|
|
11
|
+
* render non-interactive even when basePath is set).
|
|
12
|
+
*/
|
|
13
|
+
basePath?: string
|
|
14
|
+
/** Cap visible badges; the remainder collapse into a "+N" badge. */
|
|
15
|
+
max?: number
|
|
7
16
|
/** Extra classes on the wrapper row. */
|
|
8
17
|
className?: string
|
|
9
18
|
}
|
|
10
19
|
|
|
20
|
+
// The ONE tag-badge skin (OpenFrame design): rounded-rect, font-mono uppercase
|
|
21
|
+
// StatusBadge on ods-card + ods-border. Clickable badges add the accent hover.
|
|
22
|
+
const BADGE_CLASS = 'bg-ods-card border border-ods-border'
|
|
23
|
+
|
|
11
24
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* nothing when there are no tags.
|
|
25
|
+
* THE single tag-badge renderer for the whole product (hub + lib). Renders the
|
|
26
|
+
* OpenFrame `StatusBadge` chip skin for a flat `<entity>_tags[]` array, optionally
|
|
27
|
+
* clickable (links each tag to its filtered list via `basePath`). The hub's
|
|
28
|
+
* `EntityTagList` delegates here so there is exactly one tag-display design.
|
|
29
|
+
* Renders nothing when there are no tags.
|
|
17
30
|
*/
|
|
18
|
-
export function EntityTagBadges({ tags, className }: EntityTagBadgesProps) {
|
|
19
|
-
|
|
31
|
+
export function EntityTagBadges({ tags, basePath, max, className }: EntityTagBadgesProps) {
|
|
32
|
+
const items = (tags || []).filter((t): t is TagAssoc => !!t && !!t.name)
|
|
33
|
+
if (items.length === 0) return null
|
|
34
|
+
|
|
35
|
+
// Only null/undefined means "no cap"; max={0} shows none, negatives clamp to 0.
|
|
36
|
+
const visibleLimit = max == null ? items.length : Math.max(0, max)
|
|
37
|
+
const shown = items.slice(0, visibleLimit)
|
|
38
|
+
const overflow = items.length - shown.length
|
|
39
|
+
|
|
20
40
|
return (
|
|
21
|
-
<div className={`flex flex-wrap gap-2 w-full ${className || ''}`}>
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
<div className={`flex flex-wrap items-center gap-2 w-full ${className || ''}`}>
|
|
42
|
+
{shown.map((tag) => {
|
|
43
|
+
const key = tag.tag_id ?? tag.id ?? tag.slug ?? tag.name
|
|
44
|
+
const label = (tag.name || '').toUpperCase()
|
|
45
|
+
if (basePath && tag.slug) {
|
|
46
|
+
return (
|
|
47
|
+
<Link key={key} href={`${basePath}?tags=${tag.slug}`} className="inline-flex">
|
|
48
|
+
<StatusBadge
|
|
49
|
+
text={label}
|
|
50
|
+
variant="card"
|
|
51
|
+
className={`${BADGE_CLASS} cursor-pointer transition-colors hover:border-ods-accent`}
|
|
52
|
+
/>
|
|
53
|
+
</Link>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
return <StatusBadge key={key} text={label} variant="card" className={BADGE_CLASS} />
|
|
57
|
+
})}
|
|
58
|
+
{overflow > 0 && (
|
|
59
|
+
<StatusBadge text={`+${overflow}`} variant="card" className={`${BADGE_CLASS} text-ods-text-secondary`} />
|
|
60
|
+
)}
|
|
30
61
|
</div>
|
|
31
62
|
)
|
|
32
63
|
}
|
|
@@ -114,13 +114,16 @@ const innerInputStyles = cn(
|
|
|
114
114
|
// Helper: chip variant → Tag variant mapping
|
|
115
115
|
// ---------------------------------------------------------------------------
|
|
116
116
|
|
|
117
|
-
function chipVariantToTagVariant(variant?: FilterChipData["variant"]): "primary" | "outline" {
|
|
117
|
+
function chipVariantToTagVariant(variant?: FilterChipData["variant"]): "primary" | "outline" | "badge" {
|
|
118
118
|
switch (variant) {
|
|
119
119
|
case "selected":
|
|
120
120
|
return "primary"
|
|
121
|
+
// Content tags render with the unified badge skin (ods-card + ods-border,
|
|
122
|
+
// mono uppercase) — identical to the public EntityTagBadges display.
|
|
123
|
+
case "tag":
|
|
124
|
+
return "badge"
|
|
121
125
|
case "category":
|
|
122
126
|
case "subcategory":
|
|
123
|
-
case "tag":
|
|
124
127
|
default:
|
|
125
128
|
return "outline"
|
|
126
129
|
}
|
|
@@ -42,6 +42,13 @@ const tagVariants = cva(
|
|
|
42
42
|
"bg-[var(--ods-system-greys-soft-grey)] text-[var(--ods-system-greys-grey)]",
|
|
43
43
|
"hover:bg-[var(--ods-system-greys-soft-grey-hover)] active:bg-[var(--ods-system-greys-soft-grey-action)]",
|
|
44
44
|
],
|
|
45
|
+
// Matches the EntityTagBadges / StatusBadge tag skin (ods-card + ods-border,
|
|
46
|
+
// mono uppercase) so the tag-editor chips render identically to the public
|
|
47
|
+
// tag badges. Used for FilterChipData variant 'tag' (see search-input).
|
|
48
|
+
badge: [
|
|
49
|
+
"bg-ods-card text-ods-text-primary border border-ods-border font-mono uppercase tracking-wide",
|
|
50
|
+
"hover:border-ods-accent transition-colors",
|
|
51
|
+
],
|
|
45
52
|
},
|
|
46
53
|
},
|
|
47
54
|
defaultVariants: {
|