From d600db6d485cb801cef9e8141c0fca94f3b98476 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 09:06:34 +0000 Subject: [PATCH 1/2] wip(ui): form layout horizontal, TabbedForm layout passthrough, FormPage tabbed sections Claude-Session: https://claude.ai/code/session_01JQwxbXNVYZR3yMKLPJJvzL Co-authored-by: Claude --- ui/apps/console/src/components/FormPage.tsx | 328 +++++++++++++----- .../components/src/renderers/form/form.tsx | 31 +- ui/packages/plugin-form/src/TabbedForm.tsx | 13 +- 3 files changed, 280 insertions(+), 92 deletions(-) diff --git a/ui/apps/console/src/components/FormPage.tsx b/ui/apps/console/src/components/FormPage.tsx index 7514b8dd79a..9468e86f073 100644 --- a/ui/apps/console/src/components/FormPage.tsx +++ b/ui/apps/console/src/components/FormPage.tsx @@ -148,6 +148,7 @@ import { mapFieldTypeToFormType, resolveFormWidgetType, } from '@object-ui/fields'; +import { Tabs, TabsContent, TabsList, TabsTrigger, cn } from '@object-ui/components'; import { SchemaRendererContext, usePredicateScope, type ApiFetch } from '@object-ui/react'; import { pickLocalized, useObjectTranslation, useSafeFieldLabel } from '@object-ui/i18n'; import type { FormFieldSpec, FormSectionSpec, FormViewSpec } from '@object-ui/app-shell'; @@ -997,6 +998,46 @@ export function isSectionVisible( }); } +/** + * The tab value of one section of a `type: 'tabbed'` form view + * (objectstack-ai/objectstack#26843): its `name`, else its heading, else its + * position. The same answer `@object-ui/plugin-form`'s `TabbedForm` gives a + * section, so one authored `defaultTab` opens the same tab on the app's tabbed + * form and on this page. + */ +export function sectionTabKey(section: Pick, index: number): string { + return section.name || section.label || `tab-${index}`; +} + +/** + * The open tab of a tabbed form view, among the tabs actually DRAWN (a section + * its predicate hides draws no tab): the user's pick, else the form view's + * `defaultTab`, else the first drawn tab. A pick or a `defaultTab` naming a + * tab that is not drawn falls through to the next answer, so the strip never + * opens onto a panel that is not there. + */ +export function resolveActiveTab( + drawn: readonly string[], + picked: string | null, + defaultTab: string | undefined, +): string | undefined { + if (picked && drawn.includes(picked)) return picked; + if (defaultTab && drawn.includes(defaultTab)) return defaultTab; + return drawn[0]; +} + +/** + * One field row under the form view's `layout: 'horizontal'` + * (objectstack-ai/objectstack#26843): the label in a fixed column beside its + * control from `sm` up, and stacked as `vertical` draws it below `sm`. The + * arrangement `@object-ui/components`' form renderer gives a field item under + * the same key, so a form view reads the same here and in the app's forms: + * every child but the `