@vertesia/ui 0.74.0 → 0.77.0

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 (134) hide show
  1. package/lib/esm/core/components/Center.js +1 -1
  2. package/lib/esm/core/components/Center.js.map +1 -1
  3. package/lib/esm/core/components/Overlay.js +57 -0
  4. package/lib/esm/core/components/Overlay.js.map +1 -0
  5. package/lib/esm/core/components/SidePanel.js +6 -6
  6. package/lib/esm/core/components/SidePanel.js.map +1 -1
  7. package/lib/esm/core/components/index.js +1 -0
  8. package/lib/esm/core/components/index.js.map +1 -1
  9. package/lib/esm/core/components/shadcn/tabs.js +43 -5
  10. package/lib/esm/core/components/shadcn/tabs.js.map +1 -1
  11. package/lib/esm/features/agent/PayloadBuilder.js +9 -2
  12. package/lib/esm/features/agent/PayloadBuilder.js.map +1 -1
  13. package/lib/esm/features/facets/DocumentsFacetsNav.js +4 -2
  14. package/lib/esm/features/facets/DocumentsFacetsNav.js.map +1 -1
  15. package/lib/esm/features/facets/VTypeFacet.js +2 -1
  16. package/lib/esm/features/facets/VTypeFacet.js.map +1 -1
  17. package/lib/esm/features/magic-pdf/TextPageView.js +2 -4
  18. package/lib/esm/features/magic-pdf/TextPageView.js.map +1 -1
  19. package/lib/esm/features/store/collections/CreateCollection.js +1 -1
  20. package/lib/esm/features/store/collections/CreateCollection.js.map +1 -1
  21. package/lib/esm/features/store/objects/DocumentPreviewPanel.js +2 -4
  22. package/lib/esm/features/store/objects/DocumentPreviewPanel.js.map +1 -1
  23. package/lib/esm/features/store/objects/DocumentSearchResults.js +19 -21
  24. package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
  25. package/lib/esm/features/store/objects/components/ContentOverview.js +2 -4
  26. package/lib/esm/features/store/objects/components/ContentOverview.js.map +1 -1
  27. package/lib/esm/features/store/objects/components/VectorSearchWidget.js +51 -46
  28. package/lib/esm/features/store/objects/components/VectorSearchWidget.js.map +1 -1
  29. package/lib/esm/features/store/objects/search/DocumentSearchContext.js +50 -34
  30. package/lib/esm/features/store/objects/search/DocumentSearchContext.js.map +1 -1
  31. package/lib/esm/features/store/objects/search/DocumentSearchProvider.js +1 -3
  32. package/lib/esm/features/store/objects/search/DocumentSearchProvider.js.map +1 -1
  33. package/lib/esm/features/store/objects/upload/useSmartFileUploadProcessing.js +4 -11
  34. package/lib/esm/features/store/objects/upload/useSmartFileUploadProcessing.js.map +1 -1
  35. package/lib/esm/features/user/UserInfo.js +2 -2
  36. package/lib/esm/features/user/UserInfo.js.map +1 -1
  37. package/lib/esm/session/UserSessionProvider.js +6 -3
  38. package/lib/esm/session/UserSessionProvider.js.map +1 -1
  39. package/lib/esm/session/auth/composable.js +3 -3
  40. package/lib/esm/session/auth/composable.js.map +1 -1
  41. package/lib/esm/session/auth/firebase.js +7 -0
  42. package/lib/esm/session/auth/firebase.js.map +1 -1
  43. package/lib/esm/session/auth/useAuthState.js +0 -3
  44. package/lib/esm/session/auth/useAuthState.js.map +1 -1
  45. package/lib/esm/shell/apps/StandaloneApp.js +1 -1
  46. package/lib/esm/shell/apps/StandaloneApp.js.map +1 -1
  47. package/lib/esm/shell/login/EnterpriseSigninButton.js +3 -0
  48. package/lib/esm/shell/login/EnterpriseSigninButton.js.map +1 -1
  49. package/lib/esm/shell/login/InviteAcceptModal.js +1 -1
  50. package/lib/esm/shell/login/InviteAcceptModal.js.map +1 -1
  51. package/lib/esm/widgets/form/ManagedObject.js +1 -1
  52. package/lib/esm/widgets/index.js +1 -0
  53. package/lib/esm/widgets/index.js.map +1 -1
  54. package/lib/esm/widgets/markdown/MarkdownRenderer.js +24 -0
  55. package/lib/esm/widgets/markdown/MarkdownRenderer.js.map +1 -0
  56. package/lib/esm/widgets/markdown/index.js +2 -0
  57. package/lib/esm/widgets/markdown/index.js.map +1 -0
  58. package/lib/tsconfig.tsbuildinfo +1 -1
  59. package/lib/types/core/components/Overlay.d.ts +25 -0
  60. package/lib/types/core/components/Overlay.d.ts.map +1 -0
  61. package/lib/types/core/components/SidePanel.d.ts +3 -1
  62. package/lib/types/core/components/SidePanel.d.ts.map +1 -1
  63. package/lib/types/core/components/index.d.ts +1 -0
  64. package/lib/types/core/components/index.d.ts.map +1 -1
  65. package/lib/types/core/components/shadcn/tabs.d.ts.map +1 -1
  66. package/lib/types/env/index.d.ts +2 -2
  67. package/lib/types/env/index.d.ts.map +1 -1
  68. package/lib/types/features/agent/PayloadBuilder.d.ts.map +1 -1
  69. package/lib/types/features/facets/DocumentsFacetsNav.d.ts.map +1 -1
  70. package/lib/types/features/facets/VTypeFacet.d.ts.map +1 -1
  71. package/lib/types/features/magic-pdf/TextPageView.d.ts.map +1 -1
  72. package/lib/types/features/store/objects/DocumentPreviewPanel.d.ts.map +1 -1
  73. package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  74. package/lib/types/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  75. package/lib/types/features/store/objects/components/VectorSearchWidget.d.ts +4 -3
  76. package/lib/types/features/store/objects/components/VectorSearchWidget.d.ts.map +1 -1
  77. package/lib/types/features/store/objects/search/DocumentSearchContext.d.ts +5 -2
  78. package/lib/types/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
  79. package/lib/types/features/store/objects/search/DocumentSearchProvider.d.ts +2 -4
  80. package/lib/types/features/store/objects/search/DocumentSearchProvider.d.ts.map +1 -1
  81. package/lib/types/features/store/objects/upload/useSmartFileUploadProcessing.d.ts.map +1 -1
  82. package/lib/types/session/UserSessionProvider.d.ts.map +1 -1
  83. package/lib/types/session/auth/composable.d.ts +1 -1
  84. package/lib/types/session/auth/composable.d.ts.map +1 -1
  85. package/lib/types/session/auth/firebase.d.ts.map +1 -1
  86. package/lib/types/session/auth/useAuthState.d.ts.map +1 -1
  87. package/lib/types/shell/login/EnterpriseSigninButton.d.ts.map +1 -1
  88. package/lib/types/widgets/index.d.ts +1 -0
  89. package/lib/types/widgets/index.d.ts.map +1 -1
  90. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts +9 -0
  91. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts.map +1 -0
  92. package/lib/types/widgets/markdown/index.d.ts +2 -0
  93. package/lib/types/widgets/markdown/index.d.ts.map +1 -0
  94. package/lib/vertesia-ui-core.js +1 -1
  95. package/lib/vertesia-ui-core.js.map +1 -1
  96. package/lib/vertesia-ui-features.js +1 -1
  97. package/lib/vertesia-ui-features.js.map +1 -1
  98. package/lib/vertesia-ui-session.js +1 -1
  99. package/lib/vertesia-ui-session.js.map +1 -1
  100. package/lib/vertesia-ui-shell.js +1 -1
  101. package/lib/vertesia-ui-shell.js.map +1 -1
  102. package/lib/vertesia-ui-widgets.js +1 -1
  103. package/lib/vertesia-ui-widgets.js.map +1 -1
  104. package/package.json +6 -4
  105. package/src/core/components/Center.tsx +1 -1
  106. package/src/core/components/Overlay.tsx +129 -0
  107. package/src/core/components/SidePanel.tsx +38 -33
  108. package/src/core/components/index.ts +1 -0
  109. package/src/core/components/shadcn/tabs.tsx +48 -5
  110. package/src/env/index.ts +1 -1
  111. package/src/features/agent/PayloadBuilder.tsx +8 -2
  112. package/src/features/facets/DocumentsFacetsNav.tsx +4 -2
  113. package/src/features/facets/VTypeFacet.tsx +2 -1
  114. package/src/features/magic-pdf/TextPageView.tsx +3 -4
  115. package/src/features/store/collections/CreateCollection.tsx +1 -1
  116. package/src/features/store/objects/DocumentPreviewPanel.tsx +2 -4
  117. package/src/features/store/objects/DocumentSearchResults.tsx +24 -26
  118. package/src/features/store/objects/components/ContentOverview.tsx +3 -6
  119. package/src/features/store/objects/components/VectorSearchWidget.tsx +88 -60
  120. package/src/features/store/objects/search/DocumentSearchContext.ts +57 -36
  121. package/src/features/store/objects/search/DocumentSearchProvider.tsx +2 -6
  122. package/src/features/store/objects/upload/useSmartFileUploadProcessing.ts +6 -12
  123. package/src/features/user/UserInfo.tsx +2 -2
  124. package/src/session/UserSessionProvider.tsx +5 -3
  125. package/src/session/auth/composable.ts +3 -3
  126. package/src/session/auth/firebase.ts +8 -0
  127. package/src/session/auth/useAuthState.ts +0 -4
  128. package/src/shell/apps/StandaloneApp.tsx +1 -1
  129. package/src/shell/login/EnterpriseSigninButton.tsx +3 -0
  130. package/src/shell/login/InviteAcceptModal.tsx +1 -1
  131. package/src/widgets/form/ManagedObject.ts +1 -1
  132. package/src/widgets/index.ts +1 -0
  133. package/src/widgets/markdown/MarkdownRenderer.tsx +45 -0
  134. package/src/widgets/markdown/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/ui",
3
- "version": "0.74.0",
3
+ "version": "0.77.0",
4
4
  "description": "Vertesia UI components and and hooks",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -43,6 +43,7 @@
43
43
  "debounce": "^2.2.0",
44
44
  "fast-xml-parser": "^5.2.3",
45
45
  "firebase": "^10.12.2",
46
+ "framer-motion": "^12.23.12",
46
47
  "json-schema": "^0.4.0",
47
48
  "jwt-decode": "^4.0.0",
48
49
  "lodash-es": "^4.17.21",
@@ -58,9 +59,10 @@
58
59
  "remark-gfm": "^4.0.1",
59
60
  "tailwind-merge": "^3.3.0",
60
61
  "ts-md5": "^1.3.1",
61
- "@vertesia/common": "0.74.0",
62
- "@vertesia/client": "0.74.0",
63
- "@vertesia/json": "0.74.0"
62
+ "unist-util-visit": "^5.0.0",
63
+ "@vertesia/client": "0.77.0",
64
+ "@vertesia/common": "0.77.0",
65
+ "@vertesia/json": "0.77.0"
64
66
  },
65
67
  "devDependencies": {
66
68
  "@eslint/js": "^9.27.0",
@@ -7,6 +7,6 @@ interface CenterProps {
7
7
  }
8
8
  export function Center({ className, children }: CenterProps) {
9
9
  return (
10
- <div className={clsx('flex items-ceter justify-center', className)}>{children}</div>
10
+ <div className={clsx('flex items-center justify-center', className)}>{children}</div>
11
11
  )
12
12
  }
@@ -0,0 +1,129 @@
1
+ import { motion } from "framer-motion"
2
+ import { X } from "lucide-react"
3
+ import { useState, ReactNode } from "react"
4
+
5
+ import { Button } from "./Button"
6
+
7
+ interface OverlayProps {
8
+ children: ReactNode
9
+ overlayContent: ReactNode
10
+ className?: string
11
+ overlayClassName?: string
12
+ position?: 'left' | 'right' | 'top' | 'bottom' | 'center'
13
+ width?: string
14
+ height?: string
15
+ showCloseButton?: boolean
16
+ closeButtonTooltip?: string
17
+ onOpen?: () => void
18
+ onClose?: () => void
19
+ triggerClassName?: string
20
+ backdropClassName?: string
21
+ animationConfig?: {
22
+ type?: "spring" | "tween"
23
+ stiffness?: number
24
+ damping?: number
25
+ duration?: number
26
+ }
27
+ }
28
+ export function Overlay({
29
+ children,
30
+ overlayContent,
31
+ className = "",
32
+ overlayClassName = "",
33
+ position = 'right',
34
+ width,
35
+ height,
36
+ showCloseButton = true,
37
+ onOpen,
38
+ onClose,
39
+ triggerClassName = "",
40
+ backdropClassName = "",
41
+ animationConfig = { type: "spring", stiffness: 300, damping: 30 }
42
+ }: Readonly<OverlayProps>) {
43
+ const [isOpen, setIsOpen] = useState(false)
44
+
45
+ const handleOpen = () => {
46
+ setIsOpen(true)
47
+ onOpen?.()
48
+ }
49
+
50
+ const handleClose = () => {
51
+ setIsOpen(false)
52
+ onClose?.()
53
+ }
54
+
55
+ const animationProps = getAnimationProps(position)
56
+ const positionClasses = getPositionClasses(position, width, height)
57
+
58
+ return (
59
+ <div className={`flex items-center justify-center w-full ${className}`}>
60
+ <div onClick={handleOpen} className={`w-full align-left cursor-pointer ${triggerClassName}`}>
61
+ {children}
62
+ </div>
63
+ {
64
+ isOpen && (
65
+ <div className={`z-45 fixed inset-0 bg-black bg-opacity-50 ${backdropClassName}`}>
66
+ <motion.div
67
+ {...animationProps}
68
+ transition={animationConfig}
69
+ className={`${positionClasses} ${overlayClassName}`}
70
+ >
71
+ {
72
+ showCloseButton && (
73
+ <div className="absolute top-2 right-2 z-10">
74
+ <Button onClick={handleClose} variant="primary">
75
+ <X />
76
+ </Button>
77
+ </div>
78
+ )
79
+ }
80
+ <div className={showCloseButton ? "mt-8" : ""}>
81
+ {overlayContent}
82
+ </div>
83
+ </motion.div>
84
+ </div>
85
+ )
86
+ }
87
+ </div>
88
+ )
89
+ }
90
+
91
+ function getAnimationProps(position: string) {
92
+ switch (position) {
93
+ case 'left':
94
+ return { initial: { x: "-100%" }, animate: { x: 0 }, exit: { x: "-100%" } }
95
+ case 'right':
96
+ return { initial: { x: "100%" }, animate: { x: 0 }, exit: { x: "100%" } }
97
+ case 'top':
98
+ return { initial: { y: "-100%" }, animate: { y: 0 }, exit: { y: "-100%" } }
99
+ case 'bottom':
100
+ return { initial: { y: "100%" }, animate: { y: 0 }, exit: { y: "100%" } }
101
+ case 'center':
102
+ return {
103
+ initial: { opacity: 0, scale: 0.8 },
104
+ animate: { opacity: 1, scale: 1 },
105
+ exit: { opacity: 0, scale: 0.8 }
106
+ }
107
+ default:
108
+ return { initial: { x: "100%" }, animate: { x: 0 }, exit: { x: "100%" } }
109
+ }
110
+ }
111
+
112
+ function getPositionClasses(position: string, width?: string, height?: string) {
113
+ const baseClasses = "fixed bg-white shadow-lg p-4 relative"
114
+
115
+ switch (position) {
116
+ case 'left':
117
+ return `${baseClasses} left-0 top-[var(--header-height)] h-full ${width || 'w-80'}`
118
+ case 'right':
119
+ return `${baseClasses} right-0 top-[var(--header-height)] h-full ${width || 'w-80'}`
120
+ case 'top':
121
+ return `${baseClasses} top-[var(--header-height)] left-0 right-0 ${height || 'h-80'}`
122
+ case 'bottom':
123
+ return `${baseClasses} bottom-0 left-0 right-0 ${height || 'h-80'}`
124
+ case 'center':
125
+ return `${baseClasses} top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 ${width || 'w-96'} ${height || 'max-h-96'}`
126
+ default:
127
+ return `${baseClasses} right-0 top-[var(--header-height)] h-full ${width || 'w-80'}`
128
+ }
129
+ }
@@ -1,7 +1,5 @@
1
-
2
-
3
- import { useState, Fragment } from 'react';
4
- import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
1
+ import { useState } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
5
3
  import { X } from 'lucide-react';
6
4
  import { Button } from './shadcn/button';
7
5
 
@@ -10,16 +8,18 @@ interface SidePanelProps {
10
8
  onClose: () => void;
11
9
  children: React.ReactNode;
12
10
  title?: string;
11
+ panelWidth?: number;
12
+ backdrop?: boolean;
13
13
  }
14
- export function SidePanel({ isOpen, title, onClose, children }: SidePanelProps) {
15
- const [panelWidth, setPanelWidth] = useState(768);
14
+ export function SidePanel({ isOpen, title, onClose, children, panelWidth = 768, backdrop = false }: SidePanelProps) {
15
+ const [_panelWidth, setPanelWidth] = useState(panelWidth);
16
16
 
17
17
  const handleDragStart = (e: React.MouseEvent) => {
18
18
  e.preventDefault();
19
19
 
20
20
  let isDragging = true;
21
21
  const startX = e.pageX;
22
- const startWidth = panelWidth;
22
+ const startWidth = _panelWidth;
23
23
 
24
24
  const handleMouseMove = (e: MouseEvent) => {
25
25
  if (isDragging) {
@@ -40,25 +40,30 @@ export function SidePanel({ isOpen, title, onClose, children }: SidePanelProps)
40
40
  };
41
41
 
42
42
  return (
43
- <Transition show={isOpen} as={Fragment}>
44
- <Dialog as="div" className="relative z-10" onClose={onClose}>
45
- <div className="fixed inset-y-0 right-0" />
46
- <div className="fixed inset-y-0 right-0 overflow-hidden">
47
- <div className="absolute inset-0 overflow-hidden">
48
- <div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10 sm:pl-16">
49
- <TransitionChild
50
- as={Fragment}
51
- enter="transform transition ease-in-out duration-500 sm:duration-700"
52
- enterFrom="translate-x-full"
53
- enterTo="translate-x-0"
54
- leave="transform transition ease-in-out duration-500 sm:duration-700"
55
- leaveFrom="translate-x-0"
56
- leaveTo="translate-x-full"
57
- unmount={true}
58
- >
59
- <DialogPanel
43
+ <AnimatePresence>
44
+ {isOpen && (
45
+ <div className="relative z-20">
46
+ {/* Backdrop */}
47
+ {backdrop && (
48
+ <motion.div
49
+ className="fixed inset-0 bg-black/70"
50
+ initial={{ opacity: 0 }}
51
+ animate={{ opacity: 1 }}
52
+ exit={{ opacity: 0 }}
53
+ onClick={onClose}
54
+ />
55
+ )}
56
+
57
+ <div className="fixed inset-y-0 right-0 overflow-hidden">
58
+ <div className="absolute inset-0 overflow-hidden">
59
+ <div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10 sm:pl-16">
60
+ <motion.div
60
61
  className="pointer-events-auto border-l"
61
- style={{ width: `${panelWidth}px` }}
62
+ style={{ width: `${_panelWidth}px` }}
63
+ initial={{ x: "100%" }}
64
+ animate={{ x: 0 }}
65
+ exit={{ x: "100%" }}
66
+ transition={{ type: "spring", stiffness: 300, damping: 30 }}
62
67
  >
63
68
  <div className="relative flex h-full">
64
69
  {/* Drag Handle */}
@@ -67,28 +72,28 @@ export function SidePanel({ isOpen, title, onClose, children }: SidePanelProps)
67
72
  onMouseDown={handleDragStart}
68
73
  />
69
74
  <div className="flex-1 flex flex-col overflow-y-scroll gap-4 bg-background py-6 shadow-xl">
70
- <div className="px-4 sm:px-6">
75
+ <div className="px-2 sm:px-4">
71
76
  <div className="flex items-start justify-between">
72
- <DialogTitle className="w-full text-base font-semibold leading-6">
77
+ <h2 className="w-full text-base font-semibold leading-6">
73
78
  <div className="text-2xl">{title ?? ""}</div>
74
- </DialogTitle>
79
+ </h2>
75
80
  <div className="ml-3 flex h-7 items-center">
76
81
  <CloseButton onClose={onClose} />
77
82
  </div>
78
83
  </div>
79
84
  </div>
80
- <div className="px-4 sm:px-6">
85
+ <div className="px-2 sm:px-4">
81
86
  {children}
82
87
  </div>
83
88
  </div>
84
89
  </div>
85
- </DialogPanel>
86
- </TransitionChild>
90
+ </motion.div>
91
+ </div>
87
92
  </div>
88
93
  </div>
89
94
  </div>
90
- </Dialog>
91
- </Transition>
95
+ )}
96
+ </AnimatePresence>
92
97
  );
93
98
  }
94
99
 
@@ -17,6 +17,7 @@ export * from "./MenuList.js";
17
17
  export * from "./MessageBox.js";
18
18
  export * from "./Modal.js";
19
19
  export * from "./NumberInput.js";
20
+ export * from "./Overlay.js";
20
21
  export * from "./popup/index.js";
21
22
  export * from "./Portal.js";
22
23
  export * from "./RadioGroup.js";
@@ -44,15 +44,58 @@ const VTabs = ({
44
44
  responsive = false,
45
45
  variant = "tabs"
46
46
  }: TabsProps) => {
47
- const currentValue = typeof current === 'function' ? current() : current || defaultValue;
47
+ // Initialize value
48
+ const [value, setValue] = React.useState(() => {
49
+ // First check if current is provided
50
+ const currentValue = typeof current === 'function' ? current() : current;
51
+ if (currentValue) {
52
+ return currentValue;
53
+ }
48
54
 
49
- const [value, setValue] = React.useState(currentValue);
55
+ // Then check hash
56
+ const hash = window.location.hash;
57
+ const currentTab = hash ? hash.substring(1) : undefined;
58
+
59
+ // Check if the tab from hash exists in tabs
60
+ if (currentTab && tabs.some(tab => tab.name === currentTab)) {
61
+ return currentTab;
62
+ }
63
+
64
+ // Fall back to default or first tab
65
+ return defaultValue || tabs[0]?.name;
66
+ });
50
67
 
68
+ // Update when current prop changes (but don't create a loop)
51
69
  React.useEffect(() => {
52
- if (currentValue) {
70
+ const currentValue = typeof current === 'function' ? current() : current;
71
+ if (currentValue && currentValue !== value) {
53
72
  setValue(currentValue);
54
73
  }
55
- }, [currentValue]);
74
+ }, [current]);
75
+
76
+ // Listen to hash changes only when there's no current prop being controlled externally
77
+ React.useEffect(() => {
78
+ if (current) return; // Skip hash handling if controlled by parent
79
+
80
+ const handleHashChange = () => {
81
+ const hash = window.location.hash;
82
+ const currentTab = hash ? hash.substring(1) : undefined;
83
+
84
+ // Only update if the tab exists in tabs
85
+ if (currentTab && tabs.some(tab => tab.name === currentTab)) {
86
+ setValue(currentTab);
87
+ } else if (!hash && defaultValue) {
88
+ // If no hash, fall back to default
89
+ setValue(defaultValue);
90
+ }
91
+ };
92
+
93
+ // Check initial hash
94
+ handleHashChange();
95
+
96
+ window.addEventListener('hashchange', handleHashChange);
97
+ return () => window.removeEventListener('hashchange', handleHashChange);
98
+ }, [current, tabs, defaultValue]);
56
99
 
57
100
  const handleValueChange = (newValue: string) => {
58
101
  setValue(newValue);
@@ -68,7 +111,7 @@ const VTabs = ({
68
111
  return (
69
112
  <TabsContext.Provider value={{ tabs, size: fullWidth ? tabs.length : 0, current: value, setTab, responsive: responsive, variant }}>
70
113
  <TabsPrimitive.Root
71
- defaultValue={tabs[0]?.name}
114
+ defaultValue={value || tabs[0]?.name}
72
115
  value={value}
73
116
  onValueChange={handleValueChange}
74
117
  className={className}
package/src/env/index.ts CHANGED
@@ -13,7 +13,7 @@ export interface EnvProps {
13
13
  zeno: string,
14
14
  studio: string,
15
15
  },
16
- firebase: {
16
+ firebase?: {
17
17
  apiKey: string,
18
18
  authDomain: string,
19
19
  projectId: string,
@@ -129,8 +129,14 @@ export class PayloadBuilder implements ConversationWorkflowPayload {
129
129
  if (environment?.id !== this.payload.config.environment?.id) {
130
130
  this.payload.config.environment = environment;
131
131
  if (!this._preserveRunValues) {
132
- this.payload.config.model = environment?.default_model && supportsToolUse(environment.default_model, environment.provider)
133
- ? environment.default_model : undefined;
132
+ // First try to use the interaction model, then the environment default model
133
+ const interactionModel = this.payload.interaction?.model;
134
+ if (interactionModel && environment && supportsToolUse(interactionModel, environment.provider)) {
135
+ this.payload.config.model = interactionModel;
136
+ } else {
137
+ this.payload.config.model = environment?.default_model && supportsToolUse(environment.default_model, environment.provider)
138
+ ? environment.default_model : undefined;
139
+ }
134
140
  }
135
141
 
136
142
  this.onStateChanged();
@@ -42,7 +42,8 @@ export function useDocumentFilterGroups(facets: DocumentsFacetsNavProps['facets'
42
42
  const statusFilterGroup = VStringFacet({
43
43
  search: null as any, // This will be provided by the search context
44
44
  buckets: facets.status || [],
45
- name: 'Status',
45
+ name: 'status',
46
+ placeholder: 'Status',
46
47
  type: 'select',
47
48
  multiple: true
48
49
  });
@@ -51,7 +52,8 @@ export function useDocumentFilterGroups(facets: DocumentsFacetsNavProps['facets'
51
52
 
52
53
  if (facets.tags) {
53
54
  customFilterGroups.push({
54
- name: 'Tags',
55
+ name: 'tags',
56
+ placeholder: 'Tags',
55
57
  type: 'stringList',
56
58
  options: facets.tags.map((tag: string) => ({
57
59
  label: tag,
@@ -41,7 +41,8 @@ export function VTypeFacet({ buckets, typeRegistry, type = 'select', multiple =
41
41
  });
42
42
 
43
43
  const customFilterGroups: FilterGroup = {
44
- name: 'Types',
44
+ name: 'types',
45
+ placeholder: 'Types',
45
46
  type: type,
46
47
  multiple: multiple,
47
48
  options: options,
@@ -1,7 +1,5 @@
1
- import { JSONCode, Theme, XMLViewer } from '@vertesia/ui/widgets';
1
+ import { JSONCode, Theme, XMLViewer, MarkdownRenderer } from '@vertesia/ui/widgets';
2
2
  import { useEffect, useLayoutEffect, useState } from "react";
3
- import Markdown from "react-markdown";
4
- import remarkGfm from "remark-gfm";
5
3
  import { usePdfPagesInfo } from "./PdfPageProvider";
6
4
  import { ViewType } from "./types";
7
5
 
@@ -16,6 +14,7 @@ const darkTheme: Theme = {
16
14
  cdataColor: "#33CC66",
17
15
  }
18
16
 
17
+
19
18
  interface TextPageViewProps {
20
19
  pageNumber: number;
21
20
  viewType: ViewType;
@@ -86,7 +85,7 @@ function MarkdownPageView({ pageNumber }: MarkdownPageViewProps) {
86
85
  }, [pageNumber]);
87
86
  return (
88
87
  <div className="px-4 py-2 prose prose-sm max-w-none dark:prose-invert">
89
- {content ? <Markdown remarkPlugins={[remarkGfm]}>{content}</Markdown> : <div>No markdown content available</div>}
88
+ {content ? <MarkdownRenderer>{content}</MarkdownRenderer> : <div>No markdown content available</div>}
90
89
  </div>
91
90
  )
92
91
  }
@@ -74,7 +74,7 @@ export function CreateCollectionForm({ onClose, redirect = true, onAddToCollecti
74
74
  };
75
75
 
76
76
  return (
77
- <form>
77
+ <form onSubmit={(e) => e.preventDefault()}>
78
78
  <VModalBody>
79
79
  <FormItem label="Name" required>
80
80
  <Input type="text" value={payload.name || ""} onChange={(value) => setPayloadProp("name", value)} />
@@ -5,7 +5,7 @@ import {
5
5
  import { Button, Spinner, useToast } from "@vertesia/ui/core";
6
6
  import { useNavigate } from "@vertesia/ui/router";
7
7
  import { useUserSession } from "@vertesia/ui/session";
8
- import { JSONDisplay } from "@vertesia/ui/widgets";
8
+ import { JSONDisplay, MarkdownRenderer } from "@vertesia/ui/widgets";
9
9
  import {
10
10
  ChevronRight,
11
11
  Download,
@@ -16,8 +16,6 @@ import {
16
16
  X,
17
17
  } from "lucide-react";
18
18
  import { useEffect, useState } from "react";
19
- import Markdown from "react-markdown";
20
- import remarkGfm from "remark-gfm";
21
19
 
22
20
  interface DocumentPreviewPanelProps {
23
21
  objectId: string | null;
@@ -233,7 +231,7 @@ export function DocumentPreviewPanel({
233
231
  <div className="shadow rounded-md p-4 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
234
232
  {seemsMarkdown ? (
235
233
  <div className="prose prose-sm max-w-none prose-p:my-2 prose-pre:bg-gray-800 prose-pre:my-2 prose-headings:text-indigo-700 dark:prose-invert dark:prose-headings:text-indigo-300">
236
- <Markdown remarkPlugins={[remarkGfm]}>{text}</Markdown>
234
+ <MarkdownRenderer>{text}</MarkdownRenderer>
237
235
  </div>
238
236
  ) : (
239
237
  <pre className="text-wrap whitespace-pre-wrap dark:text-gray-200">
@@ -1,14 +1,15 @@
1
- import { useEffect, useRef, useState } from "react";
2
-
3
- import { ColumnLayout, ContentObject, ContentObjectItem, VectorSearchQuery } from '@vertesia/common';
4
- import { Button, Divider, ErrorBox, SidePanel, Spinner, useDebounce, useIntersectionObserver, useToast } from '@vertesia/ui/core';
1
+ import { useRef, useState } from "react";
2
+ import { ColumnLayout, ContentObject, ContentObjectItem, ComplexSearchQuery } from '@vertesia/common';
3
+ import {
4
+
5
+ Button, Divider, ErrorBox, SidePanel, Spinner, useIntersectionObserver, useToast,
6
+ FilterProvider, FilterBtn, FilterBar, FilterClear, Filter as BaseFilter
7
+ } from '@vertesia/ui/core';
5
8
  import { useNavigate } from "@vertesia/ui/router";
6
9
  import { TypeRegistry, useUserSession } from '@vertesia/ui/session';
7
10
  import { Download, RefreshCw, Eye } from 'lucide-react';
8
- import { FilterProvider, FilterBtn, FilterBar, FilterClear, Filter as BaseFilter } from '@vertesia/ui/core';
9
11
  import { useDocumentFilterGroups, useDocumentFilterHandler } from "../../facets/DocumentsFacetsNav";
10
12
  import { VectorSearchWidget } from './components/VectorSearchWidget';
11
-
12
13
  import { ContentDispositionButton } from './components/ContentDispositionButton';
13
14
  import { DocumentTable } from './DocumentTable';
14
15
  import { useDocumentSearch, useWatchDocumentSearchFacets, useWatchDocumentSearchResult } from './search/DocumentSearchContext';
@@ -90,7 +91,6 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
90
91
  const [selectedObject, setSelectedObject] = useState<ContentObjectItem | null>(null);
91
92
  const { typeRegistry } = useUserSession();
92
93
  const { search, isLoading, error, objects } = useWatchDocumentSearchResult();
93
- const [vQuery, setVQuery] = useState<VectorSearchQuery | undefined>(undefined);
94
94
  const [actualLayout, setActualLayout] = useState<ColumnLayout[]>(
95
95
  typeRegistry ? layout || getTableLayout(typeRegistry, search.query.type) : defaultLayout,
96
96
  );
@@ -111,37 +111,35 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
111
111
  }
112
112
  }, { deps: [isReady, objects.length] });
113
113
 
114
- useEffect(() => {
115
- search.search().then(() => setIsReady(true));
116
- }, []);
117
114
 
118
- //TODO _setSearchTerm state not used
119
- const [searchTerm, _setSearchTerm] = useState("");
120
- const debounceValue = useDebounce(searchTerm, 500);
121
- useEffect(() => {
122
- search.query.name = searchTerm;
123
- search.search().then(() => setIsReady(true));
124
- }, [debounceValue]);
125
-
126
- useEffect(() => {
127
- if (vQuery) {
128
- search.query.vector = vQuery;
129
- if (!actualLayout.find((c) => c.name === "Vector Similarity")) {
115
+ // Handler for vector search widget
116
+ const handleVectorSearch = (query?: ComplexSearchQuery) => {
117
+ if (query && query.vector) {
118
+ search.query.vector = query.vector;
119
+ search.query.full_text = query.full_text;
120
+ search.query.weights = query.weights;
121
+ search.query.score_aggregation = query.score_aggregation;
122
+ search.query.dynamic_scaling = query.dynamic_scaling;
123
+ if (!actualLayout.find((c) => c.name === "Search Score")) {
130
124
  const layout = [
131
125
  ...actualLayout,
132
126
  {
133
- name: "Vector Similarity",
127
+ name: "Search Score",
134
128
  field: "score",
135
129
  } satisfies ColumnLayout,
136
130
  ];
137
131
  setActualLayout(layout);
138
132
  }
139
133
  search.search().then(() => setIsReady(true));
134
+ } else if (query && query.full_text) {
135
+ search.query.full_text = query.full_text;
136
+ search.search().then(() => setIsReady(true));
140
137
  } else {
141
138
  delete search.query.vector;
139
+ delete search.query.full_text;
142
140
  search.search().then(() => setIsReady(true));
143
141
  }
144
- }, [vQuery?.values]);
142
+ };
145
143
 
146
144
  const facets = useWatchDocumentSearchFacets();
147
145
  const facetSearch = useDocumentSearch();
@@ -202,7 +200,7 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
202
200
  <div className="flex flex-row gap-4 items-center justify-between w-full">
203
201
  <div className="flex gap-2 items-center w-2/3">
204
202
  {
205
- allowSearch && <VectorSearchWidget onChange={setVQuery} isLoading={isLoading} refresh={refreshTrigger} className="w-wull" />
203
+ allowSearch && <VectorSearchWidget onChange={handleVectorSearch} isLoading={isLoading} refresh={refreshTrigger} className="w-full" />
206
204
  }
207
205
  <FilterBtn />
208
206
  </div>
@@ -223,7 +221,7 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
223
221
  <div className="flex flex-row gap-4 items-center justify-between w-full">
224
222
  <div className="flex gap-2 items-center w-2/3">
225
223
  {
226
- allowSearch && <VectorSearchWidget onChange={setVQuery} isLoading={isLoading} refresh={refreshTrigger} />
224
+ allowSearch && <VectorSearchWidget onChange={handleVectorSearch} isLoading={isLoading} refresh={refreshTrigger} />
227
225
  }
228
226
  </div>
229
227
  <div className="flex gap-1 items-center">
@@ -1,10 +1,8 @@
1
1
  import { useEffect, useState } from "react";
2
- import Markdown from "react-markdown";
3
- import remarkGfm from "remark-gfm";
4
2
 
5
3
  import { useUserSession } from "@vertesia/ui/session";
6
4
  import { Button, Spinner, useToast } from "@vertesia/ui/core";
7
- import { JSONDisplay } from "@vertesia/ui/widgets";
5
+ import { JSONDisplay, MarkdownRenderer } from "@vertesia/ui/widgets";
8
6
  import { ContentObject, ImageRenditionFormat } from "@vertesia/common";
9
7
  import { Copy, Download, SquarePen } from "lucide-react";
10
8
  import { PropertiesEditorModal } from "./PropertiesEditorModal";
@@ -314,8 +312,7 @@ export function ContentOverview({
314
312
  <div className="border shadow-xs rounded-xs max-w-7xl">
315
313
  {seemsMarkdown ? (
316
314
  <div className="vprose prose-sm p-1">
317
- <Markdown
318
- remarkPlugins={[remarkGfm]}
315
+ <MarkdownRenderer
319
316
  components={{
320
317
  a: ({ node, ...props }: { node?: any; href?: string; children?: React.ReactNode }) => {
321
318
  const href = props.href || "";
@@ -378,7 +375,7 @@ export function ContentOverview({
378
375
  }}
379
376
  >
380
377
  {text}
381
- </Markdown>
378
+ </MarkdownRenderer>
382
379
  </div>
383
380
  ) : (
384
381
  <pre className="text-wrap bg-muted text-muted p-2">