@vertesia/ui 0.73.0 → 0.76.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 (157) 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/DownloadPopover.js +17 -2
  18. package/lib/esm/features/magic-pdf/DownloadPopover.js.map +1 -1
  19. package/lib/esm/features/magic-pdf/MagicPdfView.js +26 -3
  20. package/lib/esm/features/magic-pdf/MagicPdfView.js.map +1 -1
  21. package/lib/esm/features/magic-pdf/PageSlider.js +21 -8
  22. package/lib/esm/features/magic-pdf/PageSlider.js.map +1 -1
  23. package/lib/esm/features/magic-pdf/PdfPageProvider.js +55 -0
  24. package/lib/esm/features/magic-pdf/PdfPageProvider.js.map +1 -1
  25. package/lib/esm/features/magic-pdf/TextPageView.js +20 -2
  26. package/lib/esm/features/magic-pdf/TextPageView.js.map +1 -1
  27. package/lib/esm/features/store/collections/CreateCollection.js +1 -1
  28. package/lib/esm/features/store/collections/CreateCollection.js.map +1 -1
  29. package/lib/esm/features/store/objects/DocumentPreviewPanel.js +2 -4
  30. package/lib/esm/features/store/objects/DocumentPreviewPanel.js.map +1 -1
  31. package/lib/esm/features/store/objects/DocumentSearchResults.js +19 -21
  32. package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
  33. package/lib/esm/features/store/objects/components/ContentOverview.js +2 -4
  34. package/lib/esm/features/store/objects/components/ContentOverview.js.map +1 -1
  35. package/lib/esm/features/store/objects/components/VectorSearchWidget.js +51 -46
  36. package/lib/esm/features/store/objects/components/VectorSearchWidget.js.map +1 -1
  37. package/lib/esm/features/store/objects/layout/documentLayout.js +1 -1
  38. package/lib/esm/features/store/objects/layout/documentLayout.js.map +1 -1
  39. package/lib/esm/features/store/objects/search/DocumentSearchContext.js +50 -34
  40. package/lib/esm/features/store/objects/search/DocumentSearchContext.js.map +1 -1
  41. package/lib/esm/features/store/objects/search/DocumentSearchProvider.js +1 -3
  42. package/lib/esm/features/store/objects/search/DocumentSearchProvider.js.map +1 -1
  43. package/lib/esm/features/store/objects/upload/useSmartFileUploadProcessing.js +4 -11
  44. package/lib/esm/features/store/objects/upload/useSmartFileUploadProcessing.js.map +1 -1
  45. package/lib/esm/features/user/UserInfo.js +2 -2
  46. package/lib/esm/features/user/UserInfo.js.map +1 -1
  47. package/lib/esm/session/UserSessionProvider.js +6 -3
  48. package/lib/esm/session/UserSessionProvider.js.map +1 -1
  49. package/lib/esm/session/auth/composable.js +3 -3
  50. package/lib/esm/session/auth/composable.js.map +1 -1
  51. package/lib/esm/session/auth/firebase.js +7 -0
  52. package/lib/esm/session/auth/firebase.js.map +1 -1
  53. package/lib/esm/session/auth/useAuthState.js +0 -3
  54. package/lib/esm/session/auth/useAuthState.js.map +1 -1
  55. package/lib/esm/shell/apps/StandaloneApp.js +1 -1
  56. package/lib/esm/shell/apps/StandaloneApp.js.map +1 -1
  57. package/lib/esm/shell/login/EnterpriseSigninButton.js +3 -0
  58. package/lib/esm/shell/login/EnterpriseSigninButton.js.map +1 -1
  59. package/lib/esm/shell/login/InviteAcceptModal.js +1 -1
  60. package/lib/esm/shell/login/InviteAcceptModal.js.map +1 -1
  61. package/lib/esm/widgets/form/ManagedObject.js +1 -1
  62. package/lib/esm/widgets/index.js +1 -0
  63. package/lib/esm/widgets/index.js.map +1 -1
  64. package/lib/esm/widgets/markdown/MarkdownRenderer.js +24 -0
  65. package/lib/esm/widgets/markdown/MarkdownRenderer.js.map +1 -0
  66. package/lib/esm/widgets/markdown/index.js +2 -0
  67. package/lib/esm/widgets/markdown/index.js.map +1 -0
  68. package/lib/tsconfig.tsbuildinfo +1 -1
  69. package/lib/types/core/components/Overlay.d.ts +25 -0
  70. package/lib/types/core/components/Overlay.d.ts.map +1 -0
  71. package/lib/types/core/components/SidePanel.d.ts +3 -1
  72. package/lib/types/core/components/SidePanel.d.ts.map +1 -1
  73. package/lib/types/core/components/index.d.ts +1 -0
  74. package/lib/types/core/components/index.d.ts.map +1 -1
  75. package/lib/types/core/components/shadcn/tabs.d.ts.map +1 -1
  76. package/lib/types/env/index.d.ts +2 -2
  77. package/lib/types/env/index.d.ts.map +1 -1
  78. package/lib/types/features/agent/PayloadBuilder.d.ts.map +1 -1
  79. package/lib/types/features/facets/DocumentsFacetsNav.d.ts.map +1 -1
  80. package/lib/types/features/facets/VTypeFacet.d.ts.map +1 -1
  81. package/lib/types/features/magic-pdf/DownloadPopover.d.ts.map +1 -1
  82. package/lib/types/features/magic-pdf/PageSlider.d.ts +2 -1
  83. package/lib/types/features/magic-pdf/PageSlider.d.ts.map +1 -1
  84. package/lib/types/features/magic-pdf/PdfPageProvider.d.ts +10 -0
  85. package/lib/types/features/magic-pdf/PdfPageProvider.d.ts.map +1 -1
  86. package/lib/types/features/magic-pdf/TextPageView.d.ts.map +1 -1
  87. package/lib/types/features/magic-pdf/types.d.ts +1 -1
  88. package/lib/types/features/magic-pdf/types.d.ts.map +1 -1
  89. package/lib/types/features/store/objects/DocumentPreviewPanel.d.ts.map +1 -1
  90. package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  91. package/lib/types/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  92. package/lib/types/features/store/objects/components/VectorSearchWidget.d.ts +4 -3
  93. package/lib/types/features/store/objects/components/VectorSearchWidget.d.ts.map +1 -1
  94. package/lib/types/features/store/objects/search/DocumentSearchContext.d.ts +5 -2
  95. package/lib/types/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
  96. package/lib/types/features/store/objects/search/DocumentSearchProvider.d.ts +2 -4
  97. package/lib/types/features/store/objects/search/DocumentSearchProvider.d.ts.map +1 -1
  98. package/lib/types/features/store/objects/upload/useSmartFileUploadProcessing.d.ts.map +1 -1
  99. package/lib/types/session/UserSessionProvider.d.ts.map +1 -1
  100. package/lib/types/session/auth/composable.d.ts +1 -1
  101. package/lib/types/session/auth/composable.d.ts.map +1 -1
  102. package/lib/types/session/auth/firebase.d.ts.map +1 -1
  103. package/lib/types/session/auth/useAuthState.d.ts.map +1 -1
  104. package/lib/types/shell/login/EnterpriseSigninButton.d.ts.map +1 -1
  105. package/lib/types/widgets/index.d.ts +1 -0
  106. package/lib/types/widgets/index.d.ts.map +1 -1
  107. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts +9 -0
  108. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts.map +1 -0
  109. package/lib/types/widgets/markdown/index.d.ts +2 -0
  110. package/lib/types/widgets/markdown/index.d.ts.map +1 -0
  111. package/lib/vertesia-ui-core.js +1 -1
  112. package/lib/vertesia-ui-core.js.map +1 -1
  113. package/lib/vertesia-ui-features.js +1 -1
  114. package/lib/vertesia-ui-features.js.map +1 -1
  115. package/lib/vertesia-ui-session.js +1 -1
  116. package/lib/vertesia-ui-session.js.map +1 -1
  117. package/lib/vertesia-ui-shell.js +1 -1
  118. package/lib/vertesia-ui-shell.js.map +1 -1
  119. package/lib/vertesia-ui-widgets.js +1 -1
  120. package/lib/vertesia-ui-widgets.js.map +1 -1
  121. package/package.json +6 -4
  122. package/src/core/components/Center.tsx +1 -1
  123. package/src/core/components/Overlay.tsx +129 -0
  124. package/src/core/components/SidePanel.tsx +38 -33
  125. package/src/core/components/index.ts +1 -0
  126. package/src/core/components/shadcn/tabs.tsx +48 -5
  127. package/src/env/index.ts +1 -1
  128. package/src/features/agent/PayloadBuilder.tsx +8 -2
  129. package/src/features/facets/DocumentsFacetsNav.tsx +4 -2
  130. package/src/features/facets/VTypeFacet.tsx +2 -1
  131. package/src/features/magic-pdf/DownloadPopover.tsx +38 -5
  132. package/src/features/magic-pdf/MagicPdfView.tsx +31 -5
  133. package/src/features/magic-pdf/PageSlider.tsx +44 -14
  134. package/src/features/magic-pdf/PdfPageProvider.tsx +81 -0
  135. package/src/features/magic-pdf/TextPageView.tsx +29 -2
  136. package/src/features/magic-pdf/types.ts +1 -1
  137. package/src/features/store/collections/CreateCollection.tsx +1 -1
  138. package/src/features/store/objects/DocumentPreviewPanel.tsx +2 -4
  139. package/src/features/store/objects/DocumentSearchResults.tsx +24 -26
  140. package/src/features/store/objects/components/ContentOverview.tsx +3 -6
  141. package/src/features/store/objects/components/VectorSearchWidget.tsx +88 -60
  142. package/src/features/store/objects/layout/documentLayout.tsx +1 -1
  143. package/src/features/store/objects/search/DocumentSearchContext.ts +57 -36
  144. package/src/features/store/objects/search/DocumentSearchProvider.tsx +2 -6
  145. package/src/features/store/objects/upload/useSmartFileUploadProcessing.ts +6 -12
  146. package/src/features/user/UserInfo.tsx +2 -2
  147. package/src/session/UserSessionProvider.tsx +5 -3
  148. package/src/session/auth/composable.ts +3 -3
  149. package/src/session/auth/firebase.ts +8 -0
  150. package/src/session/auth/useAuthState.ts +0 -4
  151. package/src/shell/apps/StandaloneApp.tsx +1 -1
  152. package/src/shell/login/EnterpriseSigninButton.tsx +3 -0
  153. package/src/shell/login/InviteAcceptModal.tsx +1 -1
  154. package/src/widgets/form/ManagedObject.ts +1 -1
  155. package/src/widgets/index.ts +1 -0
  156. package/src/widgets/markdown/MarkdownRenderer.tsx +45 -0
  157. 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.73.0",
3
+ "version": "0.76.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/client": "0.73.0",
62
- "@vertesia/common": "0.73.0",
63
- "@vertesia/json": "0.73.0"
62
+ "unist-util-visit": "^5.0.0",
63
+ "@vertesia/client": "0.76.0",
64
+ "@vertesia/json": "0.76.0",
65
+ "@vertesia/common": "0.76.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-10">
46
+ {/* Backdrop */}
47
+ {backdrop && (
48
+ <motion.div
49
+ className="fixed inset-0 bg-black bg-opacity-50"
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,4 +1,4 @@
1
- import { ContentObject } from "@vertesia/common";
1
+ import { ContentObject, DocumentMetadata } from "@vertesia/common";
2
2
  import { useUserSession } from "@vertesia/ui/session";
3
3
  import { Popover } from "@vertesia/ui/widgets";
4
4
  import { CloudDownload } from "lucide-react";
@@ -12,18 +12,51 @@ export function DownloadPopover({ object }: DownloadPopoverProps) {
12
12
  const onDownload = (name: string) => {
13
13
  getResourceUrl(client, object.id, name).then(url => window.open(url, '_blank'));
14
14
  }
15
+
16
+ const getProcessorType = (): string => {
17
+ if (object.metadata?.type === "document") {
18
+ const docMetadata = object.metadata as DocumentMetadata;
19
+ return docMetadata.content_processor?.type || "xml";
20
+ }
21
+ return "xml"; // default
22
+ };
23
+
24
+ const processorType = getProcessorType();
25
+
26
+ const renderDownloadOptions = () => {
27
+ if (processorType === "markdown") {
28
+ return (
29
+ <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("document.md")}>
30
+ document.md
31
+ </button>
32
+ );
33
+ }
34
+
35
+ // Default XML processor options
36
+ return (
37
+ <>
38
+ <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("annotated.pdf")}>
39
+ annotated.pdf
40
+ </button>
41
+ <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("document.xml")}>
42
+ document.xml
43
+ </button>
44
+ <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("analyzed-pages.json")}>
45
+ analyzed-pages.json
46
+ </button>
47
+ </>
48
+ );
49
+ };
50
+
15
51
  return (
16
52
  <div className="absolute bottom-[58px] right-[20px] w-[36px] h-[36px] cursor-pointer text-indigo-400 border-indigo-400 hover:border-indigo-500 hover:text-indigo-500 border-2 rounded-full shadow-xs flex items-center justify-center">
17
53
  <Popover strategy='absolute' placement='top-end' zIndex={100} offset={20}>
18
54
  <Popover.Trigger click>
19
55
  <CloudDownload className='size-6' />
20
-
21
56
  </Popover.Trigger>
22
57
  <Popover.Content>
23
58
  <div className="rounded-md shadow-md border border-gray-100 bg-white dark:bg-slate-50 dark:border-slate-100 min-w-[200px] flex flex-col divide-y">
24
- <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("annotated.pdf")}>annotated.pdf</button>
25
- <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("document.xml")}>document.xml</button>
26
- <button className="p-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-100" onClick={() => onDownload("analyzed-pages.json")}>analyzed-pages.json</button>
59
+ {renderDownloadOptions()}
27
60
  </div>
28
61
  </Popover.Content>
29
62
  </Popover>
@@ -1,4 +1,4 @@
1
- import { ContentObject } from "@vertesia/common";
1
+ import { ContentObject, DocumentMetadata } from "@vertesia/common";
2
2
  import { ErrorBox, useFetch } from "@vertesia/ui/core";
3
3
  import { useUserSession } from "@vertesia/ui/session";
4
4
  import { X } from "lucide-react";
@@ -39,8 +39,27 @@ interface _MagicPdfViewProps {
39
39
  onClose?: () => void;
40
40
  }
41
41
  function MagicPdfViewImpl({ object, onClose }: _MagicPdfViewProps) {
42
- const [viewType, setViewType] = useState<ViewType>("xml");
42
+ const getInitialViewType = (): ViewType => {
43
+ if (object.metadata?.type === "document") {
44
+ const docMetadata = object.metadata as DocumentMetadata;
45
+ const processorType = docMetadata.content_processor?.type;
46
+ if (processorType === "markdown") return "markdown";
47
+ if (processorType === "xml") return "xml";
48
+ }
49
+ return "xml"; // default
50
+ };
51
+
52
+ const getProcessorType = (): string => {
53
+ if (object.metadata?.type === "document") {
54
+ const docMetadata = object.metadata as DocumentMetadata;
55
+ return docMetadata.content_processor?.type || "xml";
56
+ }
57
+ return "xml"; // default
58
+ };
59
+
60
+ const [viewType, setViewType] = useState<ViewType>(getInitialViewType());
43
61
  const [pageNumber, setPageNumber] = useState(1);
62
+ const processorType = getProcessorType();
44
63
  const handler = useRef<HTMLDivElement>(null);
45
64
  const left = useRef<HTMLDivElement>(null);
46
65
  const right = useRef<HTMLDivElement>(null);
@@ -51,14 +70,14 @@ function MagicPdfViewImpl({ object, onClose }: _MagicPdfViewProps) {
51
70
  return (
52
71
  <>
53
72
  <div ref={left} className={`absolute top-0 left-0 bottom-0 w-[50%] bg-gray-100 dark:bg-slate-800 flex items-stretch justify-stretch py-2`}>
54
- <PageSlider className="flex-1" currentPage={pageNumber} onChange={setPageNumber} />
73
+ <PageSlider className="flex-1" currentPage={pageNumber} onChange={setPageNumber} object={object} />
55
74
  <div ref={handler} className='w-[2px] p-[2px] m-0 bg-slate-300 cursor-ew-resize'></div>
56
75
  </div>
57
76
  <div ref={right} className={`absolute top-0 left-[50%] right-0 bottom-0 flex items-stretch justify-stretch overflow-auto p-2`}>
58
77
  <TextPageView pageNumber={pageNumber} viewType={viewType} />
59
78
  </div>
60
79
  <DownloadPopover object={object} />
61
- <ContentSwitcher type={viewType} onSwitch={setViewType} />
80
+ {processorType === "xml" && <ContentSwitcher type={viewType} onSwitch={setViewType} />}
62
81
  {!!onClose &&
63
82
  <div className="absolute top-6 right-7 w-9 h-9 cursor-pointer text-red-400 border-red-400 hover:border-red-500 hover:text-red-500 border-2 rounded-full shadow-xs flex items-center justify-center"
64
83
  onClick={onClose}>
@@ -80,6 +99,8 @@ function ContentSwitcher({ type = "xml", onSwitch }: ContentSwitcherProps) {
80
99
  if (type === "xml") {
81
100
  onSwitch("json");
82
101
  } else if (type === "json") {
102
+ onSwitch("markdown");
103
+ } else if (type === "markdown") {
83
104
  onSwitch("xml");
84
105
  }
85
106
  }
@@ -87,7 +108,8 @@ function ContentSwitcher({ type = "xml", onSwitch }: ContentSwitcherProps) {
87
108
  <div className="absolute bottom-[16px] right-[20px] w-[36px] h-[36px] cursor-pointer text-indigo-400 border-indigo-400 hover:border-indigo-500 hover:text-indigo-500 border-2 rounded-full shadow-xs flex items-center justify-center"
88
109
  onClick={_onSwitch}>
89
110
  {type === "xml" && JSON}
90
- {type === "json" && XML}
111
+ {type === "json" && MARKDOWN}
112
+ {type === "markdown" && XML}
91
113
  </div>
92
114
  )
93
115
 
@@ -100,3 +122,7 @@ const JSON = <svg width="16px" height="16px" viewBox="0 0 16 16" xmlns="http://w
100
122
  const XML = <svg width="16px" height="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
101
123
  <path d="M4.708 5.578L2.061 8.224l2.647 2.646-.708.708-3-3V7.87l3-3 .708.708zm7-.708L11 5.578l2.647 2.646L11 10.87l.708.708 3-3V7.87l-3-3zM4.908 13l.894.448 5-10L9.908 3l-5 10z" />
102
124
  </svg>
125
+
126
+ const MARKDOWN = <svg width="16px" height="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
127
+ <path d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7.5L5.5 9l-1 1.5H3V5h1.5l1 2 2-2H9v6zm2.99.5L9.5 8H11V5h1v3h1.5l-2.51 3.5z"/>
128
+ </svg>