}>Обновить источник
@@ -1992,6 +2000,7 @@ export function CatalogApp() {
endPanel={
}
header={
}
brandHref="/"
center={
diff --git a/docs/APPLICATION_TEMPLATE.md b/docs/APPLICATION_TEMPLATE.md
index ddc02d2..f9d0e59 100644
--- a/docs/APPLICATION_TEMPLATE.md
+++ b/docs/APPLICATION_TEMPLATE.md
@@ -44,3 +44,7 @@ Modal остаётся portal-слоем. Его тело прокручивае
- предметное содержимое content window.
Shell владеет геометрией, слоями, переходами и breakpoint-поведением. См. готовый путь подключения в `docs/CONSUMPTION.md`.
+
+На узком экране скрывается именно действие `data-action="expand"`.
+Порядок utility actions не является признаком типа действия: плюс и обновление
+остаются доступны, даже когда стоят первыми в шапке. Исправлено при QA Node 0.3.
diff --git a/docs/COMPONENTS.md b/docs/COMPONENTS.md
index d912f61..5e19703 100644
--- a/docs/COMPONENTS.md
+++ b/docs/COMPONENTS.md
@@ -312,3 +312,12 @@ Inspector владеет:
Для Engine Environment Settings desktop-контракт точный: окно `390 px`, рабочая колонка `330 px`, строка `154 + 14 + 162 px`, высота контрола `46 px`, section header `50 px` с радиусом `12 px`. Accent-filled section headers и полноширинные range/checker/select сохраняют геометрию исходника.
Engine продолжает владеть определениями полей, типами нод и сохранением значений. В живом catalog эти collapsible sections находятся в `Guideline → Контролы`; отдельной product-вкладки Inspector нет.
+
+## ResourceRow и ResourceList
+
+Общие компактные строки ресурсов, выделенные из Mission Core AI Inference.
+Контракт, состояния и происхождение: [RESOURCE_ROW.md](RESOURCE_ROW.md).
+
+Светлый одноцветный знак шапки явно помечается `AppHeader.brandMonochrome` и
+`HeaderWorkspace.monochrome`: общая тема обеспечивает контраст на светлом фоне.
+По умолчанию флаг выключен; цветные изображения и аватары не меняются.
diff --git a/docs/RESOURCE_ROW.md b/docs/RESOURCE_ROW.md
new file mode 100644
index 0000000..89fff3c
--- /dev/null
+++ b/docs/RESOURCE_ROW.md
@@ -0,0 +1,23 @@
+# ResourceRow и ResourceList
+
+Владелец 2026-09-05 запросил перенос существующих длинных строк Mission Core
+AI Inference в Node с повторным использованием Design Guideline. Источник:
+ObservatoryWorkspace / observatory-evidence-card. Это выделение существующего
+оформления в общий компонент, без лабораторной модели данных.
+
+`ResourceList` содержит обычные `li`; внутри — `ResourceRow` с обязательным
+`title`, опциональными `icon`, `description`, `metadata`, `status`, `actions`.
+Слоты действий принимают канонические Button/IconButton. Клик по всей строке
+не назначается: самостоятельные действия имеют свои доступные подписи и focus.
+
+Геометрия источника сохранена: минимум 4.25rem, отступы .7/.85rem, значок
+2.15rem, компактный радиус и существующие токены поверхности/типографики.
+Длинное название переносится; метаданные сокращаются визуально, полный текст
+можно передать через title. На узком экране статус и действия переходят ниже.
+
+Состояния поставляет consumer: нормальное, пустой список, загрузка, ошибка,
+недоступное действие. Pending отмечается aria-busy и ActivityIndicator;
+disabled и keyboard/focus остаются у общего контрола. Строка не объявляет
+объект доступным или подключённым самостоятельно.
+
+Пример находится в живом каталоге, раздел «Контролы → Строки ресурсов».
diff --git a/packages/ui-core/styles.css b/packages/ui-core/styles.css
index f086aa6..28e17cb 100644
--- a/packages/ui-core/styles.css
+++ b/packages/ui-core/styles.css
@@ -3680,7 +3680,7 @@ textarea.nodedc-field__control {
display: none;
}
- .nodedc-application-panel__action:first-child {
+ .nodedc-application-panel__action[data-action="expand"] {
display: none;
}
@@ -3777,3 +3777,22 @@ textarea.nodedc-field__control {
transition: none;
}
}
+
+/* ResourceRow: admitted extraction of Mission Core AI Inference list geometry. */
+.nodedc-resource-list { display: grid; gap: .4rem; margin: 0; padding: 0; list-style: none; }
+.nodedc-resource-row { display: flex; align-items: center; gap: .8rem; min-height: 4.25rem; padding: .7rem .85rem; border-radius: var(--nodedc-radius-control-compact); background: var(--nodedc-glass-control-bg); }
+.nodedc-resource-row__icon { display: grid; flex: 0 0 2.15rem; height: 2.15rem; place-items: center; border-radius: var(--nodedc-radius-circle); background: var(--nodedc-panel-icon-bg); color: var(--nodedc-text-secondary); }
+.nodedc-resource-row__copy { display: grid; flex: 1; min-width: 0; gap: .16rem; }
+.nodedc-resource-row__copy strong { font-size: var(--nodedc-font-size-sm); line-height: 1.3; overflow-wrap: anywhere; }
+.nodedc-resource-row__copy > span, .nodedc-resource-row__copy > small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.nodedc-resource-row__copy > span { color: var(--nodedc-text-secondary); font-size: var(--nodedc-font-size-sm); }
+.nodedc-resource-row__copy > small { color: var(--nodedc-text-muted); font-size: var(--nodedc-font-size-xs); }
+.nodedc-resource-row__actions { display: flex; align-items: center; gap: .85rem; }
+.nodedc-resource-row__status { font-size: var(--nodedc-font-size-sm); }
+@media (max-width: 540px) {
+ .nodedc-resource-row { flex-wrap: wrap; }
+ .nodedc-resource-row__copy { flex-basis: calc(100% - 3rem); }
+ .nodedc-resource-row__actions { margin-left: auto; }
+}
+
+[data-nodedc-theme="light"] :is(.nodedc-header__brand, .nodedc-header__workspace)[data-monochrome="true"] img { filter: brightness(0); }
diff --git a/packages/ui-react/src/AppHeader.tsx b/packages/ui-react/src/AppHeader.tsx
index 88c3df8..5071f6b 100644
--- a/packages/ui-react/src/AppHeader.tsx
+++ b/packages/ui-react/src/AppHeader.tsx
@@ -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 ? (
-
{brand}
+
{brand}
) : (
-
{brand}
+
{brand}
);
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 (
-
+
{imageUrl ?
: label.slice(0, 2).toUpperCase()}
);
diff --git a/packages/ui-react/src/ResourceRow.tsx b/packages/ui-react/src/ResourceRow.tsx
new file mode 100644
index 0000000..3af99e0
--- /dev/null
+++ b/packages/ui-react/src/ResourceRow.tsx
@@ -0,0 +1,30 @@
+import type { HTMLAttributes, ReactNode } from "react";
+import { cn } from "./cn.js";
+
+export interface ResourceRowProps extends Omit, "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
+ {icon ?
{icon} : null}
+
+ {title}
+ {description ? {description} : null}
+ {metadata ? {metadata} : null}
+
+ {status ?
{status}
: null}
+ {actions ?
{actions}
: null}
+
;
+}
+
+export function ResourceList({ className, children, ...props }: HTMLAttributes) {
+ return ;
+}
diff --git a/packages/ui-react/src/index.ts b/packages/ui-react/src/index.ts
index 9111d09..e36097a 100644
--- a/packages/ui-react/src/index.ts
+++ b/packages/ui-react/src/index.ts
@@ -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";
diff --git a/registry/components.json b/registry/components.json
index 66dbe91..f2c6f1f 100644
--- a/registry/components.json
+++ b/registry/components.json
@@ -1,6 +1,30 @@
{
"schemaVersion": "1.0.0",
"components": [
+ {
+ "id": "resource-row",
+ "status": "baseline",
+ "package": "@nodedc/ui-react",
+ "exports": [
+ "ResourceRow",
+ "ResourceList"
+ ],
+ "domContract": [
+ "nodedc-resource-row",
+ "nodedc-resource-list"
+ ],
+ "summary": "Mission Core AI Inference compact rows extracted for shared resource lists.",
+ "behavior": [
+ "wrapping title",
+ "optional metadata and status",
+ "canonical action controls",
+ "responsive action placement"
+ ],
+ "rules": [
+ "The consumer supplies identity and states; the row owns presentation only.",
+ "Use ordinary list items and canonical buttons; do not nest actions inside a clickable row."
+ ]
+ },
{
"id": "glass-surface",
"status": "baseline",
@@ -254,6 +278,7 @@
"anatomy": ["left brand", "optional left context", "center navigation/workspace", "right actions/profile"],
"rules": [
"The logo occupies the same visual position and geometry across applications.",
+ "Light single-color assets may opt into brandMonochrome / monochrome for theme contrast; full-color images are unchanged by default.",
"Product routes and profile content are application data passed into stable slots.",
"The canonical application header is fixed and preserves the Launcher three-axis logo / navigation / profile positions.",
"Consumers supply data and actions but cannot override preset geometry through local className or style props."