orchid-ai 1.3.1 ā 1.3.6
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/dist/cli/components/ChatPanel.d.ts +11 -3
- package/dist/cli/components/Conversation.d.ts +1 -1
- package/dist/cli/generate-schemas.js +113 -10
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/server/schema-generator.d.ts +8 -0
- package/dist/components/ChatPanel.d.ts +11 -3
- package/dist/components/Conversation.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +20 -12
- package/dist/index.js +20 -12
- package/dist/server/components/ChatPanel.d.ts +11 -3
- package/dist/server/components/Conversation.d.ts +1 -1
- package/dist/server/index.esm.js +113 -10
- package/dist/server/index.js +113 -10
- package/dist/server/schema-generator.d.ts +8 -0
- package/dist/server/server/schema-generator.d.ts +8 -0
- package/dist/styles.css +2 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type IconName } from './Icon';
|
|
3
|
-
import { CommandTheme, ChatMessage, ModelInfo, ChatTheme, ServerConfig } from '../types/types';
|
|
3
|
+
import { CommandTheme, CommandSuggestion, ChatMessage, ModelInfo, ChatTheme, ServerConfig } from '../types/types';
|
|
4
4
|
interface ChatPanelProps {
|
|
5
5
|
isOpen: boolean;
|
|
6
6
|
setIsOpen?: (isOpen: boolean) => void;
|
|
@@ -53,6 +53,13 @@ interface ChatPanelProps {
|
|
|
53
53
|
headerIcon?: IconName | React.ReactElement;
|
|
54
54
|
headerTitle?: string;
|
|
55
55
|
headerSubtitle?: string;
|
|
56
|
+
/** When set, replaces default behavior (setFormState + onNavigate + onClose) for command suggestions. */
|
|
57
|
+
onSuggestionSelect?: (s: CommandSuggestion) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Applied to the full-screen modal root so the chat stacks above app chrome that uses high z-index
|
|
60
|
+
* (e.g. MUI AppBar ~1100, custom sidebars ~999). Default Tailwind z-50 is too low for many shells.
|
|
61
|
+
*/
|
|
62
|
+
overlayZIndex?: number;
|
|
56
63
|
}
|
|
57
64
|
/**
|
|
58
65
|
* ChatPanel component
|
|
@@ -91,7 +98,7 @@ showHistory, // Default to hidden
|
|
|
91
98
|
showProfileBubbles, // Default to hidden
|
|
92
99
|
modalPosition, // Default to left position
|
|
93
100
|
serverConfig, models, defaultModel, showUsageStats, maxFileSize, features, showFloatingButton, floatingButtonIcon, floatingButtonPosition, floatingButtonSize, floatingButtonClassName, chats, setChats, currentChatId, setCurrentChatId, chatLevel, initialQuery, setInitialQuery, headerIcon, // Default to bot icon
|
|
94
|
-
headerTitle, headerSubtitle, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
101
|
+
headerTitle, headerSubtitle, onSuggestionSelect, overlayZIndex, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
95
102
|
/**
|
|
96
103
|
* ChatInput component
|
|
97
104
|
* @param query - The current query
|
|
@@ -118,6 +125,7 @@ type FloatingChatButtonProps = {
|
|
|
118
125
|
className?: string;
|
|
119
126
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
120
127
|
size?: 'sm' | 'md' | 'lg';
|
|
128
|
+
zIndex?: number;
|
|
121
129
|
};
|
|
122
130
|
/**
|
|
123
131
|
* FloatingChatButton component
|
|
@@ -128,5 +136,5 @@ type FloatingChatButtonProps = {
|
|
|
128
136
|
* @param position - Position of the floating button
|
|
129
137
|
* @param size - Size of the floating button
|
|
130
138
|
*/
|
|
131
|
-
export declare function FloatingChatButton({ onClick, theme, icon, className, position, size, }: FloatingChatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
139
|
+
export declare function FloatingChatButton({ onClick, theme, icon, className, position, size, zIndex: floatingZIndex, }: FloatingChatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
132
140
|
export {};
|
|
@@ -72,5 +72,5 @@ interface ConversationProps {
|
|
|
72
72
|
* @param autoScroll - Whether to auto-scroll to bottom on new messages
|
|
73
73
|
* @param maxHeight - Maximum height of the conversation container
|
|
74
74
|
*/
|
|
75
|
-
export declare function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles, className, autoScroll, maxHeight, userId, serverConfig, formData,
|
|
75
|
+
export declare function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles, className, autoScroll, maxHeight, userId, serverConfig, formData, chatLevel, chats: externalChats, setChats: externalSetChats, currentChatId: externalCurrentChatId, setCurrentChatId: externalSetCurrentChatId, query: externalQuery, setQuery: externalSetQuery, onSend: externalOnSend, isLoading: externalIsLoading, attachedFiles: externalAttachedFiles, setAttachedFiles: externalSetAttachedFiles, models, defaultModel, showUsageStats, maxFileSize, features, currentModelSelection: externalCurrentModelSelection, onModelSelectionChange: externalOnModelSelectionChange, showInput, initialQuery, setInitialQuery, showClearChat, onClearChat, additionalContext, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
76
76
|
export {};
|
|
@@ -10,6 +10,7 @@ const commandTrainingRules = {
|
|
|
10
10
|
components: {
|
|
11
11
|
patterns: {
|
|
12
12
|
// React component patterns
|
|
13
|
+
// Has /g for matchAll in training.ts; do not use String.match(componentName) (groups break).
|
|
13
14
|
componentName: /(?:export\s+)?(?:function|const)\s+(\w+)(?:\s*<[^>]*>)?\s*[=(]/g,
|
|
14
15
|
props: /interface\s+(\w+)Props\s*{([^}]*)}/g,
|
|
15
16
|
state: /useState<([^>]*)>/g,
|
|
@@ -34,7 +35,8 @@ const commandTrainingRules = {
|
|
|
34
35
|
schemas: {
|
|
35
36
|
patterns: {
|
|
36
37
|
// Monastery schema patterns - updated for actual file format
|
|
37
|
-
|
|
38
|
+
// No /g: training.ts uses String.match(); with g, capture groups are omitted and modelName is always lost.
|
|
39
|
+
modelName: /\/\/\s*(\w+)\s+model\s+for\s+monastery/,
|
|
38
40
|
fields: /fields:\s*{([^}]*)}/g,
|
|
39
41
|
fieldDefinitions: /(\w+):\s*{\s*(?:required:\s*(true|false),?\s*)?type:\s*['"]([^'"]+)['"](?:,\s*required:\s*(true|false))?(?:,\s*default:\s*[^,}]+)?/g,
|
|
40
42
|
indexes: /indexes:\s*\[([^\]]*)\]/g,
|
|
@@ -557,6 +559,7 @@ class CommandTrainingCollector {
|
|
|
557
559
|
if (content.formFields?.length > 0) {
|
|
558
560
|
const form = {
|
|
559
561
|
name: content.componentName || 'Unknown Form',
|
|
562
|
+
sourceFile: path.relative(this.rootDir, component.filePath),
|
|
560
563
|
purpose: `Form component for data entry`,
|
|
561
564
|
fields: content.formFields.map((fieldName) => ({
|
|
562
565
|
name: fieldName,
|
|
@@ -685,12 +688,15 @@ class CommandTrainingCollector {
|
|
|
685
688
|
guessEntityFromComponent(componentName) {
|
|
686
689
|
if (!componentName)
|
|
687
690
|
return 'Unknown';
|
|
688
|
-
//
|
|
691
|
+
// Strip common create/edit wrappers, then UI suffixes (order matters)
|
|
689
692
|
const cleaned = componentName
|
|
693
|
+
.replace(/^(?:Create|Edit|View|New|Batch)/i, '')
|
|
690
694
|
.replace(/Form$/, '')
|
|
695
|
+
.replace(/Modal$/, '')
|
|
696
|
+
.replace(/Drawer$/, '')
|
|
697
|
+
.replace(/Dialog$/, '')
|
|
691
698
|
.replace(/Page$/, '')
|
|
692
|
-
.replace(/Component$/, '')
|
|
693
|
-
.replace(/Modal$/, '');
|
|
699
|
+
.replace(/Component$/, '');
|
|
694
700
|
return cleaned || 'Unknown';
|
|
695
701
|
}
|
|
696
702
|
guessEntitiesFromComponent(content) {
|
|
@@ -804,7 +810,7 @@ class CommandTrainingCollector {
|
|
|
804
810
|
this.log(`ā Error expanding pattern "${pattern}":`, err.message);
|
|
805
811
|
}
|
|
806
812
|
}
|
|
807
|
-
return expandedPaths;
|
|
813
|
+
return [...new Set(expandedPaths)];
|
|
808
814
|
}
|
|
809
815
|
extractFileInfo(fieldName, content) {
|
|
810
816
|
switch (fieldName) {
|
|
@@ -848,11 +854,22 @@ class CommandTrainingCollector {
|
|
|
848
854
|
hooks: [],
|
|
849
855
|
eventHandlers: [],
|
|
850
856
|
};
|
|
851
|
-
//
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
857
|
+
// Do not use String.match() on patterns.componentName ā it has /g, so match[1] is the *second*
|
|
858
|
+
// full match, not capture group 1 (yields strings like `const CustomerForm =`).
|
|
859
|
+
const declRe = /(?:export\s+)?(?:function|const)\s+(\w+)(?:\s*<[^>]*>)?\s*[=(]/g;
|
|
860
|
+
const declNames = [];
|
|
861
|
+
for (const m of content.matchAll(declRe)) {
|
|
862
|
+
declNames.push(m[1]);
|
|
863
|
+
}
|
|
864
|
+
const isLikelyComponentSymbol = (n) => /^[A-Z]/.test(n) &&
|
|
865
|
+
!/^use[A-Z]/.test(n) &&
|
|
866
|
+
!/^Styled/i.test(n);
|
|
867
|
+
const preferred = [...declNames].reverse().find((n) => isLikelyComponentSymbol(n) &&
|
|
868
|
+
(/(?:Form|Modal|Drawer|Dialog)$/.test(n) ||
|
|
869
|
+
/^FormStep/i.test(n) ||
|
|
870
|
+
(n.includes('Form') && n.length > 4)));
|
|
871
|
+
const fallback = [...declNames].reverse().find(isLikelyComponentSymbol);
|
|
872
|
+
result.componentName = preferred ?? fallback ?? null;
|
|
856
873
|
// Extract props interfaces
|
|
857
874
|
const propsMatches = content.matchAll(patterns.props);
|
|
858
875
|
for (const match of propsMatches) {
|
|
@@ -887,6 +904,7 @@ class CommandTrainingCollector {
|
|
|
887
904
|
for (const match of fieldMatches) {
|
|
888
905
|
result.formFields.push(match[1]);
|
|
889
906
|
}
|
|
907
|
+
result.formFields = [...new Set(result.formFields)];
|
|
890
908
|
// Extract imports
|
|
891
909
|
const importMatches = content.matchAll(patterns.imports);
|
|
892
910
|
for (const match of importMatches) {
|
|
@@ -1635,6 +1653,7 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
1635
1653
|
};
|
|
1636
1654
|
this.log(` ā
Generated schema with ${Object.keys(properties).length} properties`);
|
|
1637
1655
|
}
|
|
1656
|
+
this.mergeFormUiFieldsIntoEntitySchemas(trainingData, schemas);
|
|
1638
1657
|
// If no entities found, try to extract from components
|
|
1639
1658
|
if (Object.keys(schemas).length === 0) {
|
|
1640
1659
|
this.log(' ā ļø No entities found in training data, extracting from components...');
|
|
@@ -1642,6 +1661,87 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
1642
1661
|
}
|
|
1643
1662
|
return schemas;
|
|
1644
1663
|
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Merge `name="..."` fields collected from UI forms/modals into existing entity schemas
|
|
1666
|
+
* (server models stay canonical; this adds frontend surface hints).
|
|
1667
|
+
* Optional `customTrainingData.options.formEntityHints`: map form component name -> schema entity key.
|
|
1668
|
+
* Optional `formEntityPathPatterns`: `{ includes, entity }` ā first match on `form.sourceFile` wins
|
|
1669
|
+
* (disambiguates reused names like `FormStepDetails` in different feature folders).
|
|
1670
|
+
*/
|
|
1671
|
+
mergeFormUiFieldsIntoEntitySchemas(trainingData, schemas) {
|
|
1672
|
+
const forms = trainingData.customTrainingData?.structuredData?.components?.forms || [];
|
|
1673
|
+
if (!forms.length || !Object.keys(schemas).length)
|
|
1674
|
+
return;
|
|
1675
|
+
const hints = (trainingData.customTrainingData?.options?.formEntityHints || {});
|
|
1676
|
+
const pathPatterns = (trainingData.customTrainingData?.options?.formEntityPathPatterns ||
|
|
1677
|
+
[]);
|
|
1678
|
+
/** Short UI stems that don't match lowered model names */
|
|
1679
|
+
const STEM_ALIASES = {
|
|
1680
|
+
hs: 'hsreport',
|
|
1681
|
+
incident: 'incidentreport',
|
|
1682
|
+
};
|
|
1683
|
+
const schemaKeys = new Set(Object.keys(schemas));
|
|
1684
|
+
const resolveKey = (form) => {
|
|
1685
|
+
if (form.sourceFile) {
|
|
1686
|
+
for (const p of pathPatterns) {
|
|
1687
|
+
if (!p?.includes || !p?.entity)
|
|
1688
|
+
continue;
|
|
1689
|
+
if (form.sourceFile.includes(p.includes)) {
|
|
1690
|
+
const k = p.entity.toLowerCase();
|
|
1691
|
+
if (schemaKeys.has(k))
|
|
1692
|
+
return k;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
const tryKey = (raw) => {
|
|
1697
|
+
if (!raw)
|
|
1698
|
+
return undefined;
|
|
1699
|
+
const mapped = hints[raw];
|
|
1700
|
+
const stem = raw
|
|
1701
|
+
.replace(/^(?:Create|Edit|View|New|Batch)/i, '')
|
|
1702
|
+
.replace(/(?:Form|Modal|Drawer|Dialog)$/i, '');
|
|
1703
|
+
const stemLc = stem.toLowerCase();
|
|
1704
|
+
const alias = STEM_ALIASES[stemLc];
|
|
1705
|
+
if (alias && schemaKeys.has(alias))
|
|
1706
|
+
return alias;
|
|
1707
|
+
const candidates = [
|
|
1708
|
+
(mapped || raw).toLowerCase(),
|
|
1709
|
+
stemLc,
|
|
1710
|
+
];
|
|
1711
|
+
for (const c of candidates) {
|
|
1712
|
+
if (c && schemaKeys.has(c))
|
|
1713
|
+
return c;
|
|
1714
|
+
}
|
|
1715
|
+
return undefined;
|
|
1716
|
+
};
|
|
1717
|
+
return tryKey(form.name) || tryKey(form.relatedEntity) || undefined;
|
|
1718
|
+
};
|
|
1719
|
+
let mergedForms = 0;
|
|
1720
|
+
let mergedFields = 0;
|
|
1721
|
+
for (const form of forms) {
|
|
1722
|
+
const entityKey = resolveKey(form);
|
|
1723
|
+
if (!entityKey)
|
|
1724
|
+
continue;
|
|
1725
|
+
const gen = schemas[entityKey];
|
|
1726
|
+
if (!gen?.schema?.properties)
|
|
1727
|
+
continue;
|
|
1728
|
+
const props = gen.schema.properties;
|
|
1729
|
+
for (const f of form.fields || []) {
|
|
1730
|
+
const n = f?.name;
|
|
1731
|
+
if (!n || typeof n !== 'string' || props[n])
|
|
1732
|
+
continue;
|
|
1733
|
+
props[n] = {
|
|
1734
|
+
type: 'string',
|
|
1735
|
+
description: `UI form field (from ${form.name}). Cross-check server model; may overlap sharing/base shapes.`,
|
|
1736
|
+
};
|
|
1737
|
+
mergedFields += 1;
|
|
1738
|
+
}
|
|
1739
|
+
mergedForms += 1;
|
|
1740
|
+
}
|
|
1741
|
+
if (mergedForms > 0) {
|
|
1742
|
+
this.log(`\nš Merged UI form fields into ${mergedForms} entity schema(s) (+${mergedFields} properties)`);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1645
1745
|
/**
|
|
1646
1746
|
* Extract schemas from component analysis if no explicit entities found
|
|
1647
1747
|
*/
|
|
@@ -1955,6 +2055,9 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
1955
2055
|
* Map training data field types to JSON Schema types
|
|
1956
2056
|
*/
|
|
1957
2057
|
mapFieldType(type) {
|
|
2058
|
+
if (type == null || typeof type !== 'string') {
|
|
2059
|
+
return 'string';
|
|
2060
|
+
}
|
|
1958
2061
|
const typeMap = {
|
|
1959
2062
|
string: 'string',
|
|
1960
2063
|
number: 'number',
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -55,6 +55,14 @@ export declare class SchemaGenerator extends CommandTrainingCollector {
|
|
|
55
55
|
* Generate schemas from collected training data
|
|
56
56
|
*/
|
|
57
57
|
private generateSchemas;
|
|
58
|
+
/**
|
|
59
|
+
* Merge `name="..."` fields collected from UI forms/modals into existing entity schemas
|
|
60
|
+
* (server models stay canonical; this adds frontend surface hints).
|
|
61
|
+
* Optional `customTrainingData.options.formEntityHints`: map form component name -> schema entity key.
|
|
62
|
+
* Optional `formEntityPathPatterns`: `{ includes, entity }` ā first match on `form.sourceFile` wins
|
|
63
|
+
* (disambiguates reused names like `FormStepDetails` in different feature folders).
|
|
64
|
+
*/
|
|
65
|
+
private mergeFormUiFieldsIntoEntitySchemas;
|
|
58
66
|
/**
|
|
59
67
|
* Extract schemas from component analysis if no explicit entities found
|
|
60
68
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type IconName } from './Icon';
|
|
3
|
-
import { CommandTheme, ChatMessage, ModelInfo, ChatTheme, ServerConfig } from '../types/types';
|
|
3
|
+
import { CommandTheme, CommandSuggestion, ChatMessage, ModelInfo, ChatTheme, ServerConfig } from '../types/types';
|
|
4
4
|
interface ChatPanelProps {
|
|
5
5
|
isOpen: boolean;
|
|
6
6
|
setIsOpen?: (isOpen: boolean) => void;
|
|
@@ -53,6 +53,13 @@ interface ChatPanelProps {
|
|
|
53
53
|
headerIcon?: IconName | React.ReactElement;
|
|
54
54
|
headerTitle?: string;
|
|
55
55
|
headerSubtitle?: string;
|
|
56
|
+
/** When set, replaces default behavior (setFormState + onNavigate + onClose) for command suggestions. */
|
|
57
|
+
onSuggestionSelect?: (s: CommandSuggestion) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Applied to the full-screen modal root so the chat stacks above app chrome that uses high z-index
|
|
60
|
+
* (e.g. MUI AppBar ~1100, custom sidebars ~999). Default Tailwind z-50 is too low for many shells.
|
|
61
|
+
*/
|
|
62
|
+
overlayZIndex?: number;
|
|
56
63
|
}
|
|
57
64
|
/**
|
|
58
65
|
* ChatPanel component
|
|
@@ -91,7 +98,7 @@ showHistory, // Default to hidden
|
|
|
91
98
|
showProfileBubbles, // Default to hidden
|
|
92
99
|
modalPosition, // Default to left position
|
|
93
100
|
serverConfig, models, defaultModel, showUsageStats, maxFileSize, features, showFloatingButton, floatingButtonIcon, floatingButtonPosition, floatingButtonSize, floatingButtonClassName, chats, setChats, currentChatId, setCurrentChatId, chatLevel, initialQuery, setInitialQuery, headerIcon, // Default to bot icon
|
|
94
|
-
headerTitle, headerSubtitle, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
101
|
+
headerTitle, headerSubtitle, onSuggestionSelect, overlayZIndex, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
95
102
|
/**
|
|
96
103
|
* ChatInput component
|
|
97
104
|
* @param query - The current query
|
|
@@ -118,6 +125,7 @@ type FloatingChatButtonProps = {
|
|
|
118
125
|
className?: string;
|
|
119
126
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
120
127
|
size?: 'sm' | 'md' | 'lg';
|
|
128
|
+
zIndex?: number;
|
|
121
129
|
};
|
|
122
130
|
/**
|
|
123
131
|
* FloatingChatButton component
|
|
@@ -128,5 +136,5 @@ type FloatingChatButtonProps = {
|
|
|
128
136
|
* @param position - Position of the floating button
|
|
129
137
|
* @param size - Size of the floating button
|
|
130
138
|
*/
|
|
131
|
-
export declare function FloatingChatButton({ onClick, theme, icon, className, position, size, }: FloatingChatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
139
|
+
export declare function FloatingChatButton({ onClick, theme, icon, className, position, size, zIndex: floatingZIndex, }: FloatingChatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
132
140
|
export {};
|
|
@@ -72,5 +72,5 @@ interface ConversationProps {
|
|
|
72
72
|
* @param autoScroll - Whether to auto-scroll to bottom on new messages
|
|
73
73
|
* @param maxHeight - Maximum height of the conversation container
|
|
74
74
|
*/
|
|
75
|
-
export declare function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles, className, autoScroll, maxHeight, userId, serverConfig, formData,
|
|
75
|
+
export declare function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles, className, autoScroll, maxHeight, userId, serverConfig, formData, chatLevel, chats: externalChats, setChats: externalSetChats, currentChatId: externalCurrentChatId, setCurrentChatId: externalSetCurrentChatId, query: externalQuery, setQuery: externalSetQuery, onSend: externalOnSend, isLoading: externalIsLoading, attachedFiles: externalAttachedFiles, setAttachedFiles: externalSetAttachedFiles, models, defaultModel, showUsageStats, maxFileSize, features, currentModelSelection: externalCurrentModelSelection, onModelSelectionChange: externalOnModelSelectionChange, showInput, initialQuery, setInitialQuery, showClearChat, onClearChat, additionalContext, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
76
76
|
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/index.esm.js
CHANGED
|
@@ -2791,7 +2791,7 @@ const getTextFromMessage$1 = (msg) => {
|
|
|
2791
2791
|
*/
|
|
2792
2792
|
function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles = false, className = '', autoScroll = true, maxHeight = '100%',
|
|
2793
2793
|
// AI functionality props
|
|
2794
|
-
userId = 'demo-user', serverConfig, formData,
|
|
2794
|
+
userId = 'demo-user', serverConfig, formData, chatLevel = 'full',
|
|
2795
2795
|
// External chat management
|
|
2796
2796
|
chats: externalChats, setChats: externalSetChats, currentChatId: externalCurrentChatId, setCurrentChatId: externalSetCurrentChatId,
|
|
2797
2797
|
// Input-related props
|
|
@@ -2944,14 +2944,10 @@ additionalContext, }) {
|
|
|
2944
2944
|
externalSetQuery?.('');
|
|
2945
2945
|
externalSetAttachedFiles?.([]);
|
|
2946
2946
|
}, [query, attachedFiles, streamingAI, externalSetQuery, externalSetAttachedFiles]);
|
|
2947
|
-
//
|
|
2947
|
+
// Delegate to parent ā ChatPanel composes setFormState / onNavigate / onClose in its handler
|
|
2948
2948
|
const handleSuggestionClick = useCallback((s) => {
|
|
2949
|
-
if (setFormState && s.formState)
|
|
2950
|
-
setFormState(s.formState, s.actionType);
|
|
2951
|
-
if (s.path && onNavigate)
|
|
2952
|
-
onNavigate(s.path);
|
|
2953
2949
|
onSuggestionClick(s);
|
|
2954
|
-
}, [
|
|
2950
|
+
}, [onSuggestionClick]);
|
|
2955
2951
|
// Use external or internal state
|
|
2956
2952
|
const chat = externalChat || streamingAI.chat;
|
|
2957
2953
|
const isLoading = externalIsLoading || streamingAI.isLoading;
|
|
@@ -3512,8 +3508,12 @@ showHistory = false, // Default to hidden
|
|
|
3512
3508
|
showProfileBubbles = false, // Default to hidden
|
|
3513
3509
|
modalPosition = 'left', // Default to left position
|
|
3514
3510
|
serverConfig, models, defaultModel, showUsageStats, maxFileSize, features = {}, showFloatingButton = false, floatingButtonIcon, floatingButtonPosition = 'bottom-right', floatingButtonSize = 'md', floatingButtonClassName = '', chats, setChats, currentChatId, setCurrentChatId, chatLevel, initialQuery, setInitialQuery, headerIcon = 'command', // Default to bot icon
|
|
3515
|
-
headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist you with your queries', }) {
|
|
3511
|
+
headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist you with your queries', onSuggestionSelect, overlayZIndex, }) {
|
|
3516
3512
|
const handleSuggestionClick = (s) => {
|
|
3513
|
+
if (onSuggestionSelect) {
|
|
3514
|
+
onSuggestionSelect(s);
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
3517
|
if (setFormState && s.formState)
|
|
3518
3518
|
setFormState(s.formState, s.actionType);
|
|
3519
3519
|
if (s.path)
|
|
@@ -3521,6 +3521,14 @@ headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist
|
|
|
3521
3521
|
onClose();
|
|
3522
3522
|
};
|
|
3523
3523
|
const containerRef = useRef(null);
|
|
3524
|
+
const handleHistorySwitchChat = useCallback((id) => {
|
|
3525
|
+
setCurrentChatId?.(id);
|
|
3526
|
+
}, [setCurrentChatId]);
|
|
3527
|
+
const handleHistoryNewChat = useCallback(() => {
|
|
3528
|
+
const id = Date.now().toString();
|
|
3529
|
+
setChats?.((prev) => ({ ...prev, [id]: [] }));
|
|
3530
|
+
setCurrentChatId?.(id);
|
|
3531
|
+
}, [setChats, setCurrentChatId]);
|
|
3524
3532
|
// SIMPLIFIED ANIMATION STATE
|
|
3525
3533
|
const [isVisible, setIsVisible] = useState(false);
|
|
3526
3534
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
@@ -3670,7 +3678,7 @@ headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist
|
|
|
3670
3678
|
transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
3671
3679
|
}
|
|
3672
3680
|
`;
|
|
3673
|
-
return (jsxs(Fragment, { children: [jsx("style", { children: scrollbarStyles }), jsxs(ErrorBoundary, { children: [!isOpen && showFloatingButton && (jsx(FloatingChatButton, { onClick: handleOpen, theme: theme, icon: floatingButtonIcon, position: floatingButtonPosition, size: floatingButtonSize, className: floatingButtonClassName })), shouldRenderPanel && (jsxs("div", { className: "fixed inset-0 z-50", children: [jsx("div", { className: `absolute inset-0 bg-black/20 transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`, onClick: handleClose }), jsx("div", { className: `fixed z-[60] top-6 transition-all duration-300 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-75'}`, style: { ...(modalPosition === 'left'
|
|
3681
|
+
return (jsxs(Fragment, { children: [jsx("style", { children: scrollbarStyles }), jsxs(ErrorBoundary, { children: [!isOpen && showFloatingButton && (jsx(FloatingChatButton, { onClick: handleOpen, theme: theme, icon: floatingButtonIcon, position: floatingButtonPosition, size: floatingButtonSize, className: floatingButtonClassName, zIndex: overlayZIndex })), shouldRenderPanel && (jsxs("div", { className: "fixed inset-0 z-50", style: overlayZIndex !== undefined ? { zIndex: overlayZIndex } : undefined, children: [jsx("div", { className: `absolute inset-0 bg-black/20 transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`, onClick: handleClose }), jsx("div", { className: `fixed z-[60] top-6 transition-all duration-300 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-75'}`, style: { ...(modalPosition === 'left'
|
|
3674
3682
|
? { left: showHistory ? 'min(90vw, 700px)' : 'min(80vw, 550px)', marginLeft: '16px' }
|
|
3675
3683
|
: { right: showHistory ? 'min(90vw, 700px)' : 'min(80vw, 550px)', marginRight: '16px' }) }, children: jsx("button", { onClick: handleClose, className: "w-10 h-10 rounded-full shadow-lg transition-all duration-200 flex items-center justify-center text-xl font-bold hover:scale-110", style: {
|
|
3676
3684
|
backgroundColor: finalTheme.colors.surface.elevated,
|
|
@@ -3706,7 +3714,7 @@ headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist
|
|
|
3706
3714
|
}, ref: containerRef, onClick: (e) => e.stopPropagation(), children: [jsx("div", { className: `flex items-center justify-between px-6 py-4 border-b min-h-[72px] transition-all duration-500 delay-100 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-2'}`, style: {
|
|
3707
3715
|
borderColor: finalTheme.colors.border.primary,
|
|
3708
3716
|
backgroundColor: finalTheme.colors.background.primary,
|
|
3709
|
-
}, children: jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { className: `inline-flex items-center justify-center rounded-full w-8 h-8 transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100 rotate-0' : 'opacity-0 scale-75 rotate-12'}`, style: { backgroundColor: finalTheme.colors.primary[600] }, children: typeof headerIcon === 'string' ? (jsx(Icon, { name: headerIcon, size: "sm", style: { color: finalTheme.colors.text.primary } })) : (headerIcon || (jsx(Icon, { name: "command", size: "sm", style: { color: finalTheme.colors.text.primary } }))) }), jsxs("div", { className: `transition-all duration-500 delay-300 ${isOpen ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-2'}`, children: [jsx("div", { className: "font-bold text-lg leading-tight", style: { color: finalTheme.colors.text.primary }, children: headerTitle }), jsx("div", { className: "text-xs", style: { color: finalTheme.colors.text.tertiary }, children: headerSubtitle })] })] }) }), jsxs("div", { className: `flex flex-1 min-h-0 h-full transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95'}`, children: [showHistory && (jsx(ChatHistorySidebar, { chats: chats || {}, currentChatId: currentChatId || 'default', switchChat:
|
|
3717
|
+
}, children: jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { className: `inline-flex items-center justify-center rounded-full w-8 h-8 transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100 rotate-0' : 'opacity-0 scale-75 rotate-12'}`, style: { backgroundColor: finalTheme.colors.primary[600] }, children: typeof headerIcon === 'string' ? (jsx(Icon, { name: headerIcon, size: "sm", style: { color: finalTheme.colors.text.primary } })) : (headerIcon || (jsx(Icon, { name: "command", size: "sm", style: { color: finalTheme.colors.text.primary } }))) }), jsxs("div", { className: `transition-all duration-500 delay-300 ${isOpen ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-2'}`, children: [jsx("div", { className: "font-bold text-lg leading-tight", style: { color: finalTheme.colors.text.primary }, children: headerTitle }), jsx("div", { className: "text-xs", style: { color: finalTheme.colors.text.tertiary }, children: headerSubtitle })] })] }) }), jsxs("div", { className: `flex flex-1 min-h-0 h-full transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95'}`, children: [showHistory && (jsx(ChatHistorySidebar, { chats: chats || {}, currentChatId: currentChatId || 'default', switchChat: handleHistorySwitchChat, newChat: handleHistoryNewChat, theme: finalTheme, isOpen: isOpen })), jsx("div", { className: "flex-1 flex flex-col min-h-0 h-full", children: jsx("div", { className: `flex-1 px-6 py-4 min-h-0 transition-all duration-500 delay-300 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`, style: {
|
|
3710
3718
|
backgroundColor: finalTheme.colors.background.primary,
|
|
3711
3719
|
}, children: jsx(Conversation, { onSuggestionClick: handleSuggestionClick, theme: finalTheme, showProfileBubbles: showProfileBubbles, autoScroll: true, showInput: true,
|
|
3712
3720
|
// AI functionality props
|
|
@@ -3779,7 +3787,7 @@ function ChatHistorySidebar({ chats, currentChatId, switchChat, newChat, theme,
|
|
|
3779
3787
|
* @param position - Position of the floating button
|
|
3780
3788
|
* @param size - Size of the floating button
|
|
3781
3789
|
*/
|
|
3782
|
-
function FloatingChatButton({ onClick, theme = defaultTheme, icon, className = '', position = 'bottom-right', size = 'md', }) {
|
|
3790
|
+
function FloatingChatButton({ onClick, theme = defaultTheme, icon, className = '', position = 'bottom-right', size = 'md', zIndex: floatingZIndex, }) {
|
|
3783
3791
|
// Note: Theme is now handled via Tailwind classes directly
|
|
3784
3792
|
// Position classes
|
|
3785
3793
|
const positionClasses = {
|
|
@@ -3799,7 +3807,7 @@ function FloatingChatButton({ onClick, theme = defaultTheme, icon, className = '
|
|
|
3799
3807
|
md: { width: 24, height: 24 },
|
|
3800
3808
|
lg: { width: 28, height: 28 },
|
|
3801
3809
|
};
|
|
3802
|
-
return (jsx("div", { className: `fixed z-50 ${positionClasses[position]} ${className}`, children: jsx("button", { onClick: onClick, className: `
|
|
3810
|
+
return (jsx("div", { className: `fixed z-50 ${positionClasses[position]} ${className}`, style: floatingZIndex !== undefined ? { zIndex: floatingZIndex } : undefined, children: jsx("button", { onClick: onClick, className: `
|
|
3803
3811
|
${sizeClasses[size]}
|
|
3804
3812
|
bg-blue-600 hover:bg-blue-700
|
|
3805
3813
|
text-white rounded-full shadow-lg hover:shadow-xl
|
package/dist/index.js
CHANGED
|
@@ -2793,7 +2793,7 @@ const getTextFromMessage$1 = (msg) => {
|
|
|
2793
2793
|
*/
|
|
2794
2794
|
function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles = false, className = '', autoScroll = true, maxHeight = '100%',
|
|
2795
2795
|
// AI functionality props
|
|
2796
|
-
userId = 'demo-user', serverConfig, formData,
|
|
2796
|
+
userId = 'demo-user', serverConfig, formData, chatLevel = 'full',
|
|
2797
2797
|
// External chat management
|
|
2798
2798
|
chats: externalChats, setChats: externalSetChats, currentChatId: externalCurrentChatId, setCurrentChatId: externalSetCurrentChatId,
|
|
2799
2799
|
// Input-related props
|
|
@@ -2946,14 +2946,10 @@ additionalContext, }) {
|
|
|
2946
2946
|
externalSetQuery?.('');
|
|
2947
2947
|
externalSetAttachedFiles?.([]);
|
|
2948
2948
|
}, [query, attachedFiles, streamingAI, externalSetQuery, externalSetAttachedFiles]);
|
|
2949
|
-
//
|
|
2949
|
+
// Delegate to parent ā ChatPanel composes setFormState / onNavigate / onClose in its handler
|
|
2950
2950
|
const handleSuggestionClick = React.useCallback((s) => {
|
|
2951
|
-
if (setFormState && s.formState)
|
|
2952
|
-
setFormState(s.formState, s.actionType);
|
|
2953
|
-
if (s.path && onNavigate)
|
|
2954
|
-
onNavigate(s.path);
|
|
2955
2951
|
onSuggestionClick(s);
|
|
2956
|
-
}, [
|
|
2952
|
+
}, [onSuggestionClick]);
|
|
2957
2953
|
// Use external or internal state
|
|
2958
2954
|
const chat = externalChat || streamingAI.chat;
|
|
2959
2955
|
const isLoading = externalIsLoading || streamingAI.isLoading;
|
|
@@ -3514,8 +3510,12 @@ showHistory = false, // Default to hidden
|
|
|
3514
3510
|
showProfileBubbles = false, // Default to hidden
|
|
3515
3511
|
modalPosition = 'left', // Default to left position
|
|
3516
3512
|
serverConfig, models, defaultModel, showUsageStats, maxFileSize, features = {}, showFloatingButton = false, floatingButtonIcon, floatingButtonPosition = 'bottom-right', floatingButtonSize = 'md', floatingButtonClassName = '', chats, setChats, currentChatId, setCurrentChatId, chatLevel, initialQuery, setInitialQuery, headerIcon = 'command', // Default to bot icon
|
|
3517
|
-
headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist you with your queries', }) {
|
|
3513
|
+
headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist you with your queries', onSuggestionSelect, overlayZIndex, }) {
|
|
3518
3514
|
const handleSuggestionClick = (s) => {
|
|
3515
|
+
if (onSuggestionSelect) {
|
|
3516
|
+
onSuggestionSelect(s);
|
|
3517
|
+
return;
|
|
3518
|
+
}
|
|
3519
3519
|
if (setFormState && s.formState)
|
|
3520
3520
|
setFormState(s.formState, s.actionType);
|
|
3521
3521
|
if (s.path)
|
|
@@ -3523,6 +3523,14 @@ headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist
|
|
|
3523
3523
|
onClose();
|
|
3524
3524
|
};
|
|
3525
3525
|
const containerRef = React.useRef(null);
|
|
3526
|
+
const handleHistorySwitchChat = React.useCallback((id) => {
|
|
3527
|
+
setCurrentChatId?.(id);
|
|
3528
|
+
}, [setCurrentChatId]);
|
|
3529
|
+
const handleHistoryNewChat = React.useCallback(() => {
|
|
3530
|
+
const id = Date.now().toString();
|
|
3531
|
+
setChats?.((prev) => ({ ...prev, [id]: [] }));
|
|
3532
|
+
setCurrentChatId?.(id);
|
|
3533
|
+
}, [setChats, setCurrentChatId]);
|
|
3526
3534
|
// SIMPLIFIED ANIMATION STATE
|
|
3527
3535
|
const [isVisible, setIsVisible] = React.useState(false);
|
|
3528
3536
|
const [isAnimating, setIsAnimating] = React.useState(false);
|
|
@@ -3672,7 +3680,7 @@ headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist
|
|
|
3672
3680
|
transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
3673
3681
|
}
|
|
3674
3682
|
`;
|
|
3675
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: scrollbarStyles }), jsxRuntime.jsxs(ErrorBoundary, { children: [!isOpen && showFloatingButton && (jsxRuntime.jsx(FloatingChatButton, { onClick: handleOpen, theme: theme, icon: floatingButtonIcon, position: floatingButtonPosition, size: floatingButtonSize, className: floatingButtonClassName })), shouldRenderPanel && (jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50", children: [jsxRuntime.jsx("div", { className: `absolute inset-0 bg-black/20 transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`, onClick: handleClose }), jsxRuntime.jsx("div", { className: `fixed z-[60] top-6 transition-all duration-300 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-75'}`, style: { ...(modalPosition === 'left'
|
|
3683
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: scrollbarStyles }), jsxRuntime.jsxs(ErrorBoundary, { children: [!isOpen && showFloatingButton && (jsxRuntime.jsx(FloatingChatButton, { onClick: handleOpen, theme: theme, icon: floatingButtonIcon, position: floatingButtonPosition, size: floatingButtonSize, className: floatingButtonClassName, zIndex: overlayZIndex })), shouldRenderPanel && (jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50", style: overlayZIndex !== undefined ? { zIndex: overlayZIndex } : undefined, children: [jsxRuntime.jsx("div", { className: `absolute inset-0 bg-black/20 transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`, onClick: handleClose }), jsxRuntime.jsx("div", { className: `fixed z-[60] top-6 transition-all duration-300 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-75'}`, style: { ...(modalPosition === 'left'
|
|
3676
3684
|
? { left: showHistory ? 'min(90vw, 700px)' : 'min(80vw, 550px)', marginLeft: '16px' }
|
|
3677
3685
|
: { right: showHistory ? 'min(90vw, 700px)' : 'min(80vw, 550px)', marginRight: '16px' }) }, children: jsxRuntime.jsx("button", { onClick: handleClose, className: "w-10 h-10 rounded-full shadow-lg transition-all duration-200 flex items-center justify-center text-xl font-bold hover:scale-110", style: {
|
|
3678
3686
|
backgroundColor: finalTheme.colors.surface.elevated,
|
|
@@ -3708,7 +3716,7 @@ headerTitle = 'AI Logistics & Customs Expert', headerSubtitle = 'Ready to assist
|
|
|
3708
3716
|
}, ref: containerRef, onClick: (e) => e.stopPropagation(), children: [jsxRuntime.jsx("div", { className: `flex items-center justify-between px-6 py-4 border-b min-h-[72px] transition-all duration-500 delay-100 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-2'}`, style: {
|
|
3709
3717
|
borderColor: finalTheme.colors.border.primary,
|
|
3710
3718
|
backgroundColor: finalTheme.colors.background.primary,
|
|
3711
|
-
}, children: jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [jsxRuntime.jsx("span", { className: `inline-flex items-center justify-center rounded-full w-8 h-8 transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100 rotate-0' : 'opacity-0 scale-75 rotate-12'}`, style: { backgroundColor: finalTheme.colors.primary[600] }, children: typeof headerIcon === 'string' ? (jsxRuntime.jsx(Icon, { name: headerIcon, size: "sm", style: { color: finalTheme.colors.text.primary } })) : (headerIcon || (jsxRuntime.jsx(Icon, { name: "command", size: "sm", style: { color: finalTheme.colors.text.primary } }))) }), jsxRuntime.jsxs("div", { className: `transition-all duration-500 delay-300 ${isOpen ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-2'}`, children: [jsxRuntime.jsx("div", { className: "font-bold text-lg leading-tight", style: { color: finalTheme.colors.text.primary }, children: headerTitle }), jsxRuntime.jsx("div", { className: "text-xs", style: { color: finalTheme.colors.text.tertiary }, children: headerSubtitle })] })] }) }), jsxRuntime.jsxs("div", { className: `flex flex-1 min-h-0 h-full transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95'}`, children: [showHistory && (jsxRuntime.jsx(ChatHistorySidebar, { chats: chats || {}, currentChatId: currentChatId || 'default', switchChat:
|
|
3719
|
+
}, children: jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [jsxRuntime.jsx("span", { className: `inline-flex items-center justify-center rounded-full w-8 h-8 transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100 rotate-0' : 'opacity-0 scale-75 rotate-12'}`, style: { backgroundColor: finalTheme.colors.primary[600] }, children: typeof headerIcon === 'string' ? (jsxRuntime.jsx(Icon, { name: headerIcon, size: "sm", style: { color: finalTheme.colors.text.primary } })) : (headerIcon || (jsxRuntime.jsx(Icon, { name: "command", size: "sm", style: { color: finalTheme.colors.text.primary } }))) }), jsxRuntime.jsxs("div", { className: `transition-all duration-500 delay-300 ${isOpen ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-2'}`, children: [jsxRuntime.jsx("div", { className: "font-bold text-lg leading-tight", style: { color: finalTheme.colors.text.primary }, children: headerTitle }), jsxRuntime.jsx("div", { className: "text-xs", style: { color: finalTheme.colors.text.tertiary }, children: headerSubtitle })] })] }) }), jsxRuntime.jsxs("div", { className: `flex flex-1 min-h-0 h-full transition-all duration-500 delay-200 ${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95'}`, children: [showHistory && (jsxRuntime.jsx(ChatHistorySidebar, { chats: chats || {}, currentChatId: currentChatId || 'default', switchChat: handleHistorySwitchChat, newChat: handleHistoryNewChat, theme: finalTheme, isOpen: isOpen })), jsxRuntime.jsx("div", { className: "flex-1 flex flex-col min-h-0 h-full", children: jsxRuntime.jsx("div", { className: `flex-1 px-6 py-4 min-h-0 transition-all duration-500 delay-300 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`, style: {
|
|
3712
3720
|
backgroundColor: finalTheme.colors.background.primary,
|
|
3713
3721
|
}, children: jsxRuntime.jsx(Conversation, { onSuggestionClick: handleSuggestionClick, theme: finalTheme, showProfileBubbles: showProfileBubbles, autoScroll: true, showInput: true,
|
|
3714
3722
|
// AI functionality props
|
|
@@ -3781,7 +3789,7 @@ function ChatHistorySidebar({ chats, currentChatId, switchChat, newChat, theme,
|
|
|
3781
3789
|
* @param position - Position of the floating button
|
|
3782
3790
|
* @param size - Size of the floating button
|
|
3783
3791
|
*/
|
|
3784
|
-
function FloatingChatButton({ onClick, theme = defaultTheme, icon, className = '', position = 'bottom-right', size = 'md', }) {
|
|
3792
|
+
function FloatingChatButton({ onClick, theme = defaultTheme, icon, className = '', position = 'bottom-right', size = 'md', zIndex: floatingZIndex, }) {
|
|
3785
3793
|
// Note: Theme is now handled via Tailwind classes directly
|
|
3786
3794
|
// Position classes
|
|
3787
3795
|
const positionClasses = {
|
|
@@ -3801,7 +3809,7 @@ function FloatingChatButton({ onClick, theme = defaultTheme, icon, className = '
|
|
|
3801
3809
|
md: { width: 24, height: 24 },
|
|
3802
3810
|
lg: { width: 28, height: 28 },
|
|
3803
3811
|
};
|
|
3804
|
-
return (jsxRuntime.jsx("div", { className: `fixed z-50 ${positionClasses[position]} ${className}`, children: jsxRuntime.jsx("button", { onClick: onClick, className: `
|
|
3812
|
+
return (jsxRuntime.jsx("div", { className: `fixed z-50 ${positionClasses[position]} ${className}`, style: floatingZIndex !== undefined ? { zIndex: floatingZIndex } : undefined, children: jsxRuntime.jsx("button", { onClick: onClick, className: `
|
|
3805
3813
|
${sizeClasses[size]}
|
|
3806
3814
|
bg-blue-600 hover:bg-blue-700
|
|
3807
3815
|
text-white rounded-full shadow-lg hover:shadow-xl
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type IconName } from './Icon';
|
|
3
|
-
import { CommandTheme, ChatMessage, ModelInfo, ChatTheme, ServerConfig } from '../types/types';
|
|
3
|
+
import { CommandTheme, CommandSuggestion, ChatMessage, ModelInfo, ChatTheme, ServerConfig } from '../types/types';
|
|
4
4
|
interface ChatPanelProps {
|
|
5
5
|
isOpen: boolean;
|
|
6
6
|
setIsOpen?: (isOpen: boolean) => void;
|
|
@@ -53,6 +53,13 @@ interface ChatPanelProps {
|
|
|
53
53
|
headerIcon?: IconName | React.ReactElement;
|
|
54
54
|
headerTitle?: string;
|
|
55
55
|
headerSubtitle?: string;
|
|
56
|
+
/** When set, replaces default behavior (setFormState + onNavigate + onClose) for command suggestions. */
|
|
57
|
+
onSuggestionSelect?: (s: CommandSuggestion) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Applied to the full-screen modal root so the chat stacks above app chrome that uses high z-index
|
|
60
|
+
* (e.g. MUI AppBar ~1100, custom sidebars ~999). Default Tailwind z-50 is too low for many shells.
|
|
61
|
+
*/
|
|
62
|
+
overlayZIndex?: number;
|
|
56
63
|
}
|
|
57
64
|
/**
|
|
58
65
|
* ChatPanel component
|
|
@@ -91,7 +98,7 @@ showHistory, // Default to hidden
|
|
|
91
98
|
showProfileBubbles, // Default to hidden
|
|
92
99
|
modalPosition, // Default to left position
|
|
93
100
|
serverConfig, models, defaultModel, showUsageStats, maxFileSize, features, showFloatingButton, floatingButtonIcon, floatingButtonPosition, floatingButtonSize, floatingButtonClassName, chats, setChats, currentChatId, setCurrentChatId, chatLevel, initialQuery, setInitialQuery, headerIcon, // Default to bot icon
|
|
94
|
-
headerTitle, headerSubtitle, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
101
|
+
headerTitle, headerSubtitle, onSuggestionSelect, overlayZIndex, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
95
102
|
/**
|
|
96
103
|
* ChatInput component
|
|
97
104
|
* @param query - The current query
|
|
@@ -118,6 +125,7 @@ type FloatingChatButtonProps = {
|
|
|
118
125
|
className?: string;
|
|
119
126
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
120
127
|
size?: 'sm' | 'md' | 'lg';
|
|
128
|
+
zIndex?: number;
|
|
121
129
|
};
|
|
122
130
|
/**
|
|
123
131
|
* FloatingChatButton component
|
|
@@ -128,5 +136,5 @@ type FloatingChatButtonProps = {
|
|
|
128
136
|
* @param position - Position of the floating button
|
|
129
137
|
* @param size - Size of the floating button
|
|
130
138
|
*/
|
|
131
|
-
export declare function FloatingChatButton({ onClick, theme, icon, className, position, size, }: FloatingChatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
139
|
+
export declare function FloatingChatButton({ onClick, theme, icon, className, position, size, zIndex: floatingZIndex, }: FloatingChatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
132
140
|
export {};
|
|
@@ -72,5 +72,5 @@ interface ConversationProps {
|
|
|
72
72
|
* @param autoScroll - Whether to auto-scroll to bottom on new messages
|
|
73
73
|
* @param maxHeight - Maximum height of the conversation container
|
|
74
74
|
*/
|
|
75
|
-
export declare function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles, className, autoScroll, maxHeight, userId, serverConfig, formData,
|
|
75
|
+
export declare function Conversation({ chat: externalChat, onSuggestionClick, theme, showProfileBubbles, className, autoScroll, maxHeight, userId, serverConfig, formData, chatLevel, chats: externalChats, setChats: externalSetChats, currentChatId: externalCurrentChatId, setCurrentChatId: externalSetCurrentChatId, query: externalQuery, setQuery: externalSetQuery, onSend: externalOnSend, isLoading: externalIsLoading, attachedFiles: externalAttachedFiles, setAttachedFiles: externalSetAttachedFiles, models, defaultModel, showUsageStats, maxFileSize, features, currentModelSelection: externalCurrentModelSelection, onModelSelectionChange: externalOnModelSelectionChange, showInput, initialQuery, setInitialQuery, showClearChat, onClearChat, additionalContext, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
76
76
|
export {};
|
package/dist/server/index.esm.js
CHANGED
|
@@ -6771,6 +6771,7 @@ const commandTrainingRules = {
|
|
|
6771
6771
|
components: {
|
|
6772
6772
|
patterns: {
|
|
6773
6773
|
// React component patterns
|
|
6774
|
+
// Has /g for matchAll in training.ts; do not use String.match(componentName) (groups break).
|
|
6774
6775
|
componentName: /(?:export\s+)?(?:function|const)\s+(\w+)(?:\s*<[^>]*>)?\s*[=(]/g,
|
|
6775
6776
|
props: /interface\s+(\w+)Props\s*{([^}]*)}/g,
|
|
6776
6777
|
state: /useState<([^>]*)>/g,
|
|
@@ -6795,7 +6796,8 @@ const commandTrainingRules = {
|
|
|
6795
6796
|
schemas: {
|
|
6796
6797
|
patterns: {
|
|
6797
6798
|
// Monastery schema patterns - updated for actual file format
|
|
6798
|
-
|
|
6799
|
+
// No /g: training.ts uses String.match(); with g, capture groups are omitted and modelName is always lost.
|
|
6800
|
+
modelName: /\/\/\s*(\w+)\s+model\s+for\s+monastery/,
|
|
6799
6801
|
fields: /fields:\s*{([^}]*)}/g,
|
|
6800
6802
|
fieldDefinitions: /(\w+):\s*{\s*(?:required:\s*(true|false),?\s*)?type:\s*['"]([^'"]+)['"](?:,\s*required:\s*(true|false))?(?:,\s*default:\s*[^,}]+)?/g,
|
|
6801
6803
|
indexes: /indexes:\s*\[([^\]]*)\]/g,
|
|
@@ -7643,6 +7645,7 @@ class CommandTrainingCollector {
|
|
|
7643
7645
|
if (content.formFields?.length > 0) {
|
|
7644
7646
|
const form = {
|
|
7645
7647
|
name: content.componentName || 'Unknown Form',
|
|
7648
|
+
sourceFile: path__default.relative(this.rootDir, component.filePath),
|
|
7646
7649
|
purpose: `Form component for data entry`,
|
|
7647
7650
|
fields: content.formFields.map((fieldName) => ({
|
|
7648
7651
|
name: fieldName,
|
|
@@ -7771,12 +7774,15 @@ class CommandTrainingCollector {
|
|
|
7771
7774
|
guessEntityFromComponent(componentName) {
|
|
7772
7775
|
if (!componentName)
|
|
7773
7776
|
return 'Unknown';
|
|
7774
|
-
//
|
|
7777
|
+
// Strip common create/edit wrappers, then UI suffixes (order matters)
|
|
7775
7778
|
const cleaned = componentName
|
|
7779
|
+
.replace(/^(?:Create|Edit|View|New|Batch)/i, '')
|
|
7776
7780
|
.replace(/Form$/, '')
|
|
7781
|
+
.replace(/Modal$/, '')
|
|
7782
|
+
.replace(/Drawer$/, '')
|
|
7783
|
+
.replace(/Dialog$/, '')
|
|
7777
7784
|
.replace(/Page$/, '')
|
|
7778
|
-
.replace(/Component$/, '')
|
|
7779
|
-
.replace(/Modal$/, '');
|
|
7785
|
+
.replace(/Component$/, '');
|
|
7780
7786
|
return cleaned || 'Unknown';
|
|
7781
7787
|
}
|
|
7782
7788
|
guessEntitiesFromComponent(content) {
|
|
@@ -7890,7 +7896,7 @@ class CommandTrainingCollector {
|
|
|
7890
7896
|
this.log(`ā Error expanding pattern "${pattern}":`, err.message);
|
|
7891
7897
|
}
|
|
7892
7898
|
}
|
|
7893
|
-
return expandedPaths;
|
|
7899
|
+
return [...new Set(expandedPaths)];
|
|
7894
7900
|
}
|
|
7895
7901
|
extractFileInfo(fieldName, content) {
|
|
7896
7902
|
switch (fieldName) {
|
|
@@ -7934,11 +7940,22 @@ class CommandTrainingCollector {
|
|
|
7934
7940
|
hooks: [],
|
|
7935
7941
|
eventHandlers: [],
|
|
7936
7942
|
};
|
|
7937
|
-
//
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7943
|
+
// Do not use String.match() on patterns.componentName ā it has /g, so match[1] is the *second*
|
|
7944
|
+
// full match, not capture group 1 (yields strings like `const CustomerForm =`).
|
|
7945
|
+
const declRe = /(?:export\s+)?(?:function|const)\s+(\w+)(?:\s*<[^>]*>)?\s*[=(]/g;
|
|
7946
|
+
const declNames = [];
|
|
7947
|
+
for (const m of content.matchAll(declRe)) {
|
|
7948
|
+
declNames.push(m[1]);
|
|
7949
|
+
}
|
|
7950
|
+
const isLikelyComponentSymbol = (n) => /^[A-Z]/.test(n) &&
|
|
7951
|
+
!/^use[A-Z]/.test(n) &&
|
|
7952
|
+
!/^Styled/i.test(n);
|
|
7953
|
+
const preferred = [...declNames].reverse().find((n) => isLikelyComponentSymbol(n) &&
|
|
7954
|
+
(/(?:Form|Modal|Drawer|Dialog)$/.test(n) ||
|
|
7955
|
+
/^FormStep/i.test(n) ||
|
|
7956
|
+
(n.includes('Form') && n.length > 4)));
|
|
7957
|
+
const fallback = [...declNames].reverse().find(isLikelyComponentSymbol);
|
|
7958
|
+
result.componentName = preferred ?? fallback ?? null;
|
|
7942
7959
|
// Extract props interfaces
|
|
7943
7960
|
const propsMatches = content.matchAll(patterns.props);
|
|
7944
7961
|
for (const match of propsMatches) {
|
|
@@ -7973,6 +7990,7 @@ class CommandTrainingCollector {
|
|
|
7973
7990
|
for (const match of fieldMatches) {
|
|
7974
7991
|
result.formFields.push(match[1]);
|
|
7975
7992
|
}
|
|
7993
|
+
result.formFields = [...new Set(result.formFields)];
|
|
7976
7994
|
// Extract imports
|
|
7977
7995
|
const importMatches = content.matchAll(patterns.imports);
|
|
7978
7996
|
for (const match of importMatches) {
|
|
@@ -15729,6 +15747,7 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
15729
15747
|
};
|
|
15730
15748
|
this.log(` ā
Generated schema with ${Object.keys(properties).length} properties`);
|
|
15731
15749
|
}
|
|
15750
|
+
this.mergeFormUiFieldsIntoEntitySchemas(trainingData, schemas);
|
|
15732
15751
|
// If no entities found, try to extract from components
|
|
15733
15752
|
if (Object.keys(schemas).length === 0) {
|
|
15734
15753
|
this.log(' ā ļø No entities found in training data, extracting from components...');
|
|
@@ -15736,6 +15755,87 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
15736
15755
|
}
|
|
15737
15756
|
return schemas;
|
|
15738
15757
|
}
|
|
15758
|
+
/**
|
|
15759
|
+
* Merge `name="..."` fields collected from UI forms/modals into existing entity schemas
|
|
15760
|
+
* (server models stay canonical; this adds frontend surface hints).
|
|
15761
|
+
* Optional `customTrainingData.options.formEntityHints`: map form component name -> schema entity key.
|
|
15762
|
+
* Optional `formEntityPathPatterns`: `{ includes, entity }` ā first match on `form.sourceFile` wins
|
|
15763
|
+
* (disambiguates reused names like `FormStepDetails` in different feature folders).
|
|
15764
|
+
*/
|
|
15765
|
+
mergeFormUiFieldsIntoEntitySchemas(trainingData, schemas) {
|
|
15766
|
+
const forms = trainingData.customTrainingData?.structuredData?.components?.forms || [];
|
|
15767
|
+
if (!forms.length || !Object.keys(schemas).length)
|
|
15768
|
+
return;
|
|
15769
|
+
const hints = (trainingData.customTrainingData?.options?.formEntityHints || {});
|
|
15770
|
+
const pathPatterns = (trainingData.customTrainingData?.options?.formEntityPathPatterns ||
|
|
15771
|
+
[]);
|
|
15772
|
+
/** Short UI stems that don't match lowered model names */
|
|
15773
|
+
const STEM_ALIASES = {
|
|
15774
|
+
hs: 'hsreport',
|
|
15775
|
+
incident: 'incidentreport',
|
|
15776
|
+
};
|
|
15777
|
+
const schemaKeys = new Set(Object.keys(schemas));
|
|
15778
|
+
const resolveKey = (form) => {
|
|
15779
|
+
if (form.sourceFile) {
|
|
15780
|
+
for (const p of pathPatterns) {
|
|
15781
|
+
if (!p?.includes || !p?.entity)
|
|
15782
|
+
continue;
|
|
15783
|
+
if (form.sourceFile.includes(p.includes)) {
|
|
15784
|
+
const k = p.entity.toLowerCase();
|
|
15785
|
+
if (schemaKeys.has(k))
|
|
15786
|
+
return k;
|
|
15787
|
+
}
|
|
15788
|
+
}
|
|
15789
|
+
}
|
|
15790
|
+
const tryKey = (raw) => {
|
|
15791
|
+
if (!raw)
|
|
15792
|
+
return undefined;
|
|
15793
|
+
const mapped = hints[raw];
|
|
15794
|
+
const stem = raw
|
|
15795
|
+
.replace(/^(?:Create|Edit|View|New|Batch)/i, '')
|
|
15796
|
+
.replace(/(?:Form|Modal|Drawer|Dialog)$/i, '');
|
|
15797
|
+
const stemLc = stem.toLowerCase();
|
|
15798
|
+
const alias = STEM_ALIASES[stemLc];
|
|
15799
|
+
if (alias && schemaKeys.has(alias))
|
|
15800
|
+
return alias;
|
|
15801
|
+
const candidates = [
|
|
15802
|
+
(mapped || raw).toLowerCase(),
|
|
15803
|
+
stemLc,
|
|
15804
|
+
];
|
|
15805
|
+
for (const c of candidates) {
|
|
15806
|
+
if (c && schemaKeys.has(c))
|
|
15807
|
+
return c;
|
|
15808
|
+
}
|
|
15809
|
+
return undefined;
|
|
15810
|
+
};
|
|
15811
|
+
return tryKey(form.name) || tryKey(form.relatedEntity) || undefined;
|
|
15812
|
+
};
|
|
15813
|
+
let mergedForms = 0;
|
|
15814
|
+
let mergedFields = 0;
|
|
15815
|
+
for (const form of forms) {
|
|
15816
|
+
const entityKey = resolveKey(form);
|
|
15817
|
+
if (!entityKey)
|
|
15818
|
+
continue;
|
|
15819
|
+
const gen = schemas[entityKey];
|
|
15820
|
+
if (!gen?.schema?.properties)
|
|
15821
|
+
continue;
|
|
15822
|
+
const props = gen.schema.properties;
|
|
15823
|
+
for (const f of form.fields || []) {
|
|
15824
|
+
const n = f?.name;
|
|
15825
|
+
if (!n || typeof n !== 'string' || props[n])
|
|
15826
|
+
continue;
|
|
15827
|
+
props[n] = {
|
|
15828
|
+
type: 'string',
|
|
15829
|
+
description: `UI form field (from ${form.name}). Cross-check server model; may overlap sharing/base shapes.`,
|
|
15830
|
+
};
|
|
15831
|
+
mergedFields += 1;
|
|
15832
|
+
}
|
|
15833
|
+
mergedForms += 1;
|
|
15834
|
+
}
|
|
15835
|
+
if (mergedForms > 0) {
|
|
15836
|
+
this.log(`\nš Merged UI form fields into ${mergedForms} entity schema(s) (+${mergedFields} properties)`);
|
|
15837
|
+
}
|
|
15838
|
+
}
|
|
15739
15839
|
/**
|
|
15740
15840
|
* Extract schemas from component analysis if no explicit entities found
|
|
15741
15841
|
*/
|
|
@@ -16049,6 +16149,9 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
16049
16149
|
* Map training data field types to JSON Schema types
|
|
16050
16150
|
*/
|
|
16051
16151
|
mapFieldType(type) {
|
|
16152
|
+
if (type == null || typeof type !== 'string') {
|
|
16153
|
+
return 'string';
|
|
16154
|
+
}
|
|
16052
16155
|
const typeMap = {
|
|
16053
16156
|
string: 'string',
|
|
16054
16157
|
number: 'number',
|
package/dist/server/index.js
CHANGED
|
@@ -6791,6 +6791,7 @@ const commandTrainingRules = {
|
|
|
6791
6791
|
components: {
|
|
6792
6792
|
patterns: {
|
|
6793
6793
|
// React component patterns
|
|
6794
|
+
// Has /g for matchAll in training.ts; do not use String.match(componentName) (groups break).
|
|
6794
6795
|
componentName: /(?:export\s+)?(?:function|const)\s+(\w+)(?:\s*<[^>]*>)?\s*[=(]/g,
|
|
6795
6796
|
props: /interface\s+(\w+)Props\s*{([^}]*)}/g,
|
|
6796
6797
|
state: /useState<([^>]*)>/g,
|
|
@@ -6815,7 +6816,8 @@ const commandTrainingRules = {
|
|
|
6815
6816
|
schemas: {
|
|
6816
6817
|
patterns: {
|
|
6817
6818
|
// Monastery schema patterns - updated for actual file format
|
|
6818
|
-
|
|
6819
|
+
// No /g: training.ts uses String.match(); with g, capture groups are omitted and modelName is always lost.
|
|
6820
|
+
modelName: /\/\/\s*(\w+)\s+model\s+for\s+monastery/,
|
|
6819
6821
|
fields: /fields:\s*{([^}]*)}/g,
|
|
6820
6822
|
fieldDefinitions: /(\w+):\s*{\s*(?:required:\s*(true|false),?\s*)?type:\s*['"]([^'"]+)['"](?:,\s*required:\s*(true|false))?(?:,\s*default:\s*[^,}]+)?/g,
|
|
6821
6823
|
indexes: /indexes:\s*\[([^\]]*)\]/g,
|
|
@@ -7663,6 +7665,7 @@ class CommandTrainingCollector {
|
|
|
7663
7665
|
if (content.formFields?.length > 0) {
|
|
7664
7666
|
const form = {
|
|
7665
7667
|
name: content.componentName || 'Unknown Form',
|
|
7668
|
+
sourceFile: path$a.relative(this.rootDir, component.filePath),
|
|
7666
7669
|
purpose: `Form component for data entry`,
|
|
7667
7670
|
fields: content.formFields.map((fieldName) => ({
|
|
7668
7671
|
name: fieldName,
|
|
@@ -7791,12 +7794,15 @@ class CommandTrainingCollector {
|
|
|
7791
7794
|
guessEntityFromComponent(componentName) {
|
|
7792
7795
|
if (!componentName)
|
|
7793
7796
|
return 'Unknown';
|
|
7794
|
-
//
|
|
7797
|
+
// Strip common create/edit wrappers, then UI suffixes (order matters)
|
|
7795
7798
|
const cleaned = componentName
|
|
7799
|
+
.replace(/^(?:Create|Edit|View|New|Batch)/i, '')
|
|
7796
7800
|
.replace(/Form$/, '')
|
|
7801
|
+
.replace(/Modal$/, '')
|
|
7802
|
+
.replace(/Drawer$/, '')
|
|
7803
|
+
.replace(/Dialog$/, '')
|
|
7797
7804
|
.replace(/Page$/, '')
|
|
7798
|
-
.replace(/Component$/, '')
|
|
7799
|
-
.replace(/Modal$/, '');
|
|
7805
|
+
.replace(/Component$/, '');
|
|
7800
7806
|
return cleaned || 'Unknown';
|
|
7801
7807
|
}
|
|
7802
7808
|
guessEntitiesFromComponent(content) {
|
|
@@ -7910,7 +7916,7 @@ class CommandTrainingCollector {
|
|
|
7910
7916
|
this.log(`ā Error expanding pattern "${pattern}":`, err.message);
|
|
7911
7917
|
}
|
|
7912
7918
|
}
|
|
7913
|
-
return expandedPaths;
|
|
7919
|
+
return [...new Set(expandedPaths)];
|
|
7914
7920
|
}
|
|
7915
7921
|
extractFileInfo(fieldName, content) {
|
|
7916
7922
|
switch (fieldName) {
|
|
@@ -7954,11 +7960,22 @@ class CommandTrainingCollector {
|
|
|
7954
7960
|
hooks: [],
|
|
7955
7961
|
eventHandlers: [],
|
|
7956
7962
|
};
|
|
7957
|
-
//
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7963
|
+
// Do not use String.match() on patterns.componentName ā it has /g, so match[1] is the *second*
|
|
7964
|
+
// full match, not capture group 1 (yields strings like `const CustomerForm =`).
|
|
7965
|
+
const declRe = /(?:export\s+)?(?:function|const)\s+(\w+)(?:\s*<[^>]*>)?\s*[=(]/g;
|
|
7966
|
+
const declNames = [];
|
|
7967
|
+
for (const m of content.matchAll(declRe)) {
|
|
7968
|
+
declNames.push(m[1]);
|
|
7969
|
+
}
|
|
7970
|
+
const isLikelyComponentSymbol = (n) => /^[A-Z]/.test(n) &&
|
|
7971
|
+
!/^use[A-Z]/.test(n) &&
|
|
7972
|
+
!/^Styled/i.test(n);
|
|
7973
|
+
const preferred = [...declNames].reverse().find((n) => isLikelyComponentSymbol(n) &&
|
|
7974
|
+
(/(?:Form|Modal|Drawer|Dialog)$/.test(n) ||
|
|
7975
|
+
/^FormStep/i.test(n) ||
|
|
7976
|
+
(n.includes('Form') && n.length > 4)));
|
|
7977
|
+
const fallback = [...declNames].reverse().find(isLikelyComponentSymbol);
|
|
7978
|
+
result.componentName = preferred ?? fallback ?? null;
|
|
7962
7979
|
// Extract props interfaces
|
|
7963
7980
|
const propsMatches = content.matchAll(patterns.props);
|
|
7964
7981
|
for (const match of propsMatches) {
|
|
@@ -7993,6 +8010,7 @@ class CommandTrainingCollector {
|
|
|
7993
8010
|
for (const match of fieldMatches) {
|
|
7994
8011
|
result.formFields.push(match[1]);
|
|
7995
8012
|
}
|
|
8013
|
+
result.formFields = [...new Set(result.formFields)];
|
|
7996
8014
|
// Extract imports
|
|
7997
8015
|
const importMatches = content.matchAll(patterns.imports);
|
|
7998
8016
|
for (const match of importMatches) {
|
|
@@ -15749,6 +15767,7 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
15749
15767
|
};
|
|
15750
15768
|
this.log(` ā
Generated schema with ${Object.keys(properties).length} properties`);
|
|
15751
15769
|
}
|
|
15770
|
+
this.mergeFormUiFieldsIntoEntitySchemas(trainingData, schemas);
|
|
15752
15771
|
// If no entities found, try to extract from components
|
|
15753
15772
|
if (Object.keys(schemas).length === 0) {
|
|
15754
15773
|
this.log(' ā ļø No entities found in training data, extracting from components...');
|
|
@@ -15756,6 +15775,87 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
15756
15775
|
}
|
|
15757
15776
|
return schemas;
|
|
15758
15777
|
}
|
|
15778
|
+
/**
|
|
15779
|
+
* Merge `name="..."` fields collected from UI forms/modals into existing entity schemas
|
|
15780
|
+
* (server models stay canonical; this adds frontend surface hints).
|
|
15781
|
+
* Optional `customTrainingData.options.formEntityHints`: map form component name -> schema entity key.
|
|
15782
|
+
* Optional `formEntityPathPatterns`: `{ includes, entity }` ā first match on `form.sourceFile` wins
|
|
15783
|
+
* (disambiguates reused names like `FormStepDetails` in different feature folders).
|
|
15784
|
+
*/
|
|
15785
|
+
mergeFormUiFieldsIntoEntitySchemas(trainingData, schemas) {
|
|
15786
|
+
const forms = trainingData.customTrainingData?.structuredData?.components?.forms || [];
|
|
15787
|
+
if (!forms.length || !Object.keys(schemas).length)
|
|
15788
|
+
return;
|
|
15789
|
+
const hints = (trainingData.customTrainingData?.options?.formEntityHints || {});
|
|
15790
|
+
const pathPatterns = (trainingData.customTrainingData?.options?.formEntityPathPatterns ||
|
|
15791
|
+
[]);
|
|
15792
|
+
/** Short UI stems that don't match lowered model names */
|
|
15793
|
+
const STEM_ALIASES = {
|
|
15794
|
+
hs: 'hsreport',
|
|
15795
|
+
incident: 'incidentreport',
|
|
15796
|
+
};
|
|
15797
|
+
const schemaKeys = new Set(Object.keys(schemas));
|
|
15798
|
+
const resolveKey = (form) => {
|
|
15799
|
+
if (form.sourceFile) {
|
|
15800
|
+
for (const p of pathPatterns) {
|
|
15801
|
+
if (!p?.includes || !p?.entity)
|
|
15802
|
+
continue;
|
|
15803
|
+
if (form.sourceFile.includes(p.includes)) {
|
|
15804
|
+
const k = p.entity.toLowerCase();
|
|
15805
|
+
if (schemaKeys.has(k))
|
|
15806
|
+
return k;
|
|
15807
|
+
}
|
|
15808
|
+
}
|
|
15809
|
+
}
|
|
15810
|
+
const tryKey = (raw) => {
|
|
15811
|
+
if (!raw)
|
|
15812
|
+
return undefined;
|
|
15813
|
+
const mapped = hints[raw];
|
|
15814
|
+
const stem = raw
|
|
15815
|
+
.replace(/^(?:Create|Edit|View|New|Batch)/i, '')
|
|
15816
|
+
.replace(/(?:Form|Modal|Drawer|Dialog)$/i, '');
|
|
15817
|
+
const stemLc = stem.toLowerCase();
|
|
15818
|
+
const alias = STEM_ALIASES[stemLc];
|
|
15819
|
+
if (alias && schemaKeys.has(alias))
|
|
15820
|
+
return alias;
|
|
15821
|
+
const candidates = [
|
|
15822
|
+
(mapped || raw).toLowerCase(),
|
|
15823
|
+
stemLc,
|
|
15824
|
+
];
|
|
15825
|
+
for (const c of candidates) {
|
|
15826
|
+
if (c && schemaKeys.has(c))
|
|
15827
|
+
return c;
|
|
15828
|
+
}
|
|
15829
|
+
return undefined;
|
|
15830
|
+
};
|
|
15831
|
+
return tryKey(form.name) || tryKey(form.relatedEntity) || undefined;
|
|
15832
|
+
};
|
|
15833
|
+
let mergedForms = 0;
|
|
15834
|
+
let mergedFields = 0;
|
|
15835
|
+
for (const form of forms) {
|
|
15836
|
+
const entityKey = resolveKey(form);
|
|
15837
|
+
if (!entityKey)
|
|
15838
|
+
continue;
|
|
15839
|
+
const gen = schemas[entityKey];
|
|
15840
|
+
if (!gen?.schema?.properties)
|
|
15841
|
+
continue;
|
|
15842
|
+
const props = gen.schema.properties;
|
|
15843
|
+
for (const f of form.fields || []) {
|
|
15844
|
+
const n = f?.name;
|
|
15845
|
+
if (!n || typeof n !== 'string' || props[n])
|
|
15846
|
+
continue;
|
|
15847
|
+
props[n] = {
|
|
15848
|
+
type: 'string',
|
|
15849
|
+
description: `UI form field (from ${form.name}). Cross-check server model; may overlap sharing/base shapes.`,
|
|
15850
|
+
};
|
|
15851
|
+
mergedFields += 1;
|
|
15852
|
+
}
|
|
15853
|
+
mergedForms += 1;
|
|
15854
|
+
}
|
|
15855
|
+
if (mergedForms > 0) {
|
|
15856
|
+
this.log(`\nš Merged UI form fields into ${mergedForms} entity schema(s) (+${mergedFields} properties)`);
|
|
15857
|
+
}
|
|
15858
|
+
}
|
|
15759
15859
|
/**
|
|
15760
15860
|
* Extract schemas from component analysis if no explicit entities found
|
|
15761
15861
|
*/
|
|
@@ -16069,6 +16169,9 @@ class SchemaGenerator extends CommandTrainingCollector {
|
|
|
16069
16169
|
* Map training data field types to JSON Schema types
|
|
16070
16170
|
*/
|
|
16071
16171
|
mapFieldType(type) {
|
|
16172
|
+
if (type == null || typeof type !== 'string') {
|
|
16173
|
+
return 'string';
|
|
16174
|
+
}
|
|
16072
16175
|
const typeMap = {
|
|
16073
16176
|
string: 'string',
|
|
16074
16177
|
number: 'number',
|
|
@@ -55,6 +55,14 @@ export declare class SchemaGenerator extends CommandTrainingCollector {
|
|
|
55
55
|
* Generate schemas from collected training data
|
|
56
56
|
*/
|
|
57
57
|
private generateSchemas;
|
|
58
|
+
/**
|
|
59
|
+
* Merge `name="..."` fields collected from UI forms/modals into existing entity schemas
|
|
60
|
+
* (server models stay canonical; this adds frontend surface hints).
|
|
61
|
+
* Optional `customTrainingData.options.formEntityHints`: map form component name -> schema entity key.
|
|
62
|
+
* Optional `formEntityPathPatterns`: `{ includes, entity }` ā first match on `form.sourceFile` wins
|
|
63
|
+
* (disambiguates reused names like `FormStepDetails` in different feature folders).
|
|
64
|
+
*/
|
|
65
|
+
private mergeFormUiFieldsIntoEntitySchemas;
|
|
58
66
|
/**
|
|
59
67
|
* Extract schemas from component analysis if no explicit entities found
|
|
60
68
|
*/
|
|
@@ -55,6 +55,14 @@ export declare class SchemaGenerator extends CommandTrainingCollector {
|
|
|
55
55
|
* Generate schemas from collected training data
|
|
56
56
|
*/
|
|
57
57
|
private generateSchemas;
|
|
58
|
+
/**
|
|
59
|
+
* Merge `name="..."` fields collected from UI forms/modals into existing entity schemas
|
|
60
|
+
* (server models stay canonical; this adds frontend surface hints).
|
|
61
|
+
* Optional `customTrainingData.options.formEntityHints`: map form component name -> schema entity key.
|
|
62
|
+
* Optional `formEntityPathPatterns`: `{ includes, entity }` ā first match on `form.sourceFile` wins
|
|
63
|
+
* (disambiguates reused names like `FormStepDetails` in different feature folders).
|
|
64
|
+
*/
|
|
65
|
+
private mergeFormUiFieldsIntoEntitySchemas;
|
|
58
66
|
/**
|
|
59
67
|
* Extract schemas from component analysis if no explicit entities found
|
|
60
68
|
*/
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-2{bottom:.5rem}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-2{left:.5rem}.left-6{left:1.5rem}.left-\[0\.5px\]{left:.5px}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-4{right:1rem}.right-6{right:1.5rem}.right-\[-4px\]{right:-4px}.right-full{right:100%}.top-0{top:0}.top-1{top:.25rem}.top-2{top:.5rem}.top-4{top:1rem}.top-6{top:1.5rem}.z-10{z-index:10}.z-50{z-index:50}.z-\[10000\]{z-index:10000}.z-\[60\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-2{margin-left:.5rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.line-clamp-2{-webkit-box-orient:vertical;-webkit-line-clamp:2;display:-webkit-box;overflow:hidden}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.contents{display:contents}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.max-h-32{max-height:8rem}.max-h-64{max-height:16rem}.max-h-80{max-height:20rem}.max-h-full{max-height:100%}.min-h-0{min-height:0}.min-h-\[3\.5rem\]{min-height:3.5rem}.min-h-\[72px\]{min-height:72px}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-\[150px\]{width:150px}.w-full{width:100%}.min-w-0{min-width:0}.min-w-80{min-width:20rem}.max-w-32{max-width:8rem}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-grow-0{flex-grow:0}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x:-100%}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-2{--tw-translate-x:0.5rem}.translate-x-4{--tw-translate-x:1rem}.translate-x-4,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.translate-y-0{--tw-translate-y:0px}.translate-y-0,.translate-y-2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-2{--tw-translate-y:0.5rem}.translate-y-4{--tw-translate-y:1rem}.translate-y-4,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.rotate-0{--tw-rotate:0deg}.rotate-0,.rotate-12{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-12{--tw-rotate:12deg}.rotate-180{--tw-rotate:180deg}.rotate-180,.rotate-45{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate:45deg}.rotate-90{--tw-rotate:90deg}.rotate-90,.scale-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-75{--tw-scale-x:.75;--tw-scale-y:.75}.scale-75,.scale-95{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-help{cursor:help}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.resize-none{resize:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.125rem*var(--tw-space-y-reverse));margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-l-lg{border-bottom-left-radius:.5rem;border-top-left-radius:.5rem}.rounded-r-lg{border-bottom-right-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity,1))}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}.border-current{border-color:currentColor}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-orange-200{--tw-border-opacity:1;border-color:rgb(254 215 170/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity,1))}.border-opacity-50{--tw-border-opacity:0.5}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-black\/20{background-color:rgba(0,0,0,.2)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/70{background-color:rgba(0,0,0,.7)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity,1))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity,1))}.bg-opacity-20{--tw-bg-opacity:0.2}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-8{padding:2rem}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pr-4{padding-right:1rem}.pr-6{padding-right:1.5rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.text-amber-500{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity,1))}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity,1))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.text-orange-800{--tw-text-opacity:1;color:rgb(154 52 18/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/70{color:hsla(0,0%,100%,.7)}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity,1))}.line-through{text-decoration-line:line-through}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.delay-100{transition-delay:.1s}.delay-200{transition-delay:.2s}.delay-300{transition-delay:.3s}.delay-500{transition-delay:.5s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.focus-within\:shadow-sm:focus-within{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:bg-amber-200:hover{--tw-bg-opacity:1;background-color:rgb(253 230 138/var(--tw-bg-opacity,1))}.hover\:bg-black\/70:hover{background-color:rgba(0,0,0,.7)}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-orange-200:hover{--tw-bg-opacity:1;background-color:rgb(254 215 170/var(--tw-bg-opacity,1))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-white\/20:hover{background-color:hsla(0,0%,100%,.2)}.hover\:text-amber-700:hover{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.hover\:text-orange-700:hover{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-4:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity,1))}.active\:scale-95:active{--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:w-\[80vw\]{width:80vw}.sm\:w-\[90vw\]{width:90vw}}@media (min-width:768px){.md\:w-\[550px\]{width:550px}.md\:w-\[700px\]{width:700px}}
|
|
2
|
+
/*# sourceMappingURL=styles.css.map */
|