refactor: streamline debug tab from 6 to 4 sections with toggle UI
Browse files
crossword-app/frontend/src/components/DebugTab.jsx
CHANGED
|
@@ -26,6 +26,7 @@ const DebugTab = ({ debugData }) => {
|
|
| 26 |
const [activeSection, setActiveSection] = useState('overview');
|
| 27 |
const [sortBy, setSortBy] = useState('similarity');
|
| 28 |
const [sortDirection, setSortDirection] = useState('desc');
|
|
|
|
| 29 |
|
| 30 |
if (!debugData || !debugData.enabled) {
|
| 31 |
return (
|
|
@@ -37,11 +38,9 @@ const DebugTab = ({ debugData }) => {
|
|
| 37 |
|
| 38 |
const sections = [
|
| 39 |
{ id: 'overview', label: 'Overview' },
|
| 40 |
-
{ id: '
|
| 41 |
-
{ id: '
|
| 42 |
-
{ id: 'selection', label: 'Selection' }
|
| 43 |
-
{ id: 'probabilities', label: 'Probabilities' },
|
| 44 |
-
{ id: 'selected', label: 'Selected Words' }
|
| 45 |
];
|
| 46 |
|
| 47 |
const renderOverview = () => (
|
|
@@ -62,10 +61,24 @@ const DebugTab = ({ debugData }) => {
|
|
| 62 |
<h3>Selection Algorithm</h3>
|
| 63 |
<div className="debug-grid">
|
| 64 |
<div><strong>Method:</strong> {debugData.selection_method}</div>
|
| 65 |
-
<div><strong>Temperature:</strong> {debugData.selection_params.similarity_temperature}</div>
|
| 66 |
-
<div><strong>Difficulty Weight:</strong> {debugData.selection_params.difficulty_weight}</div>
|
| 67 |
<div><strong>Use Softmax:</strong> {debugData.selection_params.use_softmax_selection ? 'Yes' : 'No'}</div>
|
| 68 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
<h3>Results Summary</h3>
|
| 71 |
<div className="debug-grid">
|
|
@@ -216,6 +229,62 @@ const DebugTab = ({ debugData }) => {
|
|
| 216 |
);
|
| 217 |
};
|
| 218 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
const renderSelection = () => (
|
| 220 |
<div className="debug-section">
|
| 221 |
<h3>Selection Process</h3>
|
|
@@ -613,11 +682,9 @@ const DebugTab = ({ debugData }) => {
|
|
| 613 |
const renderSection = () => {
|
| 614 |
switch (activeSection) {
|
| 615 |
case 'overview': return renderOverview();
|
| 616 |
-
case '
|
| 617 |
-
case '
|
| 618 |
-
case 'selection': return
|
| 619 |
-
case 'probabilities': return renderProbabilities();
|
| 620 |
-
case 'selected': return renderSelected();
|
| 621 |
default: return renderOverview();
|
| 622 |
}
|
| 623 |
};
|
|
|
|
| 26 |
const [activeSection, setActiveSection] = useState('overview');
|
| 27 |
const [sortBy, setSortBy] = useState('similarity');
|
| 28 |
const [sortDirection, setSortDirection] = useState('desc');
|
| 29 |
+
const [wordPoolView, setWordPoolView] = useState('all'); // 'all' or 'candidates'
|
| 30 |
|
| 31 |
if (!debugData || !debugData.enabled) {
|
| 32 |
return (
|
|
|
|
| 38 |
|
| 39 |
const sections = [
|
| 40 |
{ id: 'overview', label: 'Overview' },
|
| 41 |
+
{ id: 'word-pool', label: 'Word Pool' },
|
| 42 |
+
{ id: 'probability-analysis', label: 'Probability Analysis' },
|
| 43 |
+
{ id: 'final-selection', label: 'Final Selection' }
|
|
|
|
|
|
|
| 44 |
];
|
| 45 |
|
| 46 |
const renderOverview = () => (
|
|
|
|
| 61 |
<h3>Selection Algorithm</h3>
|
| 62 |
<div className="debug-grid">
|
| 63 |
<div><strong>Method:</strong> {debugData.selection_method}</div>
|
| 64 |
+
<div><strong>Temperature:</strong> {debugData.selection_params.similarity_temperature} (lower = more deterministic)</div>
|
| 65 |
+
<div><strong>Difficulty Weight:</strong> {debugData.selection_params.difficulty_weight} (balance between similarity and frequency)</div>
|
| 66 |
<div><strong>Use Softmax:</strong> {debugData.selection_params.use_softmax_selection ? 'Yes' : 'No'}</div>
|
| 67 |
</div>
|
| 68 |
+
|
| 69 |
+
<h4>How it works:</h4>
|
| 70 |
+
<ul>
|
| 71 |
+
<li><strong>Composite Score</strong> = (1 - difficulty_weight) × similarity + difficulty_weight × frequency_alignment</li>
|
| 72 |
+
<li><strong>Frequency Alignment</strong>: Gaussian distribution favoring target percentiles by difficulty</li>
|
| 73 |
+
<li><strong>Softmax Selection</strong>: Probabilistic selection based on composite scores with temperature control</li>
|
| 74 |
+
</ul>
|
| 75 |
+
|
| 76 |
+
<h4>Difficulty Targets:</h4>
|
| 77 |
+
<ul>
|
| 78 |
+
<li><strong>Easy:</strong> 90th percentile (common words like CAT, DOG)</li>
|
| 79 |
+
<li><strong>Medium:</strong> 50th percentile (balanced selection)</li>
|
| 80 |
+
<li><strong>Hard:</strong> 20th percentile (rare words like QUETZAL, PLATYPUS)</li>
|
| 81 |
+
</ul>
|
| 82 |
|
| 83 |
<h3>Results Summary</h3>
|
| 84 |
<div className="debug-grid">
|
|
|
|
| 229 |
);
|
| 230 |
};
|
| 231 |
|
| 232 |
+
const renderWordPool = () => {
|
| 233 |
+
const pool = debugData.thematic_pool || [];
|
| 234 |
+
const candidates = debugData.candidate_words || [];
|
| 235 |
+
|
| 236 |
+
const isAllView = wordPoolView === 'all';
|
| 237 |
+
const currentData = isAllView ? pool : candidates;
|
| 238 |
+
|
| 239 |
+
return (
|
| 240 |
+
<div className="debug-section">
|
| 241 |
+
<h3>Word Pool</h3>
|
| 242 |
+
|
| 243 |
+
{/* Toggle buttons */}
|
| 244 |
+
<div className="pool-view-toggle" style={{ marginBottom: '15px' }}>
|
| 245 |
+
<button
|
| 246 |
+
className={`toggle-btn ${isAllView ? 'active' : ''}`}
|
| 247 |
+
onClick={() => setWordPoolView('all')}
|
| 248 |
+
style={{
|
| 249 |
+
padding: '8px 16px',
|
| 250 |
+
marginRight: '8px',
|
| 251 |
+
backgroundColor: isAllView ? '#4CAF50' : '#f0f0f0',
|
| 252 |
+
color: isAllView ? 'white' : '#333',
|
| 253 |
+
border: '1px solid #ccc',
|
| 254 |
+
borderRadius: '4px',
|
| 255 |
+
cursor: 'pointer'
|
| 256 |
+
}}
|
| 257 |
+
>
|
| 258 |
+
All Words ({pool.length})
|
| 259 |
+
</button>
|
| 260 |
+
<button
|
| 261 |
+
className={`toggle-btn ${!isAllView ? 'active' : ''}`}
|
| 262 |
+
onClick={() => setWordPoolView('candidates')}
|
| 263 |
+
style={{
|
| 264 |
+
padding: '8px 16px',
|
| 265 |
+
backgroundColor: !isAllView ? '#4CAF50' : '#f0f0f0',
|
| 266 |
+
color: !isAllView ? 'white' : '#333',
|
| 267 |
+
border: '1px solid #ccc',
|
| 268 |
+
borderRadius: '4px',
|
| 269 |
+
cursor: 'pointer'
|
| 270 |
+
}}
|
| 271 |
+
>
|
| 272 |
+
With Clues ({candidates.length})
|
| 273 |
+
</button>
|
| 274 |
+
</div>
|
| 275 |
+
|
| 276 |
+
<p>
|
| 277 |
+
{isAllView
|
| 278 |
+
? 'All words generated thematically. Click column headers to sort.'
|
| 279 |
+
: 'Words that passed filtering and got clues generated.'
|
| 280 |
+
}
|
| 281 |
+
</p>
|
| 282 |
+
|
| 283 |
+
{isAllView ? renderSortableThematicPool() : renderWordTable(candidates, true)}
|
| 284 |
+
</div>
|
| 285 |
+
);
|
| 286 |
+
};
|
| 287 |
+
|
| 288 |
const renderSelection = () => (
|
| 289 |
<div className="debug-section">
|
| 290 |
<h3>Selection Process</h3>
|
|
|
|
| 682 |
const renderSection = () => {
|
| 683 |
switch (activeSection) {
|
| 684 |
case 'overview': return renderOverview();
|
| 685 |
+
case 'word-pool': return renderWordPool();
|
| 686 |
+
case 'probability-analysis': return renderProbabilities();
|
| 687 |
+
case 'final-selection': return renderSelected();
|
|
|
|
|
|
|
| 688 |
default: return renderOverview();
|
| 689 |
}
|
| 690 |
};
|