Skip to content

Commit f433bb8

Browse files
committed
fix: itemsPerRow not working for media browse, only for favorites
- Use gap: 8px on container instead of margin on children - Fix inconsistent indentation in HTML template - Add comments explaining magic numbers and conditions - Update sync-upstream.sh to match
1 parent 5db9a96 commit f433bb8

File tree

4 files changed

+79
-30
lines changed

4 files changed

+79
-30
lines changed

.github/copilot-instructions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ When adding a new configuration option, update ALL of the following:
4141
- For readme stuff, use ONLY_SONOS_CARD, ONLY_SONOS_CARD_END, and ONLY_SONOS_CARD_START to show/hide docs for different cards.
4242
- Study the README.md and the create_dist script for more information.
4343

44+
# Upstream folder
45+
- The `src/upstream/` folder contains code synced from Home Assistant frontend via `scripts/sync-upstream.sh`.
46+
- **AVOID modifying files in `src/upstream/` directly** - every change requires updating the sync script to reapply patches.
47+
- Instead, extract custom logic to `src/utils/` or other folders outside upstream.
48+
- Pass data/callbacks from the component that uses upstream code (e.g., `media-browser.ts``ha-media-player-browse.ts`).
49+
- If upstream changes ARE necessary, also update `scripts/sync-upstream.sh` with the corresponding sed commands to reapply the change after syncing.
50+
4451
# Deployment
4552
- ALWAYS run `npm run deploy` after implementing any code changes (it builds automatically, don't build separately)
4653
- On first run, you'll be prompted to create a long-lived access token in HA (Profile → Long-Lived Access Tokens)

scripts/sync-upstream.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,47 @@ sed -i '' '/public preferredLayout.*auto/a\
9494
@property({ type: Number }) public itemsPerRow?: number;
9595
' "$UPSTREAM_DIR/ha-media-player-browse.ts"
9696

97+
# Add flex-grid rendering for custom itemsPerRow (percentage-based, adapts to container width)
98+
echo "Adding flex-grid rendering for custom itemsPerRow..."
99+
sed -i '' "/: this.hass.localize('ui.components.media-browser.no_items')}/a\\
100+
\\
101+
\`\\
102+
: this.itemsPerRow !== 4 // 4 is default, handled by lit-virtualizer below\\
103+
? html\`\\
104+
<div class=\"children flex-grid\" style=\"--items-per-row: \${this.itemsPerRow}\">\\
105+
\${children.map((child) => this._renderGridItem(child))}\\
106+
</div>\\
107+
\${currentItem.not_shown\\
108+
? html\`\\
109+
<div class=\"grid not-shown\">\\
110+
<div class=\"title\">\\
111+
\${this.hass.localize('ui.components.media-browser.not_shown', {\\
112+
count: currentItem.not_shown,\\
113+
})}\\
114+
</div>\\
115+
</div>\\
116+
\`\\
117+
: ''}\\
118+
\`
119+
" "$UPSTREAM_DIR/ha-media-player-browse.ts"
120+
121+
# Add flex-grid CSS styles
122+
echo "Adding flex-grid CSS..."
123+
sed -i '' '/padding: 16px;$/a\
124+
\ }\
125+
\
126+
\ div.children.flex-grid {\
127+
\ display: flex;\
128+
\ flex-wrap: wrap;\
129+
\ padding: 4px;\
130+
\ gap: 8px;\
131+
\ }\
132+
\
133+
\ .flex-grid .child {\
134+
\ /* 8px gap between items, so subtract gap*(n-1)/n ≈ gap for simplicity */\
135+
\ width: calc(100% / var(--items-per-row) - 8px);
136+
' "$UPSTREAM_DIR/ha-media-player-browse.ts"
137+
97138
# Remove type import of LitVirtualizer (causes duplicate registration)
98139
echo "Fixing LitVirtualizer import..."
99140
sed -i '' '/^import type { LitVirtualizer }/d' "$UPSTREAM_DIR/ha-media-player-browse.ts"

src/upstream/ha-media-player-browse.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,23 @@ export class HaMediaPlayerBrowse extends LitElement {
469469
: this.hass.localize('ui.components.media-browser.no_items')}
470470
</div>
471471
`
472+
: this.itemsPerRow !== 4 // 4 is default, handled by lit-virtualizer below
473+
? html`
474+
<div class="children flex-grid" style="--items-per-row: ${this.itemsPerRow}">
475+
${children.map((child) => this._renderGridItem(child))}
476+
</div>
477+
${currentItem.not_shown
478+
? html`
479+
<div class="grid not-shown">
480+
<div class="title">
481+
${this.hass.localize('ui.components.media-browser.not_shown', {
482+
count: currentItem.not_shown,
483+
})}
484+
</div>
485+
</div>
486+
`
487+
: ''}
488+
`
472489
: this.preferredLayout === 'grid' ||
473490
(this.preferredLayout === 'auto' && childrenMediaClass.layout === 'grid')
474491
? html`
@@ -1006,6 +1023,18 @@ export class HaMediaPlayerBrowse extends LitElement {
10061023
padding: 16px;
10071024
}
10081025
1026+
div.children.flex-grid {
1027+
display: flex;
1028+
flex-wrap: wrap;
1029+
padding: 4px;
1030+
gap: 8px;
1031+
}
1032+
1033+
.flex-grid .child {
1034+
/* 8px gap between items, so subtract gap*(n-1)/n ≈ gap for simplicity */
1035+
width: calc(100% / var(--items-per-row) - 8px);
1036+
}
1037+
10091038
:host([dialog]) .children {
10101039
grid-template-columns: repeat(auto-fit, minmax(var(--media-browse-item-size, 175px), 0.33fr));
10111040
}

src/utils/media-browse-utils.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,39 +65,11 @@ export function filterOutIgnoredMediaSources<T extends { media_content_id?: stri
6565
return items.filter((item) => !IGNORED_MEDIA_SOURCES.some((src) => item.media_content_id?.startsWith(src)));
6666
}
6767

68-
/**
69-
* Calculate grid item size based on itemsPerRow setting.
70-
* Used by media browser to support configurable grid columns.
71-
*/
7268
export function getGridItemSize(
73-
itemsPerRow: number | undefined,
69+
_itemsPerRow: number | undefined,
7470
isPortrait: boolean,
7571
): { width: string; height: string } {
76-
const defaultWidth = 100;
77-
const defaultHeight = isPortrait ? 180 : 150;
78-
79-
if (!itemsPerRow || itemsPerRow === 4) {
80-
return { width: `${defaultWidth}px`, height: `${defaultHeight}px` };
81-
}
82-
83-
// Width map based on typical card width (~400px)
84-
const widthMap: Record<number, number> = {
85-
1: 380,
86-
2: 180,
87-
3: 120,
88-
4: 100,
89-
5: 75,
90-
6: 60,
91-
7: 50,
92-
8: 42,
93-
};
94-
95-
const width = widthMap[itemsPerRow] || defaultWidth;
96-
// Scale height proportionally to maintain aspect ratio
97-
const scale = width / defaultWidth;
98-
const height = Math.round(defaultHeight * scale);
99-
100-
return { width: `${width}px`, height: `${height}px` };
72+
return { width: '100px', height: isPortrait ? '180px' : '150px' };
10173
}
10274

10375
export function itemsWithFallbacks(mediaPlayerItems: MediaPlayerItem[], config: CardConfig): MediaPlayerItem[] {

0 commit comments

Comments
 (0)