Spaces:
Sleeping
Sleeping
Commit ·
a057d80
1
Parent(s): 54db1da
feat: rewrite gap cards with severity support and generic rendering
Browse filesRewrite renderQuestionCards to sort by severity (blocking/recommended/
nice_to_have), add REQUIRED badges and color-coded borders, and render
all categories generically. Simplify gapSubmitAll to format selections
as plain text without mutating designState — the backend's
extract_decisions() handles state updates on the next chat turn.
Remove gapDimChanged and all category-specific special cases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- web/index.html +49 -51
web/index.html
CHANGED
|
@@ -1398,7 +1398,6 @@
|
|
| 1398 |
color: var(--text-primary); margin-bottom: 8px;
|
| 1399 |
}
|
| 1400 |
.gap-card .wizard-chips { margin-bottom: 0; }
|
| 1401 |
-
.gap-card .wizard-dim-row { margin-top: 4px; }
|
| 1402 |
.gap-card-submit {
|
| 1403 |
margin-top: 8px; padding: 5px 14px;
|
| 1404 |
background: var(--accent); color: var(--bg-void);
|
|
@@ -1406,6 +1405,20 @@
|
|
| 1406 |
font-family: var(--font-mono); font-size: 11px;
|
| 1407 |
font-weight: 600; cursor: pointer;
|
| 1408 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1409 |
|
| 1410 |
/* ---- RESPONSIVE ---- */
|
| 1411 |
|
|
@@ -2166,35 +2179,56 @@ async function rejectPlanCard() {
|
|
| 2166 |
|
| 2167 |
let gapSelections = {}; // { category: value } — tracks user selections across cards
|
| 2168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2169 |
function renderQuestionCards(cards) {
|
| 2170 |
if (!cards || cards.length === 0) return '';
|
| 2171 |
gapSelections = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2172 |
let html = '<div class="gap-cards" id="active-gap-cards">';
|
| 2173 |
-
html += '<div class="gap-cards-title">
|
| 2174 |
-
|
| 2175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2176 |
html += '<div class="gap-card-header">';
|
| 2177 |
html += '<div class="gap-card-dot" style="background:' + escapeHtml(card.agent_color) + ';"></div>';
|
| 2178 |
html += '<span class="gap-card-agent">' + escapeHtml(card.agent_name) + '</span>';
|
|
|
|
| 2179 |
html += '</div>';
|
|
|
|
|
|
|
| 2180 |
html += '<div class="gap-card-question">' + escapeHtml(card.question) + '</div>';
|
| 2181 |
|
| 2182 |
-
|
| 2183 |
-
|
| 2184 |
-
html += '<div class="wizard-dim-row"><span class="wizard-dim-label">Height</span><input class="wizard-dim-input gap-dim" id="gap-dim-height" type="number" onchange="gapDimChanged()"><span class="wizard-dim-unit">mm</span></div>';
|
| 2185 |
-
html += '<div class="wizard-dim-row"><span class="wizard-dim-label">Depth</span><input class="wizard-dim-input gap-dim" id="gap-dim-depth" type="number" onchange="gapDimChanged()"><span class="wizard-dim-unit">mm</span></div>';
|
| 2186 |
-
} else if (card.suggestions && card.suggestions.length > 0) {
|
| 2187 |
html += '<div class="wizard-chips">';
|
| 2188 |
for (const s of card.suggestions) {
|
| 2189 |
html += '<button class="wizard-chip" onclick="gapToggleChip(this,\'' + escapeHtml(s) + '\',\'' + escapeHtml(card.category) + '\')">' + escapeHtml(s) + '</button>';
|
| 2190 |
}
|
| 2191 |
html += '</div>';
|
| 2192 |
-
if (card.allow_custom) {
|
| 2193 |
-
html += '<input class="wizard-input" placeholder="Or type custom..." onchange="gapSetCustom(this.value,\'' + escapeHtml(card.category) + '\')">';
|
| 2194 |
-
}
|
| 2195 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2196 |
html += '</div>';
|
| 2197 |
}
|
|
|
|
| 2198 |
html += '<button class="gap-card-submit" onclick="gapSubmitAll()" style="width:100%;margin-top:4px;">Submit</button>';
|
| 2199 |
html += '</div>';
|
| 2200 |
return html;
|
|
@@ -2224,51 +2258,15 @@ function gapSetCustom(value, category) {
|
|
| 2224 |
if (card) card.querySelectorAll('.wizard-chip').forEach(c => c.classList.remove('selected'));
|
| 2225 |
}
|
| 2226 |
|
| 2227 |
-
function gapDimChanged() {
|
| 2228 |
-
const w = document.getElementById('gap-dim-width')?.value;
|
| 2229 |
-
const h = document.getElementById('gap-dim-height')?.value;
|
| 2230 |
-
const d = document.getElementById('gap-dim-depth')?.value;
|
| 2231 |
-
const parts = [];
|
| 2232 |
-
if (w) parts.push(w + 'mm wide');
|
| 2233 |
-
if (h) parts.push(h + 'mm high');
|
| 2234 |
-
if (d) parts.push(d + 'mm deep');
|
| 2235 |
-
if (parts.length > 0) gapSelections['dimension'] = parts.join(', ');
|
| 2236 |
-
else delete gapSelections['dimension'];
|
| 2237 |
-
}
|
| 2238 |
-
|
| 2239 |
function gapSubmitAll() {
|
| 2240 |
-
// Collect dimension values
|
| 2241 |
-
gapDimChanged();
|
| 2242 |
-
|
| 2243 |
const parts = [];
|
| 2244 |
for (const [category, value] of Object.entries(gapSelections)) {
|
| 2245 |
-
|
| 2246 |
-
|
| 2247 |
-
|
| 2248 |
-
else if (category === 'machining') designState.axis_recommendation = value;
|
| 2249 |
-
else if (category === 'constraint') {
|
| 2250 |
-
if (!designState.constraints) designState.constraints = [];
|
| 2251 |
-
designState.constraints.push(value);
|
| 2252 |
-
} else if (category === 'feature') {
|
| 2253 |
-
if (!designState.features) designState.features = [];
|
| 2254 |
-
designState.features.push(value);
|
| 2255 |
-
} else if (category === 'finish') {
|
| 2256 |
-
if (!designState.constraints) designState.constraints = [];
|
| 2257 |
-
designState.constraints.push('surface finish: ' + value);
|
| 2258 |
-
} else if (category === 'dimension') {
|
| 2259 |
-
if (!designState.dimensions) designState.dimensions = {};
|
| 2260 |
-
const w = document.getElementById('gap-dim-width')?.value;
|
| 2261 |
-
const h = document.getElementById('gap-dim-height')?.value;
|
| 2262 |
-
const d = document.getElementById('gap-dim-depth')?.value;
|
| 2263 |
-
if (w) designState.dimensions.width = parseFloat(w);
|
| 2264 |
-
if (h) designState.dimensions.height = parseFloat(h);
|
| 2265 |
-
if (d) designState.dimensions.depth = parseFloat(d);
|
| 2266 |
}
|
| 2267 |
-
parts.push(value);
|
| 2268 |
}
|
| 2269 |
-
|
| 2270 |
if (parts.length === 0) return;
|
| 2271 |
-
saveState();
|
| 2272 |
removeGapCards();
|
| 2273 |
sendMessage(parts.join(', '));
|
| 2274 |
}
|
|
|
|
| 1398 |
color: var(--text-primary); margin-bottom: 8px;
|
| 1399 |
}
|
| 1400 |
.gap-card .wizard-chips { margin-bottom: 0; }
|
|
|
|
| 1401 |
.gap-card-submit {
|
| 1402 |
margin-top: 8px; padding: 5px 14px;
|
| 1403 |
background: var(--accent); color: var(--bg-void);
|
|
|
|
| 1405 |
font-family: var(--font-mono); font-size: 11px;
|
| 1406 |
font-weight: 600; cursor: pointer;
|
| 1407 |
}
|
| 1408 |
+
.gap-card[data-severity="blocking"] {
|
| 1409 |
+
border-left-color: var(--error);
|
| 1410 |
+
}
|
| 1411 |
+
.gap-card[data-severity="nice_to_have"] {
|
| 1412 |
+
opacity: 0.7;
|
| 1413 |
+
}
|
| 1414 |
+
.gap-card-badge {
|
| 1415 |
+
font-family: var(--font-mono);
|
| 1416 |
+
font-size: 9px;
|
| 1417 |
+
font-weight: 700;
|
| 1418 |
+
color: var(--error);
|
| 1419 |
+
margin-left: 6px;
|
| 1420 |
+
text-transform: uppercase;
|
| 1421 |
+
}
|
| 1422 |
|
| 1423 |
/* ---- RESPONSIVE ---- */
|
| 1424 |
|
|
|
|
| 2179 |
|
| 2180 |
let gapSelections = {}; // { category: value } — tracks user selections across cards
|
| 2181 |
|
| 2182 |
+
function buildGapTitle(cards) {
|
| 2183 |
+
const counts = { blocking: 0, recommended: 0, nice_to_have: 0 };
|
| 2184 |
+
for (const c of cards) counts[c.severity || 'recommended']++;
|
| 2185 |
+
const parts = [];
|
| 2186 |
+
if (counts.blocking) parts.push(counts.blocking + ' REQUIRED');
|
| 2187 |
+
if (counts.recommended) parts.push(counts.recommended + ' RECOMMENDED');
|
| 2188 |
+
if (counts.nice_to_have) parts.push(counts.nice_to_have + ' OPTIONAL');
|
| 2189 |
+
return parts.join(', ');
|
| 2190 |
+
}
|
| 2191 |
+
|
| 2192 |
function renderQuestionCards(cards) {
|
| 2193 |
if (!cards || cards.length === 0) return '';
|
| 2194 |
gapSelections = {};
|
| 2195 |
+
|
| 2196 |
+
// Sort by severity: blocking first, recommended second, nice_to_have last
|
| 2197 |
+
const order = { blocking: 0, recommended: 1, nice_to_have: 2 };
|
| 2198 |
+
const sorted = [...cards].sort((a, b) => (order[a.severity] || 1) - (order[b.severity] || 1));
|
| 2199 |
+
|
| 2200 |
let html = '<div class="gap-cards" id="active-gap-cards">';
|
| 2201 |
+
html += '<div class="gap-cards-title">' + escapeHtml(buildGapTitle(sorted)) + '</div>';
|
| 2202 |
+
|
| 2203 |
+
for (const card of sorted) {
|
| 2204 |
+
const sev = card.severity || 'recommended';
|
| 2205 |
+
html += '<div class="gap-card" style="border-left-color:' + (sev === 'blocking' ? 'var(--error)' : sev === 'nice_to_have' ? 'var(--border)' : escapeHtml(card.agent_color)) + ';" data-category="' + escapeHtml(card.category) + '" data-severity="' + escapeHtml(sev) + '">';
|
| 2206 |
+
|
| 2207 |
+
// Header
|
| 2208 |
html += '<div class="gap-card-header">';
|
| 2209 |
html += '<div class="gap-card-dot" style="background:' + escapeHtml(card.agent_color) + ';"></div>';
|
| 2210 |
html += '<span class="gap-card-agent">' + escapeHtml(card.agent_name) + '</span>';
|
| 2211 |
+
if (sev === 'blocking') html += '<span class="gap-card-badge">REQUIRED</span>';
|
| 2212 |
html += '</div>';
|
| 2213 |
+
|
| 2214 |
+
// Question
|
| 2215 |
html += '<div class="gap-card-question">' + escapeHtml(card.question) + '</div>';
|
| 2216 |
|
| 2217 |
+
// Generic input: chips + optional custom
|
| 2218 |
+
if (card.suggestions && card.suggestions.length > 0) {
|
|
|
|
|
|
|
|
|
|
| 2219 |
html += '<div class="wizard-chips">';
|
| 2220 |
for (const s of card.suggestions) {
|
| 2221 |
html += '<button class="wizard-chip" onclick="gapToggleChip(this,\'' + escapeHtml(s) + '\',\'' + escapeHtml(card.category) + '\')">' + escapeHtml(s) + '</button>';
|
| 2222 |
}
|
| 2223 |
html += '</div>';
|
|
|
|
|
|
|
|
|
|
| 2224 |
}
|
| 2225 |
+
if (card.allow_custom) {
|
| 2226 |
+
html += '<input class="wizard-input" placeholder="Type your answer..." onchange="gapSetCustom(this.value,\'' + escapeHtml(card.category) + '\')">';
|
| 2227 |
+
}
|
| 2228 |
+
|
| 2229 |
html += '</div>';
|
| 2230 |
}
|
| 2231 |
+
|
| 2232 |
html += '<button class="gap-card-submit" onclick="gapSubmitAll()" style="width:100%;margin-top:4px;">Submit</button>';
|
| 2233 |
html += '</div>';
|
| 2234 |
return html;
|
|
|
|
| 2258 |
if (card) card.querySelectorAll('.wizard-chip').forEach(c => c.classList.remove('selected'));
|
| 2259 |
}
|
| 2260 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2261 |
function gapSubmitAll() {
|
|
|
|
|
|
|
|
|
|
| 2262 |
const parts = [];
|
| 2263 |
for (const [category, value] of Object.entries(gapSelections)) {
|
| 2264 |
+
if (value) {
|
| 2265 |
+
const label = category.replace(/_/g, ' ');
|
| 2266 |
+
parts.push(label + ': ' + value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2267 |
}
|
|
|
|
| 2268 |
}
|
|
|
|
| 2269 |
if (parts.length === 0) return;
|
|
|
|
| 2270 |
removeGapCards();
|
| 2271 |
sendMessage(parts.join(', '));
|
| 2272 |
}
|