feat: awesome badges (#781)

This commit is contained in:
Jason Rasmussen
2026-09-03 14:32:36 -04:00
committed by GitHub
parent 23c828f360
commit 6abfb6bd11
8 changed files with 212 additions and 112 deletions
+1
View File
@@ -8,3 +8,4 @@ CHANGELOG.md
.svelte-kit/
.release-please-manifest.json
**/backend/worker-configuration.d.ts
deployment/
+9 -1
View File
@@ -8,7 +8,15 @@ When adding a project to the list, please ensure that it meets the following cri
- Open source
- Non-commercial
- Link goes to the source code/README
- `sourceCodeUrl` points at the source code, and `websiteUrl` at the project's own website or documentation, if it has one. At least one of the two is required.
- The project should avoid bad practices like directly connecting to Immich's postgres database. If it does, please disclose it clearly to users.
Each entry also supports an optional `tags` array, whose values show as badges:
| Tag | Meaning |
| -------------- | --------------------------------------------------- |
| `Official` | Built and maintained by the Immich team |
| `Unmaintained` | No longer maintained |
| `Vibe-Coded` | Predominantly AI-generated with little human review |
You can add a project through https://github.com/immich-app/static-pages/edit/main/apps/awesome.immich.app/src/data/items.json.
+5 -2
View File
@@ -16,8 +16,10 @@
"@sveltejs/kit": "^2.55.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/vite": "^4.2.2",
"@types/lodash-es": "^4.17.12",
"@types/luxon": "^3.7.1",
"@types/node": "^24.13.3",
"@typescript/native": "npm:typescript@^7.0.2",
"@vitest/browser": "^4.1.0",
"dotenv": "^17.2.3",
"front-matter": "^4.0.2",
@@ -29,7 +31,6 @@
"svelte": "^5.39.5",
"svelte-check": "^4.3.2",
"tailwindcss": "^4.2.2",
"@typescript/native": "npm:typescript@^7.0.2",
"typescript": "npm:@typescript/typescript6@^6.0.2",
"vite": "^8.0.0",
"vitest": "^4.0.12",
@@ -37,6 +38,8 @@
},
"dependencies": {
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47"
"@mdi/js": "^7.4.47",
"lodash-es": "^4.18.1",
"zod": "^4.4.3"
}
}
+98 -73
View File
@@ -7,78 +7,88 @@
{
"title": "ImmichFrame",
"description": "Run an Immich slideshow in a photo frame.",
"href": "https://github.com/3rob3/ImmichFrame"
"websiteUrl": "https://immichframe.dev",
"sourceCodeUrl": "https://github.com/immichFrame/ImmichFrame"
},
{
"title": "Immich Desktop",
"description": "MacOS app that surfaces your Immich library in the Finder sidebar and every file dialog. Photos appear as on-demand files with thumbnails.",
"href": "https://kartax.github.io/immich-desktop-app/"
"websiteUrl": "https://kartax.github.io/immich-desktop-app/",
"sourceCodeUrl": "https://github.com/Kartax/immich-desktop-app"
},
{
"title": "Immich Kiosk",
"description": "Highly configurable slideshows for displaying Immich assets on browsers and devices.",
"href": "https://github.com/damongolding/immich-kiosk"
"websiteUrl": "https://immichkiosk.app",
"sourceCodeUrl": "https://github.com/damongolding/immich-kiosk"
},
{
"title": "Immich Slides",
"description": "Yet another Immich slideshow app — but with an actual settings UI. Configure layouts, filters, timing, and animations directly from the interface instead of Docker env files or URL parameters.",
"href": "https://github.com/ntrampe/slides"
"sourceCodeUrl": "https://github.com/ntrampe/slides"
},
{
"title": "Immich Android TV",
"description": "Unofficial Immich Android TV app.",
"href": "https://github.com/giejay/Immich-Android-TV"
"sourceCodeUrl": "https://github.com/giejay/Immich-Android-TV"
},
{
"title": "Immich Gallery",
"description": "Unofficial Apple TV Client for your Apple TV.",
"href": "https://github.com/mensadilabs/Immich-Gallery"
"websiteUrl": "https://mensadilabs.github.io/Immich-Gallery/",
"sourceCodeUrl": "https://github.com/mensadilabs/Immich-Gallery"
},
{
"title": "Immich Apple TV",
"description": "Unofficial Immich Apple TV app.",
"href": "https://github.com/adriankraemer/Immich-Viewer"
"websiteUrl": "https://immich.adriank.app",
"sourceCodeUrl": "https://github.com/adriankraemer/Immich-Viewer"
},
{
"title": "Immich Public Proxy",
"description": "Share your Immich photos and albums in a safe way without exposing your Immich instance to the public.",
"href": "https://github.com/alangrainger/immich-public-proxy"
"websiteUrl": "https://docs.ipp.nz",
"sourceCodeUrl": "https://github.com/alangrainger/immich-public-proxy"
},
{
"title": "View-Only Web Interface",
"description": "Display Immich photos publicly while allowing for the ability to filter and search for images.",
"href": "https://github.com/JimmyeJones/Immich-View-Only-Web-Interface",
"maintained": false
"sourceCodeUrl": "https://github.com/JimmyeJones/Immich-View-Only-Web-Interface",
"tags": ["Unmaintained"]
},
{
"title": "Mimick",
"description": "Unofficial GTK4 Immich client for Linux with persistent background sync.",
"href": "https://github.com/nicx17/mimick"
"websiteUrl": "https://mimick.nicx.dev",
"sourceCodeUrl": "https://github.com/nicx17/mimick"
},
{
"title": "Harbour-Immich",
"description": "Unofficial Immich Sailfish OS application.",
"href": "https://github.com/mezmerize/harbour-immich"
"sourceCodeUrl": "https://github.com/mezmerize/harbour-immich"
},
{
"title": "Immich Companion",
"description": "Browser extension that adds right-click save to Immich, CLIP-powered search from the toolbar, optional new tab photo memories, and inline search results that show your photos alongside Google/DuckDuckGo results.",
"href": "https://github.com/bjoernch/immich-companion"
"sourceCodeUrl": "https://github.com/bjoernch/immich-companion"
},
{
"title": "Picture Frame",
"description": "Self-hosted digital photo frame for the Raspberry Pi that syncs an Immich album, with smooth crossfades even on a Pi Zero, motion-aware screen power, and Home Assistant control over MQTT.",
"href": "https://github.com/MateEke/picture-frame"
"websiteUrl": "https://pictureframe.ekemate.hu",
"sourceCodeUrl": "https://github.com/MateEke/picture-frame"
},
{
"title": "Immich Screensaver",
"description": "Native Windows screensaver that shows photos from your Immich server, with a different photo on each monitor, album selection, configurable crossfades and a clock overlay.",
"href": "https://github.com/xchimx/ImmichScreensaver"
"websiteUrl": "https://www.schottstaedt.net/en/immich-screensaver-windows",
"sourceCodeUrl": "https://github.com/xchimx/ImmichScreensaver"
},
{
"title": "Imora",
"description": "Unofficial native Immich client for iOS, written in Swift.",
"href": "https://imora.vexcited.com"
"websiteUrl": "https://imora.vexcited.com",
"sourceCodeUrl": "https://code.vexcited.com/imora/imora"
}
]
},
@@ -90,87 +100,84 @@
{
"title": "immich-go",
"description": "An alternative to the immich-CLI that doesn't depend on nodejs. It specializes in importing Google Photos Takeout archives.",
"href": "https://github.com/simulot/immich-go"
"sourceCodeUrl": "https://github.com/simulot/immich-go"
},
{
"title": "gPhotos2Immich",
"description": "Sync Google Photos shared albums to Immich, with automatic scheduling, deduplication, and Docker support.",
"href": "https://github.com/warreth/gPhotos2Immich"
"sourceCodeUrl": "https://github.com/warreth/gPhotos2Immich"
},
{
"title": "Powershell Module PSImmich",
"description": "Powershell Module for the Immich API",
"href": "https://github.com/hanpq/PSImmich"
},
{
"title": "Immich Stack",
"description": "Automatically groups similar photos into stacks within the Immich photo management system.",
"href": "https://github.com/Majorfi/immich-stack/"
"websiteUrl": "https://getps.dev/modules/PSImmich/",
"sourceCodeUrl": "https://github.com/hanpq/PSImmich"
},
{
"title": "Immich Stack",
"description": "Auto-stack photos with identical filenames and differing extensions (i.e. JPG+RAW)",
"href": "https://github.com/sid3windr/immich-stack",
"maintained": false
"sourceCodeUrl": "https://github.com/sid3windr/immich-stack",
"tags": ["Unmaintained"]
},
{
"title": "API Album Sync",
"description": "A Python script to sync folders as albums.",
"href": "https://git.orenit.solutions/open/immichalbumpull"
"sourceCodeUrl": "https://git.orenit.solutions/open/immichalbumpull"
},
{
"title": "Create albums from folders",
"description": "A Python script to create albums based on the folder structure of an external library.",
"href": "https://github.com/Salvoxia/immich-folder-album-creator"
"sourceCodeUrl": "https://github.com/Salvoxia/immich-folder-album-creator"
},
{
"title": "Immich Birthday Sync",
"description": "Bulk-upload and -download birthdays, with CardDAV sync support",
"href": "https://github.com/sid3windr/immich-birthday"
"sourceCodeUrl": "https://github.com/sid3windr/immich-birthday"
},
{
"title": "Immich Downloader",
"description": "Downloads a configurable number of random photos based on people or album ID.",
"href": "https://github.com/jon6fingrs/immich-dl",
"maintained": false
"sourceCodeUrl": "https://github.com/jon6fingrs/immich-dl",
"tags": ["Unmaintained"]
},
{
"title": "Immich Machine Learning Load Balancer",
"description": "Speed up your machine learning by load balancing your requests to multiple computers",
"href": "https://github.com/apetersson/immich_ml_balancer",
"maintained": false
"sourceCodeUrl": "https://github.com/apetersson/immich_ml_balancer",
"tags": ["Unmaintained"]
},
{
"title": "Immich-Slideshow-Toolkit",
"description": "Commandline tools for creating video slideshow Mp4s from Immich albums.",
"href": "https://github.com/knom/immich-slideshow-toolkit",
"maintained": false
"sourceCodeUrl": "https://github.com/knom/immich-slideshow-toolkit",
"tags": ["Unmaintained"]
},
{
"title": "TeleImmich",
"description": "Sync exported Telegram backups to an Immich instance",
"href": "https://github.com/dev-mkm/TeleImmich",
"maintained": false
"sourceCodeUrl": "https://github.com/rohamaa/TeleImmich",
"tags": ["Unmaintained"]
},
{
"title": "Immich Face Recognition Export Tool",
"description": "A Python tool to export face recognition data from Immich photo management system to DigiKam-compatible XMP format files.",
"href": "https://github.com/yuhuan417/immich-scripts"
"sourceCodeUrl": "https://github.com/yuhuan417/immich-scripts"
},
{
"title": "immichpy",
"description": "The stable, typed Python API layer for Immich automation and integrations.",
"href": "https://github.com/timonrieger/immichpy"
"websiteUrl": "https://immichpy.timonrieger.de",
"sourceCodeUrl": "https://github.com/timonrieger/immichpy"
},
{
"title": "lrimmich",
"description": "Sync albums and metadata from a Lightroom Classic catalog to Immich when both share the same photo folder via an external library.",
"href": "https://github.com/haavardnk/lrimmich"
"sourceCodeUrl": "https://github.com/haavardnk/lrimmich"
},
{
"title": "Immich Custom Memories",
"description": "Create customized Memories in Immich.",
"href": "https://github.com/SinTan1729/immich-custom-memories"
"sourceCodeUrl": "https://github.com/SinTan1729/immich-custom-memories"
}
]
},
@@ -182,24 +189,26 @@
{
"title": "Immich Kodi",
"description": "Unofficial Kodi plugin for Immich.",
"href": "https://github.com/vladd11/immich-kodi",
"maintained": false
"sourceCodeUrl": "https://github.com/vladd11/immich-kodi",
"tags": ["Unmaintained"]
},
{
"title": "Lightroom Publisher: mi.Immich.Publisher",
"description": "Lightroom plugin to publish photos from Lightroom collections to Immich albums.",
"href": "https://github.com/midzelis/mi.Immich.Publisher",
"maintained": false
"sourceCodeUrl": "https://github.com/midzelis/mi.Immich.Publisher",
"tags": ["Unmaintained"]
},
{
"title": "Lightroom Immich Plugin: lrc-immich-plugin",
"description": "Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.",
"href": "https://blog.fokuspunk.de/lrc-immich-plugin/"
"websiteUrl": "https://blog.fokuspunk.de/lrc-immich-plugin/",
"sourceCodeUrl": "https://github.com/bmachek/lrc-immich-plugin"
},
{
"title": "Rollfilm",
"description": "Desktop photo manager for macOS, Windows and Linux. Import from a memory card, cull, rate and edit RAW files, then mirror photos and albums to Immich in the background. Semantic search runs locally.",
"href": "https://github.com/pasqualkreher/rollfilm"
"sourceCodeUrl": "https://github.com/pasqualkreher/rollfilm",
"tags": ["Vibe-Coded"]
}
]
},
@@ -208,51 +217,59 @@
"name": "Tools",
"types": ["tool", "tools"],
"projects": [
{
"title": "Docker Compose Builder",
"description": "Build a custom docker-compose.yml file using known options and configuration.",
"websiteUrl": "https://immich.app/docker-compose-builder",
"sourceCodeUrl": "https://github.com/immich-app/static-pages",
"tags": ["Official"]
},
{
"title": "Immich-Tiktok-Remover",
"description": "Script to search for and remove TikTok videos from your Immich library.",
"href": "https://github.com/mxc2/immich-tiktok-remover"
"sourceCodeUrl": "https://github.com/mxc2/immich-tiktok-remover"
},
{
"title": "Immich Upload Optimizer",
"description": "Automatically optimize files uploaded to Immich in order to save storage space",
"href": "https://github.com/miguelangel-nubla/immich-upload-optimizer"
"sourceCodeUrl": "https://github.com/miguelangel-nubla/immich-upload-optimizer"
},
{
"title": "FlowLogix Mirror Immich",
"description": "Sync your macOS Apple Photos to Immich with title, description, keywords and other metadata",
"href": "https://github.com/flowlogix/mirror-immich"
"sourceCodeUrl": "https://github.com/flowlogix/mirror-immich"
},
{
"title": "Telegram to Immich Bot",
"description": "Telegram bot for uploading files directly to your Immich instance, preserving the metadata",
"href": "https://github.com/myanesp/telegram-immich-bot",
"maintained": false
"sourceCodeUrl": "https://github.com/myanesp/telegram-immich-bot",
"tags": ["Unmaintained"]
},
{
"title": "Immich timelaspe selfie",
"description": "This tool creates selfie timelapse videos using Immich.",
"href": "https://github.com/ArnaudCrl/immich-automated-selfie-timelapse"
"sourceCodeUrl": "https://github.com/ArnaudCrl/immich-automated-selfie-timelapse"
},
{
"title": "Immich ReverseGeo",
"description": "Improve, correct, and get more precise reverse geocoding using Overture and GADM. Integrated geocoding",
"href": "https://immich-reversegeo.github.io"
"websiteUrl": "https://immich-reversegeo.github.io",
"sourceCodeUrl": "https://github.com/immich-reversegeo/immich-reversegeo.github.io"
},
{
"title": "Immich Power Tools",
"description": "A unofficial Immich client to provide better tools to organize and manage your Immich account.",
"href": "https://github.com/immich-power-tools/immich-power-tools"
"sourceCodeUrl": "https://github.com/immich-power-tools/immich-power-tools"
},
{
"title": "Immich Dark Theme Generator",
"description": "Generate a custom dark theme for Immich.",
"href": "https://github.com/damongolding/immich-theme-generator"
"sourceCodeUrl": "https://github.com/damongolding/immich-theme-generator"
},
{
"title": "Media Backup Manager",
"description": "Windows desktop app for downloading original photos and videos from Immich by album or year for local backups. Uses only the Immich API without direct database access.",
"href": "https://github.com/olmos-cmd/Media-Backup-Manager-for-immich"
"sourceCodeUrl": "https://github.com/olmos-cmd/Media-Backup-Manager-for-immich"
}
]
},
@@ -264,12 +281,14 @@
{
"title": "Immich Distribution",
"description": "Snap package for easy install and zero-care auto updates of Immich. Self-hosted photo management.",
"href": "https://immich-distribution.nsg.cc"
"websiteUrl": "https://immich-distribution.nsg.cc",
"sourceCodeUrl": "https://github.com/nsg/immich-distribution"
},
{
"title": "SynoCommunity",
"description": "Native Synology DSM package for Immich, installable via Package Center. Includes PostgreSQL, Redis, and ffmpeg dependencies managed automatically.",
"href": "https://docs.synocommunity.com/packages/immich/"
"websiteUrl": "https://docs.synocommunity.com/packages/immich/",
"sourceCodeUrl": "https://github.com/SynoCommunity/spksrc"
}
]
},
@@ -281,72 +300,78 @@
{
"title": "Cloudflare Tunnels with SSO/OAuth",
"description": "Setting up Cloudflare Tunnels and a SaaS App for Immich.",
"href": "https://github.com/immich-app/immich/discussions/8299"
"websiteUrl": "https://github.com/immich-app/immich/discussions/8299"
},
{
"title": "Database backup in TrueNAS",
"description": "Create a database backup with pgAdmin in TrueNAS.",
"href": "https://github.com/immich-app/immich/discussions/8809"
"websiteUrl": "https://github.com/immich-app/immich/discussions/8809"
},
{
"title": "Unraid backup scripts",
"description": "Back up your assets in Unraid with a pre-prepared script.",
"href": "https://github.com/immich-app/immich/discussions/8416"
"websiteUrl": "https://github.com/immich-app/immich/discussions/8416"
},
{
"title": "Sync folders with albums",
"description": "synchronize folders in imported library with albums having the folders name.",
"href": "https://github.com/immich-app/immich/discussions/3382"
"websiteUrl": "https://github.com/immich-app/immich/discussions/3382"
},
{
"title": "SynoCommunity Package Install",
"description": "Step-by-step guide for installing Immich as a native Synology DSM package via SynoCommunity's Package Center. Covers prerequisites, PostgreSQL setup, the installation wizard, and upgrade notes.",
"href": "https://docs.synocommunity.com/packages/immich/"
"websiteUrl": "https://docs.synocommunity.com/packages/immich/",
"sourceCodeUrl": "https://github.com/SynoCommunity/spksrc"
},
{
"title": "Immich Podman Quadlets Handbook",
"description": "A rewrite of the original Immich Docker Compose file using Podman Quadlets, with a set of extra guides in the repository's wiki.",
"href": "https://github.com/linux-universe/immich-podman-quadlets/blob/main/README.md"
"websiteUrl": "https://github.com/linux-universe/immich-podman-quadlets/blob/main/README.md",
"sourceCodeUrl": "https://github.com/linux-universe/immich-podman-quadlets"
},
{
"title": "Podman/Quadlets Install",
"description": "Documentation for simple podman setup using quadlets.",
"href": "https://github.com/tbelway/immich-podman-quadlets/blob/main/docs/install/podman-quadlet.md"
"websiteUrl": "https://github.com/tbelway/immich-podman-quadlets/blob/main/docs/install/podman-quadlet.md",
"sourceCodeUrl": "https://github.com/tbelway/immich-podman-quadlets"
},
{
"title": "Google Photos import + albums",
"description": "Import your Google Photos files into Immich and add your albums.",
"href": "https://github.com/immich-app/immich/discussions/1340"
"websiteUrl": "https://github.com/immich-app/immich/discussions/1340"
},
{
"title": "Access Immich with custom domain",
"description": "Access your local Immich installation over the internet using your own domain.",
"href": "https://github.com/ppr88/immich-guides/blob/main/open-immich-custom-domain.md"
"websiteUrl": "https://github.com/ppr88/immich-guides/blob/main/open-immich-custom-domain.md",
"sourceCodeUrl": "https://github.com/ppr88/immich-guides"
},
{
"title": "Nginx caching map server",
"description": "Increase privacy by using nginx as a caching proxy in front of a map tile server.",
"href": "https://github.com/pcouy/pcouy.github.io/blob/main/_posts/2024-08-30-proxying-a-map-tile-server-for-increased-privacy.md"
"websiteUrl": "https://pierre-couy.dev/server-admin/2024/08/proxying-a-map-tile-server-for-increased-privacy.html",
"sourceCodeUrl": "https://github.com/pcouy/pcouy.github.io"
},
{
"title": "fail2ban setup instructions",
"description": "How to configure an existing fail2ban installation to block incorrect login attempts.",
"href": "https://github.com/immich-app/immich/discussions/3243#discussioncomment-6681948"
"websiteUrl": "https://github.com/immich-app/immich/discussions/3243#discussioncomment-6681948"
},
{
"title": "Immich remote access with NordVPN Meshnet",
"description": "Access Immich with an end-to-end encrypted connection.",
"href": "https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access"
"websiteUrl": "https://meshnet.nordvpn.com/how-to/remote-files-media-access/immich-remote-access"
},
{
"title": "Trust Self Signed Certificates with Immich - OAuth Setup",
"description": "Set up Certificate Authority trust with Immich, and your private OAuth2/OpenID service, while using a private CA for HTTPS communication.",
"href": "https://github.com/immich-app/immich/discussions/18614"
"websiteUrl": "https://github.com/immich-app/immich/discussions/18614"
},
{
"title": "How-To Install Immich on Rockstor NAS",
"description": "Set up Immich with External Libraries on your Rockstor NAS server.",
"href": "https://rockstor.com/docs/interface/docker-based-rock-ons/immich.html"
"websiteUrl": "https://rockstor.com/docs/interface/docker-based-rock-ons/immich.html",
"sourceCodeUrl": "https://github.com/rockstor/rockstor-doc"
}
]
}
@@ -1,27 +1,51 @@
<script lang="ts">
import type { Project } from '$lib';
import { Card, CardBody, CardHeader, Heading, Link, Text, Badge } from '@immich/ui';
import { getProjectUrl, ProjectTag, type Project } from '$lib';
import { Badge, Card, CardBody, CardHeader, Heading, Link, Text, type Color, type IconLike } from '@immich/ui';
import { mdiAlertOutline, mdiCheckDecagram, mdiCodeTags, mdiOpenInNew, mdiRobot, mdiWeb } from '@mdi/js/mdi';
type Props = {
project: Project;
};
const { project }: Props = $props();
const badges: Record<ProjectTag, { color: Color; icon: IconLike }> = {
[ProjectTag.Official]: { color: 'primary', icon: mdiCheckDecagram },
[ProjectTag.Unmaintained]: { color: 'warning', icon: mdiAlertOutline },
[ProjectTag.VibeCoded]: { color: 'warning', icon: mdiRobot },
};
const url = $derived(getProjectUrl(project));
</script>
<Link href={project.href} underline={false}>
<Card color="secondary" class="group h-full">
<CardHeader>
<div class="flex items-center gap-2">
<Heading size="small" class="group-hover:underline">{project.title}</Heading>
{#if !project.maintained}
<Badge size="small" color="warning">Unmaintained</Badge>
{/if}
</div>
<Text color="muted" size="small">{project.href}</Text>
</CardHeader>
<CardBody>
<Text>{project.description}</Text>
</CardBody>
</Card>
</Link>
<Card color="secondary" class="h-full">
<CardHeader>
<div class="mb-2 flex flex-wrap items-center gap-2">
<Link href={url} underline={false}>
<Heading size="small" class="hover:underline">{project.title}</Heading>
</Link>
{#each project.tags as tag (tag)}
<Badge leadingIcon={badges[tag].icon} size="small" color={badges[tag].color}>{tag}</Badge>
{/each}
</div>
<Link href={url} underline={false}>
<Text color="muted" size="small" class="hover:underline">{url}</Text>
</Link>
</CardHeader>
<CardBody>
<Text>{project.description}</Text>
</CardBody>
<div class="flex flex-wrap items-center gap-2 px-4 pb-4">
{#if project.websiteUrl}
<Link href={project.websiteUrl} underline={false}>
<Badge leadingIcon={mdiWeb} size="small" color="primary" trailingIcon={mdiOpenInNew}>Website</Badge>
</Link>
{/if}
{#if project.sourceCodeUrl}
<Link href={project.sourceCodeUrl} underline={false}>
<Badge leadingIcon={mdiCodeTags} size="small" color="primary" trailingIcon={mdiOpenInNew}>Source code</Badge>
</Link>
{/if}
</div>
</Card>
+38 -13
View File
@@ -1,4 +1,6 @@
import { defaultProvider, linkCommands, type ActionProvider } from '@immich/ui';
import { orderBy } from 'lodash-es';
import { z } from 'zod';
import items from '../data/items.json';
export const siteMetadata = {
@@ -14,21 +16,38 @@ export type Category = {
projects: Project[];
};
export type Project = {
title: string;
description: string;
href: string;
maintained: boolean;
};
export enum ProjectTag {
Official = 'Official',
Unmaintained = 'Unmaintained',
VibeCoded = 'Vibe-Coded',
}
export const projectSchema = z
.object({
title: z.string(),
description: z.string(),
websiteUrl: z.string().optional(),
sourceCodeUrl: z.string().optional(),
tags: z.array(z.enum(ProjectTag)).default([]),
})
.refine(({ websiteUrl, sourceCodeUrl }) => !!(websiteUrl ?? sourceCodeUrl), {
error: 'A project requires a websiteUrl, a sourceCodeUrl, or both',
});
export type Project = z.infer<typeof projectSchema>;
export const getProjectUrl = ({ websiteUrl, sourceCodeUrl }: Project) => websiteUrl ?? sourceCodeUrl!;
export const categories = items.map((category) => ({
...category,
projects: category.projects
.map((project) => ({
...project,
maintained: project.maintained ?? true,
}))
.toSorted((a, b) => (a.maintained === b.maintained ? 0 : a.maintained ? -1 : 1)),
projects: orderBy(
category.projects.map((project) => projectSchema.parse(project)),
[
(project) => project.tags.includes(ProjectTag.Official),
(project) => !project.tags.includes(ProjectTag.Unmaintained),
],
['desc', 'desc'],
),
}));
export const getCategoryProviders = () => {
@@ -36,7 +55,13 @@ export const getCategoryProviders = () => {
defaultProvider({
name: category.name,
types: category.types,
actions: linkCommands(category.projects),
actions: linkCommands(
category.projects.map((project) => ({
title: project.title,
description: project.description,
href: getProjectUrl(project),
})),
),
}),
);
@@ -1,20 +1,25 @@
<script lang="ts">
import LinkableHeading from '$common/components/LinkableHeading.svelte';
import { categories, siteMetadata, type Category } from '$lib';
import { categories, siteMetadata, type Category, type Project } from '$lib';
import ProjectCard from '$lib/components/ProjectCard.svelte';
import { Heading, Input, Link, SiteMetadata, Text } from '@immich/ui';
import { mdiMagnify } from '@mdi/js';
let query = $state('');
const matchesQuery = (project: Project, query: string) =>
[project.title, project.description, project.websiteUrl, project.sourceCodeUrl]
.filter(Boolean)
.join('|')
.toLowerCase()
.includes(query);
const filterProjects = (query: string) => {
query = query.toLowerCase().trim();
const results: Category[] = [];
for (const category of categories) {
const projects = category.projects.filter(({ title, description, href }) =>
[title, description, href].join('|').toLowerCase().includes(query),
);
const projects = category.projects.filter((project) => !query || matchesQuery(project, query));
if (projects.length > 0) {
results.push({ ...category, projects });
@@ -53,7 +58,7 @@
</LinkableHeading>
<div class="mt-4 grid grid-cols-1 gap-4">
{#each category.projects as project (project.href)}
{#each category.projects as project (category.id + project.title)}
<ProjectCard {project} />
{/each}
</div>
+9
View File
@@ -147,6 +147,12 @@ importers:
'@mdi/js':
specifier: ^7.4.47
version: 7.4.47
lodash-es:
specifier: ^4.18.1
version: 4.18.1
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies:
'@sveltejs/adapter-static':
specifier: ^3.0.9
@@ -160,6 +166,9 @@ importers:
'@tailwindcss/vite':
specifier: ^4.2.2
version: 4.3.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
'@types/luxon':
specifier: ^3.7.1
version: 3.7.5