@makemore/agent-frontend 2.8.1 → 2.9.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/dist/chat-widget.cjs.js +378 -253
- package/dist/chat-widget.css +395 -19
- package/dist/chat-widget.esm.js +378 -253
- package/dist/chat-widget.js +342 -217
- package/dist/react.cjs.js +377 -252
- package/dist/react.esm.js +377 -252
- package/package.json +2 -2
- package/src/components/ChatWidget.js +51 -9
- package/src/components/DevToolbar.js +135 -0
- package/src/components/ModelSelector.js +34 -10
- package/src/hooks/useChat.js +17 -2
- package/src/hooks/useModels.js +28 -4
- package/src/hooks/useSystems.js +163 -0
- package/src/index.js +17 -0
- package/src/utils/config.js +7 -0
- package/src/utils/helpers.js +35 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Systems hook - manages system/agent/version discovery and selection
|
|
3
|
+
* for developer/tester UI (DevToolbar).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useEffect, useCallback } from 'preact/hooks';
|
|
7
|
+
|
|
8
|
+
const SYSTEM_KEY = 'cw_selected_system';
|
|
9
|
+
const AGENT_KEY = 'cw_selected_agent';
|
|
10
|
+
const SYSTEM_VERSION_KEY = 'cw_selected_system_version';
|
|
11
|
+
const AGENT_VERSION_KEY = 'cw_selected_agent_version';
|
|
12
|
+
|
|
13
|
+
export function useSystems(config, api, storage) {
|
|
14
|
+
const [systems, setSystems] = useState([]);
|
|
15
|
+
const [agents, setAgents] = useState([]);
|
|
16
|
+
const [selectedSystem, setSelectedSystem] = useState(null);
|
|
17
|
+
const [selectedAgent, setSelectedAgent] = useState(null);
|
|
18
|
+
const [selectedSystemVersion, setSelectedSystemVersion] = useState(null);
|
|
19
|
+
const [selectedAgentVersion, setSelectedAgentVersion] = useState(null);
|
|
20
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
21
|
+
|
|
22
|
+
// Compute the effective agent key to use for runs
|
|
23
|
+
const getEffectiveAgentKey = useCallback(() => {
|
|
24
|
+
if (selectedAgent) return selectedAgent;
|
|
25
|
+
if (selectedSystem) {
|
|
26
|
+
const sys = systems.find(s => s.slug === selectedSystem);
|
|
27
|
+
if (sys?.entry_agent) return sys.entry_agent.slug;
|
|
28
|
+
}
|
|
29
|
+
return config.agentKey;
|
|
30
|
+
}, [selectedAgent, selectedSystem, systems, config.agentKey]);
|
|
31
|
+
|
|
32
|
+
// Load systems on mount
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!config.showDevTools) return;
|
|
35
|
+
|
|
36
|
+
const loadSystems = async () => {
|
|
37
|
+
setIsLoading(true);
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(
|
|
40
|
+
`${config.backendUrl}${config.apiPaths.systems}`,
|
|
41
|
+
api.getFetchOptions({ method: 'GET' })
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (response.ok) {
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
const systemsList = data.results || data;
|
|
47
|
+
setSystems(systemsList);
|
|
48
|
+
|
|
49
|
+
// Restore saved selection or auto-select if only 1 system
|
|
50
|
+
const saved = storage?.get(SYSTEM_KEY);
|
|
51
|
+
if (saved && systemsList.some(s => s.slug === saved)) {
|
|
52
|
+
setSelectedSystem(saved);
|
|
53
|
+
} else if (systemsList.length === 1) {
|
|
54
|
+
setSelectedSystem(systemsList[0].slug);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.warn('[ChatWidget] Failed to load systems:', err);
|
|
59
|
+
} finally {
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
loadSystems();
|
|
65
|
+
}, [config.backendUrl, config.apiPaths.systems, config.showDevTools, api, storage]);
|
|
66
|
+
|
|
67
|
+
// Load agents when system changes
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!config.showDevTools) return;
|
|
70
|
+
|
|
71
|
+
const loadAgents = async () => {
|
|
72
|
+
try {
|
|
73
|
+
const systemParam = selectedSystem ? `?system=${encodeURIComponent(selectedSystem)}` : '';
|
|
74
|
+
const response = await fetch(
|
|
75
|
+
`${config.backendUrl}${config.apiPaths.agents}${systemParam}`,
|
|
76
|
+
api.getFetchOptions({ method: 'GET' })
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (response.ok) {
|
|
80
|
+
const data = await response.json();
|
|
81
|
+
const agentsList = data.results || data;
|
|
82
|
+
setAgents(agentsList);
|
|
83
|
+
|
|
84
|
+
// Restore saved agent or default to entry agent
|
|
85
|
+
const saved = storage?.get(AGENT_KEY);
|
|
86
|
+
if (saved && agentsList.some(a => a.slug === saved)) {
|
|
87
|
+
setSelectedAgent(saved);
|
|
88
|
+
} else if (selectedSystem) {
|
|
89
|
+
const sys = systems.find(s => s.slug === selectedSystem);
|
|
90
|
+
if (sys?.entry_agent) {
|
|
91
|
+
setSelectedAgent(sys.entry_agent.slug);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Restore saved agent version
|
|
96
|
+
const savedVersion = storage?.get(AGENT_VERSION_KEY);
|
|
97
|
+
if (savedVersion) {
|
|
98
|
+
setSelectedAgentVersion(savedVersion);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.warn('[ChatWidget] Failed to load agents:', err);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
loadAgents();
|
|
107
|
+
}, [config.backendUrl, config.apiPaths.agents, config.showDevTools, selectedSystem, api, storage, systems]);
|
|
108
|
+
|
|
109
|
+
// Select a system
|
|
110
|
+
const selectSystem = useCallback((slug) => {
|
|
111
|
+
setSelectedSystem(slug);
|
|
112
|
+
storage?.set(SYSTEM_KEY, slug);
|
|
113
|
+
// Reset agent selection — will be re-set by the agents useEffect
|
|
114
|
+
setSelectedAgent(null);
|
|
115
|
+
setSelectedAgentVersion(null);
|
|
116
|
+
storage?.set(AGENT_KEY, null);
|
|
117
|
+
storage?.set(AGENT_VERSION_KEY, null);
|
|
118
|
+
// Set system version
|
|
119
|
+
const sys = systems.find(s => s.slug === slug);
|
|
120
|
+
const activeVer = sys?.active_version || null;
|
|
121
|
+
setSelectedSystemVersion(activeVer);
|
|
122
|
+
storage?.set(SYSTEM_VERSION_KEY, activeVer);
|
|
123
|
+
}, [storage, systems]);
|
|
124
|
+
|
|
125
|
+
// Select an agent
|
|
126
|
+
const selectAgent = useCallback((slug) => {
|
|
127
|
+
setSelectedAgent(slug);
|
|
128
|
+
storage?.set(AGENT_KEY, slug);
|
|
129
|
+
// Reset agent version to active
|
|
130
|
+
const agent = agents.find(a => a.slug === slug);
|
|
131
|
+
const activeVer = agent?.active_version || null;
|
|
132
|
+
setSelectedAgentVersion(activeVer);
|
|
133
|
+
storage?.set(AGENT_VERSION_KEY, activeVer);
|
|
134
|
+
}, [storage, agents]);
|
|
135
|
+
|
|
136
|
+
// Select system version
|
|
137
|
+
const selectSystemVersion = useCallback((version) => {
|
|
138
|
+
setSelectedSystemVersion(version);
|
|
139
|
+
storage?.set(SYSTEM_VERSION_KEY, version);
|
|
140
|
+
}, [storage]);
|
|
141
|
+
|
|
142
|
+
// Select agent version
|
|
143
|
+
const selectAgentVersion = useCallback((version) => {
|
|
144
|
+
setSelectedAgentVersion(version);
|
|
145
|
+
storage?.set(AGENT_VERSION_KEY, version);
|
|
146
|
+
}, [storage]);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
systems,
|
|
150
|
+
agents,
|
|
151
|
+
selectedSystem,
|
|
152
|
+
selectedAgent,
|
|
153
|
+
selectedSystemVersion,
|
|
154
|
+
selectedAgentVersion,
|
|
155
|
+
isLoading,
|
|
156
|
+
selectSystem,
|
|
157
|
+
selectAgent,
|
|
158
|
+
selectSystemVersion,
|
|
159
|
+
selectAgentVersion,
|
|
160
|
+
getEffectiveAgentKey,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
package/src/index.js
CHANGED
|
@@ -52,6 +52,9 @@ class ChatWidgetInstance {
|
|
|
52
52
|
document.body.appendChild(this.container);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// Apply theme class
|
|
56
|
+
this._applyTheme();
|
|
57
|
+
|
|
55
58
|
// Render the Preact component
|
|
56
59
|
this._render();
|
|
57
60
|
|
|
@@ -59,6 +62,16 @@ class ChatWidgetInstance {
|
|
|
59
62
|
return this;
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
_applyTheme() {
|
|
66
|
+
if (!this.container) return;
|
|
67
|
+
this.container.classList.remove('cw-dark', 'cw-auto');
|
|
68
|
+
if (this.config.theme === 'dark') {
|
|
69
|
+
this.container.classList.add('cw-dark');
|
|
70
|
+
} else if (this.config.theme === 'auto') {
|
|
71
|
+
this.container.classList.add('cw-auto');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
_render(configOverrides = {}) {
|
|
63
76
|
if (!this.container) return;
|
|
64
77
|
render(
|
|
@@ -172,6 +185,10 @@ class ChatWidgetInstance {
|
|
|
172
185
|
...this.config,
|
|
173
186
|
...configUpdates,
|
|
174
187
|
};
|
|
188
|
+
// Re-apply theme if it changed
|
|
189
|
+
if ('theme' in configUpdates) {
|
|
190
|
+
this._applyTheme();
|
|
191
|
+
}
|
|
175
192
|
// Re-render with updated config
|
|
176
193
|
this._render();
|
|
177
194
|
console.log(`[ChatWidget] Instance ${this.instanceId} config updated`);
|
package/src/utils/config.js
CHANGED
|
@@ -8,6 +8,7 @@ export const DEFAULT_CONFIG = {
|
|
|
8
8
|
title: 'Chat Assistant',
|
|
9
9
|
subtitle: 'How can we help you today?',
|
|
10
10
|
primaryColor: '#0066cc',
|
|
11
|
+
headerTextColor: null, // Auto-detect based on primaryColor luminance, or set explicitly
|
|
11
12
|
position: 'bottom-right',
|
|
12
13
|
defaultJourneyType: 'general',
|
|
13
14
|
enableDebugMode: true,
|
|
@@ -40,6 +41,8 @@ export const DEFAULT_CONFIG = {
|
|
|
40
41
|
ttsVoices: '/api/tts/voices/',
|
|
41
42
|
ttsSetVoice: '/api/tts/set-voice/',
|
|
42
43
|
models: '/api/agent-runtime/models/',
|
|
44
|
+
systems: '/api/agent-runtime/systems/',
|
|
45
|
+
agents: '/api/agent-runtime/agents/',
|
|
43
46
|
},
|
|
44
47
|
|
|
45
48
|
// API case style: 'camel', 'snake', or 'auto' (accepts both, sends camelCase)
|
|
@@ -48,6 +51,9 @@ export const DEFAULT_CONFIG = {
|
|
|
48
51
|
// - 'auto': Accept both in responses, send snake_case in requests (default)
|
|
49
52
|
apiCaseStyle: 'auto',
|
|
50
53
|
|
|
54
|
+
// Theme: 'light' (default), 'dark', or 'auto' (follows prefers-color-scheme)
|
|
55
|
+
theme: 'light',
|
|
56
|
+
|
|
51
57
|
// UI options
|
|
52
58
|
showConversationSidebar: true,
|
|
53
59
|
showClearButton: true,
|
|
@@ -56,6 +62,7 @@ export const DEFAULT_CONFIG = {
|
|
|
56
62
|
showVoiceSettings: true,
|
|
57
63
|
showExpandButton: true,
|
|
58
64
|
showModelSelector: false,
|
|
65
|
+
showDevTools: false, // Show system/agent/version picker for developers/testers
|
|
59
66
|
enableVoice: true, // Enable voice input (speech-to-text)
|
|
60
67
|
|
|
61
68
|
// Model selection
|
package/src/utils/helpers.js
CHANGED
|
@@ -173,3 +173,38 @@ export function getFileTypeIcon(mimeType) {
|
|
|
173
173
|
if (mimeType.includes('text/')) return '📄';
|
|
174
174
|
return '📄';
|
|
175
175
|
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Calculate relative luminance of a hex color
|
|
179
|
+
* Returns a value between 0 (black) and 1 (white)
|
|
180
|
+
*/
|
|
181
|
+
export function getLuminance(hexColor) {
|
|
182
|
+
if (!hexColor || typeof hexColor !== 'string') return 0;
|
|
183
|
+
|
|
184
|
+
// Remove # if present
|
|
185
|
+
const hex = hexColor.replace('#', '');
|
|
186
|
+
if (hex.length !== 6 && hex.length !== 3) return 0;
|
|
187
|
+
|
|
188
|
+
// Expand 3-char hex to 6-char
|
|
189
|
+
const fullHex = hex.length === 3
|
|
190
|
+
? hex.split('').map(c => c + c).join('')
|
|
191
|
+
: hex;
|
|
192
|
+
|
|
193
|
+
const r = parseInt(fullHex.substr(0, 2), 16) / 255;
|
|
194
|
+
const g = parseInt(fullHex.substr(2, 2), 16) / 255;
|
|
195
|
+
const b = parseInt(fullHex.substr(4, 2), 16) / 255;
|
|
196
|
+
|
|
197
|
+
// sRGB luminance formula
|
|
198
|
+
const toLinear = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
199
|
+
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get contrasting text color (black or white) for a given background color
|
|
204
|
+
*/
|
|
205
|
+
export function getContrastingTextColor(bgColor) {
|
|
206
|
+
const luminance = getLuminance(bgColor);
|
|
207
|
+
// Use white text for dark backgrounds, black for light backgrounds
|
|
208
|
+
// Threshold of 0.179 is based on WCAG contrast ratio guidelines
|
|
209
|
+
return luminance > 0.179 ? '#000000' : '#ffffff';
|
|
210
|
+
}
|