Spaces:
Running
Running
File size: 8,972 Bytes
d57958f 0a4ce23 3e23f7a 1a97647 4f7950f 2845211 03151b9 3e23f7a d57958f 3e23f7a d57958f 3e23f7a 0f3e9c6 3e23f7a d57958f 3e23f7a 2845211 0a4ce23 2845211 0a4ce23 2845211 0a4ce23 2845211 0a4ce23 2845211 0a4ce23 1a97647 0a4ce23 1a97647 0a4ce23 0f3e9c6 1a97647 0a4ce23 1a97647 0a4ce23 0f3e9c6 1a97647 03151b9 4f7950f 0a4ce23 4f7950f 0a4ce23 4f7950f 0a4ce23 4f7950f 0a4ce23 4f7950f 03151b9 0a4ce23 03151b9 0a4ce23 03151b9 0a4ce23 03151b9 936b89e 3e23f7a d57958f 3e23f7a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | import React, { useState, useMemo, useRef, useEffect } from 'react';
import { ChevronRight, Download, Settings2, Search, Check, Eye, EyeOff, FileText, Square, CheckSquare } from 'lucide-react';
export default function DevMenu({
allModels,
orchestratorModel,
onOrchestratorChange,
personaMode,
onPersonaModeChange,
roleStyle,
onRoleStyleChange,
speedPriority,
onSpeedPriorityChange,
showResponseTime,
onShowResponseTimeChange,
showChatStats,
onShowChatStatsChange,
rolePrompts,
onShowRolePrompts,
onDownloadApiLog,
onDownloadChatTxt,
onDownloadChatMd,
hasApiLog,
hasChat,
}) {
const [open, setOpen] = useState(false);
const [orchOpen, setOrchOpen] = useState(false);
const [q, setQ] = useState('');
const wrapRef = useRef(null);
const searchRef = useRef(null);
useEffect(() => {
if (orchOpen && searchRef.current) searchRef.current.focus();
}, [orchOpen]);
useEffect(() => {
function handleClickOutside(e) {
if (wrapRef.current && !wrapRef.current.contains(e.target)) {
setOpen(false);
setOrchOpen(false);
setQ('');
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const filtered = useMemo(() => {
const s = q.trim().toLowerCase();
if (!s) return allModels;
return allModels.filter(row => {
const hay = `${row.name} ${row.id} ${row.provider || ''}`.toLowerCase();
return hay.includes(s);
});
}, [allModels, q]);
const currentName = useMemo(() => {
if (!orchestratorModel) return 'Default (backend)';
const m = allModels.find(m => m.id === orchestratorModel);
return m ? m.name : orchestratorModel;
}, [orchestratorModel, allModels]);
return (
<div className="dev-wrap" ref={wrapRef}>
<div className="dev-download-btns">
<button className="btn-sm btn-outline" disabled={!hasChat} onClick={onDownloadChatTxt}>
<Download size={14} /> .txt
</button>
<button className="btn-sm btn-outline" disabled={!hasChat} onClick={onDownloadChatMd}>
<Download size={14} /> .md
</button>
</div>
<div className="dev-dropdown-header">
<button className="icon-btn" onClick={() => { setOpen(o => !o); setOrchOpen(false); setQ(''); }} title="Settings">
<Settings2 size={16} />
</button>
{open && (
<div className="dev-panel">
<button onClick={() => { setOrchOpen(o => !o); setQ(''); }}>
Orchestrator model… <ChevronRight size={12} style={{ marginLeft: 'auto', opacity: 0.5 }} />
</button>
<div className="dev-panel-divider" />
<div className="dev-panel-label">Response priority</div>
<button
className={`dev-panel-choice ${!speedPriority ? 'dev-panel-choice-active' : ''}`}
onClick={() => onSpeedPriorityChange(false)}
>
{!speedPriority ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Prioritize model choice
</button>
<button
className={`dev-panel-choice ${speedPriority ? 'dev-panel-choice-active' : ''}`}
onClick={() => onSpeedPriorityChange(true)}
>
{speedPriority ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Prioritize conversation speed
</button>
<div className="dev-panel-divider" />
<div className="dev-panel-label">Expert persona input</div>
<button
className={`dev-panel-choice ${personaMode === 'structured' ? 'dev-panel-choice-active' : ''}`}
onClick={() => onPersonaModeChange('structured')}
>
{personaMode === 'structured' ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Structured expert persona input
</button>
<button
className={`dev-panel-choice ${personaMode === 'freeform' ? 'dev-panel-choice-active' : ''}`}
onClick={() => onPersonaModeChange('freeform')}
>
{personaMode === 'freeform' ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Freeform expert persona input
</button>
<div className="dev-panel-divider" />
<div className="dev-panel-label">Role generation</div>
<button
className={`dev-panel-choice ${roleStyle === 'ai_completed' ? 'dev-panel-choice-active' : ''}`}
onClick={() => onRoleStyleChange('ai_completed')}
>
{roleStyle === 'ai_completed' ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
AI completed roles
</button>
<button
className={`dev-panel-choice ${roleStyle === 'exact' ? 'dev-panel-choice-active' : ''}`}
onClick={() => onRoleStyleChange('exact')}
>
{roleStyle === 'exact' ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Exact user roles
</button>
<div className="dev-panel-divider" />
<div className="dev-panel-label">Display options</div>
<button
className={`dev-panel-choice ${showResponseTime ? 'dev-panel-choice-active' : ''}`}
onClick={() => onShowResponseTimeChange(!showResponseTime)}
>
{showResponseTime ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Response times on messages
</button>
<button
className={`dev-panel-choice ${showChatStats ? 'dev-panel-choice-active' : ''}`}
onClick={() => onShowChatStatsChange(!showChatStats)}
>
{showChatStats ? <CheckSquare size={16} className="dev-check-icon" /> : <Square size={16} className="dev-check-icon" />}
Chat stats after end
</button>
<button className="dev-panel-choice" disabled={!rolePrompts} onClick={() => { onShowRolePrompts(); setOpen(false); }}>
<FileText size={14} className="dev-check-icon" />
View role prompts
</button>
<div className="dev-panel-divider" />
<button disabled={!hasChat} className="dev-panel-download-item" onClick={() => { onDownloadChatTxt(); setOpen(false); }}>
Download chat as .txt
</button>
<button disabled={!hasChat} className="dev-panel-download-item" onClick={() => { onDownloadChatMd(); setOpen(false); }}>
Download chat as .md
</button>
<button disabled={!hasApiLog} onClick={() => { onDownloadApiLog(); setOpen(false); }}>
Download full API history
</button>
</div>
)}
{open && orchOpen && (
<div className="dev-sub-panel">
<div className="dev-sub-header">
<span className="dev-sub-title">Orchestrator</span>
<span className="dev-sub-current">{currentName}</span>
</div>
<div className="dev-sub-search">
<Search size={14} className="dev-sub-search-icon" />
<input
ref={searchRef}
type="search"
placeholder="Search models…"
value={q}
onChange={e => setQ(e.target.value)}
/>
</div>
<ul className="dev-sub-list">
<li>
<button
className={`dev-sub-item ${!orchestratorModel ? 'dev-sub-item-active' : ''}`}
onClick={() => { onOrchestratorChange(null); setOrchOpen(false); setOpen(false); setQ(''); }}
>
<strong>Default (backend)</strong>
<span className="dev-sub-provider">Use server default</span>
</button>
</li>
{filtered.map(m => (
<li key={m.id}>
<button
className={`dev-sub-item ${orchestratorModel === m.id ? 'dev-sub-item-active' : ''}`}
onClick={() => { onOrchestratorChange(m.id); setOrchOpen(false); setOpen(false); setQ(''); }}
>
<strong>{m.name}</strong>
<span className="dev-sub-provider">{m.provider}</span>
</button>
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
}
|