@flamingo-stack/openframe-frontend-core 0.0.319 → 0.0.321
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-XAGD3XZV.js → chunk-563D7EB2.js} +4 -4
- package/dist/{chunk-B34YGPIB.cjs → chunk-5HQIFRRE.cjs} +7 -7
- package/dist/{chunk-B34YGPIB.cjs.map → chunk-5HQIFRRE.cjs.map} +1 -1
- package/dist/{chunk-ARQ3R7WZ.js → chunk-6DH2TCRH.js} +2 -2
- package/dist/{chunk-ZD5ZDEL5.cjs → chunk-7C5G45KU.cjs} +1502 -1298
- package/dist/chunk-7C5G45KU.cjs.map +1 -0
- package/dist/{chunk-CGG7EWYG.cjs → chunk-7M3QIYFW.cjs} +56 -56
- package/dist/{chunk-CGG7EWYG.cjs.map → chunk-7M3QIYFW.cjs.map} +1 -1
- package/dist/{chunk-4YFOBTFE.js → chunk-BRERE4E5.js} +4 -4
- package/dist/{chunk-7ZHCRY2V.cjs → chunk-CD56IAXU.cjs} +5 -5
- package/dist/{chunk-7ZHCRY2V.cjs.map → chunk-CD56IAXU.cjs.map} +1 -1
- package/dist/{chunk-2RQV7JLA.js → chunk-DEEBJBFQ.js} +2 -2
- package/dist/{chunk-NG7P653A.js → chunk-FNK67HTC.js} +2 -2
- package/dist/{chunk-4XHPUYL7.cjs → chunk-GAQAPTBU.cjs} +37 -37
- package/dist/{chunk-4XHPUYL7.cjs.map → chunk-GAQAPTBU.cjs.map} +1 -1
- package/dist/{chunk-LF5LDJOP.js → chunk-HALW7FPF.js} +2 -2
- package/dist/{chunk-G7CRSNFS.js → chunk-J65CQLX6.js} +2 -2
- package/dist/{chunk-5B37J3B5.cjs → chunk-MN2WQYXS.cjs} +15 -15
- package/dist/{chunk-5B37J3B5.cjs.map → chunk-MN2WQYXS.cjs.map} +1 -1
- package/dist/{chunk-APEJR6DV.cjs → chunk-NEY347ME.cjs} +26 -26
- package/dist/{chunk-APEJR6DV.cjs.map → chunk-NEY347ME.cjs.map} +1 -1
- package/dist/{chunk-V2OAUUF6.cjs → chunk-PDOTUMJC.cjs} +31 -31
- package/dist/{chunk-V2OAUUF6.cjs.map → chunk-PDOTUMJC.cjs.map} +1 -1
- package/dist/{chunk-JIU2I6LU.cjs → chunk-TFCGMQPK.cjs} +25 -25
- package/dist/{chunk-JIU2I6LU.cjs.map → chunk-TFCGMQPK.cjs.map} +1 -1
- package/dist/{chunk-SVPVDSHA.js → chunk-TPJJJ6KC.js} +4152 -3948
- package/dist/chunk-TPJJJ6KC.js.map +1 -0
- package/dist/{chunk-7FIWPFLE.js → chunk-TV6VD6LP.js} +4 -4
- package/dist/{chunk-2D3RINOJ.js → chunk-V4U7ASRM.js} +2 -2
- package/dist/{chunk-QJOSQVIR.js → chunk-VNWWOPIY.js} +2 -2
- package/dist/{chunk-J4FCWWO6.cjs → chunk-WCCZGPPP.cjs} +62 -62
- package/dist/{chunk-J4FCWWO6.cjs.map → chunk-WCCZGPPP.cjs.map} +1 -1
- package/dist/{chunk-ODMCD663.js → chunk-WCRJWZUA.js} +4 -4
- package/dist/{chunk-7WMHKVV7.cjs → chunk-Y2TJ64S5.cjs} +12 -12
- package/dist/{chunk-7WMHKVV7.cjs.map → chunk-Y2TJ64S5.cjs.map} +1 -1
- package/dist/{chunk-RXCP4OV4.cjs → chunk-ZFER75HU.cjs} +9 -9
- package/dist/{chunk-RXCP4OV4.cjs.map → chunk-ZFER75HU.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/chat-context-picker.d.ts +11 -1
- package/dist/components/chat/chat-context-picker.d.ts.map +1 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/context-items-list.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +11 -1
- package/dist/components/chat/embeddable-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/remark-mention-chips.d.ts +11 -5
- package/dist/components/chat/remark-mention-chips.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +30 -7
- package/dist/components/chat/types/component.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/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +8 -2
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.d.ts +1 -0
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/components/features/index.js +7 -1
- package/dist/components/features/paths-display.d.ts +16 -1
- package/dist/components/features/paths-display.d.ts.map +1 -1
- package/dist/components/features/warning-block.d.ts +32 -0
- package/dist/components/features/warning-block.d.ts.map +1 -0
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +126 -120
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +15 -9
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- 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/index.cjs +8 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -1
- package/package.json +1 -1
- package/src/components/chat/chat-context-picker.tsx +77 -9
- package/src/components/chat/chat-input.tsx +524 -489
- package/src/components/chat/chat-message-enhanced.tsx +4 -2
- package/src/components/chat/chat-message-list.tsx +2 -0
- package/src/components/chat/context-items-list.tsx +9 -1
- package/src/components/chat/embeddable-chat.tsx +55 -56
- package/src/components/chat/remark-mention-chips.ts +13 -7
- package/src/components/chat/types/component.types.ts +31 -7
- package/src/components/features/index.ts +1 -0
- package/src/components/features/paths-display.tsx +32 -1
- package/src/components/features/warning-block.tsx +54 -0
- package/src/stories/WarningBlock.stories.tsx +167 -0
- package/dist/chunk-SVPVDSHA.js.map +0 -1
- package/dist/chunk-ZD5ZDEL5.cjs.map +0 -1
- /package/dist/{chunk-XAGD3XZV.js.map → chunk-563D7EB2.js.map} +0 -0
- /package/dist/{chunk-ARQ3R7WZ.js.map → chunk-6DH2TCRH.js.map} +0 -0
- /package/dist/{chunk-4YFOBTFE.js.map → chunk-BRERE4E5.js.map} +0 -0
- /package/dist/{chunk-2RQV7JLA.js.map → chunk-DEEBJBFQ.js.map} +0 -0
- /package/dist/{chunk-NG7P653A.js.map → chunk-FNK67HTC.js.map} +0 -0
- /package/dist/{chunk-LF5LDJOP.js.map → chunk-HALW7FPF.js.map} +0 -0
- /package/dist/{chunk-G7CRSNFS.js.map → chunk-J65CQLX6.js.map} +0 -0
- /package/dist/{chunk-7FIWPFLE.js.map → chunk-TV6VD6LP.js.map} +0 -0
- /package/dist/{chunk-2D3RINOJ.js.map → chunk-V4U7ASRM.js.map} +0 -0
- /package/dist/{chunk-QJOSQVIR.js.map → chunk-VNWWOPIY.js.map} +0 -0
- /package/dist/{chunk-ODMCD663.js.map → chunk-WCRJWZUA.js.map} +0 -0
|
@@ -25,7 +25,7 @@ import type { MessageSegment, MessageContent, ChatMessageEnhancedProps } from ".
|
|
|
25
25
|
* item would be stripped from the chip strip yet never rendered inline — lost
|
|
26
26
|
* from display entirely. The id is capture group 2 (group 1 is the boundary).
|
|
27
27
|
* Marker lowercase; id is the mention-token charset. */
|
|
28
|
-
const MENTION_MARKER_REGEX = /(
|
|
28
|
+
const MENTION_MARKER_REGEX = /(^|[^\w@])@[a-z]+:([A-Za-z0-9_.+/=-]*[A-Za-z0-9_+/=])/g
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Same regex shape as `remarkCardLinks` — kept in lockstep so the
|
|
@@ -43,7 +43,7 @@ function normalizeContent(content: MessageContent): MessageSegment[] {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>(
|
|
46
|
-
({ className, role, content, name, avatar, isTyping = false, timestamp, showAvatar = true, assistantType, authorType: authorTypeProp, assistantIcon, chatRefs, contextItems, resolveContextIcon, renderMention, renderEntityCard, NavLinkAnchor, ...props }, ref) => {
|
|
46
|
+
({ className, role, content, name, avatar, isTyping = false, timestamp, showAvatar = true, assistantType, authorType: authorTypeProp, assistantIcon, chatRefs, contextItems, resolveContextIcon, renderContextItem, renderMention, renderEntityCard, NavLinkAnchor, ...props }, ref) => {
|
|
47
47
|
const isUser = role === 'user'
|
|
48
48
|
const isError = role === 'error'
|
|
49
49
|
const authorType = authorTypeProp ?? (isUser ? 'user' : assistantType === 'mingo' ? 'mingo' : 'fae')
|
|
@@ -595,6 +595,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
595
595
|
<ChatContextChipStrip
|
|
596
596
|
items={stripContextItems}
|
|
597
597
|
resolveIcon={resolveContextIcon}
|
|
598
|
+
renderItem={renderContextItem}
|
|
598
599
|
className="mt-2"
|
|
599
600
|
/>
|
|
600
601
|
)}
|
|
@@ -628,6 +629,7 @@ const MemoizedChatMessageEnhanced = memo(ChatMessageEnhanced, (prevProps, nextPr
|
|
|
628
629
|
// message (it's set once on the optimistic send and never mutated).
|
|
629
630
|
prevProps.contextItems === nextProps.contextItems &&
|
|
630
631
|
prevProps.resolveContextIcon === nextProps.resolveContextIcon &&
|
|
632
|
+
prevProps.renderContextItem === nextProps.renderContextItem &&
|
|
631
633
|
// Host keeps this stable (module const / useCallback), so reference
|
|
632
634
|
// equality holds across streaming chunks.
|
|
633
635
|
prevProps.renderMention === nextProps.renderMention &&
|
|
@@ -107,6 +107,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
107
107
|
onLoadMore,
|
|
108
108
|
renderEntityCard,
|
|
109
109
|
resolveContextIcon,
|
|
110
|
+
renderContextItem,
|
|
110
111
|
renderMention,
|
|
111
112
|
NavLinkAnchor,
|
|
112
113
|
...props
|
|
@@ -539,6 +540,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
539
540
|
chatRefs={message.chatRefs}
|
|
540
541
|
contextItems={message.contextItems}
|
|
541
542
|
resolveContextIcon={resolveContextIcon}
|
|
543
|
+
renderContextItem={renderContextItem}
|
|
542
544
|
renderMention={renderMention}
|
|
543
545
|
renderEntityCard={renderEntityCard}
|
|
544
546
|
NavLinkAnchor={NavLinkAnchor}
|
|
@@ -141,7 +141,15 @@ export function ContextItemsList({
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
return (
|
|
144
|
-
<div
|
|
144
|
+
<div
|
|
145
|
+
role="listbox"
|
|
146
|
+
aria-label="Items"
|
|
147
|
+
onScroll={onScroll}
|
|
148
|
+
// Cap to 340px but never exceed ~half the viewport, so on short screens the
|
|
149
|
+
// results list (inside the bottom-anchored picker popover) still fits and
|
|
150
|
+
// scrolls instead of overflowing past the top edge.
|
|
151
|
+
className={cn('max-h-[min(340px,45vh)] overflow-y-auto', className)}
|
|
152
|
+
>
|
|
145
153
|
{items.map(item => {
|
|
146
154
|
const selected = selectedKeys.has(itemKey(item))
|
|
147
155
|
return (
|
|
@@ -271,6 +271,16 @@ export interface EmbeddableChatProps {
|
|
|
271
271
|
* marker the host can't render → the lib falls back to the bare token.
|
|
272
272
|
*/
|
|
273
273
|
renderMention?: (reference: { marker: string; id: string }) => React.ReactNode
|
|
274
|
+
/**
|
|
275
|
+
* Host renderer that REPLACES the default label-only context chip on a sent
|
|
276
|
+
* user bubble with a self-fetching entity chip — so a user's manually
|
|
277
|
+
* attached context (`contextItems`) renders IDENTICALLY to an inline
|
|
278
|
+
* `@marker:id` mention (same live name resolution + link). Mirror of
|
|
279
|
+
* `renderMention`, for the attached-chip strip instead of inline tokens.
|
|
280
|
+
* Return null for an item the host can't render → the lib falls back to the
|
|
281
|
+
* label pill. Keep the identity stable (module const / `useCallback`).
|
|
282
|
+
*/
|
|
283
|
+
renderContextItem?: (item: ChatContextItem) => React.ReactNode
|
|
274
284
|
}
|
|
275
285
|
|
|
276
286
|
// =============================================================================
|
|
@@ -691,6 +701,7 @@ function EmbeddableChatInner({
|
|
|
691
701
|
guideWelcome,
|
|
692
702
|
contextPicker,
|
|
693
703
|
renderMention,
|
|
704
|
+
renderContextItem,
|
|
694
705
|
}: EmbeddableChatProps) {
|
|
695
706
|
// `shell === 'none'` means the consumer hosts us inside their own panel
|
|
696
707
|
// (e.g. AppLayoutDrawer in openframe-frontend). Several drawer-shell
|
|
@@ -986,9 +997,10 @@ function EmbeddableChatInner({
|
|
|
986
997
|
useEffect(() => {
|
|
987
998
|
mentionActiveRef.current = mentionQuery !== null
|
|
988
999
|
}, [mentionQuery])
|
|
989
|
-
//
|
|
990
|
-
//
|
|
991
|
-
|
|
1000
|
+
// Keys (`"<type>:<id>"`) of context items that were committed via `@` and so
|
|
1001
|
+
// have a `@type:id` token in the draft text. MULTI-mention: several coexist.
|
|
1002
|
+
// Source of truth = the input text — deleting a token's chip drops its item.
|
|
1003
|
+
const mentionKeysRef = useRef<Set<string>>(new Set())
|
|
992
1004
|
|
|
993
1005
|
// Staged picker selection is per-conversation and Mingo-only. Clear it (and the
|
|
994
1006
|
// mention bookkeeping) whenever the active dialog changes or the mode toggles,
|
|
@@ -998,7 +1010,7 @@ function EmbeddableChatInner({
|
|
|
998
1010
|
useEffect(() => {
|
|
999
1011
|
setContextItems([])
|
|
1000
1012
|
setMentionQuery(null)
|
|
1001
|
-
|
|
1013
|
+
mentionKeysRef.current.clear()
|
|
1002
1014
|
}, [activeDialogId, activeMode])
|
|
1003
1015
|
|
|
1004
1016
|
// Map each entity type to its backend mention marker (host-declared on the
|
|
@@ -1010,35 +1022,35 @@ function EmbeddableChatInner({
|
|
|
1010
1022
|
return m
|
|
1011
1023
|
}, [contextPicker?.entityTypes])
|
|
1012
1024
|
|
|
1025
|
+
// Stable type → icon resolver (entity-type glyph). Used both for the inline
|
|
1026
|
+
// composer chips (`commitMention` meta) and the message-bubble context chips.
|
|
1027
|
+
// Keyed on `entityTypes` only so an inline `contextPicker={{…}}` doesn't
|
|
1028
|
+
// rebuild it every render (its identity flows into the message memo).
|
|
1029
|
+
const contextEntityTypes = contextPicker?.entityTypes
|
|
1030
|
+
const resolveContextIcon = useMemo(() => {
|
|
1031
|
+
const byType = new Map<string, React.ReactNode>()
|
|
1032
|
+
for (const t of contextEntityTypes ?? []) byType.set(t.type, t.icon)
|
|
1033
|
+
return (item: ChatContextItem) => byType.get(item.type)
|
|
1034
|
+
}, [contextEntityTypes])
|
|
1035
|
+
|
|
1013
1036
|
const toggleContextItem = useCallback(
|
|
1014
1037
|
(item: ChatContextItem) => {
|
|
1015
1038
|
const key = `${item.type}:${item.id}`
|
|
1016
|
-
// `@`-mention flow →
|
|
1017
|
-
// (
|
|
1018
|
-
//
|
|
1019
|
-
//
|
|
1039
|
+
// `@`-mention flow → MULTI-select: commit a `@type:id` chip into the draft
|
|
1040
|
+
// (prior mentions left in place) so each picked entity stays visible inline
|
|
1041
|
+
// AND rides out in the context. The `+` flow (mention inactive) is the same
|
|
1042
|
+
// multi-select on the chip strip.
|
|
1020
1043
|
if (mentionActiveRef.current) {
|
|
1021
|
-
//
|
|
1022
|
-
//
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
(
|
|
1028
|
-
)
|
|
1029
|
-
const grows = !alreadySelected && !mentionKeyRef.current
|
|
1030
|
-
if (grows && contextItems.length >= contextMaxItems) return
|
|
1031
|
-
chatInputRef.current?.commitMention(mentionTokenOf(key, mentionMarkerByType))
|
|
1032
|
-
setContextItems((prev) => {
|
|
1033
|
-
const withoutPrevMention = mentionKeyRef.current
|
|
1034
|
-
? prev.filter((p) => `${p.type}:${p.id}` !== mentionKeyRef.current)
|
|
1035
|
-
: prev
|
|
1036
|
-
const deduped = withoutPrevMention.filter(
|
|
1037
|
-
(p) => `${p.type}:${p.id}` !== key,
|
|
1038
|
-
)
|
|
1039
|
-
return [...deduped, item]
|
|
1044
|
+
// Already attached → no-op (the picker's `@` flow is add-then-close; the
|
|
1045
|
+
// chip already lives in the draft). Honour `maxItems` on growth.
|
|
1046
|
+
if (contextItems.some((p) => `${p.type}:${p.id}` === key)) return
|
|
1047
|
+
if (contextItems.length >= contextMaxItems) return
|
|
1048
|
+
chatInputRef.current?.commitMention(mentionTokenOf(key, mentionMarkerByType), {
|
|
1049
|
+
label: item.label,
|
|
1050
|
+
icon: resolveContextIcon(item),
|
|
1040
1051
|
})
|
|
1041
|
-
|
|
1052
|
+
setContextItems((prev) => [...prev, item])
|
|
1053
|
+
mentionKeysRef.current.add(key)
|
|
1042
1054
|
return
|
|
1043
1055
|
}
|
|
1044
1056
|
setContextItems((prev) => {
|
|
@@ -1048,32 +1060,32 @@ function EmbeddableChatInner({
|
|
|
1048
1060
|
return [...prev, item]
|
|
1049
1061
|
})
|
|
1050
1062
|
},
|
|
1051
|
-
[contextMaxItems, contextItems, mentionMarkerByType],
|
|
1063
|
+
[contextMaxItems, contextItems, mentionMarkerByType, resolveContextIcon],
|
|
1052
1064
|
)
|
|
1053
1065
|
|
|
1054
1066
|
const removeContextItem = useCallback((item: ChatContextItem) => {
|
|
1055
1067
|
const key = `${item.type}:${item.id}`
|
|
1056
1068
|
setContextItems((prev) => prev.filter((p) => `${p.type}:${p.id}` !== key))
|
|
1057
|
-
// Removing
|
|
1058
|
-
// draft, keeping text and context in lockstep (the inverse of deleting
|
|
1059
|
-
//
|
|
1060
|
-
if (
|
|
1061
|
-
|
|
1069
|
+
// Removing a mention-backed chip must also strip its `@type:id` token from
|
|
1070
|
+
// the draft, keeping text and context in lockstep (the inverse of deleting
|
|
1071
|
+
// the chip in the input, which drops the item via `handleContextValueChange`).
|
|
1072
|
+
if (mentionKeysRef.current.has(key)) {
|
|
1073
|
+
mentionKeysRef.current.delete(key)
|
|
1062
1074
|
const cur = chatInputRef.current?.getValue() ?? ''
|
|
1063
1075
|
const next = cur.replace(`@${mentionTokenOf(key, mentionMarkerByType)}`, '').replace(/\s{2,}/g, ' ').trimStart()
|
|
1064
1076
|
chatInputRef.current?.setValue(next)
|
|
1065
1077
|
}
|
|
1066
1078
|
}, [mentionMarkerByType])
|
|
1067
1079
|
|
|
1068
|
-
// Draft → context reconciliation: when the user deletes
|
|
1069
|
-
//
|
|
1070
|
-
// text and are
|
|
1080
|
+
// Draft → context reconciliation: when the user deletes a `@type:id` chip from
|
|
1081
|
+
// the input, drop the matching mention item. Checks EVERY token-backed key
|
|
1082
|
+
// (multi-mention). `+`-added items have no token in the text and are untouched.
|
|
1071
1083
|
const handleContextValueChange = useCallback((value: string) => {
|
|
1072
|
-
const key
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1084
|
+
for (const key of Array.from(mentionKeysRef.current)) {
|
|
1085
|
+
if (!value.includes(`@${mentionTokenOf(key, mentionMarkerByType)}`)) {
|
|
1086
|
+
mentionKeysRef.current.delete(key)
|
|
1087
|
+
setContextItems((prev) => prev.filter((p) => `${p.type}:${p.id}` !== key))
|
|
1088
|
+
}
|
|
1077
1089
|
}
|
|
1078
1090
|
}, [mentionMarkerByType])
|
|
1079
1091
|
|
|
@@ -1102,20 +1114,6 @@ function EmbeddableChatInner({
|
|
|
1102
1114
|
setContextPickerOpen(query !== null)
|
|
1103
1115
|
}, [])
|
|
1104
1116
|
|
|
1105
|
-
// Stable type → icon resolver for the message-bubble context chips. Built
|
|
1106
|
-
// from the host's `contextPicker.entityTypes`; null-safe when the feature
|
|
1107
|
-
// is off. Keyed on `entityTypes` (NOT the whole `contextPicker` object) so an
|
|
1108
|
-
// inline `contextPicker={{…}}` on the host doesn't rebuild this resolver every
|
|
1109
|
-
// render — that identity flows into the `ChatMessageEnhanced` memo, and a new
|
|
1110
|
-
// function each render would force a full markdown re-render of every message
|
|
1111
|
-
// on each keystroke.
|
|
1112
|
-
const contextEntityTypes = contextPicker?.entityTypes
|
|
1113
|
-
const resolveContextIcon = useMemo(() => {
|
|
1114
|
-
const byType = new Map<string, React.ReactNode>()
|
|
1115
|
-
for (const t of contextEntityTypes ?? []) byType.set(t.type, t.icon)
|
|
1116
|
-
return (item: ChatContextItem) => byType.get(item.type)
|
|
1117
|
-
}, [contextEntityTypes])
|
|
1118
|
-
|
|
1119
1117
|
// `renderMention` is HOST-provided (see the prop doc): the per-type renderer
|
|
1120
1118
|
// for inline AI mentions `@marker:id` (mirror of `renderEntityCard`). The lib
|
|
1121
1119
|
// forwards it verbatim to the message list and calls it from the `mention://`
|
|
@@ -1580,6 +1578,7 @@ function EmbeddableChatInner({
|
|
|
1580
1578
|
assistantIcon={mingoAssistantIcon}
|
|
1581
1579
|
renderEntityCard={renderEntityCard}
|
|
1582
1580
|
resolveContextIcon={resolveContextIcon}
|
|
1581
|
+
renderContextItem={renderContextItem}
|
|
1583
1582
|
renderMention={renderMention}
|
|
1584
1583
|
NavLinkAnchor={NavLinkAnchorViaRuntime}
|
|
1585
1584
|
className="flex-1"
|
|
@@ -18,18 +18,24 @@
|
|
|
18
18
|
* - marker: `[a-z]+` — the backend `ContextItemType.marker()` short form
|
|
19
19
|
* (`device`, `script`, `ticket`, `organization`, `user`, `kb`, `policy`,
|
|
20
20
|
* `query`, …). Always lowercase.
|
|
21
|
-
* - id: `[A-Za-z0-9_.+/=-]
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* - id: charset `[A-Za-z0-9_.+/=-]` (UUIDs with hyphens, fleet numeric ids,
|
|
22
|
+
* base64 global ids, etc.; matches the composer's `MENTION_TOKEN`) — but the
|
|
23
|
+
* id may NOT END in `.` or `-`, so a trailing SENTENCE period/dash isn't
|
|
24
|
+
* swallowed into the id (`@script:38.` → id `38`, with the `.` left as text).
|
|
25
|
+
* Base64 padding `=` and `+`/`/` are still valid final chars.
|
|
26
|
+
* - A LEFT boundary (start-of-text or any non-word, non-`@` character) keeps
|
|
27
|
+
* `@marker:` from matching inside emails / mid-word (`user@host:1234` never
|
|
28
|
+
* starts a token because the `@` is preceded by a word char), while still
|
|
29
|
+
* firing when the agent wraps a mention in punctuation — parentheses,
|
|
30
|
+
* brackets, quotes — e.g. `(@device:64f0a1)`. The boundary character (if
|
|
31
|
+
* any) is captured as `lead` and re-emitted as text, so it is preserved.
|
|
26
32
|
*/
|
|
27
33
|
|
|
28
34
|
import type { Plugin } from 'unified'
|
|
29
35
|
import type { Root, Text, Link } from 'mdast'
|
|
30
36
|
import { visit, SKIP } from 'unist-util-visit'
|
|
31
37
|
|
|
32
|
-
const MENTION_REGEX = /(
|
|
38
|
+
const MENTION_REGEX = /(^|[^\w@])@([a-z]+):([A-Za-z0-9_.+/=-]*[A-Za-z0-9_+/=])/g
|
|
33
39
|
|
|
34
40
|
export const remarkMentionChips: Plugin<[], Root> = () => {
|
|
35
41
|
return (tree: Root) => {
|
|
@@ -43,7 +49,7 @@ export const remarkMentionChips: Plugin<[], Root> = () => {
|
|
|
43
49
|
MENTION_REGEX.lastIndex = 0
|
|
44
50
|
let match: RegExpExecArray | null
|
|
45
51
|
while ((match = MENTION_REGEX.exec(text)) !== null) {
|
|
46
|
-
const lead = match[1] // '' or the
|
|
52
|
+
const lead = match[1] // '' or the boundary char before '@' (space, '(', etc.)
|
|
47
53
|
const marker = match[2]
|
|
48
54
|
const id = match[3]
|
|
49
55
|
const tokenStart = match.index + lead.length // position of '@'
|
|
@@ -132,6 +132,17 @@ export interface ChatMessageEnhancedProps extends Omit<HTMLAttributes<HTMLDivEle
|
|
|
132
132
|
* glyph). Optional — chips render label-only when omitted.
|
|
133
133
|
*/
|
|
134
134
|
resolveContextIcon?: (item: ChatContextItem) => ReactNode
|
|
135
|
+
/**
|
|
136
|
+
* Host renderer that REPLACES the default label-only context chip for an
|
|
137
|
+
* attached item with a self-fetching entity chip — so a user's manually
|
|
138
|
+
* attached context renders IDENTICALLY to an inline `@marker:id` mention
|
|
139
|
+
* (same live name resolution, same link). Mirror of `renderMention`, but for
|
|
140
|
+
* the `contextItems` strip instead of inline tokens. Return `null` for an
|
|
141
|
+
* item the host can't render → the lib falls back to the label pill. Keep the
|
|
142
|
+
* function identity stable (module const / `useCallback`) so the message memo
|
|
143
|
+
* holds across streaming chunks.
|
|
144
|
+
*/
|
|
145
|
+
renderContextItem?: (item: ChatContextItem) => ReactNode
|
|
135
146
|
/**
|
|
136
147
|
* Host renderer for inline AI mentions `@marker:id` — the ASSISTANT echoing
|
|
137
148
|
* `@device:<machineId>` (etc.) in its reply. DIRECT MIRROR of
|
|
@@ -215,6 +226,10 @@ export interface ChatMessageListProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
215
226
|
/** Lead-icon resolver for per-message context chips (maps a context item to
|
|
216
227
|
* its entity-type glyph). Forwarded to every message's ChatMessageEnhanced. */
|
|
217
228
|
resolveContextIcon?: (item: ChatContextItem) => ReactNode
|
|
229
|
+
/** Host renderer that REPLACES the default context chip with a self-fetching
|
|
230
|
+
* entity chip (so attached context renders like an inline mention).
|
|
231
|
+
* Forwarded verbatim to every message's ChatMessageEnhanced. */
|
|
232
|
+
renderContextItem?: (item: ChatContextItem) => ReactNode
|
|
218
233
|
/** Host renderer for inline AI mentions `@marker:id` (mirror of
|
|
219
234
|
* `renderEntityCard`). Forwarded verbatim to every message's
|
|
220
235
|
* ChatMessageEnhanced; returns a self-fetching chip per entity type. */
|
|
@@ -408,13 +423,22 @@ export interface ChatInputRef {
|
|
|
408
423
|
* context item / closes the picker so the `@query` scaffolding doesn't get
|
|
409
424
|
* sent as literal text. No-op when no mention token is active. */
|
|
410
425
|
removeMentionTrigger: () => void
|
|
411
|
-
/** Commit a
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
|
|
426
|
+
/** Commit a mention: replace the trailing `@query` being typed with the
|
|
427
|
+
* literal `@<type>:<id>` token (`token` = `"<type>:<id>"`, plus a trailing
|
|
428
|
+
* space). MULTIPLE mentions coexist — prior committed tokens are left in
|
|
429
|
+
* place. The optional `meta` gives the inline chip its display name + icon
|
|
430
|
+
* (the contenteditable renders the token as a chip); without it the chip
|
|
431
|
+
* falls back to the id. Used by the composer's `@`-mention flow so the picked
|
|
432
|
+
* entity rides out in the context — deleting the chip removes it from context. */
|
|
433
|
+
commitMention: (token: string, meta?: MentionMeta) => void
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** Display metadata for an inline mention chip rendered inside the composer. */
|
|
437
|
+
export interface MentionMeta {
|
|
438
|
+
/** Resolved entity name shown in the chip (e.g. `'ELK-PROD-07'`). */
|
|
439
|
+
label: string
|
|
440
|
+
/** Optional lead icon (entity-type glyph). */
|
|
441
|
+
icon?: ReactNode
|
|
418
442
|
}
|
|
419
443
|
|
|
420
444
|
// ========== Chat Typing Indicator Props ==========
|
|
@@ -48,6 +48,7 @@ export * from './social-links-manager'
|
|
|
48
48
|
export * from './start-with-openframe-button'
|
|
49
49
|
export * from './status-filter-component'
|
|
50
50
|
export * from './tags-selector'
|
|
51
|
+
export * from './warning-block'
|
|
51
52
|
// Unified video primitives — single source of truth across all platforms.
|
|
52
53
|
// `<Video>` replaces the deleted `<VideoPlayer>` + `<YouTubeEmbed>` pair;
|
|
53
54
|
// `<EntityVideoSection>` + `<VideoBitesDisplay>` are the public detail-page
|
|
@@ -39,6 +39,13 @@ export interface PathsDisplayProps {
|
|
|
39
39
|
* Size of the copy icon (default: 'w-6 h-6')
|
|
40
40
|
*/
|
|
41
41
|
copyIconSize?: string
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Optional icon rendered at the start of every row, before the path text.
|
|
45
|
+
* Useful for showing a platform logo next to a command (e.g. the Windows
|
|
46
|
+
* logo beside "Run PowerShell as Local Administrator").
|
|
47
|
+
*/
|
|
48
|
+
leadingIcon?: React.ReactNode
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
/**
|
|
@@ -77,7 +84,8 @@ export function PathsDisplay({
|
|
|
77
84
|
description,
|
|
78
85
|
className,
|
|
79
86
|
showCopyButtons = true,
|
|
80
|
-
copyIconSize = 'w-6 h-6'
|
|
87
|
+
copyIconSize = 'w-6 h-6',
|
|
88
|
+
leadingIcon
|
|
81
89
|
}: PathsDisplayProps) {
|
|
82
90
|
if (!paths || paths.length === 0) {
|
|
83
91
|
return null
|
|
@@ -101,6 +109,9 @@ export function PathsDisplay({
|
|
|
101
109
|
key={path}
|
|
102
110
|
className="flex items-center gap-4 p-4 border-b border-ods-border last:border-b-0"
|
|
103
111
|
>
|
|
112
|
+
{leadingIcon && (
|
|
113
|
+
<span className="shrink-0 text-ods-text-primary">{leadingIcon}</span>
|
|
114
|
+
)}
|
|
104
115
|
<span className="flex-1 min-w-0 text-h4 text-ods-text-primary truncate">
|
|
105
116
|
{path}
|
|
106
117
|
</span>
|
|
@@ -152,3 +163,23 @@ export type OpenFramePathsPlatform = keyof typeof OPENFRAME_PATHS
|
|
|
152
163
|
export function getOpenFramePaths(platform: OpenFramePathsPlatform): string[] {
|
|
153
164
|
return [...OPENFRAME_PATHS[platform]]
|
|
154
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Doctor command per platform. Run to diagnose installation issues and repair
|
|
169
|
+
* the agent (works even if the agent didn't install correctly).
|
|
170
|
+
*/
|
|
171
|
+
export const OPENFRAME_DOCTOR_COMMANDS: Record<OpenFramePathsPlatform, string> = {
|
|
172
|
+
// PowerShell: Invoke-WebRequest / .\openframe-client.exe, mirroring the
|
|
173
|
+
// Windows install command pattern.
|
|
174
|
+
windows:
|
|
175
|
+
"Set-Location ~; Invoke-WebRequest -Uri 'github.com/openframe-client' -OutFile 'openframe-client.exe'; .\\openframe-client.exe doctor",
|
|
176
|
+
darwin: 'cd ~; wget github.com/openframe-client; openframe-client doctor',
|
|
177
|
+
linux: 'cd ~; wget github.com/openframe-client; openframe-client doctor'
|
|
178
|
+
} as const
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get the OpenFrame doctor command for a specific platform
|
|
182
|
+
*/
|
|
183
|
+
export function getOpenFrameDoctorCommand(platform: OpenFramePathsPlatform): string {
|
|
184
|
+
return OPENFRAME_DOCTOR_COMMANDS[platform]
|
|
185
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { AlertTriangle } from 'lucide-react'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import { cn } from '../../utils/cn'
|
|
6
|
+
|
|
7
|
+
export interface WarningBlockProps {
|
|
8
|
+
/** Text rendered inside the highlighted warning banner header. */
|
|
9
|
+
title: React.ReactNode
|
|
10
|
+
/**
|
|
11
|
+
* Icon shown at the start of the banner. Defaults to a triangle alert icon.
|
|
12
|
+
* Pass `null` to hide the icon entirely.
|
|
13
|
+
*/
|
|
14
|
+
icon?: React.ReactNode
|
|
15
|
+
/**
|
|
16
|
+
* Body content rendered below the banner — description paragraphs, one or more
|
|
17
|
+
* `PathsDisplay` lists, command rows, etc. Fully composable so a single block
|
|
18
|
+
* can mix text and multiple path groups (see the New Device warning blocks).
|
|
19
|
+
*/
|
|
20
|
+
children?: React.ReactNode
|
|
21
|
+
/** Extra classes for the outer card container. */
|
|
22
|
+
className?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEFAULT_ICON = <AlertTriangle className="h-6 w-6" />
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* WarningBlock - a card with a highlighted "attention / warning" banner header
|
|
29
|
+
* followed by arbitrary body content.
|
|
30
|
+
*
|
|
31
|
+
* The banner reproduces the ODS warning callout (warning color on a muted
|
|
32
|
+
* warning background). Body content is fully composable: pass description
|
|
33
|
+
* paragraphs, one or more `PathsDisplay` lists, command rows, etc. as children.
|
|
34
|
+
*
|
|
35
|
+
* Spacing follows the ODS scale: card padding 16px / gap 12px, banner padding
|
|
36
|
+
* 12px / gap 16px. Typography uses `text-h3`, which is responsive (14px on
|
|
37
|
+
* mobile, 18px from `md` up) and bold by design-token default.
|
|
38
|
+
*/
|
|
39
|
+
export function WarningBlock({ title, icon = DEFAULT_ICON, children, className }: WarningBlockProps) {
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
className={cn(
|
|
43
|
+
'flex flex-col gap-3 rounded-[6px] border border-ods-border bg-ods-card p-4',
|
|
44
|
+
className
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
<div className="flex items-start gap-4 overflow-hidden rounded-[6px] bg-ods-warning-secondary p-3 text-ods-warning">
|
|
48
|
+
{icon != null && <span className="shrink-0">{icon}</span>}
|
|
49
|
+
<p className="min-w-0 flex-1 text-h3">{title}</p>
|
|
50
|
+
</div>
|
|
51
|
+
{children}
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { OSTypeIcon } from '../components/features/os-type-badge';
|
|
3
|
+
import { OPENFRAME_DOCTOR_COMMANDS, PathsDisplay } from '../components/features/paths-display';
|
|
4
|
+
import { WarningBlock } from '../components/features/warning-block';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Features/WarningBlock',
|
|
8
|
+
component: WarningBlock,
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'A card with a highlighted "attention / warning" banner header followed by composable body content. Body is passed as children, so a single block can mix description paragraphs with one or more `PathsDisplay` lists (see the New Device warning blocks). Typography is responsive via ODS tokens (14px on mobile, 18px from `md` up).',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
title: { control: 'text' },
|
|
19
|
+
},
|
|
20
|
+
decorators: [
|
|
21
|
+
(Story) => (
|
|
22
|
+
<div style={{ maxWidth: 720 }}>
|
|
23
|
+
<Story />
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
} satisfies Meta<typeof WarningBlock>;
|
|
28
|
+
|
|
29
|
+
export default meta;
|
|
30
|
+
type Story = StoryObj<typeof meta>;
|
|
31
|
+
|
|
32
|
+
const copyPath = (path: string) => {
|
|
33
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
34
|
+
void navigator.clipboard.writeText(path);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Minimal usage — a banner title with a single description paragraph. */
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
title: 'Administrator privileges are required to install the OpenFrame agent.',
|
|
42
|
+
children: (
|
|
43
|
+
<p className="text-h4 text-ods-text-primary">
|
|
44
|
+
Run the command with the appropriate permissions for your platform.
|
|
45
|
+
</p>
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Antivirus warning from the New Device screen — description, a `PathsDisplay`
|
|
52
|
+
* list of exclusion folders, then a closing paragraph.
|
|
53
|
+
*/
|
|
54
|
+
export const AntivirusWarning: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
title: 'Your antivirus may block OpenFrame installation. This is a false positive.',
|
|
57
|
+
children: (
|
|
58
|
+
<>
|
|
59
|
+
<PathsDisplay
|
|
60
|
+
title="If blocked, add these folders to your antivirus exclusions list:"
|
|
61
|
+
paths={['C:\\Program Files\\OpenFrame\\', 'C:\\Users\\[Username]\\AppData\\Local\\OpenFrame\\']}
|
|
62
|
+
onCopyPath={copyPath}
|
|
63
|
+
/>
|
|
64
|
+
<p className="text-h4 text-ods-text-primary">
|
|
65
|
+
Or temporarily disable protection during installation. OpenFrame is safe open-source software.
|
|
66
|
+
Blocks happen because new software needs time to build reputation with security vendors.
|
|
67
|
+
</p>
|
|
68
|
+
</>
|
|
69
|
+
),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Doctor mode warning from the New Device screen — description plus a single
|
|
75
|
+
* copyable command row.
|
|
76
|
+
*/
|
|
77
|
+
export const DoctorMode: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
title: 'Device not appearing or stuck pending?',
|
|
80
|
+
children: (
|
|
81
|
+
<>
|
|
82
|
+
<p className="text-h4 text-ods-text-primary">
|
|
83
|
+
Run the doctor command to diagnose installation issues and repair the agent. It works even if
|
|
84
|
+
the agent didn't install correctly.
|
|
85
|
+
</p>
|
|
86
|
+
<PathsDisplay
|
|
87
|
+
paths={[OPENFRAME_DOCTOR_COMMANDS.windows]}
|
|
88
|
+
onCopyPath={copyPath}
|
|
89
|
+
/>
|
|
90
|
+
</>
|
|
91
|
+
),
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Administrator privileges warning — a platform-specific instruction row (no
|
|
97
|
+
* copy button) framed by description paragraphs.
|
|
98
|
+
*/
|
|
99
|
+
export const AdministratorPrivileges: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
title: 'Administrator privileges are required to install the OpenFrame agent.',
|
|
102
|
+
children: (
|
|
103
|
+
<>
|
|
104
|
+
<p className="text-h4 text-ods-text-primary">
|
|
105
|
+
Run the command with the appropriate permissions for your platform:
|
|
106
|
+
</p>
|
|
107
|
+
<PathsDisplay
|
|
108
|
+
paths={['Run PowerShell as Local Administrator']}
|
|
109
|
+
showCopyButtons={false}
|
|
110
|
+
leadingIcon={<OSTypeIcon osType="windows" size="w-6 h-6" />}
|
|
111
|
+
/>
|
|
112
|
+
<p className="text-h4 text-ods-text-primary">
|
|
113
|
+
Without elevated privileges, the installation will fail silently. Right-click PowerShell and
|
|
114
|
+
select "Run as administrator," or contact your IT admin if you don't have local
|
|
115
|
+
admin rights.
|
|
116
|
+
</p>
|
|
117
|
+
</>
|
|
118
|
+
),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/** All three New Device warning blocks stacked, matching the Figma layout. */
|
|
123
|
+
export const NewDeviceBlocks: Story = {
|
|
124
|
+
args: { title: '' },
|
|
125
|
+
render: () => (
|
|
126
|
+
<div className="flex flex-col gap-6">
|
|
127
|
+
<WarningBlock title="Administrator privileges are required to install the OpenFrame agent.">
|
|
128
|
+
<p className="text-h4 text-ods-text-primary">
|
|
129
|
+
Run the command with the appropriate permissions for your platform:
|
|
130
|
+
</p>
|
|
131
|
+
<PathsDisplay
|
|
132
|
+
paths={['Run PowerShell as Local Administrator']}
|
|
133
|
+
showCopyButtons={false}
|
|
134
|
+
leadingIcon={<OSTypeIcon osType="windows" size="w-6 h-6" />}
|
|
135
|
+
/>
|
|
136
|
+
<p className="text-h4 text-ods-text-primary">
|
|
137
|
+
Without elevated privileges, the installation will fail silently. Right-click PowerShell and
|
|
138
|
+
select "Run as administrator," or contact your IT admin if you don't have local
|
|
139
|
+
admin rights.
|
|
140
|
+
</p>
|
|
141
|
+
</WarningBlock>
|
|
142
|
+
|
|
143
|
+
<WarningBlock title="Your antivirus may block OpenFrame installation. This is a false positive.">
|
|
144
|
+
<PathsDisplay
|
|
145
|
+
title="If blocked, add these folders to your antivirus exclusions list:"
|
|
146
|
+
paths={['C:\\Program Files\\OpenFrame\\', 'C:\\Users\\[Username]\\AppData\\Local\\OpenFrame\\']}
|
|
147
|
+
onCopyPath={copyPath}
|
|
148
|
+
/>
|
|
149
|
+
<p className="text-h4 text-ods-text-primary">
|
|
150
|
+
Or temporarily disable protection during installation. OpenFrame is safe open-source software.
|
|
151
|
+
Blocks happen because new software needs time to build reputation with security vendors.
|
|
152
|
+
</p>
|
|
153
|
+
</WarningBlock>
|
|
154
|
+
|
|
155
|
+
<WarningBlock title="Device not appearing or stuck pending?">
|
|
156
|
+
<p className="text-h4 text-ods-text-primary">
|
|
157
|
+
Run the doctor command to diagnose installation issues and repair the agent. It works even if
|
|
158
|
+
the agent didn't install correctly.
|
|
159
|
+
</p>
|
|
160
|
+
<PathsDisplay
|
|
161
|
+
paths={[OPENFRAME_DOCTOR_COMMANDS.windows]}
|
|
162
|
+
onCopyPath={copyPath}
|
|
163
|
+
/>
|
|
164
|
+
</WarningBlock>
|
|
165
|
+
</div>
|
|
166
|
+
),
|
|
167
|
+
};
|