feat: rewarding build UX + Docker fixes + web docs cleanup
Browse filesDocker:
- Dockerfile: add ENV PYTHONPATH=/app, mkdir designs/ & artifacts/,
make sby install resilient (pip first, then git clone fallback)
- docker-compose.yml: add ./designs:/app/designs volume, add healthcheck
pointing at GET /health
Web docs:
- Remove raw README from in-app docs index; replace with user-friendly
Getting Started (USER_GUIDE.md) and Cloud Deployment guide
Build UX (rewarding / less frustrating):
- StageProgressBar: each active/completed stage now shows a brief
encouraging description (e.g. 'Writing synthesizable Verilog')
- Milestone stages (RTL_GEN, VERIFICATION, HARDENING, SIGNOFF) get a
gold dot badge when completed
- HumanInLoopBuild: bottom status bar now shows stage-aware contextual
message instead of generic 'Building autonomously'
- HumanInLoopBuild: milestone celebration toast slides in when a key
stage is approved (auto-dismisses after 5s)
- hitl.css: stage description subtitle, milestone badge, toast-in
animation, indicator top-alignment fix
- Dockerfile +10 -4
- docker-compose.yml +7 -0
- server/api.py +9 -9
- web/src/components/StageProgressBar.tsx +39 -2
- web/src/hitl.css +105 -1
- web/src/pages/HumanInLoopBuild.tsx +56 -2
|
@@ -17,13 +17,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
| 17 |
yosys \
|
| 18 |
&& rm -rf /var/lib/apt/lists/*
|
| 19 |
|
| 20 |
-
# Install SymbiYosys
|
| 21 |
-
RUN
|
| 22 |
-
|
| 23 |
-
|
| 24 |
|
| 25 |
WORKDIR /app
|
| 26 |
|
|
|
|
|
|
|
|
|
|
| 27 |
# Install Python dependencies first (layer cache)
|
| 28 |
COPY requirements.txt .
|
| 29 |
RUN pip install --no-cache-dir --upgrade pip && \
|
|
@@ -33,6 +36,9 @@ RUN pip install --no-cache-dir --upgrade pip && \
|
|
| 33 |
# Copy application code
|
| 34 |
COPY . .
|
| 35 |
|
|
|
|
|
|
|
|
|
|
| 36 |
# HuggingFace Spaces runs as non-root user 1000
|
| 37 |
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
| 38 |
USER appuser
|
|
|
|
| 17 |
yosys \
|
| 18 |
&& rm -rf /var/lib/apt/lists/*
|
| 19 |
|
| 20 |
+
# Install SymbiYosys (try pip first, fall back to source)
|
| 21 |
+
RUN pip install --no-cache-dir symbiyosys 2>/dev/null || \
|
| 22 |
+
(git clone --depth 1 https://github.com/YosysHQ/sby /tmp/sby \
|
| 23 |
+
&& cd /tmp/sby && make install && rm -rf /tmp/sby) || true
|
| 24 |
|
| 25 |
WORKDIR /app
|
| 26 |
|
| 27 |
+
# Ensure module resolution works from /app
|
| 28 |
+
ENV PYTHONPATH=/app
|
| 29 |
+
|
| 30 |
# Install Python dependencies first (layer cache)
|
| 31 |
COPY requirements.txt .
|
| 32 |
RUN pip install --no-cache-dir --upgrade pip && \
|
|
|
|
| 36 |
# Copy application code
|
| 37 |
COPY . .
|
| 38 |
|
| 39 |
+
# Create runtime directories that the build pipeline writes into
|
| 40 |
+
RUN mkdir -p /app/designs /app/artifacts
|
| 41 |
+
|
| 42 |
# HuggingFace Spaces runs as non-root user 1000
|
| 43 |
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
| 44 |
USER appuser
|
|
@@ -12,4 +12,11 @@ services:
|
|
| 12 |
volumes:
|
| 13 |
- ./artifacts:/app/artifacts
|
| 14 |
- ./training:/app/training
|
|
|
|
| 15 |
restart: unless-stopped
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
volumes:
|
| 13 |
- ./artifacts:/app/artifacts
|
| 14 |
- ./training:/app/training
|
| 15 |
+
- ./designs:/app/designs
|
| 16 |
restart: unless-stopped
|
| 17 |
+
healthcheck:
|
| 18 |
+
test: ["CMD", "curl", "-sf", "http://localhost:7860/health"]
|
| 19 |
+
interval: 30s
|
| 20 |
+
timeout: 10s
|
| 21 |
+
retries: 3
|
| 22 |
+
start_period: 20s
|
|
@@ -251,11 +251,11 @@ def _repo_root() -> str:
|
|
| 251 |
def _docs_index() -> Dict[str, Dict[str, str]]:
|
| 252 |
root = _repo_root()
|
| 253 |
return {
|
| 254 |
-
"
|
| 255 |
-
"title": "
|
| 256 |
"section": "Product",
|
| 257 |
-
"path": os.path.join(root, "
|
| 258 |
-
"summary": "
|
| 259 |
},
|
| 260 |
"web_guide": {
|
| 261 |
"title": "Web App Guide",
|
|
@@ -269,11 +269,11 @@ def _docs_index() -> Dict[str, Dict[str, str]]:
|
|
| 269 |
"path": os.path.join(root, "docs", "INSTALL.md"),
|
| 270 |
"summary": "Installation and environment setup steps.",
|
| 271 |
},
|
| 272 |
-
"
|
| 273 |
-
"title": "
|
| 274 |
-
"section": "
|
| 275 |
-
"path": os.path.join(root, "docs", "
|
| 276 |
-
"summary": "
|
| 277 |
},
|
| 278 |
}
|
| 279 |
|
|
|
|
| 251 |
def _docs_index() -> Dict[str, Dict[str, str]]:
|
| 252 |
root = _repo_root()
|
| 253 |
return {
|
| 254 |
+
"getting_started": {
|
| 255 |
+
"title": "Getting Started",
|
| 256 |
"section": "Product",
|
| 257 |
+
"path": os.path.join(root, "docs", "USER_GUIDE.md"),
|
| 258 |
+
"summary": "Quick-start guide — build your first chip in minutes.",
|
| 259 |
},
|
| 260 |
"web_guide": {
|
| 261 |
"title": "Web App Guide",
|
|
|
|
| 269 |
"path": os.path.join(root, "docs", "INSTALL.md"),
|
| 270 |
"summary": "Installation and environment setup steps.",
|
| 271 |
},
|
| 272 |
+
"cloud_deploy": {
|
| 273 |
+
"title": "Cloud Deployment",
|
| 274 |
+
"section": "Setup",
|
| 275 |
+
"path": os.path.join(root, "docs", "CLOUD_DEPLOY.md"),
|
| 276 |
+
"summary": "Deploy AgentIC on HuggingFace Spaces or any cloud.",
|
| 277 |
},
|
| 278 |
}
|
| 279 |
|
|
@@ -17,6 +17,27 @@ const STAGES = [
|
|
| 17 |
{ key: 'SIGNOFF', label: 'Signoff' },
|
| 18 |
];
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
interface Props {
|
| 21 |
currentStage: string;
|
| 22 |
completedStages: Set<string>;
|
|
@@ -42,6 +63,7 @@ export const StageProgressBar: React.FC<Props> = ({
|
|
| 42 |
const isFailed = stage.key === failedStage;
|
| 43 |
const isWaiting = isCurrent && waitingForApproval;
|
| 44 |
const isSkipped = skippedStages?.has(stage.key) && !isCompleted && !isCurrent && !isFailed;
|
|
|
|
| 45 |
|
| 46 |
let status = 'pending';
|
| 47 |
if (isSkipped) status = 'skipped';
|
|
@@ -50,8 +72,13 @@ export const StageProgressBar: React.FC<Props> = ({
|
|
| 50 |
if (isWaiting) status = 'waiting';
|
| 51 |
if (isFailed) status = 'failed';
|
| 52 |
|
|
|
|
|
|
|
| 53 |
return (
|
| 54 |
-
<div
|
|
|
|
|
|
|
|
|
|
| 55 |
<div className="hitl-sidebar-indicator">
|
| 56 |
{isCompleted && (
|
| 57 |
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
@@ -75,7 +102,17 @@ export const StageProgressBar: React.FC<Props> = ({
|
|
| 75 |
<span className="hitl-sidebar-dot-empty" />
|
| 76 |
)}
|
| 77 |
</div>
|
| 78 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
</div>
|
| 80 |
);
|
| 81 |
})}
|
|
|
|
| 17 |
{ key: 'SIGNOFF', label: 'Signoff' },
|
| 18 |
];
|
| 19 |
|
| 20 |
+
// Brief, encouraging descriptions shown when a stage is active or just completed
|
| 21 |
+
const STAGE_DESCRIPTIONS: Record<string, string> = {
|
| 22 |
+
INIT: 'Setting up build context',
|
| 23 |
+
SPEC: 'Translating your idea into chip spec',
|
| 24 |
+
RTL_GEN: 'Writing synthesizable Verilog',
|
| 25 |
+
RTL_FIX: 'Resolving any RTL issues',
|
| 26 |
+
VERIFICATION: 'Running simulation testbench',
|
| 27 |
+
FORMAL_VERIFY: 'Proving correctness mathematically',
|
| 28 |
+
COVERAGE_CHECK: 'Checking test coverage',
|
| 29 |
+
REGRESSION: 'Running regression suite',
|
| 30 |
+
SDC_GEN: 'Generating timing constraints',
|
| 31 |
+
FLOORPLAN: 'Laying out chip floorplan',
|
| 32 |
+
HARDENING: 'Physical design & routing',
|
| 33 |
+
CONVERGENCE_REVIEW:'Checking timing convergence',
|
| 34 |
+
ECO_PATCH: 'Patching for final sign-off',
|
| 35 |
+
SIGNOFF: 'Final LVS/DRC checks',
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
// Key milestones displayed with a special accent in the sidebar
|
| 39 |
+
const MILESTONES = new Set(['RTL_GEN', 'VERIFICATION', 'HARDENING', 'SIGNOFF']);
|
| 40 |
+
|
| 41 |
interface Props {
|
| 42 |
currentStage: string;
|
| 43 |
completedStages: Set<string>;
|
|
|
|
| 63 |
const isFailed = stage.key === failedStage;
|
| 64 |
const isWaiting = isCurrent && waitingForApproval;
|
| 65 |
const isSkipped = skippedStages?.has(stage.key) && !isCompleted && !isCurrent && !isFailed;
|
| 66 |
+
const isMilestone = MILESTONES.has(stage.key);
|
| 67 |
|
| 68 |
let status = 'pending';
|
| 69 |
if (isSkipped) status = 'skipped';
|
|
|
|
| 72 |
if (isWaiting) status = 'waiting';
|
| 73 |
if (isFailed) status = 'failed';
|
| 74 |
|
| 75 |
+
const showDesc = (isCurrent || isCompleted) && !isSkipped && !isFailed;
|
| 76 |
+
|
| 77 |
return (
|
| 78 |
+
<div
|
| 79 |
+
key={stage.key}
|
| 80 |
+
className={`hitl-sidebar-stage hitl-stage--${status}${isMilestone ? ' hitl-stage--milestone' : ''}`}
|
| 81 |
+
>
|
| 82 |
<div className="hitl-sidebar-indicator">
|
| 83 |
{isCompleted && (
|
| 84 |
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
|
|
| 102 |
<span className="hitl-sidebar-dot-empty" />
|
| 103 |
)}
|
| 104 |
</div>
|
| 105 |
+
<div className="hitl-sidebar-stage-info">
|
| 106 |
+
<span className="hitl-sidebar-stage-name">{stage.label}</span>
|
| 107 |
+
{showDesc && (
|
| 108 |
+
<span className="hitl-sidebar-stage-desc">
|
| 109 |
+
{STAGE_DESCRIPTIONS[stage.key]}
|
| 110 |
+
</span>
|
| 111 |
+
)}
|
| 112 |
+
</div>
|
| 113 |
+
{isMilestone && isCompleted && (
|
| 114 |
+
<span className="hitl-sidebar-milestone-dot" title="Milestone reached" />
|
| 115 |
+
)}
|
| 116 |
</div>
|
| 117 |
);
|
| 118 |
})}
|
|
@@ -377,7 +377,7 @@
|
|
| 377 |
|
| 378 |
.hitl-sidebar-stage {
|
| 379 |
display: flex;
|
| 380 |
-
align-items:
|
| 381 |
gap: 0.625rem;
|
| 382 |
padding: 0.4375rem 1.25rem;
|
| 383 |
border-left: 2px solid transparent;
|
|
@@ -391,6 +391,7 @@
|
|
| 391 |
align-items: center;
|
| 392 |
justify-content: center;
|
| 393 |
flex-shrink: 0;
|
|
|
|
| 394 |
}
|
| 395 |
|
| 396 |
.hitl-sidebar-dot-empty {
|
|
@@ -415,6 +416,45 @@
|
|
| 415 |
transition: color 150ms;
|
| 416 |
}
|
| 417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
/* Stage status modifiers */
|
| 419 |
.hitl-stage--completed .hitl-sidebar-stage-name {
|
| 420 |
color: var(--text-secondary);
|
|
@@ -438,6 +478,70 @@
|
|
| 438 |
font-weight: 500;
|
| 439 |
}
|
| 440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
.hitl-stage--failed {
|
| 442 |
border-left-color: var(--red);
|
| 443 |
}
|
|
|
|
| 377 |
|
| 378 |
.hitl-sidebar-stage {
|
| 379 |
display: flex;
|
| 380 |
+
align-items: flex-start;
|
| 381 |
gap: 0.625rem;
|
| 382 |
padding: 0.4375rem 1.25rem;
|
| 383 |
border-left: 2px solid transparent;
|
|
|
|
| 391 |
align-items: center;
|
| 392 |
justify-content: center;
|
| 393 |
flex-shrink: 0;
|
| 394 |
+
margin-top: 0.125rem;
|
| 395 |
}
|
| 396 |
|
| 397 |
.hitl-sidebar-dot-empty {
|
|
|
|
| 416 |
transition: color 150ms;
|
| 417 |
}
|
| 418 |
|
| 419 |
+
/* Stage info wrapper (name + description) */
|
| 420 |
+
.hitl-sidebar-stage-info {
|
| 421 |
+
display: flex;
|
| 422 |
+
flex-direction: column;
|
| 423 |
+
gap: 0.1rem;
|
| 424 |
+
min-width: 0;
|
| 425 |
+
flex: 1;
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
.hitl-sidebar-stage-desc {
|
| 429 |
+
font-size: 0.6875rem;
|
| 430 |
+
color: var(--text-tertiary);
|
| 431 |
+
opacity: 0.7;
|
| 432 |
+
white-space: nowrap;
|
| 433 |
+
overflow: hidden;
|
| 434 |
+
text-overflow: ellipsis;
|
| 435 |
+
line-height: 1.3;
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
/* Milestone dot badge (gold star after milestone stages complete) */
|
| 439 |
+
.hitl-sidebar-milestone-dot {
|
| 440 |
+
width: 6px;
|
| 441 |
+
height: 6px;
|
| 442 |
+
border-radius: 50%;
|
| 443 |
+
background: #f6ad55;
|
| 444 |
+
flex-shrink: 0;
|
| 445 |
+
box-shadow: 0 0 4px rgba(246,173,85,0.6);
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
/* Milestone stage: subtly bolder left bar on active */
|
| 449 |
+
.hitl-stage--milestone.hitl-stage--active {
|
| 450 |
+
border-left-color: #f6ad55;
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
/* Stage completion flash for milestone stages */
|
| 454 |
+
.hitl-stage--milestone.hitl-stage--completed .hitl-sidebar-stage-name {
|
| 455 |
+
color: var(--text-secondary);
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
/* Stage status modifiers */
|
| 459 |
.hitl-stage--completed .hitl-sidebar-stage-name {
|
| 460 |
color: var(--text-secondary);
|
|
|
|
| 478 |
font-weight: 500;
|
| 479 |
}
|
| 480 |
|
| 481 |
+
/* Milestone toast ─────────────────────────────────────────────────── */
|
| 482 |
+
.hitl-milestone-toast {
|
| 483 |
+
position: absolute;
|
| 484 |
+
top: 0.875rem;
|
| 485 |
+
right: 1rem;
|
| 486 |
+
z-index: 50;
|
| 487 |
+
display: flex;
|
| 488 |
+
align-items: flex-start;
|
| 489 |
+
gap: 0.625rem;
|
| 490 |
+
background: var(--bg-card, #1a1917);
|
| 491 |
+
border: 1px solid rgba(246,173,85,0.35);
|
| 492 |
+
border-left: 3px solid #f6ad55;
|
| 493 |
+
border-radius: 8px;
|
| 494 |
+
padding: 0.625rem 0.875rem;
|
| 495 |
+
min-width: 240px;
|
| 496 |
+
max-width: 320px;
|
| 497 |
+
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
| 498 |
+
cursor: pointer;
|
| 499 |
+
animation: hitl-toast-in 280ms cubic-bezier(0.22,1,0.36,1) both;
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
@keyframes hitl-toast-in {
|
| 503 |
+
from { opacity: 0; transform: translateX(16px); }
|
| 504 |
+
to { opacity: 1; transform: translateX(0); }
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
.hitl-milestone-toast-icon {
|
| 508 |
+
font-size: 0.875rem;
|
| 509 |
+
color: #f6ad55;
|
| 510 |
+
flex-shrink: 0;
|
| 511 |
+
padding-top: 0.06rem;
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
.hitl-milestone-toast-body {
|
| 515 |
+
display: flex;
|
| 516 |
+
flex-direction: column;
|
| 517 |
+
gap: 0.1875rem;
|
| 518 |
+
flex: 1;
|
| 519 |
+
min-width: 0;
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
+
.hitl-milestone-toast-title {
|
| 523 |
+
font-size: 0.8125rem;
|
| 524 |
+
font-weight: 600;
|
| 525 |
+
color: var(--text-primary, #e8e5e0);
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
.hitl-milestone-toast-msg {
|
| 529 |
+
font-size: 0.75rem;
|
| 530 |
+
color: var(--text-secondary, #9a9590);
|
| 531 |
+
line-height: 1.45;
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
.hitl-milestone-toast-close {
|
| 535 |
+
background: none;
|
| 536 |
+
border: none;
|
| 537 |
+
font-size: 0.9rem;
|
| 538 |
+
color: var(--text-tertiary);
|
| 539 |
+
cursor: pointer;
|
| 540 |
+
padding: 0;
|
| 541 |
+
flex-shrink: 0;
|
| 542 |
+
line-height: 1;
|
| 543 |
+
}
|
| 544 |
+
|
| 545 |
.hitl-stage--failed {
|
| 546 |
border-left-color: var(--red);
|
| 547 |
}
|
|
@@ -13,6 +13,32 @@ const PIPELINE_STAGES = [
|
|
| 13 |
];
|
| 14 |
const TOTAL = PIPELINE_STAGES.length;
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
// Human-readable stage names
|
| 17 |
const STAGE_LABELS: Record<string, string> = {
|
| 18 |
INIT: 'Initialization', SPEC: 'Specification', RTL_GEN: 'RTL Generation',
|
|
@@ -121,6 +147,10 @@ export const HumanInLoopBuild = () => {
|
|
| 121 |
// Thinking indicator (Improvement 1)
|
| 122 |
const [thinkingData, setThinkingData] = useState<{ agent_name: string; message: string } | null>(null);
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
useEffect(() => {
|
| 125 |
if (prompt.length > 8) {
|
| 126 |
setDesignName(slugify(prompt));
|
|
@@ -297,6 +327,13 @@ export const HumanInLoopBuild = () => {
|
|
| 297 |
next.add(approvalData.stage_name);
|
| 298 |
return next;
|
| 299 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
} catch (e: any) {
|
| 301 |
setError(e?.response?.data?.detail || 'Failed to approve');
|
| 302 |
}
|
|
@@ -571,6 +608,18 @@ export const HumanInLoopBuild = () => {
|
|
| 571 |
</div>
|
| 572 |
</header>
|
| 573 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
{/* Body: sidebar + main */}
|
| 575 |
<div className="hitl-build-body">
|
| 576 |
<StageProgressBar
|
|
@@ -598,8 +647,13 @@ export const HumanInLoopBuild = () => {
|
|
| 598 |
<footer className="hitl-bottombar">
|
| 599 |
<span className="hitl-bottombar-msg">
|
| 600 |
{thinkingData && <span className="hitl-thinking-pulse" />}
|
| 601 |
-
{waitingForApproval
|
| 602 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
</span>
|
| 604 |
<div className="hitl-bottombar-progress">
|
| 605 |
<div className="hitl-bottombar-track">
|
|
|
|
| 13 |
];
|
| 14 |
const TOTAL = PIPELINE_STAGES.length;
|
| 15 |
|
| 16 |
+
// Contextual messages for the bottom status bar — what the agent is doing right now
|
| 17 |
+
const STAGE_ENCOURAGEMENTS: Record<string, string> = {
|
| 18 |
+
INIT: 'Setting up your build environment…',
|
| 19 |
+
SPEC: 'Translating your description into a chip specification…',
|
| 20 |
+
RTL_GEN: 'Writing Verilog — your chip is taking shape…',
|
| 21 |
+
RTL_FIX: 'Fixing any RTL issues automatically…',
|
| 22 |
+
VERIFICATION: 'Running simulation — making sure your logic is correct…',
|
| 23 |
+
FORMAL_VERIFY: 'Proving your chip is correct with formal methods…',
|
| 24 |
+
COVERAGE_CHECK: 'Measuring how thoroughly the tests cover your design…',
|
| 25 |
+
REGRESSION: 'Running the full regression suite…',
|
| 26 |
+
SDC_GEN: 'Generating timing constraints for physical design…',
|
| 27 |
+
FLOORPLAN: 'Planning the physical layout of your chip…',
|
| 28 |
+
HARDENING: 'Running place-and-route — turning RTL into real silicon…',
|
| 29 |
+
CONVERGENCE_REVIEW:'Checking that timing is met across all corners…',
|
| 30 |
+
ECO_PATCH: 'Applying final tweaks for clean sign-off…',
|
| 31 |
+
SIGNOFF: 'Almost there — running final LVS/DRC checks…',
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
// Milestone stages that deserve a special celebration toast
|
| 35 |
+
const MILESTONE_TOASTS: Record<string, { title: string; msg: string }> = {
|
| 36 |
+
RTL_GEN: { title: 'RTL Complete', msg: 'Your chip can now run instructions. Verilog is ready.' },
|
| 37 |
+
VERIFICATION: { title: 'Verification Passed', msg: 'All simulation tests passed. Design is logically correct.' },
|
| 38 |
+
HARDENING: { title: 'Silicon Layout Done', msg: 'Place-and-route complete. Your chip has a physical form.' },
|
| 39 |
+
SIGNOFF: { title: 'Chip Signed Off', msg: 'All checks passed. Ready for tape-out.' },
|
| 40 |
+
};
|
| 41 |
+
|
| 42 |
// Human-readable stage names
|
| 43 |
const STAGE_LABELS: Record<string, string> = {
|
| 44 |
INIT: 'Initialization', SPEC: 'Specification', RTL_GEN: 'RTL Generation',
|
|
|
|
| 147 |
// Thinking indicator (Improvement 1)
|
| 148 |
const [thinkingData, setThinkingData] = useState<{ agent_name: string; message: string } | null>(null);
|
| 149 |
|
| 150 |
+
// Milestone toast: shown briefly when a key stage completes
|
| 151 |
+
const [milestoneToast, setMilestoneToast] = useState<{ title: string; msg: string } | null>(null);
|
| 152 |
+
const milestoneTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
| 153 |
+
|
| 154 |
useEffect(() => {
|
| 155 |
if (prompt.length > 8) {
|
| 156 |
setDesignName(slugify(prompt));
|
|
|
|
| 327 |
next.add(approvalData.stage_name);
|
| 328 |
return next;
|
| 329 |
});
|
| 330 |
+
// Fire milestone toast if this is a key stage
|
| 331 |
+
const toast = MILESTONE_TOASTS[approvalData.stage_name];
|
| 332 |
+
if (toast) {
|
| 333 |
+
if (milestoneTimerRef.current) clearTimeout(milestoneTimerRef.current);
|
| 334 |
+
setMilestoneToast(toast);
|
| 335 |
+
milestoneTimerRef.current = setTimeout(() => setMilestoneToast(null), 5000);
|
| 336 |
+
}
|
| 337 |
} catch (e: any) {
|
| 338 |
setError(e?.response?.data?.detail || 'Failed to approve');
|
| 339 |
}
|
|
|
|
| 608 |
</div>
|
| 609 |
</header>
|
| 610 |
|
| 611 |
+
{/* Milestone celebration toast */}
|
| 612 |
+
{milestoneToast && (
|
| 613 |
+
<div className="hitl-milestone-toast" onClick={() => setMilestoneToast(null)}>
|
| 614 |
+
<span className="hitl-milestone-toast-icon">✦</span>
|
| 615 |
+
<div className="hitl-milestone-toast-body">
|
| 616 |
+
<span className="hitl-milestone-toast-title">{milestoneToast.title}</span>
|
| 617 |
+
<span className="hitl-milestone-toast-msg">{milestoneToast.msg}</span>
|
| 618 |
+
</div>
|
| 619 |
+
<button className="hitl-milestone-toast-close">×</button>
|
| 620 |
+
</div>
|
| 621 |
+
)}
|
| 622 |
+
|
| 623 |
{/* Body: sidebar + main */}
|
| 624 |
<div className="hitl-build-body">
|
| 625 |
<StageProgressBar
|
|
|
|
| 647 |
<footer className="hitl-bottombar">
|
| 648 |
<span className="hitl-bottombar-msg">
|
| 649 |
{thinkingData && <span className="hitl-thinking-pulse" />}
|
| 650 |
+
{waitingForApproval
|
| 651 |
+
? 'Your review is needed — inspect the stage output above'
|
| 652 |
+
: thinkingData
|
| 653 |
+
? thinkingData.message
|
| 654 |
+
: (STAGE_ENCOURAGEMENTS[currentStage] || 'Building autonomously…')}
|
| 655 |
+
{' · '}{pct}% complete
|
| 656 |
+
{estMinutes > 0 ? ` · ~${estMinutes} min left` : ''}
|
| 657 |
</span>
|
| 658 |
<div className="hitl-bottombar-progress">
|
| 659 |
<div className="hitl-bottombar-track">
|