Compare commits

..
Author SHA1 Message Date
Thomas Camlong 50bd56edb9 fix(board): harden selection and creation flows 2026-08-21 02:55:47 +02:00
Thomas Camlong 71d307a9cd feat(board): show multi-select hint 2026-08-21 02:47:05 +02:00
Thomas Camlong 49c2846d68 test(board): update selection mocks 2026-08-21 02:42:45 +02:00
Thomas Camlong 8b69688640 fix(board): persist batch item actions 2026-08-21 02:31:13 +02:00
Thomas Camlong 3a7cad9e2f feat(board): add multi-item selection actions 2026-08-21 02:15:57 +02:00
Thomas Camlong f7acf18b58 feat(board): ensure Cmd/Ctrl click correctly selects items in grid editor 2026-08-21 01:10:33 +02:00
Thomas Camlong c94dc7a2e8 feat(board): add Cmd/Ctrl multi-item selection, floating action bar, and procedural delete audio synthesis 2026-08-21 01:01:09 +02:00
Thomas Camlong 439cb7f497 fix(review): address coderabbit review findings across modals, widgets, ui, and tests 2026-08-21 00:56:16 +02:00
Thomas Camlong 858af3a48a fix(ci): add css module types and resolve typecheck/lint warnings in UI and modals 2026-08-21 00:48:39 +02:00
Thomas Camlong 7a7d4449c1 feat(apps): enhance multi-select app addition with modifier keys and helper tip 2026-08-21 00:31:19 +02:00
Thomas Camlong 1615f88a22 feat(widgets): allow creating and auto-selecting supported integrations directly from widget edit modal 2026-08-21 00:28:03 +02:00
Thomas Camlong 81ea412721 refactor(board): restore classic Mantine Menu for board add actions and remove universalCreate 2026-08-21 00:25:25 +02:00
Thomas Camlong f36f3e4d8a refactor(widgets): revert widget edit presentation from sidebar inspector to standard modal 2026-08-21 00:21:31 +02:00
Thomas Camlong e0bc1d757c refactor(create): modernize UniversalCreateModal with SelectableCard and search 2026-08-21 00:18:35 +02:00
Thomas Camlong 7690e5e542 refactor(board): restore simple add item workflow with SelectableCard and clean modal flow 2026-08-21 00:12:32 +02:00
Thomas Camlong 3ba731da2c refactor(board): remove demo / mock data badge from preview header 2026-08-21 00:07:29 +02:00
Thomas Camlong 689403b323 refactor(integrations): remove use demo service button and bottom-right badge from integration select grid 2026-08-21 00:06:56 +02:00
Thomas Camlong 36a1f8afc2 feat(board): harden Advanced Add App feature and add comprehensive test suites 2026-08-21 00:05:26 +02:00
Thomas Camlong 90bf830e34 feat(board): update widget preview to 5x3 and harden error boundaries 2026-08-20 23:59:53 +02:00
Thomas Camlong b787f21e68 refactor(apps): remove redundant new badge from app select modal create card 2026-08-20 23:43:18 +02:00
Thomas Camlong 829f628fec feat(board): implement advanced add app modal with live preview and remove today widget 2026-08-20 23:42:24 +02:00
5 changed files with 43 additions and 112 deletions

No files matched your search

+8 -18
View File
@@ -374,31 +374,21 @@ jobs:
AMD64_DIGEST: ${{ needs.preview-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.preview-arm64.outputs.digest }}
run: |
tags=(-t "${PREVIEW_IMAGE}:${PREVIEW_TAG}")
if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF}" == "refs/heads/release/v2" ]]; then
tags+=(-t "${PREVIEW_IMAGE}:pr-6545")
fi
docker buildx imagetools create "${tags[@]}" \
docker buildx imagetools create -t "${PREVIEW_IMAGE}:${PREVIEW_TAG}" \
"${PREVIEW_IMAGE}@${AMD64_DIGEST}" \
"${PREVIEW_IMAGE}@${ARM64_DIGEST}"
- name: Verify preview manifest platforms
if: "!env.ACT"
run: |
tags=("${PREVIEW_TAG}")
if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF}" == "refs/heads/release/v2" ]]; then
tags+=("pr-6545")
fi
for tag in "${tags[@]}"; do
image="${PREVIEW_IMAGE}:${tag}"
manifest="$(docker buildx imagetools inspect "$image" --raw)"
for architecture in amd64 arm64; do
jq --exit-status --arg architecture "$architecture" \
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
<<<"$manifest" >/dev/null
done
docker buildx imagetools inspect "$image"
image="${PREVIEW_IMAGE}:${PREVIEW_TAG}"
manifest="$(docker buildx imagetools inspect "$image" --raw)"
for architecture in amd64 arm64; do
jq --exit-status --arg architecture "$architecture" \
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
<<<"$manifest" >/dev/null
done
docker buildx imagetools inspect "$image"
cleanup:
name: Clean up preview image
+21 -87
View File
@@ -27,103 +27,37 @@ jobs:
- run: pnpm test:workshop
- run: pnpm test:workshop-image
publish-amd64:
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
needs: validate
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: read
packages: write
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
id: build
with:
context: .
file: apps/workshop/Dockerfile
target: production
platforms: linux/amd64
outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=workshop-linux-amd64
cache-to: type=gha,scope=workshop-linux-amd64,mode=max
publish-arm64:
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
needs: validate
runs-on: ubuntu-24.04-arm
timeout-minutes: 20
permissions:
contents: read
packages: write
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
id: build
with:
context: .
file: apps/workshop/Dockerfile
target: production
platforms: linux/arm64
outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=workshop-linux-arm64
cache-to: type=gha,scope=workshop-linux-arm64,mode=max
publish:
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
needs: [publish-amd64, publish-arm64]
needs: validate
runs-on: ubuntu-24.04
timeout-minutes: 5
timeout-minutes: 30
permissions:
contents: read
packages: write
env:
WORKSHOP_IMAGE: ghcr.io/homarr-labs/workshop
steps:
- uses: actions/checkout@v7
- uses: docker/setup-qemu-action@v4
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish multi-platform image
env:
AMD64_DIGEST: ${{ needs.publish-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.publish-arm64.outputs.digest }}
run: |
tags=(-t "${WORKSHOP_IMAGE}:sha-${GITHUB_SHA}")
if [[ "${GITHUB_REF_NAME}" == "v2" || "${GITHUB_REF_NAME}" == "release/v2" ]]; then
tags+=(-t "${WORKSHOP_IMAGE}:v2")
fi
if [[ "${GITHUB_REF_NAME}" == "main" ]]; then
tags+=(-t "${WORKSHOP_IMAGE}:latest")
fi
docker buildx imagetools create "${tags[@]}" \
"${WORKSHOP_IMAGE}@${AMD64_DIGEST}" \
"${WORKSHOP_IMAGE}@${ARM64_DIGEST}"
- name: Verify multi-platform image
run: |
image="${WORKSHOP_IMAGE}:sha-${GITHUB_SHA}"
manifest="$(docker buildx imagetools inspect "$image" --raw)"
for architecture in amd64 arm64; do
jq --exit-status --arg architecture "$architecture" \
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
<<<"$manifest" >/dev/null
done
docker buildx imagetools inspect "$image"
- uses: docker/metadata-action@v6
id: metadata
with:
images: ghcr.io/homarr-labs/workshop
tags: |
type=sha,prefix=sha-,format=long
type=raw,value=v2,enable=${{ github.ref_name == 'v2' || github.ref_name == 'release/v2' }}
type=raw,value=latest,enable=${{ github.ref_name == 'main' }}
- uses: docker/build-push-action@v7
with:
context: .
file: apps/workshop/Dockerfile
target: production
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
@@ -16,7 +16,7 @@ import beszelSystemGrid from "./img/beszel-system-grid.png";
<WidgetHeader widget={beszelSystemGridWidget} categories={["System Monitoring"]} />
This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Resizing the widget adjusts its rows and columns to keep the cards balanced. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount.
This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Cards adapt their layout based on available space. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount.
Select a system card to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts.
+2 -2
View File
@@ -104,7 +104,7 @@ const CredentialsSetup = () => {
return (
<AccountShell title={t("title")} description={t("description")}>
<form onSubmit={form.onSubmit((values) => submitAsync(values))}>
<form onSubmit={form.onSubmit((values) => void submitAsync(values))}>
<Stack gap="md">
<TextInput label={t("username")} autoComplete="username" withAsterisk {...form.getInputProps("username")} />
<UserCreatePasswordFields
@@ -119,7 +119,7 @@ const CredentialsSetup = () => {
{submitError}
</Alert>
) : null}
<Button type="submit" size="md" loading={form.submitting} rightSection={<IconArrowRight size={18} />}>
<Button type="submit" size="md" loading={mutation.isPending} rightSection={<IconArrowRight size={18} />}>
{t("create")}
</Button>
</Stack>
@@ -123,12 +123,19 @@ const MIN_CELL_HEIGHT = 80;
const ADVANCED_MIN_CELL_WIDTH = 320;
const ADVANCED_MIN_CELL_HEIGHT = 420;
const getColCount = (width: number, height: number, itemCount: number, minCellWidth = MIN_CELL_WIDTH): number => {
const getColCount = (width: number, _height: number, itemCount: number, minCellWidth = MIN_CELL_WIDTH): number => {
if (itemCount <= 1) return 1;
const maxCols = Math.min(itemCount, Math.max(1, Math.floor(width / minCellWidth)));
const aspectRatio = width / Math.max(height, MIN_CELL_HEIGHT);
const idealCols = Math.round(Math.sqrt(itemCount * aspectRatio));
return Math.min(maxCols, Math.max(1, idealCols));
let best = 1;
for (let c = 1; c <= maxCols; c++) {
const emptyCells = (c - (itemCount % c)) % c;
const bestEmpty = (best - (itemCount % best)) % best;
if (emptyCells < bestEmpty || (emptyCells === bestEmpty && c > best)) {
best = c;
}
}
return best;
};
interface MetricRowProps {