Compare commits

..

1 Commits

Author SHA1 Message Date
Matiss Janis Aboltins
c857fc9d0f [AI] Improve theme catalog responsive layout in ThemeInstaller 2026-03-21 20:30:21 +00:00
4 changed files with 40 additions and 103 deletions

View File

@@ -50,8 +50,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: web
ref: ${{github.base_ref}}
timeoutSeconds: 1200
intervalSeconds: 30
- name: Wait for ${{github.base_ref}} API build to succeed
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: master-api-build
@@ -59,8 +57,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: api
ref: ${{github.base_ref}}
timeoutSeconds: 1200
intervalSeconds: 30
- name: Wait for ${{github.base_ref}} CLI build to succeed
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: master-cli-build
@@ -68,8 +64,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: cli
ref: ${{github.base_ref}}
timeoutSeconds: 1200
intervalSeconds: 30
- name: Wait for PR build to succeed
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
@@ -78,8 +72,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: web
ref: ${{github.event.pull_request.head.sha}}
timeoutSeconds: 1200
intervalSeconds: 30
- name: Wait for API PR build to succeed
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: wait-for-api-build
@@ -87,8 +79,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: api
ref: ${{github.event.pull_request.head.sha}}
timeoutSeconds: 1200
intervalSeconds: 30
- name: Wait for CLI PR build to succeed
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: wait-for-cli-build
@@ -96,32 +86,12 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: cli
ref: ${{github.event.pull_request.head.sha}}
timeoutSeconds: 1200
intervalSeconds: 30
- name: Report build failure
if: |
steps.wait-for-web-build.outputs.conclusion == 'failure' ||
steps.wait-for-api-build.outputs.conclusion == 'failure' ||
steps.wait-for-cli-build.outputs.conclusion == 'failure' ||
steps.master-web-build.outputs.conclusion == 'failure' ||
steps.master-api-build.outputs.conclusion == 'failure' ||
steps.master-cli-build.outputs.conclusion == 'failure'
if: steps.wait-for-web-build.outputs.conclusion == 'failure' || steps.wait-for-api-build.outputs.conclusion == 'failure' || steps.wait-for-cli-build.outputs.conclusion == 'failure'
run: |
echo "Build failed on PR branch or ${{github.base_ref}}"
exit 1
- name: Warn on incomplete builds
if: |
steps.wait-for-web-build.outputs.conclusion != 'success' ||
steps.wait-for-api-build.outputs.conclusion != 'success' ||
steps.wait-for-cli-build.outputs.conclusion != 'success' ||
steps.master-web-build.outputs.conclusion != 'success' ||
steps.master-api-build.outputs.conclusion != 'success' ||
steps.master-cli-build.outputs.conclusion != 'success'
run: |
echo "::warning::Some builds did not complete successfully. Bundle stats may be incomplete."
echo "Base branch - web: ${{ steps.master-web-build.outputs.conclusion }}, api: ${{ steps.master-api-build.outputs.conclusion }}, cli: ${{ steps.master-cli-build.outputs.conclusion }}"
echo "PR - web: ${{ steps.wait-for-web-build.outputs.conclusion }}, api: ${{ steps.wait-for-api-build.outputs.conclusion }}, cli: ${{ steps.wait-for-cli-build.outputs.conclusion }}"
- name: Download web build artifact from ${{github.base_ref}}
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
@@ -132,7 +102,6 @@ jobs:
workflow_conclusion: '' # ignore the conclusion of the workflow, since we already checked it
name: build-stats
path: base
if_no_artifact_found: warn
- name: Download API build artifact from ${{github.base_ref}}
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
id: pr-api-build
@@ -142,46 +111,41 @@ jobs:
workflow_conclusion: '' # ignore the conclusion of the workflow, since we already checked it
name: api-build-stats
path: base
if_no_artifact_found: warn
- name: Download build stats from PR
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
with:
commit: ${{github.event.pull_request.head.sha}}
pr: ${{github.event.pull_request.number}}
workflow: build.yml
workflow_conclusion: '' # ignore the conclusion of the workflow, since we already checked it
name: build-stats
path: head
allow_forks: true
if_no_artifact_found: warn
- name: Download API stats from PR
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
with:
commit: ${{github.event.pull_request.head.sha}}
pr: ${{github.event.pull_request.number}}
workflow: build.yml
workflow_conclusion: '' # ignore the conclusion of the workflow, since we already checked it
name: api-build-stats
path: head
allow_forks: true
if_no_artifact_found: warn
- name: Download CLI build artifact from ${{github.base_ref}}
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
with:
branch: ${{github.base_ref}}
workflow: build.yml
workflow_conclusion: '' # ignore the conclusion of the workflow, since we already checked it
name: cli-build-stats
path: base
if_no_artifact_found: warn
- name: Download CLI stats from PR
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
with:
commit: ${{github.event.pull_request.head.sha}}
pr: ${{github.event.pull_request.number}}
workflow: build.yml
workflow_conclusion: '' # ignore the conclusion of the workflow, since we already checked it
name: cli-build-stats
path: head
allow_forks: true
if_no_artifact_found: warn
- name: Strip content hashes from stats files
run: |
if [ -f ./head/web-stats.json ]; then
@@ -198,31 +162,19 @@ jobs:
fi
done
- name: Generate combined bundle stats comment
if: ${{ !cancelled() }}
id: generate-comment
run: |
ARGS=""
for bundle in "desktop-client=web-stats.json" "loot-core=loot-core-stats.json" "api=api-stats.json" "cli=cli-stats.json"; do
NAME="${bundle%%=*}"
FILE="${bundle#*=}"
if [ -f "./base/$FILE" ] && [ -f "./head/$FILE" ]; then
ARGS="$ARGS --base $NAME=./base/$FILE --head $NAME=./head/$FILE"
else
echo "::warning::Skipping $NAME: base or head stats file missing"
fi
done
if [ -z "$ARGS" ]; then
echo "::warning::No stats files available, skipping comment generation"
echo "has_comment=false" >> "$GITHUB_OUTPUT"
exit 0
fi
node packages/ci-actions/bin/bundle-stats-comment.mjs \
$ARGS \
--base desktop-client=./base/web-stats.json \
--base loot-core=./base/loot-core-stats.json \
--base api=./base/api-stats.json \
--base cli=./base/cli-stats.json \
--head desktop-client=./head/web-stats.json \
--head loot-core=./head/loot-core-stats.json \
--head api=./head/api-stats.json \
--head cli=./head/cli-stats.json \
--identifier combined \
--format pr-body > bundle-stats-comment.md
echo "has_comment=true" >> "$GITHUB_OUTPUT"
- name: Post combined bundle stats comment
if: ${{ !cancelled() && steps.generate-comment.outputs.has_comment == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}

View File

@@ -5,7 +5,7 @@
* Heavily inspired by https://github.com/twk3/rollup-size-compare-action (MIT).
*/
import { access, readFile } from 'node:fs/promises';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
@@ -179,19 +179,8 @@ function parseArgs(argv) {
}
async function loadStats(filePath) {
const absolutePath = path.resolve(process.cwd(), filePath);
// Check if the file exists before trying to read it
try {
await access(absolutePath);
} catch {
console.error(
`[bundle-stats] Stats file not found: "${filePath}" — skipping`,
);
return null;
}
try {
const absolutePath = path.resolve(process.cwd(), filePath);
const fileContents = await readFile(absolutePath, 'utf8');
const parsed = JSON.parse(fileContents);
@@ -207,7 +196,7 @@ async function loadStats(filePath) {
? error.message
: 'Unknown error while parsing stats file';
console.error(`[bundle-stats] Failed to parse "${filePath}": ${message}`);
return null;
throw new Error(`Failed to load stats file "${filePath}": ${message}`);
}
}
@@ -698,13 +687,6 @@ async function main() {
);
const headStats = await loadStats(section.headPath);
if (!baseStats || !headStats) {
console.error(
`[bundle-stats] Skipping section "${section.name}": missing ${!baseStats ? 'base' : 'head'} stats`,
);
continue;
}
const statsDiff = getStatsDiff(baseStats, headStats);
const chunkDiff = getChunkModuleDiff(baseStats, headStats);

View File

@@ -28,10 +28,10 @@ import type {
InstalledTheme,
} from '@desktop-client/style/customThemes';
// Theme item fixed dimensions
const THEME_ITEM_HEIGHT = 140;
const THEME_ITEM_WIDTH = 140;
// Theme item dimensions
const ITEMS_PER_ROW = 3;
const THEME_ITEM_GAP = 12;
const THEME_ITEM_PADDING = 4; // horizontal padding on each side
const CATALOG_MAX_HEIGHT = 300;
type ThemeInstallerProps = {
@@ -78,15 +78,11 @@ export function ThemeInstaller({
}
}, [installedTheme]);
// Calculate items per row based on container width
const getItemsPerRow = useCallback((containerWidth: number) => {
const padding = 8; // 4px on each side
const availableWidth = containerWidth - padding;
return Math.max(
1,
Math.floor(
(availableWidth + THEME_ITEM_GAP) / (THEME_ITEM_WIDTH + THEME_ITEM_GAP),
),
// Calculate theme item width based on container width (always 3 per row)
const getItemWidth = useCallback((containerWidth: number) => {
const availableWidth = containerWidth - THEME_ITEM_PADDING * 2;
return Math.floor(
(availableWidth - (ITEMS_PER_ROW - 1) * THEME_ITEM_GAP) / ITEMS_PER_ROW,
);
}, []);
@@ -292,10 +288,10 @@ export function ThemeInstaller({
const catalogItems = [...(catalog ?? [])]
.filter(catalogTheme => !mode || catalogTheme.mode === mode)
.sort((a, b) => a.name.localeCompare(b.name));
const itemsPerRow = getItemsPerRow(width);
const itemWidth = getItemWidth(width);
const rows: CatalogTheme[][] = [];
for (let i = 0; i < catalogItems.length; i += itemsPerRow) {
rows.push(catalogItems.slice(i, i + itemsPerRow));
for (let i = 0; i < catalogItems.length; i += ITEMS_PER_ROW) {
rows.push(catalogItems.slice(i, i + ITEMS_PER_ROW));
}
return (
@@ -303,17 +299,18 @@ export function ThemeInstaller({
width={width}
height={height}
itemCount={rows.length}
itemSize={THEME_ITEM_HEIGHT + THEME_ITEM_GAP}
itemSize={itemWidth + THEME_ITEM_GAP}
itemKey={index => `row-${index}`}
renderRow={({ index, style }) => {
renderRow={({ index, key, style }) => {
const rowThemes = rows[index];
return (
<div
key={key}
style={{
...style,
display: 'flex',
gap: THEME_ITEM_GAP,
padding: '0 4px',
padding: `0 ${THEME_ITEM_PADDING}px`,
}}
>
{rowThemes.map((theme, themeIndex) => {
@@ -334,8 +331,8 @@ export function ThemeInstaller({
aria-label={theme.name}
onPress={() => handleCatalogThemeClick(theme)}
style={{
width: THEME_ITEM_WIDTH,
height: THEME_ITEM_HEIGHT,
width: itemWidth,
height: itemWidth,
padding: 8,
borderRadius: 6,
border: `2px solid ${

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [MatissJanis]
---
Custom Reports: improving responsitivity of the theme catalog