feat(ui): share resource rows and refine Node shell behavior
This commit is contained in:
@@ -4,6 +4,8 @@ export interface AppHeaderProps {
|
||||
brand: ReactNode;
|
||||
brandHref?: string;
|
||||
brandLabel?: string;
|
||||
/** Opt in only for a light, single-color mark, never a full-color image. */
|
||||
brandMonochrome?: boolean;
|
||||
left?: ReactNode;
|
||||
center?: ReactNode;
|
||||
right?: ReactNode;
|
||||
@@ -13,14 +15,15 @@ export function AppHeader({
|
||||
brand,
|
||||
brandHref,
|
||||
brandLabel = "NODE.DC",
|
||||
brandMonochrome = false,
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
}: AppHeaderProps) {
|
||||
const brandNode = brandHref ? (
|
||||
<a className="nodedc-header__brand" href={brandHref} aria-label={brandLabel}>{brand}</a>
|
||||
<a className="nodedc-header__brand" data-monochrome={brandMonochrome || undefined} href={brandHref} aria-label={brandLabel}>{brand}</a>
|
||||
) : (
|
||||
<span className="nodedc-header__brand" aria-label={brandLabel}>{brand}</span>
|
||||
<span className="nodedc-header__brand" data-monochrome={brandMonochrome || undefined} aria-label={brandLabel}>{brand}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -100,11 +103,12 @@ export interface HeaderWorkspaceProps {
|
||||
label: string;
|
||||
imageUrl?: string;
|
||||
kind?: "mark" | "avatar";
|
||||
monochrome?: boolean;
|
||||
}
|
||||
|
||||
export function HeaderWorkspace({ label, imageUrl, kind = "mark" }: HeaderWorkspaceProps) {
|
||||
export function HeaderWorkspace({ label, imageUrl, kind = "mark", monochrome = false }: HeaderWorkspaceProps) {
|
||||
return (
|
||||
<span className="nodedc-header__workspace" data-kind={kind} title={label}>
|
||||
<span className="nodedc-header__workspace" data-kind={kind} data-monochrome={monochrome || undefined} title={label}>
|
||||
{imageUrl ? <img src={imageUrl} alt="" /> : label.slice(0, 2).toUpperCase()}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { HTMLAttributes, ReactNode } from "react";
|
||||
import { cn } from "./cn.js";
|
||||
|
||||
export interface ResourceRowProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
title: ReactNode;
|
||||
icon?: ReactNode;
|
||||
description?: ReactNode;
|
||||
metadata?: ReactNode;
|
||||
status?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
/** 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}>
|
||||
{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}
|
||||
{actions ? <div className="nodedc-resource-row__actions">{actions}</div> : null}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function ResourceList({ className, children, ...props }: HTMLAttributes<HTMLUListElement>) {
|
||||
return <ul className={cn("nodedc-resource-list", className)} {...props}>{children}</ul>;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export * from "./Inspector.js";
|
||||
export * from "./Icon.js";
|
||||
export * from "./MediaSourceField.js";
|
||||
export * from "./RangeControl.js";
|
||||
export * from "./ResourceRow.js";
|
||||
export * from "./SegmentedControl.js";
|
||||
export * from "./Select.js";
|
||||
export * from "./StatusBadge.js";
|
||||
|
||||
Reference in New Issue
Block a user