feat(showcase_ui): support verification and finding cards in agent notes

This commit is contained in:
somew1nd committed 2026-09-02 00:58:48 -07:00
1 parent 99343dfe55
commit 73d7b0c116
7 files changed
+494 -52

No files matched your search

@@ -277,6 +277,30 @@ limitations under the License.
}
</div>
}
<!-- Verification Box (Pulled out into a minimalist framed card) -->
@if (milestone.checks.length > 0) {
<div class="milestone-checks-container">
@for (check of milestone.checks; track trackMarkdownLine($index, check)) {
<div
class="verification-card"
[ngClass]="check.type"
[style.margin-left.px]="(check.indent > 2 ? (check.indent - 2) * 12 : 0)"
>
<div class="card-header">
<span class="card-tag" [class.assert]="check.checkKind === 'assert'">{{ check.checkKind || 'verify' }}{{ check.atEnd ? ' @end' : '' }}</span>
</div>
<div class="card-body">
@for (seg of check.segments; track seg) {
<span
[class.bold-segment]="seg.bold"
[class.code-segment]="seg.code"
>{{ seg.text }}</span>
}
</div>
</div>
}
</div>
}
</div>
}
</div>
@@ -285,36 +309,52 @@ limitations under the License.
@if (parsedNote().otherLines.length > 0) {
<div class="other-lines-container">
@for (line of parsedNote().otherLines; track trackMarkdownLine($index, line)) {
<div
class="md-line"
[ngClass]="line.type"
[style.margin-left.px]="line.indent * 12"
>
@switch (line.type) {
@case ('checked') {
<span class="material-symbols-outlined check-icon checked">check_box</span>
}
@case ('progress') {
<span class="material-symbols-outlined check-icon progress">indeterminate_check_box</span>
}
@case ('unchecked') {
<span class="material-symbols-outlined check-icon unchecked">check_box_outline_blank</span>
}
@case ('list-item') {
<span class="bullet">•</span>
}
}
@if (line.type !== 'empty') {
<span class="line-text">
@if (line.type === 'verify' || line.type === 'finding') {
<div class="verification-card standalone" [ngClass]="line.type" [style.margin-left.px]="line.indent * 12">
<div class="card-header">
<span class="card-tag" [class.assert]="line.checkKind === 'assert'">{{ line.checkKind || 'verify' }}{{ line.atEnd ? ' @end' : '' }}</span>
</div>
<div class="card-body">
@for (seg of line.segments; track seg) {
<span
[class.bold-segment]="seg.bold"
[class.code-segment]="seg.code"
>{{ seg.text }}</span>
}
</span>
}
</div>
</div>
</div>
} @else {
<div
class="md-line"
[ngClass]="line.type"
[style.margin-left.px]="line.indent * 12"
>
@switch (line.type) {
@case ('checked') {
<span class="material-symbols-outlined check-icon checked">check_box</span>
}
@case ('progress') {
<span class="material-symbols-outlined check-icon progress">indeterminate_check_box</span>
}
@case ('unchecked') {
<span class="material-symbols-outlined check-icon unchecked">check_box_outline_blank</span>
}
@case ('list-item') {
<span class="bullet">•</span>
}
}
@if (line.type !== 'empty') {
<span class="line-text">
@for (seg of line.segments; track seg) {
<span
[class.bold-segment]="seg.bold"
[class.code-segment]="seg.code"
>{{ seg.text }}</span>
}
</span>
}
</div>
}
}
</div>
}
@@ -1110,6 +1150,12 @@ limitations under the License.
<div class="parsed-other-lines">
@for (line of parsedOut.otherLines; track trackMarkdownLine($index, line)) {
<p class="other-line" [class]="'line-' + line.type">
@if (line.type === 'verify') {
<span class="verify-badge" [class.assert]="line.checkKind === 'assert'">{{ line.checkKind || 'verify' }}{{ line.atEnd ? '@end' : '' }}</span>
}
@if (line.type === 'finding') {
<span class="finding-badge">finding</span>
}
@for (seg of line.segments; track seg) {
@if (seg.bold) {
<strong>{{ seg.text }}</strong>
@@ -404,6 +404,76 @@ $stream-row-height: 25px;
}
}
&.verify {
color: #334155;
gap: 8px;
align-items: flex-start;
.verify-badge {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: 'Google Sans Text', 'Inter', -apple-system, sans-serif;
font-size: 10.5px;
font-weight: 550;
line-height: 1.2;
letter-spacing: 0.2px;
text-transform: lowercase;
color: #475569;
background: #f1f5f9;
border: 1px solid #e2e8f0;
border-radius: 4px;
padding: 1.5px 6px;
margin-top: 1.5px;
user-select: none;
&.assert {
color: #0369a1;
background: #f0f9ff;
border-color: #e0f2fe;
}
}
.line-text {
color: #334155;
font-size: 12.5px;
line-height: 1.5;
}
}
&.finding {
color: #92400e;
gap: 8px;
align-items: flex-start;
.finding-badge {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: 'Google Sans Text', 'Inter', -apple-system, sans-serif;
font-size: 10.5px;
font-weight: 550;
line-height: 1.2;
letter-spacing: 0.2px;
text-transform: lowercase;
color: #b45309;
background: #fef3c7;
border: 1px solid #fde68a;
border-radius: 4px;
padding: 1.5px 6px;
margin-top: 1.5px;
user-select: none;
}
.line-text {
color: #92400e;
font-size: 12.5px;
line-height: 1.5;
}
}
.check-icon {
font-size: 16px;
user-select: none;
@@ -430,6 +500,84 @@ $stream-row-height: 25px;
}
}
}
.milestone-checks-container {
margin-top: 6px;
padding-left: 26px;
display: flex;
flex-direction: column;
gap: 6px;
}
}
.verification-card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 7px 10px;
display: flex;
flex-direction: column;
gap: 3px;
&.standalone {
margin-top: 6px;
margin-bottom: 6px;
}
.card-header {
display: flex;
align-items: center;
.card-tag {
font-family: 'Google Sans Text', 'Inter', -apple-system, sans-serif;
font-size: 9.5px;
font-weight: 600;
line-height: 1;
letter-spacing: 0.6px;
text-transform: uppercase;
color: #64748b;
&.assert {
color: #0284c7;
}
}
}
.card-body {
font-family: inherit;
font-size: 12px;
line-height: 1.5;
color: #334155;
word-break: break-word;
.bold-segment {
font-weight: 600;
color: #1e293b;
}
.code-segment {
font-family: 'Fira Code', 'Roboto Mono', Consolas, monospace;
font-size: 11px;
background: #f1f5f9;
color: #0f172a;
padding: 1px 5px;
border-radius: 4px;
border: 1px solid #e2e8f0;
}
}
&.finding {
background: #fffbeb;
border-color: #fef3c7;
.card-header .card-tag {
color: #b45309;
}
.card-body {
color: #92400e;
}
}
}
.other-lines-container {
@@ -447,6 +595,11 @@ $stream-row-height: 25px;
font-size: 12.5px;
color: #334155;
line-height: 1.5;
.line-text {
word-break: break-word;
color: inherit;
}
}
}
}
@@ -1763,6 +1916,14 @@ $stream-row-height: 25px;
color: #1f1f1f;
margin: 4px 0;
padding-left: 2px;
&.md-verify-item {
color: #334155;
}
&.md-finding-item {
color: #92400e;
}
}
}
@@ -290,6 +290,30 @@ limitations under the License.
}
</div>
}
<!-- Verification Box (Pulled out into a minimalist framed card) -->
@if (milestone.checks.length > 0) {
<div class="milestone-checks-container">
@for (check of milestone.checks; track trackMarkdownLine($index, check)) {
<div
class="verification-card"
[ngClass]="check.type"
[style.margin-left.px]="(check.indent > 2 ? (check.indent - 2) * 12 : 0)"
>
<div class="card-header">
<span class="card-tag" [class.assert]="check.checkKind === 'assert'">{{ check.checkKind || 'verify' }}{{ check.atEnd ? ' @end' : '' }}</span>
</div>
<div class="card-body">
@for (seg of check.segments; track seg) {
<span
[class.bold-segment]="seg.bold"
[class.code-segment]="seg.code"
>{{ seg.text }}</span>
}
</div>
</div>
}
</div>
}
</div>
}
</div>
@@ -298,36 +322,52 @@ limitations under the License.
@if (parsedNote().otherLines.length > 0) {
<div class="other-lines-container">
@for (line of parsedNote().otherLines; track trackMarkdownLine($index, line)) {
<div
class="md-line"
[ngClass]="line.type"
[style.margin-left.px]="line.indent * 12"
>
@switch (line.type) {
@case ('checked') {
<span class="material-symbols-outlined check-icon checked">check_box</span>
}
@case ('progress') {
<span class="material-symbols-outlined check-icon progress">indeterminate_check_box</span>
}
@case ('unchecked') {
<span class="material-symbols-outlined check-icon unchecked">check_box_outline_blank</span>
}
@case ('list-item') {
<span class="bullet">•</span>
}
}
@if (line.type !== 'empty') {
<span class="line-text">
@if (line.type === 'verify' || line.type === 'finding') {
<div class="verification-card standalone" [ngClass]="line.type" [style.margin-left.px]="line.indent * 12">
<div class="card-header">
<span class="card-tag" [class.assert]="line.checkKind === 'assert'">{{ line.checkKind || 'verify' }}{{ line.atEnd ? ' @end' : '' }}</span>
</div>
<div class="card-body">
@for (seg of line.segments; track seg) {
<span
[class.bold-segment]="seg.bold"
[class.code-segment]="seg.code"
>{{ seg.text }}</span>
}
</span>
}
</div>
</div>
</div>
} @else {
<div
class="md-line"
[ngClass]="line.type"
[style.margin-left.px]="line.indent * 12"
>
@switch (line.type) {
@case ('checked') {
<span class="material-symbols-outlined check-icon checked">check_box</span>
}
@case ('progress') {
<span class="material-symbols-outlined check-icon progress">indeterminate_check_box</span>
}
@case ('unchecked') {
<span class="material-symbols-outlined check-icon unchecked">check_box_outline_blank</span>
}
@case ('list-item') {
<span class="bullet">•</span>
}
}
@if (line.type !== 'empty') {
<span class="line-text">
@for (seg of line.segments; track seg) {
<span
[class.bold-segment]="seg.bold"
[class.code-segment]="seg.code"
>{{ seg.text }}</span>
}
</span>
}
</div>
}
}
</div>
}
@@ -878,6 +878,84 @@ $text-muted: #a1a1aa;
}
}
}
.milestone-checks-container {
margin-top: 8px;
padding-left: 28px;
display: flex;
flex-direction: column;
gap: 8px;
}
}
.verification-card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 8px 12px;
display: flex;
flex-direction: column;
gap: 4px;
&.standalone {
margin-top: 8px;
margin-bottom: 8px;
}
.card-header {
display: flex;
align-items: center;
.card-tag {
font-family: 'Google Sans Text', 'Inter', -apple-system, sans-serif;
font-size: 10.5px;
font-weight: 600;
line-height: 1;
letter-spacing: 0.6px;
text-transform: uppercase;
color: #64748b;
&.assert {
color: #0284c7;
}
}
}
.card-body {
font-family: inherit;
font-size: 13.5px;
line-height: 1.6;
color: #334155;
word-break: break-word;
.bold-segment {
font-weight: 600;
color: #1e293b;
}
.code-segment {
font-family: 'Fira Code', 'Roboto Mono', Consolas, monospace;
font-size: 12px;
background: #f1f5f9;
color: #0f172a;
padding: 1.5px 6px;
border-radius: 4px;
border: 1px solid #e2e8f0;
}
}
&.finding {
background: #fffbeb;
border-color: #fef3c7;
.card-header .card-tag {
color: #b45309;
}
.card-body {
color: #92400e;
}
}
}
.other-lines-container {