@makolabs/ripple 0.4.1-0 → 0.5.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/README.md +165 -205
- package/dist/adapters/ai/OpenAIAdapter.d.ts +115 -0
- package/dist/adapters/ai/OpenAIAdapter.js +568 -0
- package/dist/adapters/ai/index.d.ts +3 -0
- package/dist/adapters/ai/index.js +3 -0
- package/dist/adapters/ai/types.d.ts +108 -0
- package/dist/adapters/ai/types.js +31 -0
- package/dist/adapters/storage/BaseAdapter.js +31 -31
- package/dist/ai/AIChatInterface.svelte +435 -0
- package/dist/ai/AIChatInterface.svelte.d.ts +18 -0
- package/dist/ai/ChatInput.svelte +211 -0
- package/dist/ai/ChatInput.svelte.d.ts +18 -0
- package/dist/ai/CodeRenderer.svelte +174 -0
- package/dist/ai/CodeRenderer.svelte.d.ts +8 -0
- package/dist/ai/ComposeDropdown.svelte +171 -0
- package/dist/ai/ComposeDropdown.svelte.d.ts +9 -0
- package/dist/ai/MermaidRenderer.svelte +89 -0
- package/dist/ai/MermaidRenderer.svelte.d.ts +7 -0
- package/dist/ai/MessageBox.svelte +403 -0
- package/dist/ai/MessageBox.svelte.d.ts +12 -0
- package/dist/ai/ThinkingDisplay.svelte +275 -0
- package/dist/ai/ThinkingDisplay.svelte.d.ts +9 -0
- package/dist/ai/ai-chat-interface.d.ts +161 -0
- package/dist/ai/ai-chat-interface.js +63 -0
- package/dist/ai/content-detector.d.ts +41 -0
- package/dist/ai/content-detector.js +153 -0
- package/dist/config/ai.d.ts +13 -0
- package/dist/config/ai.js +43 -0
- package/dist/elements/accordion/accordion.js +1 -1
- package/dist/elements/badge/Badge.svelte +14 -3
- package/dist/elements/dropdown/Dropdown.svelte +2 -2
- package/dist/elements/dropdown/Select.svelte +1 -1
- package/dist/elements/progress/Progress.svelte +7 -10
- package/dist/file-browser/FileBrowser.svelte +1 -1
- package/dist/forms/DateRange.svelte +18 -16
- package/dist/forms/NumberInput.svelte +1 -1
- package/dist/forms/RadioInputs.svelte +1 -1
- package/dist/forms/RadioPill.svelte +1 -1
- package/dist/forms/Tags.svelte +2 -2
- package/dist/helper/date.d.ts +1 -0
- package/dist/helper/date.js +6 -0
- package/dist/index.d.ts +65 -1
- package/dist/index.js +11 -0
- package/dist/layout/activity-list/ActivityList.svelte +94 -0
- package/dist/layout/activity-list/ActivityList.svelte.d.ts +4 -0
- package/dist/layout/activity-list/activity-list.d.ts +152 -0
- package/dist/layout/activity-list/activity-list.js +59 -0
- package/dist/layout/card/Card.svelte +1 -5
- package/dist/layout/card/metric-card.d.ts +18 -18
- package/dist/layout/table/Cells.svelte +1 -7
- package/dist/layout/table/Cells.svelte.d.ts +1 -1
- package/dist/modal/Modal.svelte +4 -2
- package/dist/modal/Modal.svelte.d.ts +1 -1
- package/dist/modal/modal.d.ts +19 -18
- package/dist/modal/modal.js +7 -6
- package/dist/sonner/sonner.svelte +1 -7
- package/dist/types/markdown.d.ts +14 -0
- package/dist/utils/Portal.svelte +1 -1
- package/package.json +128 -121
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export declare const aiChatInterface: import("tailwind-variants").TVReturnType<{
|
|
2
|
+
color: {
|
|
3
|
+
default: {
|
|
4
|
+
userMessage: string;
|
|
5
|
+
sendButton: string;
|
|
6
|
+
background: string;
|
|
7
|
+
};
|
|
8
|
+
primary: {
|
|
9
|
+
userMessage: string;
|
|
10
|
+
sendButton: string;
|
|
11
|
+
background: string;
|
|
12
|
+
};
|
|
13
|
+
secondary: {
|
|
14
|
+
userMessage: string;
|
|
15
|
+
sendButton: string;
|
|
16
|
+
background: string;
|
|
17
|
+
};
|
|
18
|
+
success: {
|
|
19
|
+
userMessage: string;
|
|
20
|
+
sendButton: string;
|
|
21
|
+
background: string;
|
|
22
|
+
};
|
|
23
|
+
warning: {
|
|
24
|
+
userMessage: string;
|
|
25
|
+
sendButton: string;
|
|
26
|
+
background: string;
|
|
27
|
+
};
|
|
28
|
+
danger: {
|
|
29
|
+
userMessage: string;
|
|
30
|
+
sendButton: string;
|
|
31
|
+
background: string;
|
|
32
|
+
};
|
|
33
|
+
info: {
|
|
34
|
+
userMessage: string;
|
|
35
|
+
sendButton: string;
|
|
36
|
+
background: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
loading: {
|
|
40
|
+
true: {
|
|
41
|
+
sendButton: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
disabled: {
|
|
45
|
+
true: {
|
|
46
|
+
sendButton: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}, {
|
|
50
|
+
userMessage: string;
|
|
51
|
+
aiMessage: string;
|
|
52
|
+
sendButton: string;
|
|
53
|
+
background: string;
|
|
54
|
+
}, undefined, {
|
|
55
|
+
color: {
|
|
56
|
+
default: {
|
|
57
|
+
userMessage: string;
|
|
58
|
+
sendButton: string;
|
|
59
|
+
background: string;
|
|
60
|
+
};
|
|
61
|
+
primary: {
|
|
62
|
+
userMessage: string;
|
|
63
|
+
sendButton: string;
|
|
64
|
+
background: string;
|
|
65
|
+
};
|
|
66
|
+
secondary: {
|
|
67
|
+
userMessage: string;
|
|
68
|
+
sendButton: string;
|
|
69
|
+
background: string;
|
|
70
|
+
};
|
|
71
|
+
success: {
|
|
72
|
+
userMessage: string;
|
|
73
|
+
sendButton: string;
|
|
74
|
+
background: string;
|
|
75
|
+
};
|
|
76
|
+
warning: {
|
|
77
|
+
userMessage: string;
|
|
78
|
+
sendButton: string;
|
|
79
|
+
background: string;
|
|
80
|
+
};
|
|
81
|
+
danger: {
|
|
82
|
+
userMessage: string;
|
|
83
|
+
sendButton: string;
|
|
84
|
+
background: string;
|
|
85
|
+
};
|
|
86
|
+
info: {
|
|
87
|
+
userMessage: string;
|
|
88
|
+
sendButton: string;
|
|
89
|
+
background: string;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
loading: {
|
|
93
|
+
true: {
|
|
94
|
+
sendButton: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
disabled: {
|
|
98
|
+
true: {
|
|
99
|
+
sendButton: string;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
}, {
|
|
103
|
+
userMessage: string;
|
|
104
|
+
aiMessage: string;
|
|
105
|
+
sendButton: string;
|
|
106
|
+
background: string;
|
|
107
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
108
|
+
color: {
|
|
109
|
+
default: {
|
|
110
|
+
userMessage: string;
|
|
111
|
+
sendButton: string;
|
|
112
|
+
background: string;
|
|
113
|
+
};
|
|
114
|
+
primary: {
|
|
115
|
+
userMessage: string;
|
|
116
|
+
sendButton: string;
|
|
117
|
+
background: string;
|
|
118
|
+
};
|
|
119
|
+
secondary: {
|
|
120
|
+
userMessage: string;
|
|
121
|
+
sendButton: string;
|
|
122
|
+
background: string;
|
|
123
|
+
};
|
|
124
|
+
success: {
|
|
125
|
+
userMessage: string;
|
|
126
|
+
sendButton: string;
|
|
127
|
+
background: string;
|
|
128
|
+
};
|
|
129
|
+
warning: {
|
|
130
|
+
userMessage: string;
|
|
131
|
+
sendButton: string;
|
|
132
|
+
background: string;
|
|
133
|
+
};
|
|
134
|
+
danger: {
|
|
135
|
+
userMessage: string;
|
|
136
|
+
sendButton: string;
|
|
137
|
+
background: string;
|
|
138
|
+
};
|
|
139
|
+
info: {
|
|
140
|
+
userMessage: string;
|
|
141
|
+
sendButton: string;
|
|
142
|
+
background: string;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
loading: {
|
|
146
|
+
true: {
|
|
147
|
+
sendButton: string;
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
disabled: {
|
|
151
|
+
true: {
|
|
152
|
+
sendButton: string;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
}, {
|
|
156
|
+
userMessage: string;
|
|
157
|
+
aiMessage: string;
|
|
158
|
+
sendButton: string;
|
|
159
|
+
background: string;
|
|
160
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
161
|
+
export type AIChatInterfaceVariants = Parameters<typeof aiChatInterface>[0];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const aiChatInterface = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
userMessage: 'max-w-[70%] ml-auto px-4 py-3 rounded-3xl text-sm leading-relaxed shadow-lg',
|
|
5
|
+
aiMessage: 'max-w-[80%] mr-auto px-4 py-3 rounded-3xl text-sm leading-relaxed bg-white border border-default-200 text-default-900 shadow-sm',
|
|
6
|
+
sendButton: 'flex-shrink-0 w-11 h-11 rounded-full flex items-center justify-center transition-all duration-200 shadow-sm hover:shadow-md',
|
|
7
|
+
background: ''
|
|
8
|
+
},
|
|
9
|
+
variants: {
|
|
10
|
+
color: {
|
|
11
|
+
default: {
|
|
12
|
+
userMessage: 'bg-default-900 text-white',
|
|
13
|
+
sendButton: 'bg-default-900 hover:bg-default-800 text-white disabled:bg-default-300 disabled:text-default-500',
|
|
14
|
+
background: 'bg-gray-50'
|
|
15
|
+
},
|
|
16
|
+
primary: {
|
|
17
|
+
userMessage: 'bg-primary-600 text-white',
|
|
18
|
+
sendButton: 'bg-primary-600 hover:bg-primary-700 text-white disabled:bg-primary-300 disabled:text-primary-100',
|
|
19
|
+
background: 'bg-blue-50'
|
|
20
|
+
},
|
|
21
|
+
secondary: {
|
|
22
|
+
userMessage: 'bg-secondary-600 text-white',
|
|
23
|
+
sendButton: 'bg-secondary-600 hover:bg-secondary-700 text-white disabled:bg-secondary-300 disabled:text-secondary-100',
|
|
24
|
+
background: 'bg-purple-50'
|
|
25
|
+
},
|
|
26
|
+
success: {
|
|
27
|
+
userMessage: 'bg-success-600 text-white',
|
|
28
|
+
sendButton: 'bg-success-600 hover:bg-success-700 text-white disabled:bg-success-300 disabled:text-success-100',
|
|
29
|
+
background: 'bg-green-50'
|
|
30
|
+
},
|
|
31
|
+
warning: {
|
|
32
|
+
userMessage: 'bg-warning-600 text-white',
|
|
33
|
+
sendButton: 'bg-warning-600 hover:bg-warning-700 text-white disabled:bg-warning-300 disabled:text-warning-100',
|
|
34
|
+
background: 'bg-yellow-50'
|
|
35
|
+
},
|
|
36
|
+
danger: {
|
|
37
|
+
userMessage: 'bg-danger-600 text-white',
|
|
38
|
+
sendButton: 'bg-danger-600 hover:bg-danger-700 text-white disabled:bg-danger-300 disabled:text-danger-100',
|
|
39
|
+
background: 'bg-red-50'
|
|
40
|
+
},
|
|
41
|
+
info: {
|
|
42
|
+
userMessage: 'bg-info-600 text-white',
|
|
43
|
+
sendButton: 'bg-info-600 hover:bg-info-700 text-white disabled:bg-info-300 disabled:text-info-100',
|
|
44
|
+
background: 'bg-cyan-50'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
loading: {
|
|
48
|
+
true: {
|
|
49
|
+
sendButton: 'cursor-not-allowed'
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
disabled: {
|
|
53
|
+
true: {
|
|
54
|
+
sendButton: 'cursor-not-allowed opacity-50'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
defaultVariants: {
|
|
59
|
+
color: 'primary',
|
|
60
|
+
loading: false,
|
|
61
|
+
disabled: false
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content detection utilities for AI chat messages
|
|
3
|
+
*/
|
|
4
|
+
export interface CodeBlock {
|
|
5
|
+
language: string;
|
|
6
|
+
code: string;
|
|
7
|
+
startIndex: number;
|
|
8
|
+
endIndex: number;
|
|
9
|
+
}
|
|
10
|
+
export interface MermaidBlock {
|
|
11
|
+
diagram: string;
|
|
12
|
+
startIndex: number;
|
|
13
|
+
endIndex: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ContentSegment {
|
|
16
|
+
type: 'text' | 'code' | 'mermaid';
|
|
17
|
+
content: string;
|
|
18
|
+
language?: string;
|
|
19
|
+
startIndex: number;
|
|
20
|
+
endIndex: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Detects if content contains Mermaid diagram syntax
|
|
24
|
+
*/
|
|
25
|
+
export declare function detectMermaidDiagrams(content: string): MermaidBlock[];
|
|
26
|
+
/**
|
|
27
|
+
* Detects code blocks in content
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectCodeBlocks(content: string): CodeBlock[];
|
|
30
|
+
/**
|
|
31
|
+
* Parses content into segments (text, code, mermaid)
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseContentSegments(content: string): ContentSegment[];
|
|
34
|
+
/**
|
|
35
|
+
* Simple check if content contains any Mermaid diagrams
|
|
36
|
+
*/
|
|
37
|
+
export declare function hasMermaidDiagrams(content: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Simple check if content contains any code blocks
|
|
40
|
+
*/
|
|
41
|
+
export declare function hasCodeBlocks(content: string): boolean;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content detection utilities for AI chat messages
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Detects if content contains Mermaid diagram syntax
|
|
6
|
+
*/
|
|
7
|
+
export function detectMermaidDiagrams(content) {
|
|
8
|
+
const mermaidBlocks = [];
|
|
9
|
+
// Pattern to match mermaid code blocks
|
|
10
|
+
// Handles various Mermaid diagram types and whitespace scenarios
|
|
11
|
+
const mermaidRegex = /```(?:mermaid|graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|stateDiagram-v2|erDiagram|journey|gantt|pie|gitgraph|mindmap)\s*\r?\n([\s\S]*?)\r?\n```/gi;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = mermaidRegex.exec(content)) !== null) {
|
|
14
|
+
mermaidBlocks.push({
|
|
15
|
+
diagram: match[1].trim(),
|
|
16
|
+
startIndex: match.index,
|
|
17
|
+
endIndex: match.index + match[0].length
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return mermaidBlocks;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Detects code blocks in content
|
|
24
|
+
*/
|
|
25
|
+
export function detectCodeBlocks(content) {
|
|
26
|
+
const codeBlocks = [];
|
|
27
|
+
// Pattern to match code blocks with language specification
|
|
28
|
+
// Handles: ```language, ```, and various whitespace scenarios
|
|
29
|
+
const codeRegex = /```(\w+)?\s*\r?\n([\s\S]*?)\r?\n```/gi;
|
|
30
|
+
let match;
|
|
31
|
+
while ((match = codeRegex.exec(content)) !== null) {
|
|
32
|
+
const language = match[1] || 'text';
|
|
33
|
+
const code = match[2].trim();
|
|
34
|
+
// Skip empty code blocks
|
|
35
|
+
if (!code) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Skip if it's a mermaid diagram (handled separately)
|
|
39
|
+
if (!isMermaidLanguage(language)) {
|
|
40
|
+
codeBlocks.push({
|
|
41
|
+
language,
|
|
42
|
+
code,
|
|
43
|
+
startIndex: match.index,
|
|
44
|
+
endIndex: match.index + match[0].length
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return codeBlocks;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Checks if a language identifier is for Mermaid diagrams
|
|
52
|
+
*/
|
|
53
|
+
function isMermaidLanguage(language) {
|
|
54
|
+
const mermaidLanguages = [
|
|
55
|
+
'mermaid',
|
|
56
|
+
'graph',
|
|
57
|
+
'flowchart',
|
|
58
|
+
'sequenceDiagram',
|
|
59
|
+
'classDiagram',
|
|
60
|
+
'stateDiagram',
|
|
61
|
+
'stateDiagram-v2',
|
|
62
|
+
'erDiagram',
|
|
63
|
+
'journey',
|
|
64
|
+
'gantt',
|
|
65
|
+
'pie',
|
|
66
|
+
'gitgraph',
|
|
67
|
+
'mindmap'
|
|
68
|
+
];
|
|
69
|
+
return mermaidLanguages.includes(language.toLowerCase());
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parses content into segments (text, code, mermaid)
|
|
73
|
+
*/
|
|
74
|
+
export function parseContentSegments(content) {
|
|
75
|
+
console.log('parseContentSegments', content);
|
|
76
|
+
const segments = [];
|
|
77
|
+
const mermaidBlocks = detectMermaidDiagrams(content);
|
|
78
|
+
const codeBlocks = detectCodeBlocks(content);
|
|
79
|
+
// Combine all blocks and sort by position
|
|
80
|
+
const allBlocks = [
|
|
81
|
+
...mermaidBlocks.map(block => ({ ...block, type: 'mermaid' })),
|
|
82
|
+
...codeBlocks.map(block => ({ ...block, type: 'code' }))
|
|
83
|
+
].sort((a, b) => a.startIndex - b.startIndex);
|
|
84
|
+
let currentIndex = 0;
|
|
85
|
+
for (const block of allBlocks) {
|
|
86
|
+
// Add text segment before this block
|
|
87
|
+
if (currentIndex < block.startIndex) {
|
|
88
|
+
const textContent = content.slice(currentIndex, block.startIndex).trim();
|
|
89
|
+
if (textContent) {
|
|
90
|
+
segments.push({
|
|
91
|
+
type: 'text',
|
|
92
|
+
content: textContent,
|
|
93
|
+
startIndex: currentIndex,
|
|
94
|
+
endIndex: block.startIndex
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Add the block segment
|
|
99
|
+
if (block.type === 'mermaid') {
|
|
100
|
+
segments.push({
|
|
101
|
+
type: 'mermaid',
|
|
102
|
+
content: block.diagram,
|
|
103
|
+
startIndex: block.startIndex,
|
|
104
|
+
endIndex: block.endIndex
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const codeBlock = block;
|
|
109
|
+
segments.push({
|
|
110
|
+
type: 'code',
|
|
111
|
+
content: codeBlock.code,
|
|
112
|
+
language: codeBlock.language,
|
|
113
|
+
startIndex: block.startIndex,
|
|
114
|
+
endIndex: block.endIndex
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
currentIndex = block.endIndex;
|
|
118
|
+
}
|
|
119
|
+
// Add remaining text after last block
|
|
120
|
+
if (currentIndex < content.length) {
|
|
121
|
+
const textContent = content.slice(currentIndex).trim();
|
|
122
|
+
if (textContent) {
|
|
123
|
+
segments.push({
|
|
124
|
+
type: 'text',
|
|
125
|
+
content: textContent,
|
|
126
|
+
startIndex: currentIndex,
|
|
127
|
+
endIndex: content.length
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// If no blocks found, return entire content as text
|
|
132
|
+
if (segments.length === 0) {
|
|
133
|
+
segments.push({
|
|
134
|
+
type: 'text',
|
|
135
|
+
content: content,
|
|
136
|
+
startIndex: 0,
|
|
137
|
+
endIndex: content.length
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return segments;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Simple check if content contains any Mermaid diagrams
|
|
144
|
+
*/
|
|
145
|
+
export function hasMermaidDiagrams(content) {
|
|
146
|
+
return detectMermaidDiagrams(content).length > 0;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Simple check if content contains any code blocks
|
|
150
|
+
*/
|
|
151
|
+
export function hasCodeBlocks(content) {
|
|
152
|
+
return detectCodeBlocks(content).length > 0;
|
|
153
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface AIConfig {
|
|
2
|
+
provider: 'openai' | 'anthropic' | 'ollama';
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getAIConfig(): AIConfig;
|
|
8
|
+
export declare function validateAIConfig(config: AIConfig): boolean;
|
|
9
|
+
export declare const ENV_GUIDE: {
|
|
10
|
+
openai: string[];
|
|
11
|
+
anthropic: string[];
|
|
12
|
+
ollama: string[];
|
|
13
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { browser } from '$app/environment';
|
|
2
|
+
import { dev } from '$app/environment';
|
|
3
|
+
export function getAIConfig() {
|
|
4
|
+
if (!browser) {
|
|
5
|
+
return {
|
|
6
|
+
provider: 'openai'
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
const config = {
|
|
10
|
+
provider: import.meta.env.VITE_AI_PROVIDER || 'openai',
|
|
11
|
+
apiKey: import.meta.env.VITE_OPENAI_API_KEY,
|
|
12
|
+
model: import.meta.env.VITE_OPENAI_MODEL || 'gpt-4-turbo-preview',
|
|
13
|
+
baseUrl: import.meta.env.VITE_OPENAI_BASE_URL || 'https://api.openai.com/v1'
|
|
14
|
+
};
|
|
15
|
+
// In development, provide helpful warnings
|
|
16
|
+
if (dev && !config.apiKey && config.provider === 'openai') {
|
|
17
|
+
console.warn('⚠️ OpenAI API key not found. Set VITE_OPENAI_API_KEY in your .env file');
|
|
18
|
+
console.warn('💡 Create a .env file with: VITE_OPENAI_API_KEY=sk-your-key-here');
|
|
19
|
+
}
|
|
20
|
+
return config;
|
|
21
|
+
}
|
|
22
|
+
export function validateAIConfig(config) {
|
|
23
|
+
switch (config.provider) {
|
|
24
|
+
case 'openai':
|
|
25
|
+
return !!config.apiKey;
|
|
26
|
+
case 'anthropic':
|
|
27
|
+
return !!config.apiKey;
|
|
28
|
+
case 'ollama':
|
|
29
|
+
return !!config.baseUrl;
|
|
30
|
+
default:
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Environment variables guide
|
|
35
|
+
export const ENV_GUIDE = {
|
|
36
|
+
openai: [
|
|
37
|
+
'VITE_OPENAI_API_KEY=sk-your-openai-key',
|
|
38
|
+
'VITE_OPENAI_MODEL=gpt-4-turbo-preview (optional)',
|
|
39
|
+
'VITE_OPENAI_BASE_URL=https://api.openai.com/v1 (optional)'
|
|
40
|
+
],
|
|
41
|
+
anthropic: ['VITE_ANTHROPIC_API_KEY=your-anthropic-key', 'VITE_AI_PROVIDER=anthropic'],
|
|
42
|
+
ollama: ['VITE_OLLAMA_BASE_URL=http://localhost:11434', 'VITE_AI_PROVIDER=ollama']
|
|
43
|
+
};
|
|
@@ -5,7 +5,7 @@ export const accordion = tv({
|
|
|
5
5
|
base: 'w-full relative overflow-hidden rounded-xl transition-all duration-200 bg-white border border-default-200',
|
|
6
6
|
header: 'w-full flex cursor-pointer items-center justify-between p-4 transition-colors duration-200',
|
|
7
7
|
title: 'flex text-default-900 text-lg font-medium leading-tight',
|
|
8
|
-
body: 'w-full border-t border-default-200 p-4 pt-3 transition-all'
|
|
8
|
+
body: 'w-full border-t border-default-200 p-4 pt-3 transition-all'
|
|
9
9
|
},
|
|
10
10
|
variants: {
|
|
11
11
|
color: {
|
|
@@ -30,9 +30,20 @@
|
|
|
30
30
|
{@render children()}
|
|
31
31
|
{#if onClose}
|
|
32
32
|
<button
|
|
33
|
-
onclick={handleOnClose}
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
onclick={handleOnClose}
|
|
34
|
+
class="flex h-full cursor-pointer items-center"
|
|
35
|
+
aria-label="Close"
|
|
36
|
+
title="Close"
|
|
37
|
+
tabindex="0"
|
|
38
|
+
type="button"
|
|
39
|
+
>
|
|
40
|
+
<svg
|
|
41
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
42
|
+
width="32"
|
|
43
|
+
height="32"
|
|
44
|
+
viewBox="0 0 32 32"
|
|
45
|
+
class="size-2"
|
|
46
|
+
>
|
|
36
47
|
<path
|
|
37
48
|
fill="currentColor"
|
|
38
49
|
d="M26.113 4.116a1.25 1.25 0 0 1 1.768 1.768L17.766 15.999l10.115 10.114a1.25 1.25 0 1 1-1.768 1.768L16 17.766L5.884 27.881a1.25 1.25 0 1 1-1.768-1.768L14.231 16L4.116 5.884a1.25 1.25 0 1 1 1.768-1.768l10.115 10.115z"
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
function handleClickOutside(event: MouseEvent) {
|
|
117
117
|
// Only handle if this specific dropdown is open
|
|
118
118
|
if (!isOpen) return;
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
const target = event.target as Node;
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
// Check if click is outside both the dropdown and trigger
|
|
123
123
|
if (
|
|
124
124
|
dropdownRef &&
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
{disabled}
|
|
209
209
|
aria-expanded={open}
|
|
210
210
|
onclick={handleToggle}
|
|
211
|
-
class="absolute inset-0
|
|
211
|
+
class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
|
212
212
|
tabindex={disabled ? -1 : 0}
|
|
213
213
|
></button>
|
|
214
214
|
<span class="flex min-h-[1.5rem] flex-1 flex-wrap items-center gap-1 overflow-hidden">
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
// Derived values
|
|
46
46
|
const percentage = $derived(segments ? 100 : calculatePercentage(value, max));
|
|
47
47
|
const segmentPercentages = $derived(
|
|
48
|
-
segments?.map(segment => ({
|
|
48
|
+
segments?.map((segment) => ({
|
|
49
49
|
...segment,
|
|
50
50
|
percentage: calculatePercentage(segment.value, max)
|
|
51
51
|
})) || []
|
|
@@ -78,11 +78,7 @@
|
|
|
78
78
|
);
|
|
79
79
|
|
|
80
80
|
const fillClass = $derived(
|
|
81
|
-
cn(
|
|
82
|
-
'h-full rounded-full transition-all',
|
|
83
|
-
segments ? '' : getColorClass(color),
|
|
84
|
-
barClass
|
|
85
|
-
)
|
|
81
|
+
cn('h-full rounded-full transition-all', segments ? '' : getColorClass(color), barClass)
|
|
86
82
|
);
|
|
87
83
|
|
|
88
84
|
const labelTextClass = $derived(
|
|
@@ -110,7 +106,7 @@
|
|
|
110
106
|
{#if segments}
|
|
111
107
|
{#each segmentPercentages as segment}
|
|
112
108
|
{#if segment.percentage > 0}
|
|
113
|
-
<div
|
|
109
|
+
<div
|
|
114
110
|
class={cn(getColorClass(segment.color), barClass)}
|
|
115
111
|
style="width: {segment.percentage}%"
|
|
116
112
|
title={segment.label || `${segment.value} (${segment.percentage}%)`}
|
|
@@ -121,9 +117,9 @@
|
|
|
121
117
|
<div class={fillClass} style="width: {percentage}%"></div>
|
|
122
118
|
{/if}
|
|
123
119
|
</div>
|
|
124
|
-
|
|
120
|
+
|
|
125
121
|
{#if segments && (showLabels || showValues)}
|
|
126
|
-
<div class="flex justify-between
|
|
122
|
+
<div class="mt-1 flex justify-between">
|
|
127
123
|
{#each segmentPercentages as segment}
|
|
128
124
|
{#if segment.percentage > 0}
|
|
129
125
|
<div class={labelTextClass}>
|
|
@@ -131,7 +127,8 @@
|
|
|
131
127
|
{segment.label}
|
|
132
128
|
{/if}
|
|
133
129
|
{#if showValues}
|
|
134
|
-
{#if showLabels && segment.label}
|
|
130
|
+
{#if showLabels && segment.label}
|
|
131
|
+
({/if}
|
|
135
132
|
{segment.percentage}%
|
|
136
133
|
{#if showLabels && segment.label}){/if}
|
|
137
134
|
{/if}
|