# Cross-Source Enrichment Review (`cross-source-01`) - SignalOS page block

> Full-page cross-source enrichment review block with tag entities, clusters, relationships, and source connection map.

- **Version:** 0.5.0
- **Kind:** block (group: semantics-details) · **Category:** pages
- **Install:** `npx shadcn@latest add @signalos/cross-source-01`
- **Registry dependencies (pulled automatically):** @signalos/tokens, @signalos/utils, @signalos/button, @signalos/badge, @signalos/form-dialog, @signalos/select, @signalos/checkbox, @signalos/input, @signalos/label, @signalos/tooltip, @signalos/split-panel, @signalos/page-header, @signalos/textarea, @signalos/dialog
- **npm dependencies:** lucide-react@^1.7.0
- **Files installed:** `src/components/blocks/cross-source-01/CrossSource.tsx`, `src/components/blocks/cross-source-01/CrossSource.types.ts`, `src/components/blocks/cross-source-01/CrossSource.utils.ts`, `src/components/blocks/cross-source-01/components/CrossSourceTabSwitcher.tsx`, `src/components/blocks/cross-source-01/components/TagQueueList.tsx`, `src/components/blocks/cross-source-01/components/TagDetailPanel.tsx`, `src/components/blocks/cross-source-01/components/ClustersPanel.tsx`, `src/components/blocks/cross-source-01/components/RelQueueList.tsx`, `src/components/blocks/cross-source-01/components/SourceConnectionMap.tsx`, `src/components/blocks/cross-source-01/components/RelationshipDetailPanel.tsx`, `src/components/blocks/cross-source-01/components/ClusterAddDialog.tsx`, `src/components/blocks/cross-source-01/components/CrossSourceAddDialog.tsx`, `src/components/blocks/cross-source-01/components/TagEntityAddDialog.tsx`, `src/components/blocks/cross-source-01/components/EditRelationshipForm.tsx`, `src/components/blocks/cross-source-01/components/FieldSelect.tsx`, `src/components/blocks/cross-source-01/components/CrossSourcePageHeader.tsx`, `src/components/blocks/cross-source-01/components/TagsTab.tsx`, `src/components/blocks/cross-source-01/components/RelationshipsTab.tsx`

## Access

This is a private registry: pulling source requires a `SIGNALOS_REGISTRY_TOKEN`
(GitHub fine-grained PAT with read access to the signal-widgets repo) and an
`@signalos` entry in components.json `"registries"`. Previews and this document are public.

## Usage

```tsx
// after: npx shadcn@latest add @signalos/cross-source-01
// mount the pulled page in a route, e.g. src/app/semantics-details/page.tsx
import CrossSourceReview from "@/components/blocks/cross-source-01/CrossSource"

export default function Page() {
  return <CrossSourceReview />
}
```

## Props

### `RelationshipType`

```ts
export type RelationshipType =
  | "references"
  | "has_many"
  | "belongs_to"
  | "many_to_many"
  | "one_to_one"
  | "measures"
  | "targets"
  | "sponsors"
  | "conducted_at"
  | "co_occurs"
  | "related_to"
```

### `CrossSourceReviewProps`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `title` | `string \| undefined` | no | - | Title shown in the page header. |
| `connectorIds` | `string[] \| undefined` | no | - | Source connector identifiers. |
| `relationships` | `CrossSourceRelationship[] \| undefined` | no | - | Cross-source relationships to review. |
| `flatTagEntities` | `FlatTagEntry[] \| undefined` | no | - | Flat list of tag entities to review. |
| `allClusters` | `ClustersWithConnector[] \| undefined` | no | - | All clusters (with connector id attached). |
| `allAvailableTags` | `string[] \| undefined` | no | - | All available tag names for dropdowns. |
| `allClusterEntities` | `string[] \| undefined` | no | - | All cluster entity names. |
| `entityClusterMap` | `Record<string, string> \| undefined` | no | - | Map from entity name to its cluster tag. |
| `catalogTables` | `Record<string, string[]> \| undefined` | no | - | Available tables per connector for relationship editing. |
| `catalogColumns` | `Record<string, string[]> \| undefined` | no | - | Available columns per connector.table for relationship editing. |
| `isConfirmed` | `boolean \| undefined` | no | - | Whether this stage has already been confirmed. |
| `stageActions` | `ReactNode` | no | - | Optional action buttons (e.g. Rerun, Reset) rendered in the header. |
| `isSubmitting` | `boolean \| undefined` | no | - | Whether a submit is in progress. |
| `errorMessage` | `string \| undefined` | no | - | Error message to show in the banner. |
| `activeTab` | `ReviewTab \| undefined` | no | - | Active review tab. |
| `relDecisions` | `Record<number, RelDecision> \| undefined` | no | - | Per-relationship decision map. |
| `selectedRelIdx` | `number \| null \| undefined` | no | - | Index of the selected relationship, or null. |
| `deletedRelIndices` | `Set<number> \| undefined` | no | - | Set of relationship indices marked for deletion. |
| `isEditingRel` | `boolean \| undefined` | no | - | Whether the selected relationship is being edited. |
| `relEditForm` | `EditRelForm \| undefined` | no | - | Current relationship edit form values. |
| `tagDecisions` | `Record<number, TagDecision> \| undefined` | no | - | Per-tag decision map. |
| `selectedTagIdx` | `number \| undefined` | no | - | Index of the selected tag entity. |
| `deletedTagIndices` | `Set<number> \| undefined` | no | - | Set of tag indices marked for deletion. |
| `clusterEdits` | `Record<number, ClusterEditValue> \| undefined` | no | - | Per-cluster edit map. |
| `editingClusterIdx` | `number \| null \| undefined` | no | - | Index of the cluster currently being edited. |
| `clusterDialogOpen` | `boolean \| undefined` | no | - | Whether the cluster edit dialog is open. |
| `clusterEditState` | `ClusterEditState \| undefined` | no | - | Current cluster edit state. |
| `displayRelationships` | `CrossSourceRelationship[] \| undefined` | no | - | Derived display relationships (with edits applied). |
| `changeSummaries` | `Record<number, string[]> \| undefined` | no | - | Per-relationship change summaries. |
| `sourceStats` | `Record<string, { tables: Set<string>; relCount: number; }> \| undefined` | no | - | Source stats for the connection map. |
| `totalFlagged` | `number \| undefined` | no | - | Total number of flagged items. |
| `editFromTables` | `string[] \| undefined` | no | - | Derived tables for the from-side edit selects. |
| `editToTables` | `string[] \| undefined` | no | - | Derived tables for the to-side edit selects. |
| `editFromCols` | `string[] \| undefined` | no | - | Derived columns for the from-side edit selects. |
| `editToCols` | `string[] \| undefined` | no | - | Derived columns for the to-side edit selects. |
| `editHasChanges` | `boolean \| undefined` | no | - | Whether the edit form has unsaved changes. |
| `editIsFormValid` | `boolean \| undefined` | no | - | Whether the edit form is valid for saving. |
| `onTabChange` | `((tab: ReviewTab) => void) \| undefined` | no | - | Fired when the active tab changes. |
| `onSubmit` | `((action: "confirm") => void) \| undefined` | no | - | Fired when the user clicks submit. |
| `onDismissError` | `(() => void) \| undefined` | no | - | Fired when the user dismisses the error banner. |
| `onSelectRel` | `((idx: number) => void) \| undefined` | no | - | Fired when a relationship is selected. |
| `onAcceptRel` | `((idx: number) => void) \| undefined` | no | - | Fired when a relationship is accepted. |
| `onStartEditing` | `(() => void) \| undefined` | no | - | Fired when the edit mode starts. |
| `onCancelEditing` | `(() => void) \| undefined` | no | - | Fired when the edit mode is cancelled. |
| `onSaveRelEdit` | `(() => void) \| undefined` | no | - | Fired when the rel edit is saved. |
| `onEditFromSourceChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit from-source changes. |
| `onEditFromTableChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit from-table changes. |
| `onEditFromColumnChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit from-column changes. |
| `onEditToSourceChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit to-source changes. |
| `onEditToTableChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit to-table changes. |
| `onEditToColumnChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit to-column changes. |
| `onEditRelTypeChange` | `((v: RelationshipType) => void) \| undefined` | no | - | Fired when the edit rel-type changes. |
| `onEditExplanationChange` | `((v: string) => void) \| undefined` | no | - | Fired when the edit explanation changes. |
| `onToggleDeleteRel` | `((idx: number) => void) \| undefined` | no | - | Fired when a rel is toggled for deletion. |
| `onSelectTag` | `((idx: number) => void) \| undefined` | no | - | Fired when a tag is selected. |
| `onSetTagDecision` | `((gi: number, status: "accepted" \| "flagged") => void) \| undefined` | no | - | Fired when a tag decision changes. |
| `onUpdateTagEdit` | `((gi: number, field: "tags" \| "primary_tag", value: string \| string[]) => void) \| undefined` | no | - | Fired when a tag edit field changes. |
| `onToggleDeleteTag` | `((gi: number) => void) \| undefined` | no | - | Fired when a tag is toggled for deletion. |
| `onOpenEditCluster` | `((idx: number) => void) \| undefined` | no | - | Fired when the cluster edit dialog is opened. |
| `onSetClusterDialogOpen` | `((open: boolean) => void) \| undefined` | no | - | Fired when the cluster dialog open state changes. |
| `onSetClusterEditState` | `((state: ClusterEditState) => void) \| undefined` | no | - | Fired when the cluster edit state changes. |
| `onSaveClusterEdit` | `((idx: number, state: ClusterEditState) => void) \| undefined` | no | - | Fired when a cluster edit is saved. |
| `onUndoClusterEdit` | `((idx: number) => void) \| undefined` | no | - | Fired when a cluster edit is undone. |
| `addRelDialogOpen` | `boolean \| undefined` | no | - | Whether the add-relationship dialog is open. |
| `onSetAddRelDialogOpen` | `((open: boolean) => void) \| undefined` | no | - | Fired when the add-relationship dialog open state changes. |
| `isAddRelSubmitting` | `boolean \| undefined` | no | - | Whether the add-relationship dialog is submitting. |
| `addRelErrorMessage` | `string \| null \| undefined` | no | - | Error message shown inside the add-relationship dialog. |
| `addRelForm` | `CrossSourceRelAddFormState \| undefined` | no | - | Controlled form values for the add-relationship dialog. |
| `addRelFormErrors` | `CrossSourceRelAddFormErrors \| undefined` | no | - | Validation errors for the add-relationship dialog. |
| `onAddRelFormChange` | `(<K extends keyof CrossSourceRelAddFormState>(field: K, value: CrossSourceRelAddFormState[K]) => void) \| undefined` | no | - | Called when a field in the add-relationship form changes. |
| `onAddRelFormErrorsChange` | `((errors: CrossSourceRelAddFormErrors) => void) \| undefined` | no | - | Called when the add-relationship form errors change. |
| `onAddRelSubmit` | `((values: CrossSourceRelAddValues) => void \| Promise<void>) \| undefined` | no | - | Fired when the add-relationship form is submitted. |
| `addClusterDialogOpen` | `boolean \| undefined` | no | - | Whether the add-cluster dialog is open. |
| `onSetAddClusterDialogOpen` | `((open: boolean) => void) \| undefined` | no | - | Fired when the add-cluster dialog open state changes. |
| `isAddClusterSubmitting` | `boolean \| undefined` | no | - | Whether the add-cluster dialog is submitting. |
| `addClusterErrorMessage` | `string \| null \| undefined` | no | - | Error message shown inside the add-cluster dialog. |
| `addClusterForm` | `ClusterAddFormState \| undefined` | no | - | Controlled form values for the add-cluster dialog. |
| `addClusterFormErrors` | `ClusterAddFormErrors \| undefined` | no | - | Validation errors for the add-cluster dialog. |
| `onAddClusterFormChange` | `(<K extends keyof ClusterAddFormState>(field: K, value: ClusterAddFormState[K]) => void) \| undefined` | no | - | Called when a field in the add-cluster form changes. |
| `onAddClusterFormErrorsChange` | `((errors: ClusterAddFormErrors) => void) \| undefined` | no | - | Called when the add-cluster form errors change. |
| `onAddClusterSubmit` | `((values: ClusterAddValues) => void \| Promise<void>) \| undefined` | no | - | Fired when the add-cluster form is submitted. |
| `addTagEntityDialogOpen` | `boolean \| undefined` | no | - | Whether the add-tag-entity dialog is open. |
| `onSetAddTagEntityDialogOpen` | `((open: boolean) => void) \| undefined` | no | - | Fired when the add-tag-entity dialog open state changes. |
| `isAddTagEntitySubmitting` | `boolean \| undefined` | no | - | Whether the add-tag-entity dialog is submitting. |
| `addTagEntityErrorMessage` | `string \| null \| undefined` | no | - | Error message shown inside the add-tag-entity dialog. |
| `addTagEntityForm` | `TagEntityAddFormState \| undefined` | no | - | Controlled form values for the add-tag-entity dialog. |
| `addTagEntityFormErrors` | `TagEntityAddFormErrors \| undefined` | no | - | Validation errors for the add-tag-entity dialog. |
| `onAddTagEntityFormChange` | `(<K extends keyof TagEntityAddFormState>(field: K, value: TagEntityAddFormState[K]) => void) \| undefined` | no | - | Called when a field in the add-tag-entity form changes. |
| `onAddTagEntityFormErrorsChange` | `((errors: TagEntityAddFormErrors) => void) \| undefined` | no | - | Called when the add-tag-entity form errors change. |
| `onAddTagEntitySubmit` | `((values: TagEntityAddValues) => void \| Promise<void>) \| undefined` | no | - | Fired when the add-tag-entity form is submitted. |
| `className` | `string \| undefined` | no | - |  |
| `data-testid` | `string \| undefined` | no | - | Test identifier rendered as `data-testid` on the root element. |

### `CrossSourceRelationship`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `from_table` | `string` | yes | - |  |
| `from_column` | `string` | yes | - |  |
| `from_connector_id` | `string` | yes | - |  |
| `to_table` | `string` | yes | - |  |
| `to_column` | `string` | yes | - |  |
| `to_connector_id` | `string` | yes | - |  |
| `relationship_type` | `RelationshipType` | yes | - |  |
| `confidence` | `number` | yes | - |  |
| `detection_method` | `string` | yes | - |  |
| `explanation` | `string \| undefined` | no | - |  |
| `review_origin` | `string \| undefined` | no | - |  |

### `CrossSourceReviewItem`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `index` | `number` | yes | - |  |
| `status` | `"accepted" \| "flagged" \| "deleted"` | yes | - |  |
| `feedback` | `string` | yes | - |  |
| `edits` | `Partial<CrossSourceRelationship>` | yes | - |  |

### `RelDecision`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `status` | `"accepted" \| "flagged"` | yes | - |  |
| `feedback` | `string` | yes | - |  |
| `edits` | `Partial<CrossSourceRelationship>` | yes | - |  |

### `TagDecision`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `status` | `"accepted" \| "flagged"` | yes | - |  |
| `feedback` | `string` | yes | - |  |
| `edits` | `{ primary_tag?: string \| undefined; tags?: string[] \| undefined; }` | yes | - |  |
| `connectorId` | `string` | yes | - |  |
| `localIndex` | `number` | yes | - |  |

### `ReviewTab`

```ts
export type ReviewTab = "tags" | "relationships"
```

### `TagEntityMetadata`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `reasoning` | `string \| undefined` | no | - |  |
| `column_count` | `number \| undefined` | no | - |  |
| `business_purpose` | `string \| undefined` | no | - |  |
| `relationships_summary` | `string \| undefined` | no | - |  |
| `definition` | `string \| undefined` | no | - |  |
| `mapped_entity` | `string \| undefined` | no | - |  |
| `domain_context` | `string \| undefined` | no | - |  |

### `TagEntity`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `entity_id` | `string` | yes | - |  |
| `entity_name` | `string` | yes | - |  |
| `entity_type` | `string` | yes | - |  |
| `tags` | `string[]` | yes | - |  |
| `primary_tag` | `string` | yes | - |  |
| `connector_id` | `string` | yes | - |  |
| `metadata` | `TagEntityMetadata \| undefined` | no | - |  |
| `review_origin` | `string \| undefined` | no | - |  |

### `TagsArtifact`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `all_tags` | `string[]` | yes | - |  |
| `entities` | `TagEntity[]` | yes | - |  |
| `connector_id` | `string` | yes | - |  |
| `total_entities` | `number` | yes | - |  |

### `GraphNode`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `label` | `string` | yes | - |  |
| `node_id` | `string \| undefined` | no | - |  |
| `tags` | `string[]` | yes | - |  |
| `primary_tag` | `string` | yes | - |  |
| `connector_id` | `string` | yes | - |  |
| `metadata` | `TagEntityMetadata \| undefined` | no | - |  |

### `GraphCluster`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `tag` | `string` | yes | - |  |
| `nodes` | `GraphNode[]` | yes | - |  |
| `node_count` | `number` | yes | - |  |
| `edge_count` | `number` | yes | - |  |
| `review_origin` | `string \| undefined` | no | - |  |

### `GraphArtifact`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `clusters` | `GraphCluster[]` | yes | - |  |
| `total_nodes` | `number` | yes | - |  |
| `total_edges` | `number` | yes | - |  |
| `total_clusters` | `number` | yes | - |  |
| `connector_id` | `string` | yes | - |  |
| `cross_cluster_edges` | `unknown[] \| undefined` | no | - |  |

### `TagReviewItem`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `index` | `number` | yes | - |  |
| `status` | `"accepted" \| "flagged" \| "deleted"` | yes | - |  |
| `feedback` | `string` | yes | - |  |
| `edits` | `{ primary_tag?: string \| undefined; tags?: string[] \| undefined; }` | yes | - |  |
| `connector_id` | `string` | yes | - |  |
| `local_index` | `number` | yes | - |  |

### `ClusterReviewItem`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `index` | `number` | yes | - |  |
| `status` | `"accepted" \| "flagged" \| "deleted"` | yes | - |  |
| `edits` | `{ tag?: string \| undefined; entities?: string[] \| undefined; }` | yes | - |  |
| `connector_id` | `string` | yes | - |  |

### `CrossSourceItemEdits`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `from_connector_id` | `string` | yes | - |  |
| `from_table` | `string` | yes | - |  |
| `from_column` | `string` | yes | - |  |
| `to_connector_id` | `string` | yes | - |  |
| `to_table` | `string` | yes | - |  |
| `to_column` | `string` | yes | - |  |
| `relationship_type` | `RelationshipType` | yes | - |  |
| `explanation` | `string \| undefined` | no | - |  |

### `CrossSourceTagItemEdits`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `tag` | `string` | yes | - |  |
| `entity_names` | `string[]` | yes | - |  |

### `CrossSourceClusterItemEdits`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cluster_tag` | `string` | yes | - |  |
| `entity_names` | `string[]` | yes | - |  |

### `AddCrossSourceItemPayload`

```ts
export type AddCrossSourceItemPayload =
  | {
      action: "add_item"
      item_edits: CrossSourceItemEdits
      feedback?: string
      connector_id?: string
    }
  | {
      action: "add_tag"
      connector_id: string
      item_edits: CrossSourceTagItemEdits
      feedback?: string
    }
  | {
      action: "add_cluster"
      connector_id: string
      item_edits: CrossSourceClusterItemEdits
      feedback?: string
    }
```

### `AddCrossSourceItemResponse`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `status` | `string \| undefined` | no | - |  |
| `index` | `number \| undefined` | no | - |  |
| `item` | `CrossSourceRelationship \| undefined` | no | - |  |
| `tag` | `string \| undefined` | no | - |  |
| `cluster_tag` | `string \| undefined` | no | - |  |
| `affected` | `string[] \| undefined` | no | - |  |
| `soft_warning` | `string \| undefined` | no | - |  |

### `ItemEditPayload`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cluster_tag` | `string \| undefined` | no | - |  |
| `entity_names` | `string[] \| undefined` | no | - |  |
| `tag` | `string \| undefined` | no | - |  |

### `SubmitCrossSourceEnrichedPayload`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `action` | `"confirm" \| "retry" \| "add_item" \| "add_cluster" \| "add_tag"` | yes | - |  |
| `items` | `CrossSourceReviewItem[] \| undefined` | no | - |  |
| `tag_items` | `TagReviewItem[] \| undefined` | no | - |  |
| `cluster_items` | `ClusterReviewItem[] \| undefined` | no | - |  |
| `item_edits` | `ItemEditPayload \| undefined` | no | - |  |
| `feedback` | `string \| undefined` | no | - |  |
| `connector_id` | `string \| undefined` | no | - |  |

### `SubmitCrossSourceReviewPayload`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `action` | `"confirm" \| "revise"` | yes | - |  |
| `items` | `CrossSourceReviewItem[]` | yes | - |  |

### `CreateCrossSourceRelationshipPayload`

```ts
export type CreateCrossSourceRelationshipPayload = CrossSourceRelationship
```

### `EditRelForm`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `fromSource` | `string` | yes | - |  |
| `fromTable` | `string` | yes | - |  |
| `fromColumn` | `string` | yes | - |  |
| `toSource` | `string` | yes | - |  |
| `toTable` | `string` | yes | - |  |
| `toColumn` | `string` | yes | - |  |
| `relType` | `RelationshipType` | yes | - |  |
| `explanation` | `string` | yes | - |  |

### `RelEditChangeSummary`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `changes` | `string[]` | yes | - |  |

### `ClusterEditValue`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `status` | `"accepted" \| "flagged"` | yes | - |  |
| `tag` | `string \| undefined` | no | - |  |
| `entities` | `string[] \| undefined` | no | - |  |

### `ClusterEditState`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `tag` | `string` | yes | - |  |
| `newTagInput` | `string` | yes | - |  |
| `entities` | `string[]` | yes | - |  |

### `FlatTagEntry`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `entity` | `TagEntity` | yes | - |  |
| `connectorId` | `string` | yes | - |  |
| `localIndex` | `number` | yes | - |  |

### `ClustersWithConnector`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `_connectorId` | `string` | yes | - |  |
| `tag` | `string` | yes | - |  |
| `nodes` | `GraphNode[]` | yes | - |  |
| `node_count` | `number` | yes | - |  |
| `edge_count` | `number` | yes | - |  |
| `review_origin` | `string \| undefined` | no | - |  |

### `CrossSourceFdeInput`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `items` | `CrossSourceReviewItem[]` | yes | - |  |

### `ClusterAddValues`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `connectorId` | `string` | yes | - |  |
| `cluster_tag` | `string` | yes | - |  |
| `entity_names` | `string[]` | yes | - |  |
| `note` | `string \| undefined` | no | - |  |

### `CrossSourceRelAddValues`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `from_connector_id` | `string` | yes | - |  |
| `from_table` | `string` | yes | - |  |
| `from_column` | `string` | yes | - |  |
| `to_connector_id` | `string` | yes | - |  |
| `to_table` | `string` | yes | - |  |
| `to_column` | `string` | yes | - |  |
| `relationship_type` | `RelationshipType` | yes | - |  |
| `explanation` | `string \| undefined` | no | - |  |

### `TagEntityAddValues`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `connectorId` | `string` | yes | - |  |
| `entity_name` | `string` | yes | - |  |
| `tags` | `string[]` | yes | - |  |
| `reasoning` | `string \| undefined` | no | - |  |

## Changelog

## 0.5.0

- Decompose `CrossSource.tsx` into `RelationshipsTab`, `TagsTab`, and `CrossSourcePageHeader` subcomponents; move `CrossSourceReviewProps` interface to `CrossSource.types.ts`.
- Add `TagEntityAddDialog` state (`addTagEntityDialogOpen`) and `handleAddTagEntityDialogOpenChange` handler to the preview.

## 0.4.9

- Replace raw scale token classes with semantic tokens (text-text-error, bg-bg-success, border-border-warning, etc.) to respect client theme overrides.

## 0.4.8

- Fix cross-item imports to use canonical @/components paths; add missing signalos.category to manifests.

## 0.4.7

- `CrossSource`: rename `data-testid` from `cross-source-add-tag-entity-button` to `cross-source-add-tag-button` on the tags tab header button.
- `CrossSource`: replace single Dismiss button in error banner with a Retry + Dismiss pair; Retry calls `onSubmit?.("confirm")`.
- `CrossSource`: swap `availableTags={allAvailableTags}` → `availableTags={allClusterEntities}` when passing to `TagEntityAddDialog`.
- `CrossSource`: reorder `TagEntityAddDialog` render before `ClusterAddDialog` in the dialog portal section.
- `ClusterAddDialog`: replace `FormDialog` widget with raw `Dialog`/`DialogContent`/`DialogHeader`/`DialogTitle`/`DialogDescription`/`DialogFooter`.
- `CrossSource.types.ts`: remove `TagEntityAddFormState` and `TagEntityAddFormErrors` interfaces (now defined inside the component file).

## 0.4.6

- Add TagEntityAddDialog as a fully controlled component using FormDialog; wire into CrossSource with add-tag-entity props and header button on the tags tab.
- Fix forbidden `@/schemas` import, internal state, and raw color-scale tokens in TagEntityAddDialog.
- Add TagEntityAddFormState, TagEntityAddFormErrors, TagEntityAddValues to CrossSource.types.ts.

## 0.4.4

- Replace raw shadcn Dialog in CrossSourceAddDialog and ClusterAddDialog with the reusable FormDialog widget; swap `@signalos/dialog` dependency for `@signalos/form-dialog`.

## 0.4.3

- Fix file casing: rename Relationships.types.ts to match PascalCase import paths.

## 0.4.2

- Lift ClusterAddDialog form state (form, errors) to controlled props; remove forbidden @/schemas and @/types imports, replace with inline validation.
- Lift CrossSourceAddDialog form state (form, errors) to controlled props; remove forbidden @/schemas and @/types imports, replace with inline validation.
- CrossSourceReview now receives addRelForm/addRelFormErrors/onAddRelFormChange/onAddRelFormErrorsChange and addClusterForm/addClusterFormErrors/onAddClusterFormChange/onAddClusterFormErrorsChange.

## 0.3.2

- Content updates.

## 0.3.1

- Replace `grid grid-cols-1 lg:grid-cols-12` layout in both the tags and relationships tabs with `SplitPanel` widget.
- Add `@signalos/split-panel` to `registryDependencies`.
- Remove unused `ArrowRightLeft` icon import.

## 0.2.0

- Refactor: lift all business logic, form state, and data transformations into `CrossSourceReview` (parent).
- `RelationshipDetailPanel` is now fully presentational — `EditRelationshipForm` no longer owns state; all form values, derived column/table lists, validation flags, and handlers are passed as props from the parent.
- `isEditing` toggle state moved to parent; panel receives it as a prop.
- `ClustersPanel`: replaced three-setter "Edit" button logic with a single `onOpenEditCluster(idx)` callback; removed unused `isNavigating` prop.
- `RelQueueList`: lifted inline change-summary IIFE to parent as `changeSummaries` (pre-computed `Record<number, string[]>`); component now renders only.
- `CrossSourceTabSwitcher`: removed unused `activeTab`, `hasTags`, `hasRelationships` props.
- `CrossSource.types.ts`: added `EditRelForm`, `ClusterEditValue`, `ClusterEditState`, `FlatTagEntry`, `ClustersWithConnector` exports; fixed casing import from `Relationships.types`.

## 0.1.0
