diff --git a/frontend/src/lib/linkml/linkml-schema-service.ts b/frontend/src/lib/linkml/linkml-schema-service.ts index 67e2ec695c..bfc8947e42 100644 --- a/frontend/src/lib/linkml/linkml-schema-service.ts +++ b/frontend/src/lib/linkml/linkml-schema-service.ts @@ -1018,14 +1018,43 @@ class LinkMLSchemaService { if (!annotations?.custodian_types) { return null; } - // Handle both string array and comma-separated string formats - const types = annotations.custodian_types; + return this.parseCustodianTypesAnnotation(annotations.custodian_types); + } + + /** + * Parse custodian_types annotation value into string array + * Handles multiple formats: + * - Array: ["A", "L", "M"] + * - JSON stringified array: '["A", "L"]' or '["A"]' + * - Comma-separated string: "A, L, M" + * - Single value: "A" + */ + private parseCustodianTypesAnnotation(types: unknown): string[] | null { + // Already an array if (Array.isArray(types)) { return types as string[]; } + if (typeof types === 'string') { - return types.split(',').map((t: string) => t.trim()); + const trimmed = types.trim(); + + // Check if it's a JSON stringified array (starts with '[' and ends with ']') + if (trimmed.startsWith('[') && trimmed.endsWith(']')) { + try { + const parsed = JSON.parse(trimmed); + if (Array.isArray(parsed)) { + return parsed.map((t: unknown) => String(t).trim()); + } + } catch { + // JSON parse failed, fall through to comma-separated handling + console.warn(`[LinkMLSchemaService] Failed to parse custodian_types JSON: ${trimmed}`); + } + } + + // Comma-separated string or single value + return trimmed.split(',').map((t: string) => t.trim()).filter(t => t.length > 0); } + return null; } @@ -1039,14 +1068,7 @@ class LinkMLSchemaService { if (!slot?.annotations?.custodian_types) { return null; } - const types = slot.annotations.custodian_types; - if (Array.isArray(types)) { - return types as string[]; - } - if (typeof types === 'string') { - return types.split(',').map((t: string) => t.trim()); - } - return null; + return this.parseCustodianTypesAnnotation(slot.annotations.custodian_types); } /** @@ -1061,12 +1083,7 @@ class LinkMLSchemaService { // Currently enum annotations might be on individual values if ((enumDef as unknown as { annotations?: Record })?.annotations?.custodian_types) { const types = (enumDef as unknown as { annotations: Record }).annotations.custodian_types; - if (Array.isArray(types)) { - return types as string[]; - } - if (typeof types === 'string') { - return types.split(',').map((t: string) => t.trim()); - } + return this.parseCustodianTypesAnnotation(types); } return null; } diff --git a/frontend/src/pages/LinkMLViewerPage.tsx b/frontend/src/pages/LinkMLViewerPage.tsx index 342f7e9bc4..7c61d0fceb 100644 --- a/frontend/src/pages/LinkMLViewerPage.tsx +++ b/frontend/src/pages/LinkMLViewerPage.tsx @@ -1145,7 +1145,7 @@ const LinkMLViewerPage: React.FC = () => { showTooltip={true} hoveredType={hoveredCustodianType} onTypeHover={setHoveredCustodianType} - onFaceClick={handleCustodianTypeFilter} + // Removed onFaceClick - cube should be decorative, not trigger filtering className="linkml-viewer__custodian-indicator" /> ) : ( @@ -1229,7 +1229,7 @@ const LinkMLViewerPage: React.FC = () => { showTooltip={true} hoveredType={hoveredCustodianType} onTypeHover={setHoveredCustodianType} - onFaceClick={handleCustodianTypeFilter} + // Removed onFaceClick - cube should be decorative, not trigger filtering className="linkml-viewer__custodian-indicator" /> ) : ( @@ -1242,12 +1242,6 @@ const LinkMLViewerPage: React.FC = () => { ) )} - {slot.slot_uri && ( -
- {t('uri')} - {slot.slot_uri} -
- )} {slot.range && (
{t('range')} @@ -1322,7 +1316,7 @@ const LinkMLViewerPage: React.FC = () => { showTooltip={true} hoveredType={hoveredCustodianType} onTypeHover={setHoveredCustodianType} - onFaceClick={handleCustodianTypeFilter} + // Removed onFaceClick - cube should be decorative, not trigger filtering className="linkml-viewer__custodian-indicator" /> ) : (