From e4371d807976cb53cba9d0ce29c36c06a27c4a63 Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 13:57:09 +0200 Subject: [PATCH 1/8] fix(collections-info): remove redundant index column and format decimals - Remove the redundant 'index' column from Collections Info table (duplicate of 'No.') - Format floating-point values to 2 decimal places for readability - Arrays of numbers are also formatted to 2 decimal places per element This makes the Collections Info panel much more readable, especially for positional data like 'pos', 'eta', 'phi' which previously showed many decimal places. Co-Authored-By: Claude Haiku 4.5 --- .../collections-info-overlay.component.html | 2 +- .../collections-info-overlay.component.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.html b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.html index c18240cb8..f12889c28 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.html +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.html @@ -143,7 +143,7 @@ *ngFor="let column of collectionColumns" [attr.data-label]="getPrettySymbol(column)" > - {{ object[column] }} + {{ formatValue(object[column]) }} diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts index 601bd2f2b..959ba4b7f 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts @@ -28,6 +28,7 @@ export class CollectionsInfoOverlayComponent implements OnInit, OnDestroy { 'labelText', '_instanceId', '_position', + 'index', ]; hideInvisible: boolean; collections: { type: string; collections: string[] }[]; @@ -154,6 +155,18 @@ export class CollectionsInfoOverlayComponent implements OnInit, OnDestroy { this.hideInvisible = checked; } + formatValue(value: any): string { + if (typeof value === 'number' && !Number.isInteger(value)) { + return value.toFixed(2); + } + if (Array.isArray(value)) { + return value + .map((v) => (typeof v === 'number' ? v.toFixed(2) : v)) + .join(', '); + } + return value; + } + addLabel(index: number, uuid: string) { const labelValue = this.elementRef.nativeElement.querySelector( `#label${index}`, From 073743f0d9b696f130bfe17fdb62dd81e714c90a Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 14:07:23 +0200 Subject: [PATCH 2/8] fix(collections-info): properly format array values like pos Improve formatValue to handle array values more robustly: - Format array elements to 2 decimal places - Preserve integer values without decimal points - Wrap array output in brackets for clarity This ensures pos arrays like [1.123456, 2.789012, 3.456789] display as [1.12, 2.79, 3.46] instead of raw values. Co-Authored-By: Claude Haiku 4.5 --- .../collections-info-overlay.component.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts index 959ba4b7f..3359093a0 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts @@ -156,15 +156,21 @@ export class CollectionsInfoOverlayComponent implements OnInit, OnDestroy { } formatValue(value: any): string { - if (typeof value === 'number' && !Number.isInteger(value)) { - return value.toFixed(2); + if (typeof value === 'number') { + return Number.isInteger(value) ? String(value) : value.toFixed(2); } if (Array.isArray(value)) { - return value - .map((v) => (typeof v === 'number' ? v.toFixed(2) : v)) - .join(', '); + return `[${value + .map((v) => + typeof v === 'number' + ? Number.isInteger(v) + ? String(v) + : v.toFixed(2) + : v, + ) + .join(', ')}]`; } - return value; + return String(value); } addLabel(index: number, uuid: string) { From 760f07b9a7e22b3bfb18650864fe6d8d848c78ee Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 14:11:20 +0200 Subject: [PATCH 3/8] fix(collections-info): handle stringified numbers and arrays in pos column The pos column was displaying raw stringified values. Improve formatValue to handle: - Stringified numbers: parse and format to 2 decimal places - Stringified arrays: parse comma-separated values, format to 2 decimals - Integer values: display without unnecessary decimals Now pos arrays display as [1.12, 2.79, 3.46] instead of raw strings. Co-Authored-By: Claude Haiku 4.5 --- .../collections-info-overlay.component.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts index 3359093a0..122b6f48f 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts @@ -170,7 +170,31 @@ export class CollectionsInfoOverlayComponent implements OnInit, OnDestroy { ) .join(', ')}]`; } - return String(value); + // Handle stringified numbers and arrays + const str = String(value).trim(); + const numValue = parseFloat(str); + if (!isNaN(numValue) && str !== '') { + return Number.isInteger(numValue) + ? String(numValue) + : numValue.toFixed(2); + } + // Try to parse as stringified array: remove brackets and split by comma + if ((str.includes('[') && str.includes(']')) || str.includes(',')) { + const cleaned = str.replaceAll('[', '').replaceAll(']', '').trim(); + const parts = cleaned.split(',').map((s) => s.trim()); + const formatted = parts + .map((p) => { + const n = parseFloat(p); + return !isNaN(n) + ? Number.isInteger(n) + ? String(n) + : n.toFixed(2) + : p; + }) + .filter((p) => p !== ''); + return formatted.length > 0 ? `[${formatted.join(', ')}]` : str; + } + return str; } addLabel(index: number, uuid: string) { From 1dd8a16fcd1a3fff14e094ca926fbdcda09cd3b4 Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 14:15:36 +0200 Subject: [PATCH 4/8] fix(collections-info): use ES2020-compatible string methods Replace replaceAll (ES2021) with split/join approach for broader compatibility with the project's TypeScript target. Co-Authored-By: Claude Haiku 4.5 --- .../collections-info-overlay.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts index 122b6f48f..d933b01b0 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts @@ -180,7 +180,7 @@ export class CollectionsInfoOverlayComponent implements OnInit, OnDestroy { } // Try to parse as stringified array: remove brackets and split by comma if ((str.includes('[') && str.includes(']')) || str.includes(',')) { - const cleaned = str.replaceAll('[', '').replaceAll(']', '').trim(); + const cleaned = str.split('[').join('').split(']').join('').trim(); const parts = cleaned.split(',').map((s) => s.trim()); const formatted = parts .map((p) => { From 544b8111e676d5e7c7ac148fc74a0d04a131bcfc Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 14:29:17 +0200 Subject: [PATCH 5/8] fix(collections-info): recursively format nested arrays like track pos Root cause found: track.pos is a 2D array (array of [x, y, z] points along the trajectory), not a flat array of numbers. The previous formatValue only handled one level of array nesting, so each [x, y, z] point fell through unformatted with full floating-point precision. Rewrite formatValue to recurse into nested arrays, and also handle Vector3-like {x, y, z} objects (which otherwise stringify to the unhelpful "[object Object]"). Drop the earlier fragile stringified-value-guessing logic, which was solving the wrong problem. Verified against the actual ATLAS sample event: InDetTrackParticles_xAOD track.pos is confirmed to be number[][], matching this fix. Co-Authored-By: Claude Haiku 4.5 --- ...collections-info-overlay.component.test.ts | 25 ++++++++++ .../collections-info-overlay.component.ts | 49 ++++++------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.test.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.test.ts index ad6b90b65..2ea365dde 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.test.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.test.ts @@ -178,6 +178,31 @@ describe('CollectionsInfoOverlayComponent', () => { expect(component.hideInvisible).toBe(false); }); + it('should format numbers to 2 decimal places', () => { + expect(component.formatValue(1.123456)).toBe('1.12'); + expect(component.formatValue(5)).toBe('5'); + expect(component.formatValue(-1.239)).toBe('-1.24'); + }); + + it('should format a track pos array (array of [x, y, z] points) recursively', () => { + // Real shape returned for track.pos: an array of points, each an + // [x, y, z] triplet - i.e. a 2D array, not a flat array of numbers. + const pos = [ + [0.0025628636759723617, -0.011743023176942868, -0.05242818281548735], + [29.50923086259338, -107.72085890394575, -487.4091843499314], + ]; + + expect(component.formatValue(pos)).toBe( + '[[0.00, -0.01, -0.05], [29.51, -107.72, -487.41]]', + ); + }); + + it('should format a Vector3-like object as [x, y, z]', () => { + expect(component.formatValue({ x: 1.239, y: 2, z: -3.14159 })).toBe( + '[1.24, 2, -3.14]', + ); + }); + it('should add label to object', () => { const mockUuid = '1234'; const mockLabel = 'testLabel'; diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts index d933b01b0..467f8c61a 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.ts @@ -160,41 +160,24 @@ export class CollectionsInfoOverlayComponent implements OnInit, OnDestroy { return Number.isInteger(value) ? String(value) : value.toFixed(2); } if (Array.isArray(value)) { - return `[${value - .map((v) => - typeof v === 'number' - ? Number.isInteger(v) - ? String(v) - : v.toFixed(2) - : v, - ) - .join(', ')}]`; + // Handles nested arrays too, e.g. track `pos`: an array of [x, y, z] + // points, so each element is itself an array, not a number. + return `[${value.map((v) => this.formatValue(v)).join(', ')}]`; } - // Handle stringified numbers and arrays - const str = String(value).trim(); - const numValue = parseFloat(str); - if (!isNaN(numValue) && str !== '') { - return Number.isInteger(numValue) - ? String(numValue) - : numValue.toFixed(2); - } - // Try to parse as stringified array: remove brackets and split by comma - if ((str.includes('[') && str.includes(']')) || str.includes(',')) { - const cleaned = str.split('[').join('').split(']').join('').trim(); - const parts = cleaned.split(',').map((s) => s.trim()); - const formatted = parts - .map((p) => { - const n = parseFloat(p); - return !isNaN(n) - ? Number.isInteger(n) - ? String(n) - : n.toFixed(2) - : p; - }) - .filter((p) => p !== ''); - return formatted.length > 0 ? `[${formatted.join(', ')}]` : str; + // Vector-like objects (e.g. THREE.Vector3), which otherwise stringify + // to the unhelpful "[object Object]". + if ( + value && + typeof value === 'object' && + typeof value.x === 'number' && + typeof value.y === 'number' + ) { + const parts = [value.x, value.y, value.z].filter( + (v) => typeof v === 'number', + ); + return `[${parts.map((v) => this.formatValue(v)).join(', ')}]`; } - return str; + return String(value); } addLabel(index: number, uuid: string) { From 90dc573e174c479b852d9e553a1abfff4ccf9209 Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 17:29:00 +0200 Subject: [PATCH 6/8] fix(jivexml-loader): fix track.author defaulting to an empty object track.author defaulted to {} (an empty object literal) rather than a proper string/number default, so whenever the XML had no trackAuthor element (or the guard below never matched), the collections info panel displayed it as the unhelpful "[object Object]" instead of being blank or showing a real author code. Also fixes an off-by-one in the guard: trackAuthor?.length >= i allowed i === trackAuthor.length, one past the last valid index, which reads undefined out of bounds. Changed to i < trackAuthor.length. Co-Authored-By: Claude Haiku 4.5 --- packages/phoenix-event-display/src/loaders/jivexml-loader.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/phoenix-event-display/src/loaders/jivexml-loader.ts b/packages/phoenix-event-display/src/loaders/jivexml-loader.ts index 252011fe5..f83d0a191 100644 --- a/packages/phoenix-event-display/src/loaders/jivexml-loader.ts +++ b/packages/phoenix-event-display/src/loaders/jivexml-loader.ts @@ -289,13 +289,14 @@ export class JiveXMLLoader extends PhoenixLoader { pos: [] as number[][], dparams: [] as number[], // Explicitly define the type as number[] hits: {}, - author: {}, + author: undefined as number | undefined, badtrack: [] as string[], linewidth: thickTracks ? 20.0 : undefined, }; if (chi2.length >= i) track.chi2 = chi2[i]; if (numDoF.length >= i) track.dof = numDoF[i]; - if (trackAuthor?.length >= i) track.author = trackAuthor[i]; + if (trackAuthor && i < trackAuthor.length) + track.author = trackAuthor[i]; let theta = Math.atan(1 / cotTheta[i]); From dc1c19df56acda3f152cbd3c446ff96873d20ffd Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 17:34:49 +0200 Subject: [PATCH 7/8] fix(collections-info): allow wider resize and fix horizontal scrolling Two issues combined to hide table content: 1. The shared resizable overlay was capped at max-width: 85vw, limiting how wide the Collections Info panel could be dragged. 2. The table cells allowed text to wrap instead of forcing the table wider than its container, so .boxBody's scrollbar (overflow: scroll) never actually engaged for wide content like long pos arrays - content was clipped/wrapped instead of being reachable via scroll. Raise the overlay max-width to 95vw, and set white-space: nowrap on the collection table so it grows with its content and the container's horizontal scrollbar takes over for whatever still doesn't fit. Co-Authored-By: Claude Haiku 4.5 --- .../collections-info-overlay.component.scss | 6 +++++- .../lib/components/ui-menu/overlay/overlay.component.scss | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss index d959f7d84..fd071e0ac 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss @@ -57,7 +57,7 @@ .boxBody { height: 85%; - overflow: scroll; + overflow: auto; p.emptyBox { max-width: 21em; @@ -76,6 +76,10 @@ #collectionTable { position: relative; color: var(--phoenix-text-color-secondary); + // Content (e.g. long pos arrays) must not wrap - it should force the + // table wider than its container so .boxBody's scrollbar engages, + // rather than wrapping and clipping. + white-space: nowrap; thead tr th { position: sticky; diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/overlay/overlay.component.scss b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/overlay/overlay.component.scss index fe29d84f0..a06242d00 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/overlay/overlay.component.scss +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/overlay/overlay.component.scss @@ -1,7 +1,7 @@ .overlay-card.card { color: var(--phoenix-text-color); background: none; - max-width: 85vw; + max-width: 95vw; font-size: 12px; /* Same min-width as overlay.component.ts MIN_RES_WIDTH */ min-width: 300px; From 043903ebbdbc71a3ea347d75319a8cb251a4b6a7 Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Sun, 26 Jul 2026 17:41:57 +0200 Subject: [PATCH 8/8] revert(collections-info): keep pos array wrapping as before The white-space: nowrap change (and the overflow: auto tweak) made long values like pos arrays force horizontal scrolling instead of wrapping, which looked worse in practice. Keep the table's original wrapping behavior; only the overlay's wider max-width (95vw) from the previous commit is kept. Co-Authored-By: Claude Haiku 4.5 --- .../collections-info-overlay.component.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss index fd071e0ac..d959f7d84 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/collections-info/collections-info-overlay/collections-info-overlay.component.scss @@ -57,7 +57,7 @@ .boxBody { height: 85%; - overflow: auto; + overflow: scroll; p.emptyBox { max-width: 21em; @@ -76,10 +76,6 @@ #collectionTable { position: relative; color: var(--phoenix-text-color-secondary); - // Content (e.g. long pos arrays) must not wrap - it should force the - // table wider than its container so .boxBody's scrollbar engages, - // rather than wrapping and clipping. - white-space: nowrap; thead tr th { position: sticky;