diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee48cd..ebd642a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -245,6 +245,14 @@ auto-generated per-PR notes; this file is the curated, human-readable history. each pairing an `onclick: close` backdrop with an `onclick: stopPropagation` panel. The cell-detail drawer's resize-drag one-shot click-swallow listener (#101) is superseded by the same general fix. (#110) +- The fullscreen schema graph's rich node card had no overflow cap on its + `idx:` skip-index line — unlike columns (capped at `MAX_COLS` with a "+N + more" row), every skip-index was joined onto one unbounded line. A + heavily-indexed table (e.g. an OTel-style log table with a bloom filter per + Map key/value plus a tokenbf on the body) produced a single line 1700px+ + wide, blowing the card — and the whole graph layout — out of proportion. + `buildCardModel` now caps the line at `CARD.MAX_IDX` (6) with a "+N more" + suffix, mirroring the columns' overflow pattern. ## [0.1.5] - 2026-06-29 diff --git a/src/core/schema-cards.js b/src/core/schema-cards.js index 06752c3..b210c73 100644 --- a/src/core/schema-cards.js +++ b/src/core/schema-cards.js @@ -20,6 +20,9 @@ export const CARD = { BADGE_W: 26, // approx width of one role badge (PK/SK/PARTITION/SAMPLING) MIN_W: 130, MAX_COLS: 16, + MAX_IDX: 6, // skip-indices have no dedicated overflow row (unlike columns) — cap the + // single idx: line itself so a heavily-indexed table (bloom filters per + // Map key/value, tokenbf on Body, …) can't blow the card absurdly wide. MAX_TYPE: 28, // truncate the displayed column type — a big Enum/Tuple/Map would // otherwise blow the card (and the whole graph) absurdly wide. }; @@ -64,8 +67,10 @@ export function buildCardModel(node, tableRow, columns, skipIndices) { })); const overflow = Math.max(0, allCols.length - CARD.MAX_COLS); const idx = skipIndices || []; + const idxOverflow = Math.max(0, idx.length - CARD.MAX_IDX); const skipLine = idx.length - ? 'idx: ' + idx.map((i) => i.name + ' (' + (i.type || '') + ')').join(', ') + ? 'idx: ' + idx.slice(0, CARD.MAX_IDX).map((i) => i.name + ' (' + (i.type || '') + ')').join(', ') + + (idxOverflow ? ', +' + idxOverflow + ' more' : '') : ''; return { title: n.label || n.id || '', kind: n.kind || 'table', summary, cols, overflow, skipLine }; } diff --git a/tests/unit/schema-cards.test.js b/tests/unit/schema-cards.test.js index 8c1b7d1..23d4eb9 100644 --- a/tests/unit/schema-cards.test.js +++ b/tests/unit/schema-cards.test.js @@ -36,6 +36,18 @@ describe('buildCardModel', () => { expect(m.overflow).toBe(1); expect(m.skipLine).toBe('idx: idx_a (minmax)'); }); + it('caps the skip-index line at MAX_IDX with a "+N more" suffix (no dedicated overflow row)', () => { + const idx = Array.from({ length: CARD.MAX_IDX + 2 }, (_, i) => ({ name: 'idx_' + i, type: 'bloom_filter' })); + const m = buildCardModel({ label: 'db.t', kind: 'table' }, {}, [], idx); + const shown = idx.slice(0, CARD.MAX_IDX).map((i) => i.name + ' (bloom_filter)').join(', '); + expect(m.skipLine).toBe('idx: ' + shown + ', +2 more'); + }); + it('does not append an overflow suffix when the index count is within MAX_IDX', () => { + const idx = Array.from({ length: CARD.MAX_IDX }, (_, i) => ({ name: 'idx_' + i, type: 'set' })); + const m = buildCardModel({ label: 'db.t', kind: 'table' }, {}, [], idx); + expect(m.skipLine).not.toContain('more'); + expect(m.skipLine.match(/idx_/g)).toHaveLength(CARD.MAX_IDX); + }); it('degrades to a header-only card for a leaf with no row/columns/indices', () => { const leaf = buildCardModel({ id: 'ext:mysql', label: 'mysql', kind: 'external' }); expect(leaf.summary).toBe('external · — rows · —'); // engine falls back to kind