xertica-ui 1.0.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.
- package/App.tsx +182 -0
- package/README.md +330 -0
- package/assets/xertica-logo.svg +38 -0
- package/assets/xertica-x-logo.svg +21 -0
- package/bin/cli.ts +193 -0
- package/components/AssistenteXertica.tsx +2003 -0
- package/components/AudioPlayer.tsx +203 -0
- package/components/CodeBlock.tsx +242 -0
- package/components/DocumentEditor.tsx +504 -0
- package/components/ForgotPasswordPage.tsx +170 -0
- package/components/FormattedDocument.tsx +87 -0
- package/components/HomeContent.tsx +123 -0
- package/components/HomePage.tsx +70 -0
- package/components/LanguageSelector.tsx +54 -0
- package/components/LoginPage.tsx +199 -0
- package/components/MarkdownMessage.tsx +62 -0
- package/components/ModernChatInput.tsx +502 -0
- package/components/PodcastPlayer.tsx +409 -0
- package/components/ResetPasswordPage.tsx +234 -0
- package/components/Sidebar.tsx +489 -0
- package/components/TemplateContent.tsx +629 -0
- package/components/TemplatePage.tsx +70 -0
- package/components/ThemeToggle.tsx +65 -0
- package/components/VerifyEmailPage.tsx +187 -0
- package/components/XerticaLogo.tsx +69 -0
- package/components/XerticaOrbe.tsx +1339 -0
- package/components/XerticaXLogo.tsx +53 -0
- package/components/examples/DrawingMapExample.tsx +530 -0
- package/components/examples/FilterableMapExample.tsx +380 -0
- package/components/examples/LocationPickerExample.tsx +330 -0
- package/components/examples/MapExamples.tsx +280 -0
- package/components/examples/MapShowcase.tsx +446 -0
- package/components/examples/RouteMapExamples.tsx +329 -0
- package/components/examples/SimpleFilterableMap.tsx +192 -0
- package/components/examples/index.ts +52 -0
- package/components/figma/ImageWithFallback.tsx +27 -0
- package/components/index.ts +44 -0
- package/components/media/AudioPlayer.tsx +278 -0
- package/components/media/FloatingMediaWrapper.tsx +166 -0
- package/components/media/VideoPlayer.tsx +285 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +159 -0
- package/components/ui/alert.tsx +91 -0
- package/components/ui/aspect-ratio.tsx +11 -0
- package/components/ui/avatar.tsx +65 -0
- package/components/ui/badge.tsx +55 -0
- package/components/ui/breadcrumb.tsx +109 -0
- package/components/ui/button.tsx +78 -0
- package/components/ui/calendar.tsx +235 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +353 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +177 -0
- package/components/ui/context-menu.tsx +252 -0
- package/components/ui/dialog.tsx +138 -0
- package/components/ui/drawer.tsx +134 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/empty.tsx +90 -0
- package/components/ui/file-upload.tsx +152 -0
- package/components/ui/form.tsx +195 -0
- package/components/ui/google-maps-loader.tsx +379 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/index.ts +242 -0
- package/components/ui/input-otp.tsx +77 -0
- package/components/ui/input.tsx +38 -0
- package/components/ui/label.tsx +24 -0
- package/components/ui/map-config.ts +12 -0
- package/components/ui/map-layers.tsx +129 -0
- package/components/ui/map.exports.ts +31 -0
- package/components/ui/map.tsx +412 -0
- package/components/ui/menubar.tsx +276 -0
- package/components/ui/navigation-menu.tsx +162 -0
- package/components/ui/notification-badge.tsx +61 -0
- package/components/ui/page-header.tsx +229 -0
- package/components/ui/pagination.tsx +127 -0
- package/components/ui/popover.tsx +48 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/radio-group.tsx +56 -0
- package/components/ui/rating.tsx +102 -0
- package/components/ui/resizable.tsx +405 -0
- package/components/ui/route-map.tsx +246 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/search.tsx +70 -0
- package/components/ui/select.tsx +176 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/sheet.tsx +138 -0
- package/components/ui/sidebar.tsx +726 -0
- package/components/ui/simple-map.tsx +92 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/slider.tsx +58 -0
- package/components/ui/sonner.tsx +77 -0
- package/components/ui/stats-card.tsx +84 -0
- package/components/ui/stepper.tsx +126 -0
- package/components/ui/switch.tsx +34 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +26 -0
- package/components/ui/timeline.tsx +140 -0
- package/components/ui/toggle-group.tsx +71 -0
- package/components/ui/toggle.tsx +46 -0
- package/components/ui/tooltip.tsx +61 -0
- package/components/ui/tree-view.tsx +123 -0
- package/components/ui/use-mobile.ts +24 -0
- package/components/ui/utils.ts +6 -0
- package/components/ui/xertica-assistant.tsx +1420 -0
- package/contexts/ApiKeyContext.tsx +123 -0
- package/contexts/AssistenteContext.tsx +118 -0
- package/contexts/BrandColorsContext.tsx +551 -0
- package/contexts/LanguageContext.tsx +36 -0
- package/contexts/ThemeContext.tsx +85 -0
- package/dist/cli.js +20922 -0
- package/eslint.config.js +41 -0
- package/guidelines/Guidelines.md +61 -0
- package/hooks/useTheme.ts +4 -0
- package/imports/Podcast.tsx +389 -0
- package/imports/XerticaAi.tsx +46 -0
- package/imports/XerticaX.tsx +20 -0
- package/imports/svg-aueiaqngck.ts +11 -0
- package/imports/svg-v9krss1ozd.ts +16 -0
- package/imports/svg-vhrdofe3qe.ts +5 -0
- package/index.css +4448 -0
- package/index.html +14 -0
- package/main.tsx +10 -0
- package/package.json +119 -0
- package/postcss.config.js +6 -0
- package/routes.tsx +33 -0
- package/styles/globals.css +15 -0
- package/styles/xertica/app-overrides/chat.css +61 -0
- package/styles/xertica/app-overrides/scrollbar.css +33 -0
- package/styles/xertica/base.css +70 -0
- package/styles/xertica/integrations/google-maps.css +76 -0
- package/styles/xertica/integrations/sonner.css +73 -0
- package/styles/xertica/theme-map.css +88 -0
- package/styles/xertica/tokens.css +190 -0
- package/tsconfig.json +31 -0
- package/tsconfig.node.json +10 -0
- package/utils/gemini.ts +140 -0
- package/vite-env.d.ts +12 -0
- package/vite.config.ts +36 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ApiKeyContextType {
|
|
4
|
+
geminiApiKey: string;
|
|
5
|
+
setGeminiApiKey: (key: string) => void;
|
|
6
|
+
isApiKeyValid: boolean;
|
|
7
|
+
googleMapsApiKey: string;
|
|
8
|
+
setGoogleMapsApiKey: (key: string) => void;
|
|
9
|
+
isGoogleMapsKeyValid: boolean;
|
|
10
|
+
reloadMapsApi: () => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ApiKeyContext = createContext<ApiKeyContextType | undefined>(undefined);
|
|
14
|
+
|
|
15
|
+
export function ApiKeyProvider({ children }: { children: ReactNode }) {
|
|
16
|
+
// Lista de chaves conhecidas como vazadas (para limpeza automática)
|
|
17
|
+
const LEAKED_KEYS = [
|
|
18
|
+
'AIzaSyCMsAMytBeOK0Qd7RDFKyA5IW9eWt2WTJg',
|
|
19
|
+
'AIzaSyAiYWEIEmx212Up9zfM8kqyMXB4jLs8gq0'
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const [geminiApiKey, setGeminiApiKeyState] = useState<string>(() => {
|
|
23
|
+
const saved = localStorage.getItem('xertica-gemini-api-key');
|
|
24
|
+
|
|
25
|
+
// Se a chave salva estiver na lista de vazadas, removê-la
|
|
26
|
+
if (saved && LEAKED_KEYS.includes(saved)) {
|
|
27
|
+
localStorage.removeItem('xertica-gemini-api-key');
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return saved || '';
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const [googleMapsApiKey, setGoogleMapsApiKeyState] = useState<string>(() => {
|
|
35
|
+
const saved = localStorage.getItem('xertica-googlemaps-api-key');
|
|
36
|
+
return saved || '';
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const [isApiKeyValid, setIsApiKeyValid] = useState(false);
|
|
40
|
+
const [isGoogleMapsKeyValid, setIsGoogleMapsKeyValid] = useState(false);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (geminiApiKey) {
|
|
44
|
+
localStorage.setItem('xertica-gemini-api-key', geminiApiKey);
|
|
45
|
+
// Validação básica do formato da chave
|
|
46
|
+
setIsApiKeyValid(geminiApiKey.startsWith('AIzaSy') && geminiApiKey.length > 20);
|
|
47
|
+
} else {
|
|
48
|
+
localStorage.removeItem('xertica-gemini-api-key');
|
|
49
|
+
setIsApiKeyValid(false);
|
|
50
|
+
}
|
|
51
|
+
}, [geminiApiKey]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (googleMapsApiKey) {
|
|
55
|
+
localStorage.setItem('xertica-googlemaps-api-key', googleMapsApiKey);
|
|
56
|
+
// Validação básica do formato da chave do Google Maps
|
|
57
|
+
setIsGoogleMapsKeyValid(googleMapsApiKey.startsWith('AIzaSy') && googleMapsApiKey.length > 20);
|
|
58
|
+
|
|
59
|
+
// Recarregar automaticamente o Google Maps quando a chave mudar
|
|
60
|
+
const reloadMaps = async () => {
|
|
61
|
+
if (googleMapsApiKey.length < 10) return;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const { reloadGoogleMaps } = await import('../components/ui/google-maps-loader');
|
|
65
|
+
await reloadGoogleMaps(googleMapsApiKey);
|
|
66
|
+
console.log('[ApiKeyContext] Google Maps recarregado com sucesso');
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('[ApiKeyContext] Erro ao recarregar Google Maps:', error);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
reloadMaps();
|
|
73
|
+
} else {
|
|
74
|
+
localStorage.removeItem('xertica-googlemaps-api-key');
|
|
75
|
+
setIsGoogleMapsKeyValid(false);
|
|
76
|
+
}
|
|
77
|
+
}, [googleMapsApiKey]);
|
|
78
|
+
|
|
79
|
+
const setGeminiApiKey = (key: string) => {
|
|
80
|
+
setGeminiApiKeyState(key.trim());
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const setGoogleMapsApiKey = (key: string) => {
|
|
84
|
+
setGoogleMapsApiKeyState(key.trim());
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const reloadMapsApi = async () => {
|
|
88
|
+
if (!googleMapsApiKey || googleMapsApiKey.length < 10) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
// Importar dinamicamente a função de reload
|
|
94
|
+
const { reloadGoogleMaps } = await import('../components/ui/google-maps-loader');
|
|
95
|
+
await reloadGoogleMaps(googleMapsApiKey);
|
|
96
|
+
console.log('[ApiKeyContext] Google Maps recarregado manualmente');
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('[ApiKeyContext] Erro ao recarregar Google Maps:', error);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<ApiKeyContext.Provider value={{
|
|
104
|
+
geminiApiKey,
|
|
105
|
+
setGeminiApiKey,
|
|
106
|
+
isApiKeyValid,
|
|
107
|
+
googleMapsApiKey,
|
|
108
|
+
setGoogleMapsApiKey,
|
|
109
|
+
isGoogleMapsKeyValid,
|
|
110
|
+
reloadMapsApi
|
|
111
|
+
}}>
|
|
112
|
+
{children}
|
|
113
|
+
</ApiKeyContext.Provider>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function useApiKey() {
|
|
118
|
+
const context = useContext(ApiKeyContext);
|
|
119
|
+
if (!context) {
|
|
120
|
+
throw new Error('useApiKey must be used within ApiKeyProvider');
|
|
121
|
+
}
|
|
122
|
+
return context;
|
|
123
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface Message {
|
|
4
|
+
id: string;
|
|
5
|
+
type: 'user' | 'assistant';
|
|
6
|
+
content: string;
|
|
7
|
+
timestamp: Date;
|
|
8
|
+
isFavorite?: boolean;
|
|
9
|
+
attachmentType?: 'file' | 'audio' | 'image' | 'podcast' | 'document' | 'search';
|
|
10
|
+
attachmentName?: string;
|
|
11
|
+
audioUrl?: string;
|
|
12
|
+
isPodcastGenerating?: boolean;
|
|
13
|
+
documentContent?: string;
|
|
14
|
+
documentTitle?: string;
|
|
15
|
+
searchResults?: SearchResult[];
|
|
16
|
+
searchSources?: SearchSource[];
|
|
17
|
+
searchCommands?: SearchCommand[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Conversa {
|
|
21
|
+
id: string;
|
|
22
|
+
titulo: string;
|
|
23
|
+
mensagens: Message[];
|
|
24
|
+
ultimaMensagem: string;
|
|
25
|
+
timestamp: string;
|
|
26
|
+
favorita?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SearchResult {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
description: string;
|
|
33
|
+
type: 'document' | 'file' | 'project' | 'conversation' | 'contact';
|
|
34
|
+
path: string;
|
|
35
|
+
relevance: number;
|
|
36
|
+
lastModified?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SearchSource {
|
|
40
|
+
name: string;
|
|
41
|
+
count: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SearchCommand {
|
|
45
|
+
id: string;
|
|
46
|
+
icon: string;
|
|
47
|
+
label: string;
|
|
48
|
+
description: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface AssistenteContextType {
|
|
52
|
+
conversaAtual: string;
|
|
53
|
+
setConversaAtual: (id: string) => void;
|
|
54
|
+
conversas: Conversa[];
|
|
55
|
+
setConversas: React.Dispatch<React.SetStateAction<Conversa[]>>;
|
|
56
|
+
isTyping: boolean;
|
|
57
|
+
setIsTyping: (typing: boolean) => void;
|
|
58
|
+
abaSelecionada: 'chat' | 'historico' | 'favoritos';
|
|
59
|
+
setAbaSelecionada: (aba: 'chat' | 'historico' | 'favoritos') => void;
|
|
60
|
+
editingDocument: { content: string; title: string } | null;
|
|
61
|
+
setEditingDocument: (doc: { content: string; title: string } | null) => void;
|
|
62
|
+
searchFilter: 'all' | 'document' | 'project' | 'conversation' | 'file' | 'contact';
|
|
63
|
+
setSearchFilter: (filter: 'all' | 'document' | 'project' | 'conversation' | 'file' | 'contact') => void;
|
|
64
|
+
savedSearches: string[];
|
|
65
|
+
setSavedSearches: React.Dispatch<React.SetStateAction<string[]>>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const AssistenteContext = createContext<AssistenteContextType | undefined>(undefined);
|
|
69
|
+
|
|
70
|
+
export function AssistenteProvider({ children }: { children: ReactNode }) {
|
|
71
|
+
const [conversaAtual, setConversaAtual] = useState<string>('1');
|
|
72
|
+
const [conversas, setConversas] = useState<Conversa[]>([
|
|
73
|
+
{
|
|
74
|
+
id: '1',
|
|
75
|
+
titulo: 'Nova Conversa',
|
|
76
|
+
mensagens: [],
|
|
77
|
+
ultimaMensagem: '',
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
favorita: false
|
|
80
|
+
}
|
|
81
|
+
]);
|
|
82
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
83
|
+
const [abaSelecionada, setAbaSelecionada] = useState<'chat' | 'historico' | 'favoritos'>('chat');
|
|
84
|
+
const [editingDocument, setEditingDocument] = useState<{ content: string; title: string } | null>(null);
|
|
85
|
+
const [searchFilter, setSearchFilter] = useState<'all' | 'document' | 'project' | 'conversation' | 'file' | 'contact'>('all');
|
|
86
|
+
const [savedSearches, setSavedSearches] = useState<string[]>([]);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<AssistenteContext.Provider
|
|
90
|
+
value={{
|
|
91
|
+
conversaAtual,
|
|
92
|
+
setConversaAtual,
|
|
93
|
+
conversas,
|
|
94
|
+
setConversas,
|
|
95
|
+
isTyping,
|
|
96
|
+
setIsTyping,
|
|
97
|
+
abaSelecionada,
|
|
98
|
+
setAbaSelecionada,
|
|
99
|
+
editingDocument,
|
|
100
|
+
setEditingDocument,
|
|
101
|
+
searchFilter,
|
|
102
|
+
setSearchFilter,
|
|
103
|
+
savedSearches,
|
|
104
|
+
setSavedSearches,
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
{children}
|
|
108
|
+
</AssistenteContext.Provider>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function useAssistente() {
|
|
113
|
+
const context = useContext(AssistenteContext);
|
|
114
|
+
if (!context) {
|
|
115
|
+
throw new Error('useAssistente must be used within AssistenteProvider');
|
|
116
|
+
}
|
|
117
|
+
return context;
|
|
118
|
+
}
|