CallMeDaniel Claude Opus 4.6 (1M context) commited on
Commit
a057d80
·
1 Parent(s): 54db1da

feat: rewrite gap cards with severity support and generic rendering

Browse files

Rewrite 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>

Files changed (1) hide show
  1. 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">\u26a0 MISSING INFO</div>';
2174
- for (const card of cards) {
2175
- html += '<div class="gap-card" style="border-left-color:' + escapeHtml(card.agent_color) + ';" data-category="' + escapeHtml(card.category) + '">';
 
 
 
 
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
- if (card.category === 'dimension') {
2183
- html += '<div class="wizard-dim-row"><span class="wizard-dim-label">Width</span><input class="wizard-dim-input gap-dim" id="gap-dim-width" type="number" onchange="gapDimChanged()"><span class="wizard-dim-unit">mm</span></div>';
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
- // Update designState
2246
- if (category === 'material') designState.material = value;
2247
- else if (category === 'shape') { designState.part_name = value; designState.description = value; }
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
  }