import type { ResolvedToolcraftAppSchema, ResolvedToolcraftControlSchema, } from "@repo/toolcraft-runtime"; import { isToolcraftProductSectionControl } from "./controls"; import { getToolcraftSectionLabel } from "./sections"; import { getToolcraftLooseTargetPrefix, getToolcraftStrictTargetPrefix, normalizeToolcraftSemanticText, } from "./semantic"; export type ToolcraftControlLayoutControlEntry = { control: ResolvedToolcraftControlSchema; controlId: string; loosePrefix: string | null; sectionLabel: string; sectionId: string; sectionTitle: string | undefined; }; export type ToolcraftControlLayoutSectionFact = { controls: readonly [string, ResolvedToolcraftControlSchema][]; sectionLabel: string; sectionId: string; sectionLoosePrefixes: ReadonlySet; sectionTitle: string | undefined; semanticClusters: ReadonlySet; }; export type ToolcraftControlLayoutFacts = { colorSectionLoosePrefixes: ReadonlyMap; controlsByTarget: ReadonlyMap; loosePrefixSections: ReadonlyMap>; sectionTitleCounts: ReadonlyMap; sectionLabelsById: ReadonlyMap; sections: readonly ToolcraftControlLayoutSectionFact[]; strictPrefixSections: ReadonlyMap>; visibleControls: readonly ToolcraftControlLayoutControlEntry[]; }; function getToolcraftControlSemanticCluster( control: ResolvedToolcraftControlSchema, ): string { return ( control.semanticGroup?.trim() || getToolcraftLooseTargetPrefix(control.target) || control.orderRole || control.type ); } function addSectionToPrefixMap( sectionMap: Map>, prefix: string | null, sectionId: string, ): void { if (!prefix) { return; } const sections = sectionMap.get(prefix) ?? new Set(); sections.add(sectionId); sectionMap.set(prefix, sections); } function updateSectionTitleCounts( sectionTitleCounts: Map, sectionTitle: string | undefined, ): void { if (!sectionTitle) { return; } const normalizedSectionTitle = normalizeToolcraftSemanticText(sectionTitle); const titleCount = sectionTitleCounts.get(normalizedSectionTitle); sectionTitleCounts.set(normalizedSectionTitle, { count: (titleCount?.count ?? 0) + 1, label: titleCount?.label ?? sectionTitle, }); } export function buildToolcraftControlLayoutFacts( schema: ResolvedToolcraftAppSchema, ): ToolcraftControlLayoutFacts { const sections: ToolcraftControlLayoutSectionFact[] = []; const visibleControls: ToolcraftControlLayoutControlEntry[] = []; const strictPrefixSections = new Map>(); const loosePrefixSections = new Map>(); const colorSectionLoosePrefixes = new Map(); const sectionTitleCounts = new Map(); const controlsByTarget = new Map(); const sectionLabelsById = new Map(); for (const [sectionIndex, section] of (schema.panels.controls?.sections ?? []).entries()) { const sectionTitle = section.title?.trim(); const sectionLabel = getToolcraftSectionLabel(sectionTitle, sectionIndex); const controls = Object.entries(section.controls).filter(([, control]) => isToolcraftProductSectionControl(control), ); if (controls.length === 0) { continue; } updateSectionTitleCounts(sectionTitleCounts, sectionTitle); sectionLabelsById.set(section.id, sectionLabel); const sectionLoosePrefixes = new Set( controls .map(([, control]) => getToolcraftLooseTargetPrefix(control.target)) .filter((prefix): prefix is string => Boolean(prefix)), ); const semanticClusters = new Set( controls.map(([, control]) => getToolcraftControlSemanticCluster(control)), ); const isColorOnlySection = controls.every( ([, control]) => control.type === "color", ); sections.push({ controls, sectionLabel, sectionId: section.id, sectionLoosePrefixes, sectionTitle, semanticClusters, }); for (const [controlId, control] of controls) { const strictPrefix = getToolcraftStrictTargetPrefix(control.target); const entry: ToolcraftControlLayoutControlEntry = { control, controlId, loosePrefix: getToolcraftLooseTargetPrefix(control.target), sectionLabel, sectionId: section.id, sectionTitle, }; visibleControls.push(entry); controlsByTarget.set(control.target, entry); addSectionToPrefixMap(strictPrefixSections, strictPrefix, section.id); addSectionToPrefixMap(loosePrefixSections, entry.loosePrefix, section.id); if ( control.type === "color" && isColorOnlySection && entry.loosePrefix ) { colorSectionLoosePrefixes.set(entry.loosePrefix, `${sectionLabel} / ${controlId}`); } } } return { colorSectionLoosePrefixes, controlsByTarget, loosePrefixSections, sectionTitleCounts, sectionLabelsById, sections, strictPrefixSections, visibleControls, }; }