@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.
Files changed (93) hide show
  1. package/dist/{chunk-L6IBKPVM.js → chunk-EKBM4FHK.js} +2 -2
  2. package/dist/{chunk-SWZUZYWR.js → chunk-EWA2NFUR.js} +2 -2
  3. package/dist/{chunk-TYIBMDUZ.cjs → chunk-FZZBCRID.cjs} +7 -7
  4. package/dist/{chunk-TYIBMDUZ.cjs.map → chunk-FZZBCRID.cjs.map} +1 -1
  5. package/dist/{chunk-G2HHSZ3S.cjs → chunk-GE64T3JT.cjs} +9 -9
  6. package/dist/{chunk-G2HHSZ3S.cjs.map → chunk-GE64T3JT.cjs.map} +1 -1
  7. package/dist/{chunk-YWDC5BXM.cjs → chunk-L5RSJE2I.cjs} +1940 -915
  8. package/dist/chunk-L5RSJE2I.cjs.map +1 -0
  9. package/dist/{chunk-BVFRD34B.js → chunk-OHOUSDAY.js} +2 -2
  10. package/dist/{chunk-MVQ3OODK.cjs → chunk-S4SVD5JI.cjs} +9 -9
  11. package/dist/{chunk-MVQ3OODK.cjs.map → chunk-S4SVD5JI.cjs.map} +1 -1
  12. package/dist/{chunk-N5IKPYRL.js → chunk-SWIR5EB2.js} +2 -2
  13. package/dist/{chunk-6DCKL73F.cjs → chunk-TCJ5B2ZD.cjs} +24 -24
  14. package/dist/{chunk-6DCKL73F.cjs.map → chunk-TCJ5B2ZD.cjs.map} +1 -1
  15. package/dist/{chunk-ENBGG2K2.js → chunk-V5JY5RSY.js} +2954 -1929
  16. package/dist/chunk-V5JY5RSY.js.map +1 -0
  17. package/dist/components/chat/embeddable-chat.d.ts +13 -0
  18. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  19. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +104 -10
  20. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  21. package/dist/components/chat/hooks/use-slash-commands.d.ts +6 -0
  22. package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
  23. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  24. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  25. package/dist/components/chat/index.cjs +2 -2
  26. package/dist/components/chat/index.js +1 -1
  27. package/dist/components/chat/types/unified-chat-state.types.d.ts +81 -0
  28. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  29. package/dist/components/contact/index.cjs +3 -3
  30. package/dist/components/contact/index.js +2 -2
  31. package/dist/components/features/index.cjs +2 -2
  32. package/dist/components/features/index.js +1 -1
  33. package/dist/components/index.cjs +73 -51
  34. package/dist/components/index.cjs.map +1 -1
  35. package/dist/components/index.js +26 -4
  36. package/dist/components/index.js.map +1 -1
  37. package/dist/components/navigation/app-header.d.ts +7 -0
  38. package/dist/components/navigation/app-header.d.ts.map +1 -1
  39. package/dist/components/navigation/app-layout-drawer.d.ts +65 -0
  40. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -0
  41. package/dist/components/navigation/app-layout.d.ts +9 -1
  42. package/dist/components/navigation/app-layout.d.ts.map +1 -1
  43. package/dist/components/navigation/header-mingo-button.d.ts +21 -0
  44. package/dist/components/navigation/header-mingo-button.d.ts.map +1 -0
  45. package/dist/components/navigation/index.cjs +24 -2
  46. package/dist/components/navigation/index.cjs.map +1 -1
  47. package/dist/components/navigation/index.d.ts +5 -1
  48. package/dist/components/navigation/index.d.ts.map +1 -1
  49. package/dist/components/navigation/index.js +23 -1
  50. package/dist/components/onboarding-guides/index.cjs +18 -18
  51. package/dist/components/onboarding-guides/index.js +3 -3
  52. package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
  53. package/dist/components/tickets/index.cjs +80 -66
  54. package/dist/components/tickets/index.cjs.map +1 -1
  55. package/dist/components/tickets/index.js +20 -6
  56. package/dist/components/tickets/index.js.map +1 -1
  57. package/dist/components/ui/index.cjs +2 -2
  58. package/dist/components/ui/index.js +1 -1
  59. package/dist/index.cjs +26 -2
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.js +25 -1
  62. package/dist/utils/embed-authed-fetch.d.ts +80 -0
  63. package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
  64. package/dist/utils/index.cjs +70 -5
  65. package/dist/utils/index.cjs.map +1 -1
  66. package/dist/utils/index.d.ts +1 -1
  67. package/dist/utils/index.d.ts.map +1 -1
  68. package/dist/utils/index.js +70 -6
  69. package/dist/utils/index.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/components/chat/embeddable-chat.tsx +154 -37
  72. package/src/components/chat/hooks/use-nats-chat-adapter.ts +601 -23
  73. package/src/components/chat/hooks/use-slash-commands.ts +10 -1
  74. package/src/components/chat/hooks/use-sse-chat-adapter.ts +45 -0
  75. package/src/components/chat/hooks/use-unified-chat.ts +59 -0
  76. package/src/components/chat/types/unified-chat-state.types.ts +116 -0
  77. package/src/components/navigation/app-header.tsx +23 -0
  78. package/src/components/navigation/app-layout-drawer.tsx +620 -0
  79. package/src/components/navigation/app-layout.tsx +65 -26
  80. package/src/components/navigation/header-mingo-button.tsx +58 -0
  81. package/src/components/navigation/index.ts +17 -1
  82. package/src/components/tickets/hooks/use-ticket-engagements.ts +24 -4
  83. package/src/stories/AppLayoutDrawer.stories.tsx +228 -0
  84. package/src/utils/.embed-authed-fetch.md +7 -0
  85. package/src/utils/__tests__/embed-authed-fetch.test.ts +103 -1
  86. package/src/utils/embed-authed-fetch.ts +247 -7
  87. package/src/utils/index.ts +5 -1
  88. package/dist/chunk-ENBGG2K2.js.map +0 -1
  89. package/dist/chunk-YWDC5BXM.cjs.map +0 -1
  90. /package/dist/{chunk-L6IBKPVM.js.map → chunk-EKBM4FHK.js.map} +0 -0
  91. /package/dist/{chunk-SWZUZYWR.js.map → chunk-EWA2NFUR.js.map} +0 -0
  92. /package/dist/{chunk-BVFRD34B.js.map → chunk-OHOUSDAY.js.map} +0 -0
  93. /package/dist/{chunk-N5IKPYRL.js.map → chunk-SWIR5EB2.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.217",
3
+ "version": "0.0.218",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -191,7 +191,7 @@
191
191
  "test:ui": "vitest --ui",
192
192
  "test:run": "vitest run",
193
193
  "test:coverage": "vitest run --coverage",
194
- "yalc:watch": "NODE_OPTIONS='--max-old-space-size=4096' nodemon --watch src --watch tsconfig.json --watch tsup.config.ts --watch tailwind.config.ts --watch package.json -e ts,tsx,json,css --ignore dist --ignore node_modules --ignore '*.lock' --ignore .yalc --ignore yalc.lock --exec 'rm -rf dist && tsup && yalc push --changed'",
194
+ "yalc:watch": "NODE_OPTIONS='--max-old-space-size=4096' nodemon --watch src --watch tsconfig.json --watch tsup.config.ts --watch tailwind.config.ts --watch package.json -e ts,tsx,json,css --ignore dist --ignore node_modules --ignore '*.lock' --ignore .yalc --ignore yalc.lock --exec 'rm -rf dist && tsup && tsc -p tsconfig.declarations.json && yalc push --changed'",
195
195
  "yalc:push": "yalc push",
196
196
  "yalc:publish": "yalc publish",
197
197
  "generate:icons": "node scripts/generate-icons.mjs",
@@ -47,6 +47,7 @@ import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
47
47
 
48
48
  import { ChatFooter } from './chat-container'
49
49
  import { ChatInput } from './chat-input'
50
+ import { ChatSidebar } from './chat-sidebar'
50
51
  import { MingoOnboardingCard } from './mingo-onboarding-card'
51
52
  import { MingoOnboardingCardSkeleton } from './mingo-onboarding-card-skeleton'
52
53
  import { ChatMessageList } from './chat-message-list'
@@ -167,6 +168,20 @@ export interface EmbeddableChatProps {
167
168
  * else `'mingo'`.
168
169
  */
169
170
  defaultActiveMode?: ChatMode
171
+
172
+ /**
173
+ * Wrapper shell around the chat body.
174
+ * - `'drawer'` (default): wraps in a body-level Radix Drawer (slide-in
175
+ * overlay from the right) — the original MPH / standalone behaviour.
176
+ * - `'none'`: no shell. Renders only the chat body so the consumer can
177
+ * host it inside their own container (e.g. `AppLayoutDrawerContent`).
178
+ * The internal "Ask AI" trigger and iOS body scroll-lock are also
179
+ * suppressed — those are Drawer-shell concerns. The consumer is
180
+ * responsible for mount/unmount and for opening/closing via the
181
+ * `open` / `onOpenChange` props (which the in-body close button still
182
+ * drives).
183
+ */
184
+ shell?: 'drawer' | 'none'
170
185
  }
171
186
 
172
187
  // =============================================================================
@@ -562,7 +577,13 @@ function EmbeddableChatInner({
562
577
  activeMode: controlledActiveMode,
563
578
  onActiveModeChange,
564
579
  defaultActiveMode,
580
+ shell = 'drawer',
565
581
  }: EmbeddableChatProps) {
582
+ // `shell === 'none'` means the consumer hosts us inside their own panel
583
+ // (e.g. AppLayoutDrawer in openframe-frontend). Several drawer-shell
584
+ // concerns are unconditional in this codebase — gate them off here so
585
+ // we don't double-up with the host's behaviour.
586
+ const shellLess = shell === 'none'
566
587
  const runtime = useRequiredChatRuntime()
567
588
  const source = runtime.source as DocSource
568
589
  const commandsUrl = runtime.endpoints.commandsUrl
@@ -603,12 +624,15 @@ function EmbeddableChatInner({
603
624
 
604
625
  // iOS scroll-lock — react-aria's two-layer approach plus body fixed-pos.
605
626
  // See hub original for the long mechanism comment; logic copied verbatim.
606
- usePreventScroll({ isDisabled: !isOpen })
627
+ // Skipped in shell-less mode: the host (AppLayoutDrawer) is in-layout, so
628
+ // there's no body-level overlay that should block page scrolling.
629
+ usePreventScroll({ isDisabled: !isOpen || shellLess })
607
630
 
608
631
  const navigatingAwayRef = useRef(false)
609
632
 
610
633
  useEffect(() => {
611
634
  if (!isOpen) return
635
+ if (shellLess) return
612
636
  if (typeof window === 'undefined') return
613
637
  if (!isIOS()) return
614
638
 
@@ -634,7 +658,7 @@ function EmbeddableChatInner({
634
658
  if (window.location.pathname !== openPathname) return
635
659
  window.scrollTo(0, scrollY)
636
660
  }
637
- }, [isOpen])
661
+ }, [isOpen, shellLess])
638
662
 
639
663
  // Imperative handle to the chat input.
640
664
  const chatInputRef = useRef<ChatInputRef | null>(null)
@@ -755,8 +779,46 @@ function EmbeddableChatInner({
755
779
  currentCacheHitRatePct,
756
780
  currentUsageBreakdown,
757
781
  displayRef,
782
+ // ─── Dialog management (Mingo-mode sidebar) ───
783
+ dialogs,
784
+ activeDialogId,
785
+ selectDialog,
786
+ startNewDialog,
787
+ isDialogsLoading,
788
+ hasMoreDialogs,
789
+ loadMoreDialogs,
758
790
  } = useUnifiedChat({ modes: effectiveModes, activeMode })
759
791
 
792
+ // Whether the in-panel dialog sidebar should render. Gated on:
793
+ // 1. Mingo mode is active (Guide has localStorage-only history that
794
+ // isn't yet structured as a sidebar list — see
795
+ // [[chat-architecture-and-migration]] for the asymmetry).
796
+ // 2. The host wired `fetchDialogs` — managed-dialog mode, not the
797
+ // bare-transport Tauri flow.
798
+ const showSidebar =
799
+ activeMode === 'mingo' && effectiveModes.mingo?.fetchDialogs !== undefined
800
+
801
+ // Pending startNewDialog promise — used to gate the "Start new chat"
802
+ // button while creation is in flight so a double-click doesn't spawn
803
+ // two backend dialogs.
804
+ const [isStartingNewDialog, setIsStartingNewDialog] = useState<boolean>(false)
805
+ const handleStartNewDialog = useCallback(async () => {
806
+ if (isStartingNewDialog) return
807
+ setIsStartingNewDialog(true)
808
+ try {
809
+ await startNewDialog()
810
+ } finally {
811
+ setIsStartingNewDialog(false)
812
+ }
813
+ }, [isStartingNewDialog, startNewDialog])
814
+
815
+ const handleSelectDialog = useCallback(
816
+ (id: string) => {
817
+ selectDialog(id)
818
+ },
819
+ [selectDialog],
820
+ )
821
+
760
822
  // Chat-attachment hooks (v2 attachment feature).
761
823
  const {
762
824
  attachments: stagedAttachments,
@@ -930,8 +992,9 @@ function EmbeddableChatInner({
930
992
  return (
931
993
  <>
932
994
  {/* Floating "Ask AI" button — sticky-dock pattern. See hub original
933
- for the full mechanism explanation. */}
934
- {showInternalTrigger && (
995
+ for the full mechanism explanation. Suppressed in shell-less mode:
996
+ the host controls open/close, so an internal trigger would race. */}
997
+ {showInternalTrigger && !shellLess && (
935
998
  <div
936
999
  aria-hidden={isOpen}
937
1000
  className={`sticky bottom-0 h-0 z-[9990] pointer-events-none ${
@@ -953,36 +1016,17 @@ function EmbeddableChatInner({
953
1016
  </div>
954
1017
  )}
955
1018
 
956
- <Drawer open={isOpen} onOpenChange={(o: boolean) => !o && handleClose()}>
957
- <ChatPanelContext.Provider value={chatPanelHandle}>
958
- {/*
959
- Panel-level handle for descendants (inline cards via
960
- `ChatCardNavWrap`, markdown-body links via
961
- `NavLinkAnchorViaRuntime`) to close the panel after same-tab
962
- navigation. Same-tab clicks fire `closeChat`; new-tab clicks
963
- leave the panel open while the new tab loads.
964
- */}
965
- <DrawerContent
966
- side="right"
967
- flush
968
- resizable
969
- minSize={480}
970
- maxSize={1280}
971
- defaultSize={640}
972
- storageKey="mingo-chat-width"
973
- resizeAriaLabel="Resize chat panel"
974
- overlayClassName="mingo-chat-overlay md:bg-black/20"
975
- aria-describedby={undefined}
976
- className="
977
- mingo-chat-content !bg-ods-bg shadow-2xl
978
- focus:outline-none focus-visible:outline-none
979
- w-screen md:w-auto
980
- "
981
- >
982
- <VisuallyHidden>
983
- <DialogPrimitive.Title>{sourceLabel} AI Assistant</DialogPrimitive.Title>
984
- </VisuallyHidden>
985
-
1019
+ {/*
1020
+ Conditional shell — overlay (Drawer, MPH default) vs inline
1021
+ (positioned `<aside>` anchored to the nearest positioned
1022
+ ancestor, openframe-frontend's nav-embedded chat). Both share
1023
+ the same `body` JSX so the chat content is defined exactly
1024
+ once; the IIFE keeps both branches type-balanced inside JSX
1025
+ (no opening tag in one ternary branch + closing in another).
1026
+ */}
1027
+ {(() => {
1028
+ const body = (
1029
+ <>
986
1030
  <div className="flex h-full flex-col overflow-hidden">
987
1031
  {/* Figma node 7363:205930 — top-navigation. Title intentionally
988
1032
  omitted; a chevron-left + "New Chat" back-style affordance
@@ -1021,6 +1065,27 @@ function EmbeddableChatInner({
1021
1065
  </button>
1022
1066
  </div>
1023
1067
 
1068
+ {/* Sidebar + chat-panel row. When Mingo is the active mode and
1069
+ `fetchDialogs` is wired, the in-panel `<ChatSidebar>` lists
1070
+ the user's backend-driven dialog history. Guide mode keeps
1071
+ history in localStorage and currently renders no sidebar. */}
1072
+ <div className="flex flex-1 min-h-0 overflow-hidden">
1073
+ {showSidebar && (
1074
+ <ChatSidebar
1075
+ className="w-72 shrink-0"
1076
+ dialogs={dialogs}
1077
+ activeDialogId={activeDialogId ?? undefined}
1078
+ onDialogSelect={handleSelectDialog}
1079
+ onNewChat={() => { void handleStartNewDialog() }}
1080
+ isLoading={isDialogsLoading && dialogs.length === 0}
1081
+ isCreatingDialog={isStartingNewDialog}
1082
+ hasNextPage={hasMoreDialogs}
1083
+ isFetchingNextPage={isDialogsLoading && dialogs.length > 0}
1084
+ onLoadMore={() => { void loadMoreDialogs() }}
1085
+ />
1086
+ )}
1087
+ <div className="flex flex-1 flex-col min-h-0 min-w-0">
1088
+
1024
1089
  {showModeToggle ? (
1025
1090
  <div
1026
1091
  role="radiogroup"
@@ -1222,11 +1287,63 @@ function EmbeddableChatInner({
1222
1287
  </div>
1223
1288
  </div>
1224
1289
  </div>
1290
+ </div>
1291
+ </div>
1225
1292
  </div>
1226
1293
  {galleryModal}
1227
- </DrawerContent>
1228
- </ChatPanelContext.Provider>
1229
- </Drawer>
1294
+ </>
1295
+ )
1296
+
1297
+ // Shell-less branch — host (e.g. AppLayoutDrawer) provides the panel
1298
+ // chrome, focus trap, animation, and size. We render only the chat
1299
+ // body plus ChatPanelContext so descendants can still close after
1300
+ // same-tab navigation via the host's `onOpenChange`.
1301
+ if (shellLess) {
1302
+ return (
1303
+ <ChatPanelContext.Provider value={chatPanelHandle}>
1304
+ {body}
1305
+ </ChatPanelContext.Provider>
1306
+ )
1307
+ }
1308
+
1309
+ // Body-level Radix Drawer — backdrop, iOS scroll-lock, focus
1310
+ // trap, drag-to-resize handle. Slides in from the right edge.
1311
+ return (
1312
+ <Drawer open={isOpen} onOpenChange={(o: boolean) => !o && handleClose()}>
1313
+ <ChatPanelContext.Provider value={chatPanelHandle}>
1314
+ {/*
1315
+ Panel-level handle for descendants (inline cards via
1316
+ `ChatCardNavWrap`, markdown-body links via
1317
+ `NavLinkAnchorViaRuntime`) to close the panel after same-tab
1318
+ navigation. Same-tab clicks fire `closeChat`; new-tab clicks
1319
+ leave the panel open while the new tab loads.
1320
+ */}
1321
+ <DrawerContent
1322
+ side="right"
1323
+ flush
1324
+ resizable
1325
+ minSize={showSidebar ? 720 : 480}
1326
+ maxSize={1600}
1327
+ defaultSize={showSidebar ? 960 : 640}
1328
+ storageKey={showSidebar ? 'mingo-chat-width-with-sidebar' : 'mingo-chat-width'}
1329
+ resizeAriaLabel="Resize chat panel"
1330
+ overlayClassName="mingo-chat-overlay md:bg-black/20"
1331
+ aria-describedby={undefined}
1332
+ className="
1333
+ mingo-chat-content !bg-ods-bg shadow-2xl
1334
+ focus:outline-none focus-visible:outline-none
1335
+ w-screen md:w-auto
1336
+ "
1337
+ >
1338
+ <VisuallyHidden>
1339
+ <DialogPrimitive.Title>{sourceLabel} AI Assistant</DialogPrimitive.Title>
1340
+ </VisuallyHidden>
1341
+ {body}
1342
+ </DrawerContent>
1343
+ </ChatPanelContext.Provider>
1344
+ </Drawer>
1345
+ )
1346
+ })()}
1230
1347
  </>
1231
1348
  )
1232
1349
  }