xertica-ui 1.2.5 → 1.2.7
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/components/CodeBlock.tsx +9 -9
- package/components/Header.tsx +58 -0
- package/components/HomeContent.tsx +13 -37
- package/components/HomePage.tsx +4 -2
- package/components/Sidebar.tsx +27 -17
- package/components/TemplatePage.tsx +4 -2
- package/components/index.ts +19 -2
- package/components/media/AudioPlayer.tsx +79 -79
- package/components/media/VideoPlayer.tsx +36 -36
- package/components/ui/drawer.tsx +12 -0
- package/components/ui/index.ts +132 -132
- package/components/ui/input.tsx +1 -1
- package/components/ui/map.tsx +1 -1
- package/components/ui/page-header.tsx +42 -12
- package/components/ui/sheet.tsx +5 -4
- package/components/ui/tree-view.tsx +1 -1
- package/contexts/index.ts +6 -0
- package/dist/components/AssistenteXertica.d.ts +8 -0
- package/dist/components/AudioPlayer.d.ts +12 -0
- package/dist/components/CodeBlock.d.ts +8 -0
- package/dist/components/DocumentEditor.d.ts +7 -0
- package/dist/components/ForgotPasswordPage.d.ts +1 -0
- package/dist/components/FormattedDocument.d.ts +7 -0
- package/dist/components/Header.d.ts +10 -0
- package/dist/components/HomeContent.d.ts +8 -0
- package/dist/components/HomePage.d.ts +8 -0
- package/dist/components/LanguageSelector.d.ts +7 -0
- package/dist/components/LoginPage.d.ts +5 -0
- package/dist/components/MarkdownMessage.d.ts +6 -0
- package/dist/components/ModernChatInput.d.ts +14 -0
- package/dist/components/PodcastPlayer.d.ts +15 -0
- package/dist/components/ResetPasswordPage.d.ts +1 -0
- package/dist/components/Sidebar.d.ts +22 -0
- package/dist/components/TemplateContent.d.ts +8 -0
- package/dist/components/TemplatePage.d.ts +8 -0
- package/dist/components/ThemeToggle.d.ts +8 -0
- package/dist/components/VerifyEmailPage.d.ts +1 -0
- package/dist/components/XerticaLogo.d.ts +7 -0
- package/dist/components/XerticaOrbe.d.ts +6 -0
- package/dist/components/XerticaXLogo.d.ts +7 -0
- package/dist/components/figma/ImageWithFallback.d.ts +2 -0
- package/dist/components/index.d.ts +23 -0
- package/dist/components/media/AudioPlayer.d.ts +9 -0
- package/dist/components/media/FloatingMediaWrapper.d.ts +14 -0
- package/dist/components/media/VideoPlayer.d.ts +8 -0
- package/dist/components/ui/accordion.d.ts +7 -0
- package/dist/components/ui/alert-dialog.d.ts +14 -0
- package/dist/components/ui/alert.d.ts +9 -0
- package/dist/components/ui/aspect-ratio.d.ts +3 -0
- package/dist/components/ui/avatar.d.ts +8 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/breadcrumb.d.ts +11 -0
- package/dist/components/ui/button.d.ts +14 -0
- package/dist/components/ui/calendar.d.ts +8 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/carousel.d.ts +19 -0
- package/dist/components/ui/chart.d.ts +40 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/collapsible.d.ts +5 -0
- package/dist/components/ui/command.d.ts +16 -0
- package/dist/components/ui/context-menu.d.ts +25 -0
- package/dist/components/ui/dialog.d.ts +15 -0
- package/dist/components/ui/drawer.d.ts +14 -0
- package/dist/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/components/ui/empty.d.ts +8 -0
- package/dist/components/ui/file-upload.d.ts +10 -0
- package/dist/components/ui/form.d.ts +24 -0
- package/dist/components/ui/google-maps-loader.d.ts +30 -0
- package/dist/components/ui/hover-card.d.ts +6 -0
- package/dist/components/ui/index.d.ts +81 -0
- package/dist/components/ui/input-otp.d.ts +11 -0
- package/dist/components/ui/input.d.ts +6 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/map-config.d.ts +12 -0
- package/dist/components/ui/map-layers.d.ts +48 -0
- package/dist/components/ui/map.d.ts +53 -0
- package/dist/components/ui/map.exports.d.ts +25 -0
- package/dist/components/ui/menubar.d.ts +26 -0
- package/dist/components/ui/navigation-menu.d.ts +14 -0
- package/dist/components/ui/notification-badge.d.ts +11 -0
- package/dist/components/ui/page-header.d.ts +58 -0
- package/dist/components/ui/pagination.d.ts +13 -0
- package/dist/components/ui/popover.d.ts +7 -0
- package/dist/components/ui/progress.d.ts +4 -0
- package/dist/components/ui/radio-group.d.ts +5 -0
- package/dist/components/ui/rating.d.ts +12 -0
- package/dist/components/ui/resizable.d.ts +27 -0
- package/dist/components/ui/route-map.d.ts +26 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/search.d.ts +8 -0
- package/dist/components/ui/select.d.ts +15 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/sheet.d.ts +14 -0
- package/dist/components/ui/sidebar.d.ts +69 -0
- package/dist/components/ui/simple-map.d.ts +51 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/slider.d.ts +4 -0
- package/dist/components/ui/sonner.d.ts +23 -0
- package/dist/components/ui/stats-card.d.ts +14 -0
- package/dist/components/ui/stepper.d.ts +17 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/components/ui/tabs.d.ts +7 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/timeline.d.ts +12 -0
- package/dist/components/ui/toggle-group.d.ts +12 -0
- package/dist/components/ui/toggle.d.ts +12 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tree-view.d.ts +15 -0
- package/dist/components/ui/use-mobile.d.ts +2 -0
- package/dist/components/ui/utils.d.ts +2 -0
- package/dist/components/ui/xertica-assistant.d.ts +196 -0
- package/dist/contexts/ApiKeyContext.d.ts +15 -0
- package/dist/contexts/AssistenteContext.d.ts +71 -0
- package/dist/contexts/BrandColorsContext.d.ts +15 -0
- package/dist/contexts/LanguageContext.d.ts +11 -0
- package/dist/contexts/ThemeContext.d.ts +13 -0
- package/dist/contexts/index.d.ts +6 -0
- package/dist/contexts/theme-data.d.ts +81 -0
- package/dist/index.es.js +82597 -0
- package/dist/index.umd.js +82616 -0
- package/dist/routes.d.ts +10 -0
- package/dist/utils/gemini.d.ts +8 -0
- package/dist/xertica-ui.css +4470 -0
- package/package.json +18 -5
- package/vite.config.ts +14 -5
package/components/CodeBlock.tsx
CHANGED
|
@@ -137,11 +137,11 @@ interface CodeBlockProps {
|
|
|
137
137
|
showLineNumbers?: boolean;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
export const CodeBlock = ({
|
|
141
|
-
code,
|
|
142
|
-
language = 'tsx',
|
|
140
|
+
export const CodeBlock = ({
|
|
141
|
+
code,
|
|
142
|
+
language = 'tsx',
|
|
143
143
|
filename,
|
|
144
|
-
showLineNumbers = false
|
|
144
|
+
showLineNumbers = false
|
|
145
145
|
}: CodeBlockProps) => {
|
|
146
146
|
const [copied, setCopied] = useState(false);
|
|
147
147
|
|
|
@@ -161,7 +161,7 @@ export const CodeBlock = ({
|
|
|
161
161
|
} else {
|
|
162
162
|
throw new Error('Clipboard API not available');
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
setCopied(true);
|
|
166
166
|
setTimeout(() => setCopied(false), 2000);
|
|
167
167
|
} catch (err) {
|
|
@@ -175,10 +175,10 @@ export const CodeBlock = ({
|
|
|
175
175
|
document.body.appendChild(textArea);
|
|
176
176
|
textArea.focus();
|
|
177
177
|
textArea.select();
|
|
178
|
-
|
|
178
|
+
|
|
179
179
|
const successful = document.execCommand('copy');
|
|
180
180
|
document.body.removeChild(textArea);
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
if (successful) {
|
|
183
183
|
setCopied(true);
|
|
184
184
|
setTimeout(() => setCopied(false), 2000);
|
|
@@ -202,7 +202,7 @@ export const CodeBlock = ({
|
|
|
202
202
|
<span className="text-xs text-muted-foreground uppercase">{language}</span>
|
|
203
203
|
</div>
|
|
204
204
|
)}
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
<div className="relative">
|
|
207
207
|
<Button
|
|
208
208
|
variant="ghost"
|
|
@@ -220,7 +220,7 @@ export const CodeBlock = ({
|
|
|
220
220
|
<div className="overflow-x-auto w-full max-w-full">
|
|
221
221
|
<SyntaxHighlighter
|
|
222
222
|
language={language}
|
|
223
|
-
style={elegantTheme}
|
|
223
|
+
style={elegantTheme as any}
|
|
224
224
|
showLineNumbers={showLineNumbers}
|
|
225
225
|
wrapLines={true}
|
|
226
226
|
customStyle={{
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Menu, ChevronRight } from 'lucide-react';
|
|
3
|
+
import { Button } from './ui/button';
|
|
4
|
+
import { ThemeToggle } from './ThemeToggle';
|
|
5
|
+
import { LanguageSelector } from './LanguageSelector';
|
|
6
|
+
|
|
7
|
+
interface HeaderProps {
|
|
8
|
+
sidebarExpanded: boolean;
|
|
9
|
+
onToggleSidebar: () => void;
|
|
10
|
+
title: string;
|
|
11
|
+
showLanguageSelector?: boolean;
|
|
12
|
+
showThemeToggle?: boolean;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Header({
|
|
17
|
+
sidebarExpanded,
|
|
18
|
+
onToggleSidebar,
|
|
19
|
+
title,
|
|
20
|
+
showLanguageSelector = true,
|
|
21
|
+
showThemeToggle = true,
|
|
22
|
+
className
|
|
23
|
+
}: HeaderProps) {
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<header className={`bg-card shadow-sm border-b border-border px-[24px] py-[14px] flex-shrink-0 ${className || ''}`}>
|
|
27
|
+
<div className="flex items-center justify-between p-[0px]">
|
|
28
|
+
<div className="flex items-center gap-2 text-muted-foreground">
|
|
29
|
+
{/* Botão menu para mobile */}
|
|
30
|
+
<Button
|
|
31
|
+
variant="ghost"
|
|
32
|
+
size="sm"
|
|
33
|
+
onClick={onToggleSidebar}
|
|
34
|
+
className="md:hidden mr-2 p-2"
|
|
35
|
+
>
|
|
36
|
+
<Menu className="w-5 h-5" />
|
|
37
|
+
</Button>
|
|
38
|
+
<span className="text-primary font-medium">Xertica.ai</span>
|
|
39
|
+
<ChevronRight className="w-4 h-4" />
|
|
40
|
+
<span className="text-foreground font-medium">{title}</span>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
{/* Controles do usuário */}
|
|
44
|
+
<div className="flex items-center gap-3">
|
|
45
|
+
{/* Seletor de idioma */}
|
|
46
|
+
{showLanguageSelector && (
|
|
47
|
+
<LanguageSelector variant="minimal" showIcon={false} className="hidden sm:flex" />
|
|
48
|
+
)}
|
|
49
|
+
|
|
50
|
+
{/* Toggle de tema */}
|
|
51
|
+
{showThemeToggle && (
|
|
52
|
+
<ThemeToggle className="hover:bg-accent" />
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</header>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from './ui/card';
|
|
3
|
+
import { Header } from './Header';
|
|
3
4
|
import { Button } from './ui/button';
|
|
4
5
|
import { ChevronRight, Menu, FileText } from 'lucide-react';
|
|
5
6
|
import { ScrollArea } from './ui/scroll-area';
|
|
@@ -19,7 +20,7 @@ interface HomeContentProps {
|
|
|
19
20
|
export function HomeContent({ sidebarExpanded, assistenteExpanded = false, onToggleSidebar, onToggleAssistente }: HomeContentProps) {
|
|
20
21
|
const location = useLocation();
|
|
21
22
|
const navigate = useNavigate();
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
// Tradução direta para português
|
|
24
25
|
const labelTranslations: Record<string, string> = {
|
|
25
26
|
'home': 'Início',
|
|
@@ -28,48 +29,23 @@ export function HomeContent({ sidebarExpanded, assistenteExpanded = false, onTog
|
|
|
28
29
|
|
|
29
30
|
// Get current route info
|
|
30
31
|
const currentRoute = getRouteByPath(location.pathname);
|
|
31
|
-
const breadcrumbLabel = currentRoute?.label
|
|
32
|
+
const breadcrumbLabel = currentRoute?.label
|
|
32
33
|
? labelTranslations[currentRoute.label.toLowerCase()] || currentRoute.label
|
|
33
34
|
: 'Início';
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
|
-
<div
|
|
39
|
-
className={`flex-1 flex flex-col overflow-hidden transition-all duration-300 ${
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
assistenteExpanded ? 'md:pr-[420px]' : 'md:pr-20'
|
|
43
|
-
}`}
|
|
39
|
+
<div
|
|
40
|
+
className={`flex-1 flex flex-col overflow-hidden transition-all duration-300 ${sidebarExpanded ? 'md:pl-64' : 'md:pl-20'
|
|
41
|
+
} ${assistenteExpanded ? 'md:pr-[420px]' : 'md:pr-20'
|
|
42
|
+
}`}
|
|
44
43
|
>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<Button
|
|
51
|
-
variant="ghost"
|
|
52
|
-
size="sm"
|
|
53
|
-
onClick={onToggleSidebar}
|
|
54
|
-
className="md:hidden mr-2 p-2"
|
|
55
|
-
>
|
|
56
|
-
<Menu className="w-5 h-5" />
|
|
57
|
-
</Button>
|
|
58
|
-
<span className="text-primary font-medium">Xertica.ai</span>
|
|
59
|
-
<ChevronRight className="w-4 h-4" />
|
|
60
|
-
<span className="text-foreground font-medium">{breadcrumbLabel}</span>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
{/* Controles do usuário */}
|
|
64
|
-
<div className="flex items-center gap-3">
|
|
65
|
-
{/* Seletor de idioma */}
|
|
66
|
-
<LanguageSelector variant="minimal" showIcon={false} className="hidden sm:flex" />
|
|
67
|
-
|
|
68
|
-
{/* Toggle de tema */}
|
|
69
|
-
<ThemeToggle className="hover:bg-accent" />
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</header>
|
|
44
|
+
<Header
|
|
45
|
+
sidebarExpanded={sidebarExpanded}
|
|
46
|
+
onToggleSidebar={onToggleSidebar || (() => { })}
|
|
47
|
+
title={breadcrumbLabel}
|
|
48
|
+
/>
|
|
73
49
|
|
|
74
50
|
{/* Área de conteúdo */}
|
|
75
51
|
<main className="flex-1 overflow-hidden bg-muted">
|
|
@@ -104,7 +80,7 @@ export function HomeContent({ sidebarExpanded, assistenteExpanded = false, onTog
|
|
|
104
80
|
</p>
|
|
105
81
|
</CardContent>
|
|
106
82
|
<CardFooter>
|
|
107
|
-
<Button
|
|
83
|
+
<Button
|
|
108
84
|
variant="outline"
|
|
109
85
|
className="w-full"
|
|
110
86
|
onClick={() => navigate('/template')}
|
package/components/HomePage.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { useLocation, useNavigate } from 'react-router';
|
|
|
3
3
|
import { Sidebar } from './Sidebar';
|
|
4
4
|
import { HomeContent } from './HomeContent';
|
|
5
5
|
import { AssistenteXertica } from './AssistenteXertica';
|
|
6
|
+
import { routes } from '../routes';
|
|
6
7
|
|
|
7
8
|
interface HomePageProps {
|
|
8
9
|
user: { email: string } | null;
|
|
@@ -46,15 +47,16 @@ export function HomePage({ user, onLogout }: HomePageProps) {
|
|
|
46
47
|
|
|
47
48
|
return (
|
|
48
49
|
<div className="h-screen flex bg-muted overflow-hidden relative">
|
|
49
|
-
<Sidebar
|
|
50
|
+
<Sidebar
|
|
50
51
|
expanded={sidebarExpanded}
|
|
51
52
|
onToggle={handleToggleSidebar}
|
|
52
53
|
user={user}
|
|
53
54
|
onLogout={onLogout}
|
|
54
55
|
location={location}
|
|
55
56
|
navigate={navigate}
|
|
57
|
+
routes={routes}
|
|
56
58
|
/>
|
|
57
|
-
<HomeContent
|
|
59
|
+
<HomeContent
|
|
58
60
|
sidebarExpanded={sidebarExpanded}
|
|
59
61
|
assistenteExpanded={assistenteExpanded}
|
|
60
62
|
onToggleSidebar={handleToggleSidebar}
|
package/components/Sidebar.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
MoreHorizontal,
|
|
8
8
|
ChevronRight,
|
|
9
9
|
} from "lucide-react";
|
|
10
|
-
import
|
|
10
|
+
// routes import removed to make it reusable
|
|
11
11
|
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
|
|
12
12
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
|
13
13
|
import { Tooltip, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
|
|
@@ -15,8 +15,9 @@ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
|
15
15
|
import { cn } from "./ui/utils";
|
|
16
16
|
import { XerticaLogo } from "./XerticaLogo";
|
|
17
17
|
import { XerticaXLogo } from "./XerticaXLogo";
|
|
18
|
+
import { Button } from "./ui/button";
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
+
// Validar se SidebarTooltipContent já existe em outro lugar ou manter aqui
|
|
20
21
|
function SidebarTooltipContent({
|
|
21
22
|
className,
|
|
22
23
|
sideOffset = 0,
|
|
@@ -34,15 +35,23 @@ function SidebarTooltipContent({
|
|
|
34
35
|
{...props}
|
|
35
36
|
>
|
|
36
37
|
{children}
|
|
37
|
-
<TooltipPrimitive.Arrow
|
|
38
|
-
className="fill-popover z-50 drop-shadow-sm"
|
|
39
|
-
width={8}
|
|
38
|
+
<TooltipPrimitive.Arrow
|
|
39
|
+
className="fill-popover z-50 drop-shadow-sm"
|
|
40
|
+
width={8}
|
|
40
41
|
height={4}
|
|
41
42
|
/>
|
|
42
43
|
</TooltipPrimitive.Content>
|
|
43
44
|
);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
// Interface for Route Config (copied/imported type)
|
|
48
|
+
export interface RouteConfig {
|
|
49
|
+
path: string;
|
|
50
|
+
label: string;
|
|
51
|
+
icon: React.ComponentType<any>;
|
|
52
|
+
component?: React.ComponentType<any>;
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
interface SubMenuItem {
|
|
47
56
|
id: string;
|
|
48
57
|
label: string;
|
|
@@ -64,6 +73,7 @@ interface SidebarProps {
|
|
|
64
73
|
onLogout: () => void;
|
|
65
74
|
location: { pathname: string };
|
|
66
75
|
navigate: (path: string) => void;
|
|
76
|
+
routes: RouteConfig[];
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
export function Sidebar({
|
|
@@ -73,6 +83,7 @@ export function Sidebar({
|
|
|
73
83
|
onLogout,
|
|
74
84
|
location,
|
|
75
85
|
navigate,
|
|
86
|
+
routes,
|
|
76
87
|
}: SidebarProps) {
|
|
77
88
|
const navRef = useRef<HTMLDivElement>(null);
|
|
78
89
|
const [hasOverflow, setHasOverflow] = useState(false);
|
|
@@ -154,9 +165,9 @@ export function Sidebar({
|
|
|
154
165
|
? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
155
166
|
: "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
|
|
156
167
|
: "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 " +
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
168
|
+
(item.active
|
|
169
|
+
? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
170
|
+
: "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground")
|
|
160
171
|
}
|
|
161
172
|
>
|
|
162
173
|
<Icon className="w-5 h-5 flex-shrink-0" />
|
|
@@ -210,7 +221,7 @@ export function Sidebar({
|
|
|
210
221
|
<span className="truncate">{item.label}</span>
|
|
211
222
|
</button>
|
|
212
223
|
)}
|
|
213
|
-
|
|
224
|
+
|
|
214
225
|
{/* Subitens */}
|
|
215
226
|
{hasSubItems && item.subItems && (
|
|
216
227
|
<>
|
|
@@ -246,9 +257,9 @@ export function Sidebar({
|
|
|
246
257
|
? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
247
258
|
: "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
|
|
248
259
|
: "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 " +
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
260
|
+
(item.active
|
|
261
|
+
? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
262
|
+
: "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground")
|
|
252
263
|
}
|
|
253
264
|
>
|
|
254
265
|
<Icon className="w-5 h-5 flex-shrink-0" />
|
|
@@ -281,11 +292,10 @@ export function Sidebar({
|
|
|
281
292
|
<TooltipProvider delayDuration={300}>
|
|
282
293
|
{/* Sidebar */}
|
|
283
294
|
<div
|
|
284
|
-
className={`bg-sidebar text-sidebar-foreground transition-all duration-300 ease-in-out flex flex-col z-50 ${
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}`}
|
|
295
|
+
className={`bg-sidebar text-sidebar-foreground transition-all duration-300 ease-in-out flex flex-col z-50 ${expanded
|
|
296
|
+
? "fixed inset-0 md:fixed md:inset-y-0 md:left-0 md:w-64"
|
|
297
|
+
: "fixed inset-y-0 left-0 w-20 -translate-x-full md:translate-x-0"
|
|
298
|
+
}`}
|
|
289
299
|
>
|
|
290
300
|
{/* Botão Toggle Menu */}
|
|
291
301
|
<div className="p-[14px] pt-[13px] pr-[14px] pb-[12px] pl-[14px]">
|
|
@@ -3,6 +3,7 @@ import { useLocation, useNavigate } from 'react-router';
|
|
|
3
3
|
import { Sidebar } from './Sidebar';
|
|
4
4
|
import { TemplateContent } from './TemplateContent';
|
|
5
5
|
import { AssistenteXertica } from './AssistenteXertica';
|
|
6
|
+
import { routes } from '../routes';
|
|
6
7
|
|
|
7
8
|
interface TemplatePageProps {
|
|
8
9
|
user: { email: string } | null;
|
|
@@ -46,15 +47,16 @@ export function TemplatePage({ user, onLogout }: TemplatePageProps) {
|
|
|
46
47
|
|
|
47
48
|
return (
|
|
48
49
|
<div className="h-screen flex bg-muted overflow-hidden relative">
|
|
49
|
-
<Sidebar
|
|
50
|
+
<Sidebar
|
|
50
51
|
expanded={sidebarExpanded}
|
|
51
52
|
onToggle={handleToggleSidebar}
|
|
52
53
|
user={user}
|
|
53
54
|
onLogout={onLogout}
|
|
54
55
|
location={location}
|
|
55
56
|
navigate={navigate}
|
|
57
|
+
routes={routes}
|
|
56
58
|
/>
|
|
57
|
-
<TemplateContent
|
|
59
|
+
<TemplateContent
|
|
58
60
|
sidebarExpanded={sidebarExpanded}
|
|
59
61
|
assistenteExpanded={assistenteExpanded}
|
|
60
62
|
onToggleSidebar={handleToggleSidebar}
|
package/components/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import '../index.css';
|
|
1
2
|
// ============================================================================
|
|
2
3
|
// Xertica Assistant Exports
|
|
3
4
|
// ============================================================================
|
|
4
5
|
|
|
5
6
|
export { XerticaAssistant } from './ui/xertica-assistant';
|
|
6
|
-
export type {
|
|
7
|
+
export type {
|
|
7
8
|
XerticaAssistantProps,
|
|
8
9
|
Message,
|
|
9
10
|
Conversation,
|
|
@@ -41,4 +42,20 @@ export { XerticaOrbe } from './XerticaOrbe';
|
|
|
41
42
|
// Layout
|
|
42
43
|
export { Sidebar } from './Sidebar';
|
|
43
44
|
export { ThemeToggle } from './ThemeToggle';
|
|
44
|
-
export { LanguageSelector } from './LanguageSelector';
|
|
45
|
+
export { LanguageSelector } from './LanguageSelector';
|
|
46
|
+
|
|
47
|
+
// Connected Components (require Providers)
|
|
48
|
+
export { AssistenteXertica } from './AssistenteXertica';
|
|
49
|
+
|
|
50
|
+
// State Management & Contexts
|
|
51
|
+
export * from '../contexts';
|
|
52
|
+
|
|
53
|
+
// Layout & Common Components
|
|
54
|
+
export { Header } from './Header';
|
|
55
|
+
export { DocumentEditor } from './DocumentEditor';
|
|
56
|
+
|
|
57
|
+
// Media Components
|
|
58
|
+
export { VideoPlayer } from './media/VideoPlayer';
|
|
59
|
+
export { AudioPlayer } from './media/AudioPlayer';
|
|
60
|
+
export { FloatingMediaWrapper } from './media/FloatingMediaWrapper';
|
|
61
|
+
export { PodcastPlayer } from './PodcastPlayer';
|
|
@@ -14,12 +14,12 @@ export interface AudioPlayerProps {
|
|
|
14
14
|
className?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function AudioPlayer({
|
|
18
|
-
src,
|
|
17
|
+
export function AudioPlayer({
|
|
18
|
+
src,
|
|
19
19
|
title = "Audio",
|
|
20
20
|
artist,
|
|
21
21
|
autoPlay = false,
|
|
22
|
-
className
|
|
22
|
+
className
|
|
23
23
|
}: AudioPlayerProps) {
|
|
24
24
|
const audioRef = useRef<HTMLAudioElement>(null);
|
|
25
25
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -38,9 +38,9 @@ export function AudioPlayer({
|
|
|
38
38
|
if (audioRef.current) {
|
|
39
39
|
setCurrentTime(audioRef.current.currentTime);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
setIsFloating(floating);
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
if (!floating) {
|
|
45
45
|
setIsManualFloating(false);
|
|
46
46
|
if (containerRef.current) {
|
|
@@ -55,21 +55,21 @@ export function AudioPlayer({
|
|
|
55
55
|
if (audio) {
|
|
56
56
|
// If we are switching modes, restore time and play state
|
|
57
57
|
if (Math.abs(audio.currentTime - currentTime) > 0.5) {
|
|
58
|
-
|
|
58
|
+
audio.currentTime = currentTime;
|
|
59
59
|
}
|
|
60
60
|
if (isPlaying) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
const playPromise = audio.play();
|
|
62
|
+
if (playPromise !== undefined) {
|
|
63
|
+
playPromise.catch(error => {
|
|
64
|
+
// Auto-play might be blocked or interrupted
|
|
65
|
+
console.log("Playback interrupted during switch:", error);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
// We depend on isFloating to trigger this check when the DOM node is recreated
|
|
71
71
|
// But we also need to attach listeners to the new node
|
|
72
|
-
}, [isFloating]);
|
|
72
|
+
}, [isFloating]);
|
|
73
73
|
|
|
74
74
|
// Auto-float on scroll logic
|
|
75
75
|
useEffect(() => {
|
|
@@ -79,17 +79,17 @@ export function AudioPlayer({
|
|
|
79
79
|
const observer = new IntersectionObserver(
|
|
80
80
|
([entry]) => {
|
|
81
81
|
if (isPlaying && !entry.isIntersecting && !isFloating) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
// Sync time before floating
|
|
83
|
+
if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
|
|
84
|
+
setIsFloating(true);
|
|
85
85
|
}
|
|
86
86
|
else if (entry.isIntersecting && isFloating && !isManualFloating) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
// Sync time before docking
|
|
88
|
+
if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
|
|
89
|
+
handleSetFloating(false);
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
|
-
{ threshold: 0.2 }
|
|
92
|
+
{ threshold: 0.2 }
|
|
93
93
|
);
|
|
94
94
|
|
|
95
95
|
observer.observe(container);
|
|
@@ -161,7 +161,7 @@ export function AudioPlayer({
|
|
|
161
161
|
|
|
162
162
|
const PlayerControls = ({ isCompact = false }) => (
|
|
163
163
|
<div className={cn("flex items-center gap-3 w-full", isCompact ? "px-3 py-1" : "px-4 py-3")}>
|
|
164
|
-
<Button
|
|
164
|
+
<Button
|
|
165
165
|
onClick={togglePlay}
|
|
166
166
|
size="icon"
|
|
167
167
|
variant="outline"
|
|
@@ -174,70 +174,70 @@ export function AudioPlayer({
|
|
|
174
174
|
</Button>
|
|
175
175
|
|
|
176
176
|
<div className="flex-1 min-w-0 flex flex-col justify-center gap-1.5">
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
</div>
|
|
182
|
-
<div className="text-muted-foreground font-medium tabular-nums shrink-0 text-[10px] sm:text-xs">
|
|
183
|
-
{formatTime(currentTime)} / {formatTime(duration || 0)}
|
|
184
|
-
</div>
|
|
177
|
+
<div className="flex items-center justify-between text-xs leading-none">
|
|
178
|
+
<div className="flex items-center gap-2 truncate">
|
|
179
|
+
<span className="font-medium text-foreground truncate">{title}</span>
|
|
180
|
+
{artist && !isCompact && <span className="text-muted-foreground hidden sm:inline-block border-l pl-2 border-border truncate">{artist}</span>}
|
|
185
181
|
</div>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
182
|
+
<div className="text-muted-foreground font-medium tabular-nums shrink-0 text-[10px] sm:text-xs">
|
|
183
|
+
{formatTime(currentTime)} / {formatTime(duration || 0)}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<Slider
|
|
188
|
+
value={[currentTime]}
|
|
189
|
+
max={duration || 100}
|
|
190
|
+
step={1}
|
|
191
|
+
onValueChange={handleSeek}
|
|
192
|
+
className="w-full"
|
|
193
|
+
/>
|
|
194
194
|
</div>
|
|
195
195
|
|
|
196
196
|
<div className="flex items-center gap-1 shrink-0">
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
<Button
|
|
200
|
-
variant="ghost"
|
|
201
|
-
size="icon"
|
|
202
|
-
className="h-8 w-8 text-muted-foreground hover:text-foreground"
|
|
203
|
-
onClick={() => {
|
|
204
|
-
const newMuted = !isMuted;
|
|
205
|
-
setIsMuted(newMuted);
|
|
206
|
-
if (audioRef.current) audioRef.current.muted = newMuted;
|
|
207
|
-
}}
|
|
208
|
-
>
|
|
209
|
-
{isMuted ? <VolumeX className="w-4 h-4" /> : <Volume2 className="w-4 h-4" />}
|
|
210
|
-
</Button>
|
|
211
|
-
<div className="w-0 overflow-hidden group-hover/volume:w-20 transition-all duration-300">
|
|
212
|
-
<Slider
|
|
213
|
-
value={[isMuted ? 0 : volume]}
|
|
214
|
-
max={1}
|
|
215
|
-
step={0.01}
|
|
216
|
-
onValueChange={handleVolumeChange}
|
|
217
|
-
className="w-20"
|
|
218
|
-
/>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
)}
|
|
222
|
-
|
|
223
|
-
{!isCompact && (
|
|
224
|
-
<div className="w-px h-6 bg-border mx-1 hidden sm:block" />
|
|
225
|
-
)}
|
|
226
|
-
|
|
227
|
-
{!isCompact && (
|
|
197
|
+
{!isCompact && (
|
|
198
|
+
<div className="hidden sm:flex items-center gap-2 group/volume mr-2">
|
|
228
199
|
<Button
|
|
229
200
|
variant="ghost"
|
|
230
201
|
size="icon"
|
|
231
202
|
className="h-8 w-8 text-muted-foreground hover:text-foreground"
|
|
232
203
|
onClick={() => {
|
|
233
|
-
|
|
234
|
-
|
|
204
|
+
const newMuted = !isMuted;
|
|
205
|
+
setIsMuted(newMuted);
|
|
206
|
+
if (audioRef.current) audioRef.current.muted = newMuted;
|
|
235
207
|
}}
|
|
236
|
-
title="Modo Flutuante"
|
|
237
208
|
>
|
|
238
|
-
<
|
|
209
|
+
{isMuted ? <VolumeX className="w-4 h-4" /> : <Volume2 className="w-4 h-4" />}
|
|
239
210
|
</Button>
|
|
240
|
-
|
|
211
|
+
<div className="w-0 overflow-hidden group-hover/volume:w-20 transition-all duration-300">
|
|
212
|
+
<Slider
|
|
213
|
+
value={[isMuted ? 0 : volume]}
|
|
214
|
+
max={1}
|
|
215
|
+
step={0.01}
|
|
216
|
+
onValueChange={handleVolumeChange}
|
|
217
|
+
className="w-20"
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{!isCompact && (
|
|
224
|
+
<div className="w-px h-6 bg-border mx-1 hidden sm:block" />
|
|
225
|
+
)}
|
|
226
|
+
|
|
227
|
+
{!isCompact && (
|
|
228
|
+
<Button
|
|
229
|
+
variant="ghost"
|
|
230
|
+
size="icon"
|
|
231
|
+
className="h-8 w-8 text-muted-foreground hover:text-foreground"
|
|
232
|
+
onClick={() => {
|
|
233
|
+
setIsManualFloating(true);
|
|
234
|
+
handleSetFloating(true);
|
|
235
|
+
}}
|
|
236
|
+
title="Modo Flutuante"
|
|
237
|
+
>
|
|
238
|
+
<Maximize2 className="w-4 h-4" />
|
|
239
|
+
</Button>
|
|
240
|
+
)}
|
|
241
241
|
</div>
|
|
242
242
|
</div>
|
|
243
243
|
);
|
|
@@ -247,21 +247,21 @@ export function AudioPlayer({
|
|
|
247
247
|
<FloatingMediaWrapper
|
|
248
248
|
isFloating={isFloating}
|
|
249
249
|
setIsFloating={handleSetFloating}
|
|
250
|
-
|
|
250
|
+
onClose={() => {
|
|
251
251
|
setIsPlaying(false);
|
|
252
|
-
setIsFloating(false);
|
|
252
|
+
setIsFloating(false);
|
|
253
253
|
if (audioRef.current) audioRef.current.pause();
|
|
254
254
|
}}
|
|
255
255
|
title={title}
|
|
256
|
-
aspectRatio={320/110}
|
|
256
|
+
aspectRatio={320 / 110}
|
|
257
257
|
minHeight={110}
|
|
258
258
|
minWidth={320}
|
|
259
259
|
className="w-full"
|
|
260
260
|
>
|
|
261
261
|
<div className={cn(
|
|
262
262
|
"bg-card w-full overflow-hidden flex flex-col justify-center",
|
|
263
|
-
isFloating
|
|
264
|
-
? "h-full bg-transparent text-primary-foreground [&_.text-muted-foreground]:text-primary-foreground/80 [&_.text-foreground]:text-primary-foreground [&_.text-primary]:text-primary-foreground [&_.border-primary\\/20]:border-primary-foreground/30 [&_.hover\\:bg-primary\\/5]:hover:bg-primary-foreground/10 [&_[data-slot=slider-track]]:bg-primary-foreground/20 [&_[data-slot=slider-range]]:bg-primary-foreground [&_[data-slot=slider-thumb]]:border-primary-foreground"
|
|
263
|
+
isFloating
|
|
264
|
+
? "h-full bg-transparent text-primary-foreground [&_.text-muted-foreground]:text-primary-foreground/80 [&_.text-foreground]:text-primary-foreground [&_.text-primary]:text-primary-foreground [&_.border-primary\\/20]:border-primary-foreground/30 [&_.hover\\:bg-primary\\/5]:hover:bg-primary-foreground/10 [&_[data-slot=slider-track]]:bg-primary-foreground/20 [&_[data-slot=slider-range]]:bg-primary-foreground [&_[data-slot=slider-thumb]]:border-primary-foreground"
|
|
265
265
|
: "border rounded-md shadow-sm"
|
|
266
266
|
)}>
|
|
267
267
|
<audio
|