@flamingo-stack/openframe-frontend-core 0.0.217 → 0.0.218
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-L6IBKPVM.js → chunk-EKBM4FHK.js} +2 -2
- package/dist/{chunk-SWZUZYWR.js → chunk-EWA2NFUR.js} +2 -2
- package/dist/{chunk-TYIBMDUZ.cjs → chunk-FZZBCRID.cjs} +7 -7
- package/dist/{chunk-TYIBMDUZ.cjs.map → chunk-FZZBCRID.cjs.map} +1 -1
- package/dist/{chunk-G2HHSZ3S.cjs → chunk-GE64T3JT.cjs} +9 -9
- package/dist/{chunk-G2HHSZ3S.cjs.map → chunk-GE64T3JT.cjs.map} +1 -1
- package/dist/{chunk-YWDC5BXM.cjs → chunk-L5RSJE2I.cjs} +1940 -915
- package/dist/chunk-L5RSJE2I.cjs.map +1 -0
- package/dist/{chunk-BVFRD34B.js → chunk-OHOUSDAY.js} +2 -2
- package/dist/{chunk-MVQ3OODK.cjs → chunk-S4SVD5JI.cjs} +9 -9
- package/dist/{chunk-MVQ3OODK.cjs.map → chunk-S4SVD5JI.cjs.map} +1 -1
- package/dist/{chunk-N5IKPYRL.js → chunk-SWIR5EB2.js} +2 -2
- package/dist/{chunk-6DCKL73F.cjs → chunk-TCJ5B2ZD.cjs} +24 -24
- package/dist/{chunk-6DCKL73F.cjs.map → chunk-TCJ5B2ZD.cjs.map} +1 -1
- package/dist/{chunk-ENBGG2K2.js → chunk-V5JY5RSY.js} +2954 -1929
- package/dist/chunk-V5JY5RSY.js.map +1 -0
- package/dist/components/chat/embeddable-chat.d.ts +13 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +104 -10
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-slash-commands.d.ts +6 -0
- package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-unified-chat.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/unified-chat-state.types.d.ts +81 -0
- package/dist/components/chat/types/unified-chat-state.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/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/index.cjs +73 -51
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +26 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/app-header.d.ts +7 -0
- package/dist/components/navigation/app-header.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts +65 -0
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -0
- package/dist/components/navigation/app-layout.d.ts +9 -1
- package/dist/components/navigation/app-layout.d.ts.map +1 -1
- package/dist/components/navigation/header-mingo-button.d.ts +21 -0
- package/dist/components/navigation/header-mingo-button.d.ts.map +1 -0
- package/dist/components/navigation/index.cjs +24 -2
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.d.ts +5 -1
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +23 -1
- package/dist/components/onboarding-guides/index.cjs +18 -18
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +80 -66
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.js +20 -6
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +26 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -1
- package/dist/utils/embed-authed-fetch.d.ts +80 -0
- package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
- package/dist/utils/index.cjs +70 -5
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +70 -6
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/chat/embeddable-chat.tsx +154 -37
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +601 -23
- package/src/components/chat/hooks/use-slash-commands.ts +10 -1
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +45 -0
- package/src/components/chat/hooks/use-unified-chat.ts +59 -0
- package/src/components/chat/types/unified-chat-state.types.ts +116 -0
- package/src/components/navigation/app-header.tsx +23 -0
- package/src/components/navigation/app-layout-drawer.tsx +620 -0
- package/src/components/navigation/app-layout.tsx +65 -26
- package/src/components/navigation/header-mingo-button.tsx +58 -0
- package/src/components/navigation/index.ts +17 -1
- package/src/components/tickets/hooks/use-ticket-engagements.ts +24 -4
- package/src/stories/AppLayoutDrawer.stories.tsx +228 -0
- package/src/utils/.embed-authed-fetch.md +7 -0
- package/src/utils/__tests__/embed-authed-fetch.test.ts +103 -1
- package/src/utils/embed-authed-fetch.ts +247 -7
- package/src/utils/index.ts +5 -1
- package/dist/chunk-ENBGG2K2.js.map +0 -1
- package/dist/chunk-YWDC5BXM.cjs.map +0 -1
- /package/dist/{chunk-L6IBKPVM.js.map → chunk-EKBM4FHK.js.map} +0 -0
- /package/dist/{chunk-SWZUZYWR.js.map → chunk-EWA2NFUR.js.map} +0 -0
- /package/dist/{chunk-BVFRD34B.js.map → chunk-OHOUSDAY.js.map} +0 -0
- /package/dist/{chunk-N5IKPYRL.js.map → chunk-SWIR5EB2.js.map} +0 -0
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { useEffect, useState } from "react";
|
|
17
|
+
import { embedAuthedFetch } from "../../../utils/embed-authed-fetch";
|
|
17
18
|
import type {
|
|
18
19
|
SlashCommandActionId,
|
|
19
20
|
SlashCommandSummaryAction,
|
|
@@ -44,6 +45,12 @@ export type {
|
|
|
44
45
|
* (e.g. `https://hub.openframe.ai/api/commands` → the base arg is
|
|
45
46
|
* ignored).
|
|
46
47
|
*
|
|
48
|
+
* Auth: rides on `embedAuthedFetch` (same bearer-act-as + 401 self-heal
|
|
49
|
+
* as the chat stream / identity / attachment calls) so a host that runs
|
|
50
|
+
* proxy-impersonation or a refresh-capable auth adapter gets its creds
|
|
51
|
+
* attached here too — no host-side `window.fetch` patch required. Hosts
|
|
52
|
+
* with no adapter fall through to cookie auth unchanged.
|
|
53
|
+
*
|
|
47
54
|
* Returns the parsed `commands` array; on any non-2xx (auth,
|
|
48
55
|
* rate-limit, chat-disabled) returns an empty array — the autocomplete
|
|
49
56
|
* UI silently shows no suggestions rather than surfacing 401/403/429
|
|
@@ -56,7 +63,9 @@ export async function fetchSlashCommands(
|
|
|
56
63
|
): Promise<SlashCommandSummary[]> {
|
|
57
64
|
const url = new URL(commandsUrl, window.location.origin);
|
|
58
65
|
if (prefix) url.searchParams.set("q", prefix);
|
|
59
|
-
|
|
66
|
+
// `headers: {}` opts out of the default `Content-Type: application/json`
|
|
67
|
+
// — this is a bare GET with no body, so no content-type is needed.
|
|
68
|
+
const res = await embedAuthedFetch(url.toString(), { signal, headers: {} });
|
|
60
69
|
if (!res.ok) return [];
|
|
61
70
|
const data = (await res.json()) as { commands?: SlashCommandSummary[] };
|
|
62
71
|
return data.commands ?? [];
|
|
@@ -57,6 +57,7 @@ import type {
|
|
|
57
57
|
UnifiedSendMessageOptions,
|
|
58
58
|
StreamingPhase,
|
|
59
59
|
} from '../types/unified-chat-state.types'
|
|
60
|
+
import type { DialogItem } from '../types/component.types'
|
|
60
61
|
|
|
61
62
|
// =============================================================================
|
|
62
63
|
// Public types
|
|
@@ -1054,5 +1055,49 @@ export function useSseChatAdapter(
|
|
|
1054
1055
|
/** Cross-call usage breakdown (Haiku rewriter/classifier/summarizer
|
|
1055
1056
|
* token counts). null until the trailing usage frame lands. */
|
|
1056
1057
|
currentUsageBreakdown: latestMeta?.breakdown ?? null,
|
|
1058
|
+
// ─── Dialog management — stubs for v1 ────────────────────────────────
|
|
1059
|
+
// Guide mode currently keeps its history in `localStorage` opaquely
|
|
1060
|
+
// under the hood (`runtime.source` namespaced key). Surfacing that
|
|
1061
|
+
// history as a structured dialog list is a follow-up; for now the
|
|
1062
|
+
// shape is satisfied with empty defaults so the unified contract
|
|
1063
|
+
// type-checks and EmbeddableChat hides sidebar affordances when
|
|
1064
|
+
// `dialogs.length === 0`.
|
|
1065
|
+
dialogs: SSE_EMPTY_DIALOGS,
|
|
1066
|
+
activeDialogId: null,
|
|
1067
|
+
selectDialog: noopSelectDialog,
|
|
1068
|
+
startNewDialog: noopStartNewDialog,
|
|
1069
|
+
deleteDialog: noopDeleteDialog,
|
|
1070
|
+
isDialogsLoading: false,
|
|
1071
|
+
isMessagesLoading: false,
|
|
1072
|
+
hasMoreDialogs: false,
|
|
1073
|
+
loadMoreDialogs: noopAsync,
|
|
1074
|
+
hasMoreMessages: false,
|
|
1075
|
+
loadMoreMessages: noopAsync,
|
|
1076
|
+
approveRequest: noopApproveRequest,
|
|
1077
|
+
rejectRequest: noopRejectRequest,
|
|
1078
|
+
dialogTokenUsage: null,
|
|
1079
|
+
connectionState: 'connected' as const,
|
|
1057
1080
|
}
|
|
1058
1081
|
}
|
|
1082
|
+
|
|
1083
|
+
// ─── Stable no-op references for the Guide-mode dialog-management stubs ──
|
|
1084
|
+
// Plain module-scope constants so the adapter's return identity stays
|
|
1085
|
+
// stable across renders — consumers that memo on these fields don't get
|
|
1086
|
+
// spurious re-runs.
|
|
1087
|
+
const SSE_EMPTY_DIALOGS: DialogItem[] = []
|
|
1088
|
+
const noopSelectDialog = (_id: string | null): void => {
|
|
1089
|
+
/* Guide mode has no managed dialog list yet */
|
|
1090
|
+
}
|
|
1091
|
+
const noopStartNewDialog = async (): Promise<string | null> => null
|
|
1092
|
+
const noopDeleteDialog = async (_id: string): Promise<void> => {
|
|
1093
|
+
/* no-op until Guide localStorage history is exposed */
|
|
1094
|
+
}
|
|
1095
|
+
const noopAsync = async (): Promise<void> => {
|
|
1096
|
+
/* no-op pagination stub */
|
|
1097
|
+
}
|
|
1098
|
+
const noopApproveRequest = async (_id: string): Promise<void> => {
|
|
1099
|
+
/* Guide mode has no tool-call approval workflow */
|
|
1100
|
+
}
|
|
1101
|
+
const noopRejectRequest = async (_id: string, _reason?: string): Promise<void> => {
|
|
1102
|
+
/* Guide mode has no tool-call approval workflow */
|
|
1103
|
+
}
|
|
@@ -141,6 +141,40 @@ export function useUnifiedChat(
|
|
|
141
141
|
[activeState],
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
+
// Dialog-management forwards — one thin wrapper per action so the
|
|
145
|
+
// returned identity stays stable as long as the active adapter's
|
|
146
|
+
// identity does. We don't recreate per-call to avoid spurious child
|
|
147
|
+
// re-renders downstream.
|
|
148
|
+
const selectDialog = useCallback(
|
|
149
|
+
(id: string | null) => activeState.selectDialog(id),
|
|
150
|
+
[activeState],
|
|
151
|
+
)
|
|
152
|
+
const startNewDialog = useCallback(
|
|
153
|
+
() => activeState.startNewDialog(),
|
|
154
|
+
[activeState],
|
|
155
|
+
)
|
|
156
|
+
const deleteDialog = useCallback(
|
|
157
|
+
(id: string) => activeState.deleteDialog(id),
|
|
158
|
+
[activeState],
|
|
159
|
+
)
|
|
160
|
+
const loadMoreDialogs = useCallback(
|
|
161
|
+
() => activeState.loadMoreDialogs(),
|
|
162
|
+
[activeState],
|
|
163
|
+
)
|
|
164
|
+
const loadMoreMessages = useCallback(
|
|
165
|
+
() => activeState.loadMoreMessages(),
|
|
166
|
+
[activeState],
|
|
167
|
+
)
|
|
168
|
+
const approveRequest = useCallback(
|
|
169
|
+
(requestId: string) => activeState.approveRequest(requestId),
|
|
170
|
+
[activeState],
|
|
171
|
+
)
|
|
172
|
+
const rejectRequest = useCallback(
|
|
173
|
+
(requestId: string, reason?: string) =>
|
|
174
|
+
activeState.rejectRequest(requestId, reason),
|
|
175
|
+
[activeState],
|
|
176
|
+
)
|
|
177
|
+
|
|
144
178
|
return useMemo<UnifiedChatState>(
|
|
145
179
|
() => ({
|
|
146
180
|
messages: activeState.messages,
|
|
@@ -158,6 +192,24 @@ export function useUnifiedChat(
|
|
|
158
192
|
currentOutputTokens: activeState.currentOutputTokens,
|
|
159
193
|
currentCacheHitRatePct: activeState.currentCacheHitRatePct,
|
|
160
194
|
currentUsageBreakdown: activeState.currentUsageBreakdown,
|
|
195
|
+
// Dialog management (forwarded from active adapter)
|
|
196
|
+
dialogs: activeState.dialogs,
|
|
197
|
+
activeDialogId: activeState.activeDialogId,
|
|
198
|
+
selectDialog,
|
|
199
|
+
startNewDialog,
|
|
200
|
+
deleteDialog,
|
|
201
|
+
isDialogsLoading: activeState.isDialogsLoading,
|
|
202
|
+
isMessagesLoading: activeState.isMessagesLoading,
|
|
203
|
+
hasMoreDialogs: activeState.hasMoreDialogs,
|
|
204
|
+
loadMoreDialogs,
|
|
205
|
+
hasMoreMessages: activeState.hasMoreMessages,
|
|
206
|
+
loadMoreMessages,
|
|
207
|
+
// Approvals
|
|
208
|
+
approveRequest,
|
|
209
|
+
rejectRequest,
|
|
210
|
+
// Token usage + connection
|
|
211
|
+
dialogTokenUsage: activeState.dialogTokenUsage,
|
|
212
|
+
connectionState: activeState.connectionState,
|
|
161
213
|
}),
|
|
162
214
|
[
|
|
163
215
|
activeState,
|
|
@@ -166,6 +218,13 @@ export function useUnifiedChat(
|
|
|
166
218
|
clearMessages,
|
|
167
219
|
discussRef,
|
|
168
220
|
displayRef,
|
|
221
|
+
selectDialog,
|
|
222
|
+
startNewDialog,
|
|
223
|
+
deleteDialog,
|
|
224
|
+
loadMoreDialogs,
|
|
225
|
+
loadMoreMessages,
|
|
226
|
+
approveRequest,
|
|
227
|
+
rejectRequest,
|
|
169
228
|
],
|
|
170
229
|
)
|
|
171
230
|
}
|
|
@@ -21,6 +21,44 @@ import type { ScrollAnchor } from './message.types'
|
|
|
21
21
|
import type { ChatRef } from '../chat-ref.types'
|
|
22
22
|
import type { ChatSource } from '../hooks/use-sse-chat-adapter'
|
|
23
23
|
import type { ChatAttachment } from '../utils/chat-attachment-markdown'
|
|
24
|
+
import type { DialogItem } from './component.types'
|
|
25
|
+
|
|
26
|
+
// ─── Per-dialog token usage (Mingo backend telemetry) ────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Token usage snapshot for the active Mingo dialog, hydrated from the
|
|
30
|
+
* backend's `dialog.tokenUsage` GraphQL field. Mirrors the shape returned
|
|
31
|
+
* by the openframe `dialogs` GraphQL endpoint so hosts can pass it through
|
|
32
|
+
* `fetchDialogMessages` without re-mapping.
|
|
33
|
+
*
|
|
34
|
+
* Null when no Mingo dialog is active, when the backend hasn't reported
|
|
35
|
+
* usage yet, or when the active mode is Guide (SSE telemetry surfaces via
|
|
36
|
+
* `currentInputTokens`/`currentOutputTokens` instead).
|
|
37
|
+
*/
|
|
38
|
+
export interface DialogTokenUsage {
|
|
39
|
+
/** Backend chat-type discriminator, e.g. `ADMIN_AI_CHAT`. Free-form. */
|
|
40
|
+
chatType?: string
|
|
41
|
+
/** Total input tokens consumed so far in this dialog. */
|
|
42
|
+
inputTokensSize: number
|
|
43
|
+
/** Total output tokens emitted so far in this dialog. */
|
|
44
|
+
outputTokensSize: number
|
|
45
|
+
/** Sum of input + output. May be > input + output when the backend
|
|
46
|
+
* counts cache reads in a separate bucket — trust the backend value. */
|
|
47
|
+
totalTokensSize: number
|
|
48
|
+
/** Current LLM context-window occupancy in tokens — what the model
|
|
49
|
+
* has loaded right now, not the cumulative dialog sum. */
|
|
50
|
+
contextSize?: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ─── Connection state (transport-level) ──────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* High-level transport connection status. Drives any UI affordance that
|
|
57
|
+
* needs to communicate "live tail is/isn't healthy" to the user. SSE/Guide
|
|
58
|
+
* adapter currently reports `'connected'` whenever a turn isn't streaming
|
|
59
|
+
* — the SSE side is request-response and has no long-lived socket.
|
|
60
|
+
*/
|
|
61
|
+
export type ChatConnectionState = 'connected' | 'connecting' | 'disconnected'
|
|
24
62
|
|
|
25
63
|
// ─── Streaming phase (unified across transports) ─────────────────────────────
|
|
26
64
|
|
|
@@ -212,4 +250,82 @@ export interface UnifiedChatState {
|
|
|
212
250
|
* arrives. Always null in Mingo mode.
|
|
213
251
|
*/
|
|
214
252
|
currentUsageBreakdown: UnifiedUsageBreakdown | null
|
|
253
|
+
|
|
254
|
+
// ─── Dialog management (Mingo: backend; Guide: localStorage) ──────────────
|
|
255
|
+
//
|
|
256
|
+
// All fields below are optional in practice: a transport adapter that
|
|
257
|
+
// doesn't manage multi-dialog history (older `useNatsChatAdapter`
|
|
258
|
+
// configs, the Guide adapter with localStorage disabled) returns the
|
|
259
|
+
// empty/null defaults so existing consumers keep working unchanged.
|
|
260
|
+
// Callers gate UI on `dialogs.length > 0` or `activeDialogId != null`.
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* History of dialogs available for the active mode. Mingo: paginated
|
|
264
|
+
* from the openframe backend. Guide: list of recent threads from
|
|
265
|
+
* localStorage. Empty when the active adapter doesn't expose dialog
|
|
266
|
+
* management.
|
|
267
|
+
*/
|
|
268
|
+
dialogs: DialogItem[]
|
|
269
|
+
|
|
270
|
+
/** Currently-selected dialog id, or `null` when no dialog is active
|
|
271
|
+
* (draft state — "start a new conversation"). */
|
|
272
|
+
activeDialogId: string | null
|
|
273
|
+
|
|
274
|
+
/** Switch the panel to an existing dialog. Idempotent — selecting the
|
|
275
|
+
* active id is a no-op. Pass `null` to drop back to draft state. */
|
|
276
|
+
selectDialog: (id: string | null) => void
|
|
277
|
+
|
|
278
|
+
/** Allocate a fresh dialog on the backend (Mingo) or in localStorage
|
|
279
|
+
* (Guide) and switch to it. Returns the new dialog id. When the
|
|
280
|
+
* adapter doesn't support creation, resolves to `null`. */
|
|
281
|
+
startNewDialog: () => Promise<string | null>
|
|
282
|
+
|
|
283
|
+
/** Delete a dialog from history. No-op when the adapter doesn't
|
|
284
|
+
* expose `deleteDialog` (Guide localStorage always supports it;
|
|
285
|
+
* Mingo gates on the host-provided callback). */
|
|
286
|
+
deleteDialog: (id: string) => Promise<void>
|
|
287
|
+
|
|
288
|
+
/** True while the dialog list is being fetched for the first time. */
|
|
289
|
+
isDialogsLoading: boolean
|
|
290
|
+
|
|
291
|
+
/** True while message history for the active dialog is being fetched. */
|
|
292
|
+
isMessagesLoading: boolean
|
|
293
|
+
|
|
294
|
+
/** Whether more dialogs remain on the server (Mingo cursor pagination). */
|
|
295
|
+
hasMoreDialogs: boolean
|
|
296
|
+
|
|
297
|
+
/** Fetch the next page of dialogs. No-op when `hasMoreDialogs` is false. */
|
|
298
|
+
loadMoreDialogs: () => Promise<void>
|
|
299
|
+
|
|
300
|
+
/** Whether more historical messages remain in the active dialog. */
|
|
301
|
+
hasMoreMessages: boolean
|
|
302
|
+
|
|
303
|
+
/** Fetch the next page of historical messages for the active dialog. */
|
|
304
|
+
loadMoreMessages: () => Promise<void>
|
|
305
|
+
|
|
306
|
+
// ─── Approval mutations (Mingo agent tool-call workflow) ──────────────────
|
|
307
|
+
|
|
308
|
+
/** Approve an in-flight tool-call request. Errors surface via the
|
|
309
|
+
* host-supplied toast in the callback config (lib does not own UI). */
|
|
310
|
+
approveRequest: (requestId: string) => Promise<void>
|
|
311
|
+
|
|
312
|
+
/** Reject an in-flight tool-call request. Optional `reason` is
|
|
313
|
+
* forwarded to the backend when supported. */
|
|
314
|
+
rejectRequest: (requestId: string, reason?: string) => Promise<void>
|
|
315
|
+
|
|
316
|
+
// ─── Per-dialog token usage (Mingo only) ──────────────────────────────────
|
|
317
|
+
|
|
318
|
+
/** Cumulative token usage for the active Mingo dialog. Null in Guide
|
|
319
|
+
* mode or when the backend hasn't reported usage yet. Per-turn
|
|
320
|
+
* metadata (`currentInputTokens` etc.) is orthogonal — that fires on
|
|
321
|
+
* every SSE `message_start`, while this hydrates from the dialog
|
|
322
|
+
* fetch + live `TOKEN_USAGE` events. */
|
|
323
|
+
dialogTokenUsage: DialogTokenUsage | null
|
|
324
|
+
|
|
325
|
+
// ─── Connection state ─────────────────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
/** High-level transport connection state. Drives reconnect UI in
|
|
328
|
+
* Mingo mode; always `'connected'` in Guide mode (SSE is request-
|
|
329
|
+
* response and has no persistent socket to monitor). */
|
|
330
|
+
connectionState: ChatConnectionState
|
|
215
331
|
}
|
|
@@ -10,6 +10,7 @@ import { Menu01Icon, SearchIcon, XmarkIcon } from '../icons-v2-generated'
|
|
|
10
10
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, SquareAvatar } from '../ui'
|
|
11
11
|
import { HeaderButton } from './header-button'
|
|
12
12
|
import { HeaderGlobalSearch } from './header-global-search'
|
|
13
|
+
import { HeaderMingoButton } from './header-mingo-button'
|
|
13
14
|
import { HeaderOrganizationFilter } from './header-organization-filter'
|
|
14
15
|
|
|
15
16
|
export interface AppHeaderProps {
|
|
@@ -21,6 +22,13 @@ export interface AppHeaderProps {
|
|
|
21
22
|
onOrgChange?: (id: string) => void
|
|
22
23
|
showNotifications?: boolean
|
|
23
24
|
unreadCount?: number
|
|
25
|
+
/** Render the "Mingo AI" launcher button (drawer-style trigger for an
|
|
26
|
+
* in-layout `AppLayoutDrawer` hosting the chat panel). Defaults to off. */
|
|
27
|
+
showMingoAI?: boolean
|
|
28
|
+
/** Click handler for the Mingo AI button — typically toggles the drawer. */
|
|
29
|
+
onMingoAI?: () => void
|
|
30
|
+
/** Whether the Mingo drawer is currently open (visually pressed state). */
|
|
31
|
+
isMingoAIActive?: boolean
|
|
24
32
|
// User block
|
|
25
33
|
showUser?: boolean
|
|
26
34
|
userName?: string
|
|
@@ -49,6 +57,9 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
49
57
|
onOrgChange,
|
|
50
58
|
showNotifications,
|
|
51
59
|
unreadCount = 0,
|
|
60
|
+
showMingoAI = false,
|
|
61
|
+
onMingoAI,
|
|
62
|
+
isMingoAIActive = false,
|
|
52
63
|
showUser,
|
|
53
64
|
userName,
|
|
54
65
|
userEmail,
|
|
@@ -118,6 +129,18 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
118
129
|
/>
|
|
119
130
|
)}
|
|
120
131
|
|
|
132
|
+
{/* Mingo AI launcher — placed to the LEFT of notifications so the user
|
|
133
|
+
menu cluster (notifications → avatar) stays anchored to the right. */}
|
|
134
|
+
{showMingoAI && (
|
|
135
|
+
<HeaderMingoButton
|
|
136
|
+
onClick={onMingoAI}
|
|
137
|
+
isActive={isMingoAIActive}
|
|
138
|
+
iconOnly={!isMdUp}
|
|
139
|
+
disabled={disabled || !onMingoAI}
|
|
140
|
+
className={dimmedClass}
|
|
141
|
+
/>
|
|
142
|
+
)}
|
|
143
|
+
|
|
121
144
|
{/* Notifications button */}
|
|
122
145
|
{showNotifications && (
|
|
123
146
|
<NotificationsHeaderButton
|