feat(ui): add measured linear progress to resource rows

This commit is contained in:
Codex
2026-09-05 23:19:36 +03:00
parent 8a79dfe84d
commit 17e150b1c7
10 changed files with 121 additions and 4 deletions
+15
View File
@@ -3796,3 +3796,18 @@ textarea.nodedc-field__control {
}
[data-nodedc-theme="light"] :is(.nodedc-header__brand, .nodedc-header__workspace)[data-monochrome="true"] img { filter: brightness(0); }
/* Owner-admitted linear progress in the shared resource row. */
.nodedc-progress-bar { min-width: 3rem; height: var(--nodedc-space-2); overflow: hidden; border-radius: var(--nodedc-radius-circle); background: var(--nodedc-glass-control-bg); }
.nodedc-progress-bar__fill { display: block; width: 100%; height: 100%; background: var(--nodedc-text-secondary); border-radius: inherit; transform: scaleX(var(--nodedc-progress-fraction)); transform-origin: left; transition: transform var(--nodedc-duration-normal) var(--nodedc-ease-standard); }
.nodedc-progress-bar[data-indeterminate] > .nodedc-progress-bar__fill { width: 35%; animation: nodedc-progress-travel 1.5s ease-in-out infinite alternate; }
@keyframes nodedc-progress-travel { from { transform: translateX(-100%); } to { transform: translateX(285%); } }
.nodedc-resource-row--progress > .nodedc-resource-row__copy { flex: 0 1 35%; }
.nodedc-resource-row--progress > .nodedc-progress-bar { flex: 1; }
@media (max-width: 40rem) {
.nodedc-resource-row--progress > .nodedc-resource-row__copy { flex-basis: calc(100% - 3rem); }
}
@media (prefers-reduced-motion: reduce) {
.nodedc-progress-bar__fill { transition: none; }
.nodedc-progress-bar[data-indeterminate] > .nodedc-progress-bar__fill { animation: none; width: 100%; transform: scaleX(0.5); }
}
+20
View File
@@ -0,0 +1,20 @@
import type { CSSProperties, HTMLAttributes } from "react";
import { cn } from "./cn.js";
export interface ProgressBarProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
label: string;
/** Completed fraction from 0 to 1. Omit when the amount is unknown. */
value?: number;
valueText?: string;
}
export function ProgressBar({ label, value, valueText, className, style, ...props }: ProgressBarProps) {
const fraction = typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : undefined;
return <div {...props} className={cn("nodedc-progress-bar", className)} role="progressbar"
aria-label={label} aria-valuemin={0} aria-valuemax={100}
aria-valuenow={fraction === undefined ? undefined : Math.round(fraction * 100)}
aria-valuetext={valueText} data-indeterminate={fraction === undefined || undefined}
style={{ ...style, "--nodedc-progress-fraction": fraction ?? 0 } as CSSProperties}>
<span className="nodedc-progress-bar__fill" />
</div>;
}
+6 -3
View File
@@ -1,4 +1,5 @@
import type { HTMLAttributes, ReactNode } from "react";
import { ProgressBar, type ProgressBarProps } from "./ProgressBar.js";
import { cn } from "./cn.js";
export interface ResourceRowProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
@@ -8,19 +9,21 @@ export interface ResourceRowProps extends Omit<HTMLAttributes<HTMLDivElement>, "
metadata?: ReactNode;
status?: ReactNode;
actions?: ReactNode;
progress?: Pick<ProgressBarProps, "label" | "value" | "valueText">;
}
/** Compact resource presentation extracted from Mission Core AI Inference.
* Actions stay canonical controls; the row itself is not a nested button. */
export function ResourceRow({ title, icon, description, metadata, status, actions, className, ...props }: ResourceRowProps) {
return <div className={cn("nodedc-resource-row", className)} {...props}>
export function ResourceRow({ title, icon, description, metadata, status, actions, progress, className, ...props }: ResourceRowProps) {
return <div className={cn("nodedc-resource-row", progress && "nodedc-resource-row--progress", className)} {...props}>
{icon ? <span className="nodedc-resource-row__icon" aria-hidden="true">{icon}</span> : null}
<div className="nodedc-resource-row__copy">
<strong>{title}</strong>
{description ? <span>{description}</span> : null}
{metadata ? <small>{metadata}</small> : null}
</div>
{status ? <div className="nodedc-resource-row__status">{status}</div> : null}
{progress ? <ProgressBar {...progress} /> : null}
{!progress && status ? <div className="nodedc-resource-row__status">{status}</div> : null}
{actions ? <div className="nodedc-resource-row__actions">{actions}</div> : null}
</div>;
}
+2
View File
@@ -28,3 +28,5 @@ export * from "./Toast.js";
export * from "./UserProfileMenu.js";
export * from "./Window.js";
export * from "./WorkspaceWindow.js";
export { ProgressBar, type ProgressBarProps } from "./ProgressBar.js";