refactor: packages (#479)

* refactor: packages

* feat: migrate ui website
This commit is contained in:
Jason Rasmussen
2026-05-04 14:37:01 -04:00
committed by GitHub
parent 4390115d10
commit b0b7f4df82
720 changed files with 32905 additions and 350 deletions
+65 -111
View File
@@ -12,25 +12,13 @@ concurrency:
cancel-in-progress: true
env:
tofu_version: '1.10.2'
tg_version: '0.83.0'
ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
MISE_TRUSTED_CONFIG_PATHS: ${{ github.workspace }}/.mise/config.toml
jobs:
build-frontend:
name: Build Frontend
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
app_name: ['root', 'api', 'awesome', 'my', 'get', 'buy', 'datasets', 'futo-backups-survey']
include:
- app_name: futo-backups-survey
app_dir: futo-backups-survey.immich.app
defaults:
run:
working-directory: apps/${{ matrix.app_dir || format('{0}.immich.app', matrix.app_name) }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -40,130 +28,97 @@ jobs:
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Get pnpm store directory
id: pnpm-store
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install
run: pnpm install
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
env:
PUBLIC_IMMICH_PAY_HOST: ${{ env.ENVIRONMENT == 'dev' && 'https://futopay-test.azurewebsites.net' || 'https://pay.futo.org' }}
PUBLIC_CF_TURNSTILE_SITE: ${{ secrets.CF_TURNSTILE_DEFAULT_INVISIBLE_SITE_KEY }}
run: pnpm build
- name: Upload build output
- name: Upload builds
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-frontend-output-${{ matrix.app_name }}
name: build
path: |
apps/*/build
apps/*/backend/dist
include-hidden-files: true
path: apps/${{ matrix.app_dir || format('{0}.immich.app', matrix.app_name) }}/${{ matrix.build_path || 'build' }}
retention-days: 1
build-backend:
name: Build Backend
runs-on: ubuntu-latest
strategy:
matrix:
app_name: ['datasets', 'futo-backups-survey']
include:
- app_name: futo-backups-survey
app_dir: futo-backups-survey.immich.app
defaults:
run:
working-directory: apps/${{ matrix.app_dir || format('{0}.immich.app', matrix.app_name) }}/backend
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Run pnpm install
run: pnpm install
- name: Build backend
run: pnpm exec wrangler build
- name: Upload build output
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-backend-output-${{ matrix.app_name }}
path: apps/${{ matrix.app_dir || format('{0}.immich.app', matrix.app_name) }}/backend/dist
if-no-files-found: error
retention-days: 1
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: [build-frontend, build-backend]
needs: [build]
if: ${{ !github.event.pull_request.head.repo.fork }}
strategy:
fail-fast: false
matrix:
app_name: ['root', 'api', 'awesome', 'my', 'get', 'buy', 'datasets', 'futo-backups-survey']
include:
- app_name: futo-backups-survey
app_dir: futo-backups-survey.immich.app
app_name: ['root', 'ui', 'api', 'awesome', 'my', 'get', 'buy', 'datasets', 'futo-backups-survey']
env:
TF_VAR_app_name: ${{ matrix.app_name }}
TF_VAR_stage: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || '' }}
TF_VAR_dist_dir: ${{ github.workspace }}/dist/backend
TF_VAR_migrations_dir: ${{ github.workspace }}/apps/${{ matrix.app_dir || format('{0}.immich.app', matrix.app_name) }}/backend/migrations
TF_VAR_dist_dir: ${{ github.workspace }}/apps/${{ matrix.app_name }}.immich.app/backend/dist
TF_VAR_migrations_dir: ${{ github.workspace }}/apps/${{ matrix.app_name }}.immich.app/backend/migrations
OP_SERVICE_ACCOUNT_TOKEN: ${{ github.ref == 'refs/heads/main' && secrets.OP_TF_PROD_ENV || secrets.OP_TF_DEV_ENV }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: { persist-credentials: false }
- name: Get build artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: 'build-frontend-output-${{ matrix.app_name }}'
path: '${{ github.workspace }}/dist/frontend'
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Check if the app has a backend folder
id: check-backend
run: |
APP_DIR="${{ matrix.app_dir || format('{0}.immich.app', matrix.app_name) }}"
if [ -d "apps/${APP_DIR}/backend" ]; then
echo "has_backend=true" >> $GITHUB_ENV
else
echo "has_backend=false" >> $GITHUB_ENV
fi
- name: Get pnpm store directory
id: pnpm-store
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Get backend build artifact
if: ${{ env.has_backend == 'true' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
name: 'build-backend-output-${{ matrix.app_name }}'
path: '${{ github.workspace }}/dist/backend'
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install
run: pnpm install --frozen-lockfile
- name: Install 1Password CLI
uses: 1password/install-cli-action@8d006a0d0a4fd505af7f7ce589e7f768385ff5e4 # v3.0.0
- name: Install Terragrunt
uses: eLco/setup-terragrunt@2c5116f8979bb261072ef57c97ac026cd9c27434 # v1.0.3
- name: Download builds
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
terragrunt_version: ${{ env.tg_version }}
- name: 'Install OpenTofu'
uses: opentofu/setup-opentofu@fc711fa910b93cba0f3fbecaafc9f42fd0c411cb # v2.0.0
with:
tofu_version: ${{ env.tofu_version }}
tofu_wrapper: false
name: build
path: apps # restore pre-built build and dist folders
- name: Deploy All
working-directory: ${{ github.workspace }}/deployment
env:
APP_NAME: ${{ matrix.app_name }}
APP_DIR: ${{ github.workspace }}/apps/${{ matrix.app_name }}.immich.app
run: |
INCLUDE_DIRS=""
if [ "${{ env.has_backend }}" = "true" ]; then
INCLUDE_DIRS='**/workers/${{ matrix.app_name }}'
fi
QUEUE_ARGS=(--queue-include-dir='**/!(workers)/**')
if [ -n "$INCLUDE_DIRS" ]; then
QUEUE_ARGS+=(--queue-include-dir="$INCLUDE_DIRS")
QUEUE_ARGS=(
--queue-include-dir='modules/cloudflare/pages-project'
--queue-include-dir='modules/cloudflare/static-pages'
)
if [ -d "$APP_DIR/backend/dist" ]; then
QUEUE_ARGS+=(--queue-include-dir="modules/cloudflare/workers/${APP_NAME}")
fi
op run --env-file='.env' -- terragrunt run --all apply \
@@ -178,14 +133,13 @@ jobs:
echo "output=$(op run --no-masking --env-file='../../../.env' -- terragrunt output -json | jq -c .)" >> $GITHUB_OUTPUT
- name: Publish Frontend to Cloudflare Pages
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ fromJson(steps.deploy-output.outputs.output).pages_project_name.value }}
directory: 'dist/frontend'
branch: ${{ fromJson(steps.deploy-output.outputs.output).pages_branch.value }}
wranglerVersion: '3'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
BUILD_DIR: apps/${{ matrix.app_name }}.immich.app/build
PROJECT_NAME: ${{ fromJson(steps.deploy-output.outputs.output).pages_project_name.value }}
BRANCH_NAME: ${{ fromJson(steps.deploy-output.outputs.output).pages_branch.value }}
run: mise run deploy
- name: Save preview URL
if: ${{ github.event_name == 'pull_request' }}
+6 -15
View File
@@ -4,8 +4,7 @@ on:
types: [closed]
env:
tofu_version: '1.10.2'
tg_version: '0.83.0'
MISE_TRUSTED_CONFIG_PATHS: ${{ github.workspace }}/.mise/config.toml
jobs:
deploy:
@@ -14,31 +13,23 @@ jobs:
fail-fast: false
matrix:
environment: ['dev', 'prod']
app_name: ['root', 'api', 'awesome', 'my', 'get', 'buy', 'datasets']
app_name: ['root', 'ui', 'api', 'awesome', 'my', 'get', 'buy', 'datasets']
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Install 1Password CLI
uses: 1password/install-cli-action@8d006a0d0a4fd505af7f7ce589e7f768385ff5e4 # v3.0.0
- name: Install Terragrunt
uses: eLco/setup-terragrunt@2c5116f8979bb261072ef57c97ac026cd9c27434 # v1.0.3
with:
terragrunt_version: ${{ env.tg_version }}
- name: 'Install OpenTofu'
uses: opentofu/setup-opentofu@fc711fa910b93cba0f3fbecaafc9f42fd0c411cb # v2.0.0
with:
tofu_version: ${{ env.tofu_version }}
tofu_wrapper: false
- name: Destroy All
working-directory: ${{ github.workspace }}/deployment
env:
ENVIRONMENT: ${{ matrix.environment }}
TF_VAR_app_name: ${{ matrix.app_name }}
TF_VAR_dist_dir: ${{ github.workspace }}/dist
TF_VAR_dist_dir: ${{ github.workspace }}/apps/${{ matrix.app_name }}.immich.app/backend/dist
TF_VAR_stage: pr-${{ github.event.number }}
OP_SERVICE_ACCOUNT_TOKEN: ${{ matrix.environment == 'prod' && secrets.OP_TF_PROD_ENV || secrets.OP_TF_DEV_ENV }}
run: op run --env-file=".env" -- terragrunt run --all destroy --non-interactive --queue-exclude-dir '**/pages-project' -- -refresh=false -lock-timeout=5m
+57
View File
@@ -0,0 +1,57 @@
name: Publish
on:
release:
types: [published]
permissions:
contents: read
id-token: write
packages: write
env:
MISE_TRUSTED_CONFIG_PATHS: ${{ github.workspace }}/.mise/config.toml
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Get pnpm store directory
id: pnpm-store
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm build:packages
- name: Find package
id: find-package
env:
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
run: |
PACKAGE_NAME="${RELEASE_TAG_NAME%-*}"
echo "Package is $PACKAGE_NAME"
echo "package_name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
- name: Publish
run: pnpm --filter ./packages/${{ steps.find-package.outputs.package_name }} publish --no-git-checks
+28
View File
@@ -0,0 +1,28 @@
name: Release Please
on:
workflow_dispatch:
release:
types: [published]
pull_request:
types: [unlabeled]
push:
branches:
- main
permissions: {}
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
+31 -5
View File
@@ -9,6 +9,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
MISE_TRUSTED_CONFIG_PATHS: ${{ github.workspace }}/.mise/config.toml
jobs:
test:
name: Test
@@ -22,8 +25,31 @@ jobs:
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Get pnpm store directory
id: pnpm-store
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Run install
run: pnpm install
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm build:packages
- name: Run sync
run: pnpm sync
if: ${{ !cancelled() }}
- name: Type check
run: pnpm check
if: ${{ !cancelled() }}
- name: Run linter
run: pnpm lint
@@ -33,10 +59,10 @@ jobs:
run: pnpm format
if: ${{ !cancelled() }}
- name: Type check
run: pnpm check
if: ${{ !cancelled() }}
- name: Run unit tests
run: pnpm test
if: ${{ !cancelled() }}
- name: Run medium tests
run: pnpm test:medium
if: ${{ !cancelled() }}
+8 -3
View File
@@ -6,9 +6,13 @@ node_modules
# Output
.output
.vercel
apps/*/.svelte-kit
apps/*/build
apps/*/.wrangler
apps/**/.svelte-kit
apps/**/build
apps/**/.wrangler
packages/**/.svelte-kit
packages/**/build
packages/**/dist
# OS
.DS_Store
@@ -26,4 +30,5 @@ vite.config.ts.timestamp-*
# Planning
.planning/
.claude/
apps/*/.planning
+6 -3
View File
@@ -1,9 +1,12 @@
[tools]
opentofu = "1.11.6"
terragrunt = "1.0.2"
node = "24.15"
pnpm = "10.33.2"
wrangler = "4.66.0"
[env]
TF_VAR_dist_dir="{{config_root}}/dist"
[tasks.deploy]
run = "wrangler pages deploy ${BUILD_DIR} --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
[tasks.tg]
run = "op run '--env-file={{config_root}}/deployment/.env' -- terragrunt"
@@ -11,7 +14,7 @@ description = "Wrapper for terragrunt"
dir = "{{cwd}}"
[tasks."tg:fmt"]
run = "mise run tg hclfmt"
run = "mise run tg hcl format"
description = "Format terragrunt files"
[tasks.tf]
+6
View File
@@ -0,0 +1,6 @@
{
"packages/sql-tools": "0.5.2",
"packages/kysely-adapter-cloudflare": "0.1.0",
"packages/svelte-markdown-preprocess": "0.5.1",
"packages/ui": "0.77.1"
}
+2 -2
View File
@@ -8,7 +8,7 @@
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest"
},
@@ -31,7 +31,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.76.0",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47",
"simple-icons": "^16.0.0",
"svelte-highlight": "^7.8.4",
+1 -1
View File
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
+2 -1
View File
@@ -19,7 +19,8 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
// TODO remove after $common is gone...
'@immich/ui': resolve('../../packages/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
},
+3
View File
@@ -23,6 +23,9 @@ export default defineConfig({
}
: undefined,
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
+2 -2
View File
@@ -7,7 +7,7 @@
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest"
},
@@ -35,7 +35,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.76.0",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47"
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
+2 -1
View File
@@ -15,7 +15,8 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
'@immich/ui': resolve('../../packages/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
prerender: {
handleHttpError: ({ path, message }) => {
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
allow: ['../../common'],
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
+2 -2
View File
@@ -8,7 +8,7 @@
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest"
},
@@ -31,7 +31,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.76.0",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47",
"simple-icons": "^16.0.0",
"svelte-highlight": "^7.8.4"
+1 -1
View File
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
+1 -1
View File
@@ -19,7 +19,7 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
'@immich/ui': resolve('../../packages/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
},
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
allow: ['../../common'],
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
@@ -5,6 +5,7 @@
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev",
"build": "wrangler build",
"start": "wrangler dev",
"test": "vitest",
"cf-typegen": "wrangler types"
+2 -2
View File
@@ -8,7 +8,7 @@
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest"
},
@@ -31,7 +31,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.76.0",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47",
"exifreader": "^4.32.0",
"mediabunny": "^1.31.0",
+1 -1
View File
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
@@ -84,7 +84,6 @@
const currentEntry = entriesToCheckForSubDirectories.pop();
if (isFileSystemDirectoryEntry(currentEntry)) {
// eslint-disable-next-line unicorn/prefer-spread
entriesToCheckForSubDirectories = entriesToCheckForSubDirectories.concat(
await getContentsFromFileSystemDirectoryEntry(currentEntry),
);
+1 -1
View File
@@ -20,7 +20,7 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
'@immich/ui': resolve('../../packages/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
},
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
allow: ['../../common'],
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
@@ -5,6 +5,7 @@
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"build": "wrangler build",
"cf-typegen": "wrangler types"
},
"devDependencies": {
@@ -8,7 +8,7 @@
"dev": "vite dev",
"build": "svelte-kit sync && vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest",
"db:migrate:local": "cd backend && wrangler d1 migrations apply futo-backups-survey --local"
@@ -1,7 +1,7 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
@theme inline {
@@ -1,7 +1,6 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import dotenv from 'dotenv';
import { resolve } from 'node:path';
dotenv.config({ path: '../../.env' });
@@ -15,8 +14,6 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
},
};
@@ -12,6 +12,9 @@ export default defineConfig({
'/api': 'http://localhost:8787',
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
+2 -2
View File
@@ -8,7 +8,7 @@
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest"
},
@@ -31,7 +31,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.76.0",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47",
"simple-icons": "^16.0.0",
"svelte-highlight": "^7.8.4"
+1 -1
View File
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
+2 -1
View File
@@ -17,7 +17,8 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
'@immich/ui': resolve('../../packages/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
},
};
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
allow: ['../../common'],
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
+2 -2
View File
@@ -8,7 +8,7 @@
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync",
"test": "vitest"
},
@@ -31,7 +31,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.76.0",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47",
"simple-icons": "^16.0.0",
"svelte-highlight": "^7.8.4"
+1 -1
View File
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
+2 -1
View File
@@ -17,7 +17,8 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
'@immich/ui': resolve('../../packages/ui/dist'),
'@mdi/js': resolve('./node_modules/@mdi/js'),
},
},
};
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
allow: ['../../common'],
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
+3 -3
View File
@@ -8,13 +8,13 @@
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"import-post": "dotenv -- uv run ../../import-post.py",
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-check --tsconfig ./tsconfig.json",
"uuid": "npx -y uuid v7",
"sync": "svelte-kit sync",
"test": "vitest"
},
"devDependencies": {
"@immich/svelte-markdown-preprocess": "^0.5.1",
"@immich/svelte-markdown-preprocess": "workspace:*",
"@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.55.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
@@ -38,7 +38,7 @@
"vitest-browser-svelte": "^2.0.0"
},
"dependencies": {
"@immich/ui": "^0.77.1",
"@immich/ui": "workspace:*",
"@mdi/js": "^7.4.47",
"feed": "^5.1.0",
"front-matter": "^4.0.2",
+1 -1
View File
@@ -1,7 +1,7 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../node_modules/@immich/ui/dist';
@source '../../../../packages/ui/src';
@source '../../../../common';
@theme {
+2 -1
View File
@@ -27,7 +27,8 @@ const config = {
alias: {
$common: '../../common',
'$common/*': '../../common/*',
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
// TODO remove after $common is gone...
'@immich/ui': resolve('../../packages/ui/dist'),
},
prerender: {
handleHttpError: ({ path, message }) => {
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
allow: ['../../common'],
},
},
optimizeDeps: {
exclude: ['@immich/ui', '@immich/svelte-markdown-preprocess'],
},
test: {
expect: { requireAssertions: true },
environment: 'node',
+54
View File
@@ -0,0 +1,54 @@
{
"name": "ui.immich.app",
"private": true,
"version": "0.0.1",
"type": "module",
"license": "GNU Affero General Public License version 3",
"repository": {
"type": "git",
"url": "git+https://github.com/immich-app/ui.git",
"directory": "packages/ui"
},
"scripts": {
"create": "node scripts/create.js",
"start": "vite dev",
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json",
"sync": "svelte-kit sync"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.13.0",
"@sveltejs/package": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/postcss": "^4.2.2",
"@tailwindcss/vite": "^4.2.2",
"@types/luxon": "^3.7.1",
"autoprefixer": "^10.4.20",
"globals": "^17.0.0",
"svelte": "^5.37.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^4.2.2",
"typescript": "^6.0.0",
"vite": "^8.0.0",
"vitest": "^4.1.0"
},
"dependencies": {
"@immich/svelte-markdown-preprocess": "workspace:*",
"@immich/ui": "workspace:*",
"@internationalized/date": "^3.10.0",
"@mdi/js": "^7.4.47",
"bits-ui": "^2.15.7",
"luxon": "^3.7.2",
"simple-icons": "^16.0.0",
"svelte": "^5.39.5",
"svelte-check": "^4.3.2",
"svelte-highlight": "^7.8.4",
"tailwind-merge": "^3.0.0",
"tailwind-variants": "^3.0.0"
},
"volta": {
"node": "24.15.0"
}
}
+5
View File
@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
+13
View File
@@ -0,0 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};
+26
View File
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
try {
const preference = JSON.parse(localStorage.getItem('immich-ui-theme'));
const prefersDark = globalThis.matchMedia('(prefers-color-scheme: dark)').matches;
if (preference === 'light' || (preference !== 'dark' && !prefersDark)) {
document.documentElement.classList.remove('dark');
document.documentElement.classList.add('light');
}
} catch (error) {
// noop
console.log(error);
}
</script>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" class="bg-light text-dark">
<div>%sveltekit.body%</div>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@import '$common/theme/theme.css';
@source '../../../../packages/ui/src';
@source '../../../../common';
@@ -0,0 +1,528 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ñëîé_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2500 2000" style="enable-background:new 0 0 2500 2000;" xml:space="preserve">
<g id="_x31_2">
<path style="fill:#D4E8FF;" d="M551.957,1587.155c0,0-253.835-637.931-71.167-779.983
c122.096-94.948,251.512,114.359,405.979,3.631c137.519-98.579-54.207-346.912,175.247-411.876
c241.177-68.283,438.762,484.704,641.128,408.245c245.557-92.778,489.822,95.563,278.732,781.201L551.957,1587.155z"/>
<path style="fill:#DCE6FF;" d="M353.35,1430.117c-4.261-11.542-8.477-23.501-12.53-35.545l9.99-2.626
c4.027,11.966,8.215,23.848,12.449,35.312L353.35,1430.117z M2140.503,1415.212l-10.344-1.129
c1.718-12.286,3.348-24.659,4.848-36.776l10.36,1.002C2143.863,1390.468,2142.226,1402.885,2140.503,1415.212z M329.313,1358.769
c-3.648-11.903-7.185-24.027-10.515-36.036l10.123-2.193c3.31,11.937,6.826,23.988,10.452,35.82L329.313,1358.769z
M2149.634,1341.345l-10.375-0.867c1.319-12.335,2.54-24.744,3.629-36.88l10.389,0.728
C2152.185,1316.51,2150.958,1328.964,2149.634,1341.345z M309.275,1286.485c-3.01-12.127-5.874-24.388-8.513-36.446l10.232-1.749
c2.622,11.982,5.469,24.169,8.46,36.22L309.275,1286.485z M2156.269,1267.258l-10.402-0.582
c0.882-12.327,1.656-24.761,2.299-36.956l10.41,0.429C2157.931,1242.396,2157.155,1254.88,2156.269,1267.258z M293.293,1213.412
c-2.318-12.276-4.464-24.654-6.377-36.792l10.322-1.271c1.901,12.057,4.033,24.352,6.335,36.547L293.293,1213.412z
M2160.168,1193.007l-10.418-0.267c0.407-12.381,0.687-24.829,0.833-36.997l10.422,0.097
C2160.858,1168.064,2160.577,1180.569,2160.168,1193.007z M281.685,1139.68c-1.554-12.399-2.906-24.869-4.02-37.064l10.388-0.742
c1.106,12.104,2.448,24.482,3.99,36.791L281.685,1139.68z M2150.626,1118.744c-0.123-12.391-0.389-24.834-0.792-36.985
l10.418-0.269c0.404,12.214,0.672,24.72,0.796,37.174L2150.626,1118.744z M274.933,1065.456
c-0.691-12.472-1.15-24.993-1.363-37.217l10.421-0.143c0.211,12.122,0.666,24.54,1.351,36.909L274.933,1065.456z
M2148.159,1044.806c-0.715-12.374-1.595-24.791-2.614-36.904l10.393-0.684c1.026,12.183,1.91,24.672,2.63,37.117
L2148.159,1044.806z M284.09,991.173l-10.42-0.189c0.29-12.505,0.85-25.03,1.664-37.227l10.405,0.542
C284.931,966.379,284.377,978.785,284.09,991.173z M2141.929,971.072c-1.392-12.372-2.969-24.729-4.686-36.728l10.34-1.155
c1.728,12.079,3.315,24.519,4.717,36.972L2141.929,971.072z M289.043,917.522l-10.37-0.92c1.416-12.458,3.142-24.909,5.13-37.009
l10.314,1.324C292.15,892.883,290.443,905.199,289.043,917.522z M2131.409,897.756c-2.168-12.294-4.549-24.542-7.075-36.403
l10.241-1.705c2.548,11.956,4.947,24.299,7.132,36.688L2131.409,897.756z M898.797,896.456
c-14.482-2.018-24.957-7.845-39.467-17.908l6.587-7.139c12.712,8.792,21.893,14.067,33.99,15.887L898.797,896.456z
M934.957,874.538l-9.282-4.19c4.771-8.258,8.988-19.718,12.536-34.06l10.181,1.967
C944.602,853.582,940.207,865.45,934.957,874.538z M828.742,852.126c-8.303-8.173-17.175-17.578-27.125-28.755l8.19-5.697
c9.809,11.018,18.54,20.274,26.69,28.297L828.742,852.126z M301.078,844.571l-10.232-1.755c2.7-12.296,5.755-24.557,9.08-36.442
l10.118,2.212C306.761,820.32,303.744,832.427,301.078,844.571z M2115.91,825.19c-3.086-12.167-6.416-24.23-9.896-35.852
l10.076-2.357c3.513,11.735,6.874,23.913,9.988,36.194L2115.91,825.19z M955.405,801.179l-10.337-1.176
c1.601-10.996,3.035-23.038,4.385-36.813l10.383,0.795C958.475,777.889,957.025,790.055,955.405,801.179z M776.091,793.691
c-8.472-10.113-17.259-20.803-24.831-30.029l8.437-5.409c7.557,9.208,16.326,19.876,24.77,29.956L776.091,793.691z
M1539.124,792.933c-8.116,0-16.08-2.143-23.669-6.37l5.557-7.794c11.4,6.361,22.615,6.375,34.725,1.302l4.484,8.316
C1553.06,791.404,1545.963,792.933,1539.124,792.933z M321.13,773.084l-9.967-2.694c4.141-11.97,8.684-23.874,13.504-35.383
l9.774,3.198C329.691,749.548,325.213,761.283,321.13,773.084z M1594.301,764.302l-7.657-6.249
c7.902-7.565,16.373-16.897,25.897-28.529l8.443,5.402C1611.215,746.856,1602.487,756.466,1594.301,764.302z M1485.82,757.967
c-6.745-9.197-13.429-20.029-20.434-33.112l9.42-3.941c6.806,12.71,13.268,23.191,19.76,32.042L1485.82,757.967z M2094.5,753.888
c-4.198-11.936-8.678-23.686-13.316-34.924l9.792-3.157c4.694,11.376,9.229,23.268,13.477,35.346L2094.5,753.888z M726.561,733.628
c-9.536-11.519-17.482-20.976-25.009-29.76l8.309-5.561c7.562,8.826,15.54,18.321,25.111,29.882L726.561,733.628z M962.973,726.781
l-10.401-0.595c0.79-10.794,1.594-22.924,2.457-37.087l10.407,0.496C964.571,703.791,963.765,715.954,962.973,726.781z
M350.063,704.103l-9.535-3.72c5.721-11.454,11.818-22.701,18.297-33.699l9.242,4.259
C361.692,681.764,355.694,692.831,350.063,704.103z M1644.861,704.101l-8.709-5.062c7.406-9.953,15.057-20.498,22.8-31.151
l8.768,4.98C1659.958,683.548,1652.3,694.103,1644.861,704.101z M1448.609,690.477c-4.721-10.467-9.599-21.886-14.913-34.91
l9.803-3.126c5.276,12.927,10.114,24.253,14.791,34.624L1448.609,690.477z M2065.852,684.731
c-5.603-11.537-11.53-22.744-17.617-33.309l9.288-4.18c6.189,10.744,12.213,22.134,17.906,33.855L2065.852,684.731z
M675.82,674.677c-9.457-10.376-18.26-19.611-26.911-28.233l7.797-6.113c8.762,8.732,17.669,18.076,27.23,28.567L675.82,674.677z
M967.605,652.433l-10.409-0.464c0.702-12.307,1.4-24.621,2.144-37.152l10.408,0.483
C969.004,627.824,968.306,640.132,967.605,652.433z M1690.57,641.754l-8.695-5.08c8.708-11.646,16.496-21.721,23.811-30.799
l8.489,5.344C1706.932,620.209,1699.21,630.197,1690.57,641.754z M388.736,639.154l-8.784-4.958
c8.19-11.338,16.634-21.577,25.099-30.434l7.962,5.944C404.845,618.252,396.678,628.159,388.736,639.154z M1419.78,620.412
c-4.78-12.38-9.469-24.768-13.426-35.253l9.887-2.915c3.951,10.47,8.633,22.838,13.403,35.192L1419.78,620.412z M620.253,619.767
c-10.652-9.167-21.126-17.266-31.131-24.07l6.356-7.301c10.323,7.021,21.105,15.355,32.046,24.77L620.253,619.767z M2028,619.373
c-7.468-10.772-15.298-20.96-23.273-30.28l8.312-5.557c8.179,9.559,16.203,19.997,23.847,31.023L2028,619.373z M442.245,584.643
l-6.321-7.325c12.446-8.392,25.4-14.444,38.501-17.988l3.05,8.81C465.558,571.362,453.705,576.915,442.245,584.643z
M1739.312,581.825l-8.097-5.8c9.774-10.66,19.036-19.833,28.314-28.042l7.372,6.511
C1757.882,562.474,1748.857,571.415,1739.312,581.825z M972.083,578.203l-10.404-0.548c0.903-13.391,1.799-25.551,2.739-37.176
l10.396,0.657C973.876,552.724,972.983,564.849,972.083,578.203z M554.664,576.274c-13.005-5.72-25.797-9.372-38.02-10.852
l1.415-9.127c13.349,1.617,27.227,5.564,41.249,11.732L554.664,576.274z M1977.916,561.382
c-10.033-9.087-20.423-17.107-30.878-23.838l6.137-7.446c10.925,7.034,21.759,15.395,32.2,24.85L1977.916,561.382z
M1393.061,549.907c-5.185-13.707-9.457-24.883-13.446-35.172l9.858-2.987c3.999,10.312,8.279,21.508,13.472,35.237
L1393.061,549.907z M1798.094,530.977l-5.974-7.549c12.818-7.926,25.764-13.797,38.48-17.447l3.219,8.762
C1822.087,518.112,1810.067,523.574,1798.094,530.977z M1911.813,519.691c-12.908-4.813-25.974-7.791-38.835-8.853l0.969-9.173
c13.923,1.15,28.025,4.358,41.916,9.537L1911.813,519.691z M978.148,504.15l-10.381-0.815c1.299-12.926,2.712-25.409,4.2-37.103
l10.357,1.03C980.845,478.885,979.44,491.296,978.148,504.15z M1365.747,479.696c-5.036-12.425-9.781-23.783-14.509-34.722
l9.736-3.287c4.753,10.999,9.523,22.416,14.584,34.902L1365.747,479.696z M987.63,430.529l-10.315-1.318
c2.108-12.898,4.425-25.302,6.887-36.867l10.242,1.703C992.01,405.485,989.717,417.758,987.63,430.529z M1335.755,410.578
c-5.6-11.907-11.245-23.299-16.779-33.86l9.457-3.871c5.591,10.671,11.293,22.176,16.946,34.195L1335.755,410.578z
M1003.295,357.997l-10.112-2.233c3.635-12.864,7.643-24.981,11.911-36.015l9.861,2.98
C1010.782,333.518,1006.859,345.384,1003.295,357.997z M1300.467,343.656c-6.855-11.411-13.857-22.124-20.812-31.84l8.807-4.926
c7.096,9.913,14.231,20.829,21.211,32.446L1300.467,343.656z M1030.608,288.978l-9.346-4.077
c6.843-12.256,14.432-23.093,22.556-32.211l8.187,5.7C1044.33,267.004,1037.13,277.295,1030.608,288.978z M1255.768,281.936
c-9.107-10.091-18.506-19.03-27.937-26.568l6.991-6.833c9.848,7.871,19.636,17.177,29.094,27.657L1255.768,281.936z
M1081.083,234.513l-5.619-7.759c12.346-6.984,25.949-11.526,40.43-13.498l1.587,9.105
C1104.444,224.136,1092.198,228.224,1081.083,234.513z M1194.948,234.507c-12.141-5.785-24.787-9.74-37.589-11.755l1.827-9.07
c13.894,2.187,27.59,6.466,40.707,12.716L1194.948,234.507z"/>
<path style="fill:#EEF7FF;" d="M2206.552,1351.257c-86.487-14.052-72.374,112.901-195.656,112.901
c-112.716,0-148.776-483.459-230.786-345.259c-420.434,708.496-176.296-301.301-510.66-314.133
c-151.19-5.802-251.936,170.909-289.087,483.783c-53.853,282.849-178.746-215.734-307.274-169.377
c-81.686,29.462-45.566,377.18-180.203,371.804c-123.2-4.919-114.013-153.77-200.5-139.719
c-93.118,15.129-120.931,239.833-120.931,239.833h2156.027C2327.483,1591.089,2299.67,1366.386,2206.552,1351.257z"/>
<path style="fill:#DCE6FF;" d="M2129.962,1488.678c-79.566-6-66.582,48.21-179.998,48.21
c-103.696,0-100.846-121.493-170.239-147.313c-127.789-47.55-233.141,193.106-282.684,72.326
c-94.097-320.482-368.535-481.202-495.143,0c-49.543,120.781-165.401-94.734-282.685-72.326
c-72.193,13.794-66.543,147.313-170.239,147.313c-113.416,0-100.432-54.21-179.998-48.21
c-85.666,6.46-111.253,102.412-111.253,102.412h1983.493C2241.216,1591.089,2215.629,1495.138,2129.962,1488.678z"/>
<path style="fill:#CFDDF9;" d="M171.456,1591.089c0,0-12.672,53.229,49.994,65.766c85.714,17.147,587.991,38.921,465.763,73.155
c-68.998,19.325,35.223,45.567,371.291,55.229s912.737-28.198,718.812-84.217s598.295,6.59,550.167-109.932H171.456z"/>
</g>
<path id="_x31_1" style="fill:#EB5648;" d="M2009.456,817.785l42.833-66.942l-38.521-24.534c4.609-13.872,7.236-28.128,7.968-42.384
l45.098-9.931l-17.158-77.552L2006.274,606c-7.12-15.021-16.595-29.049-28.299-41.468l25.293-39.53l-67.097-42.733l-25.293,39.53
c-15.063-4.982-30.58-7.641-46.057-8.091l-9.013-40.737l-77.732,17.118l9.013,40.737c-13.85,6.908-26.802,15.836-38.365,26.682
l-38.521-24.534l-42.833,66.942l38.521,24.534c-5.376,16.181-8.052,32.884-8.173,49.5l-43.401,9.557l17.158,77.552l45.098-9.931
c6.679,12.624,15.077,24.452,25.11,35.1l-25.293,39.53l67.097,42.733l25.293-39.53c15.064,4.982,30.58,7.641,46.057,8.091
l10.504,47.479l77.732-17.118l-10.504-47.479c13.85-6.908,26.803-15.836,38.365-26.682L2009.456,817.785z M1824.785,738.96
c-83.167-46.28-13.367-172.334,70.093-127.159C1978.04,658.08,1908.242,784.134,1824.785,738.96z"/>
<g id="_x31_0">
<rect x="1119.265" y="1430.586" style="fill:#255084;" width="225.579" height="203.841"/>
<rect x="1119.265" y="1310.954" style="fill:#1D3F70;" width="225.579" height="231.516"/>
<path style="fill:#1D3F70;" d="M1577.691,1674.354H886.418c-13.876,0-25.125-11.223-25.125-25.067l0,0
c0-13.844,11.249-25.067,25.125-25.067h691.273c13.876,0,25.125,11.223,25.125,25.067l0,0
C1602.815,1663.131,1591.567,1674.354,1577.691,1674.354z"/>
<path style="fill:#255084;" d="M595.738,1458.239V719.126c0-28.13,22.804-50.934,50.934-50.934h1170.763
c28.13,0,50.934,22.804,50.934,50.934v739.113c0,28.13-22.804,50.934-50.934,50.934H646.672
C618.542,1509.172,595.738,1486.369,595.738,1458.239z"/>
<ellipse style="fill:#1D3F70;" cx="1232.054" cy="1461.334" rx="26.67" ry="26.608"/>
<polygon style="fill:#A9BFF7;" points="1818.54,717.589 1818.54,920.938 1515.455,1377.307 645.572,1087.226 645.572,717.589 "/>
<path style="fill:#CE4ABF;" d="M645.572,1087.226c0,0,175.016-159.88,352.465-62.365c177.448,97.515,209.457,293.074,447.32,273.48
c244.134-20.11,81.454-345.06,373.183-377.404v480.117H645.572V1087.226z"/>
<path style="fill:#D9E4F9;" d="M645.572,717.589c0,0,44.485,185.63,327.502,141.677
c258.981-40.221,245.781,233.411,415.168,243.233s219.204-186.254,281.643-254.964c62.439-68.71,148.654-56.979,148.654-56.979
v-72.967H645.572z"/>
<path style="fill:#1D3F70;" d="M1181.783,1126.751c-0.201,0-0.404-0.049-0.592-0.152L819.996,928.426
c-0.597-0.327-0.815-1.076-0.487-1.673c0.328-0.596,1.078-0.814,1.673-0.487l361.195,198.173c0.597,0.327,0.815,1.076,0.487,1.673
C1182.64,1126.52,1182.218,1126.751,1181.783,1126.751z"/>
<rect x="987.748" y="1015.379" style="fill:#1D3F70;" width="13.438" height="13.407"/>
<ellipse style="fill:#1D3F70;" cx="820.588" cy="927.346" rx="5.661" ry="5.648"/>
<path style="fill:#1D3F70;" d="M1187.445,1125.518c0,3.119-2.535,5.648-5.661,5.648c-3.127,0-5.661-2.529-5.661-5.648
c0-3.119,2.535-5.648,5.661-5.648C1184.91,1119.87,1187.445,1122.399,1187.445,1125.518z"/>
<rect x="1438.637" y="1291.33" style="fill:#1D3F70;" width="13.438" height="13.407"/>
<path style="fill:#1D3F70;" d="M1207.492,1319.167c-0.635,0-1.173-0.487-1.227-1.131c-0.056-0.679,0.449-1.273,1.127-1.329
l483.298-39.827c0.705-0.06,1.273,0.448,1.329,1.126c0.057,0.679-0.448,1.273-1.126,1.329l-483.299,39.827
C1207.56,1319.165,1207.526,1319.167,1207.492,1319.167z"/>
<path style="fill:#1D3F70;" d="M1213.154,1317.935c0,3.119-2.535,5.648-5.661,5.648c-3.127,0-5.661-2.529-5.661-5.648
c0-3.119,2.535-5.648,5.661-5.648C1210.62,1312.287,1213.154,1314.816,1213.154,1317.935z"/>
<path style="fill:#1D3F70;" d="M1696.453,1278.107c0,3.119-2.535,5.648-5.661,5.648c-3.127,0-5.661-2.529-5.661-5.648
s2.534-5.648,5.661-5.648C1693.918,1272.459,1696.453,1274.988,1696.453,1278.107z"/>
</g>
<g id="_x39__1_">
<path style="fill:#FFFFFF;" d="M1497.472,957.778h210.687c14.386,0,26.049,11.662,26.049,26.049v114.128
c0,14.386-11.662,26.049-26.049,26.049h-210.687c-14.386,0-26.049-11.662-26.049-26.049V983.826
C1471.423,969.44,1483.085,957.778,1497.472,957.778z"/>
<rect x="1659.866" y="995.346" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2693.1719 -664.4758)" style="fill:#397CAA;" width="37.916" height="38.004"/>
<rect x="1659.866" y="1048.431" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2746.2568 -611.391)" style="fill:#FF7B7B;" width="37.916" height="38.004"/>
<rect x="1621.862" y="995.346" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2655.1677 -626.4717)" style="fill:#2E6999;" width="37.916" height="38.004"/>
<rect x="1621.862" y="1048.431" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2708.2527 -573.3868)" style="fill:#FF6666;" width="37.916" height="38.004"/>
<rect x="1583.857" y="995.346" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2617.1636 -588.4674)" style="fill:#285989;" width="37.916" height="38.004"/>
<rect x="1583.857" y="1048.431" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2670.2483 -535.3826)" style="fill:#F95C58;" width="37.916" height="38.004"/>
<rect x="1545.853" y="995.346" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2579.1592 -550.4631)" style="fill:#1F4777;" width="37.916" height="38.004"/>
<rect x="1545.853" y="1048.431" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2632.2441 -497.3783)" style="fill:#EB5648;" width="37.916" height="38.004"/>
<rect x="1507.849" y="995.346" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2541.155 -512.4589)" style="fill:#103663;" width="37.916" height="38.004"/>
<rect x="1507.849" y="1048.431" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 2594.2397 -459.3741)" style="fill:#CE3932;" width="37.916" height="38.004"/>
</g>
<g id="_x38_">
<path style="fill:#667ECC;" d="M1071.766,886.92H802.951c-10.958,0-19.842-8.883-19.842-19.842v-96.952
c0-10.958,8.883-19.842,19.842-19.842h268.814c10.958,0,19.842,8.883,19.842,19.842v96.952
C1091.607,878.037,1082.724,886.92,1071.766,886.92z"/>
<rect x="808.585" y="793.42" style="fill:#1D3F70;" width="33.654" height="33.576"/>
<rect x="825.412" y="810.208" style="fill:#1D3F70;" width="33.654" height="33.576"/>
<rect x="877.607" y="793.42" style="fill:#1D3F70;" width="33.654" height="33.576"/>
<rect x="894.434" y="810.208" style="fill:#A9BFF7;" width="33.654" height="33.576"/>
<polygon style="fill:#A9BFF7;" points="980.286,793.42 980.286,810.21 963.457,810.21 963.457,826.995 946.628,826.995
946.628,793.42 "/>
<rect x="963.457" y="810.21" style="fill:#1D3F70;" width="16.829" height="16.785"/>
<polygon style="fill:#A9BFF7;" points="997.11,810.21 997.11,843.785 963.457,843.785 963.457,826.995 980.286,826.995
980.286,810.21 "/>
<polygon style="fill:#1D3F70;" points="1049.308,793.42 1049.308,810.21 1032.479,810.21 1032.479,826.996 1015.65,826.996
1015.65,793.42 "/>
<rect x="1032.479" y="810.21" style="fill:#A9BFF7;" width="16.829" height="16.785"/>
<polygon style="fill:#1D3F70;" points="1066.132,810.21 1066.132,843.786 1032.479,843.786 1032.479,826.996 1049.308,826.996
1049.308,810.21 "/>
</g>
<g id="_x37_">
<path style="fill:#A9BFF7;" d="M989.829,1338.329h-93.476c-12.594,0-22.802-10.209-22.802-22.803v-93.153
c0-12.594,10.209-22.803,22.802-22.803h93.476c12.594,0,22.802,10.209,22.802,22.803v93.153
C1012.632,1328.12,1002.423,1338.329,989.829,1338.329z"/>
<path style="fill:#FFFFFF;" d="M977.546,1264.208v39.094h-39.231v-39.094H977.546L977.546,1264.208z M987.608,1254.146h-59.356
v59.219h59.356V1254.146L987.608,1254.146z"/>
<rect x="898.574" y="1224.537" style="fill:#A523A2;" width="59.356" height="59.218"/>
<path style="fill:#EB5648;" d="M928.252,1314.912c-0.397,0-0.793-0.151-1.096-0.454c-0.604-0.605-0.603-1.586,0.003-2.189
l59.356-59.219c0.606-0.604,1.586-0.603,2.189,0.002c0.604,0.605,0.603,1.586-0.002,2.189l-59.356,59.219
C929.043,1314.762,928.648,1314.912,928.252,1314.912z"/>
</g>
<g id="_x36_">
<path style="fill:#667ECC;" d="M529.568,895.462V602.193c0-13.408,10.87-24.278,24.278-24.278h140.92
c13.408,0,24.278,10.87,24.278,24.278v293.269c0,13.408-10.87,24.278-24.278,24.278h-140.92
C540.438,919.739,529.568,908.87,529.568,895.462z"/>
<path style="fill:#FFFFFF;" d="M692.937,874.572l-18.762-18.719c2.915-3.499,4.505-7.86,4.505-12.467
c0-5.223-2.038-10.133-5.739-13.825c-7.638-7.62-20.064-7.62-27.702,0c-3.701,3.692-5.739,8.603-5.739,13.825
c0,5.222,2.038,10.132,5.739,13.825c3.819,3.81,8.835,5.715,13.851,5.715c4.443,0,8.883-1.502,12.494-4.492l18.77,18.727
L692.937,874.572z M647.822,854.622c-3.008-3.001-4.665-6.992-4.665-11.236c0-4.245,1.657-8.235,4.665-11.236
c3.106-3.099,7.188-4.649,11.268-4.649c4.081,0,8.162,1.55,11.268,4.649c3.008,3.001,4.665,6.992,4.665,11.236
c0,4.244-1.657,8.234-4.665,11.236C664.145,860.82,654.035,860.82,647.822,854.622z"/>
<path style="fill:#FFFFFF;" d="M666.487,741.276c-6.066,0-12.132-2.304-16.75-6.911c-4.475-4.465-6.94-10.402-6.94-16.717
c0-6.315,2.465-12.252,6.94-16.717c9.236-9.214,24.264-9.214,33.5,0l0,0c4.476,4.465,6.94,10.402,6.94,16.717
c0,6.315-2.465,12.252-6.94,16.717C678.619,738.973,672.553,741.276,666.487,741.276z M666.487,697.675
c-5.131,0-10.261,1.948-14.167,5.845c-3.783,3.774-5.866,8.792-5.866,14.128c0,5.337,2.083,10.354,5.866,14.128
c7.812,7.793,20.522,7.793,28.333,0c3.783-3.774,5.866-8.792,5.866-14.128c0-5.336-2.083-10.354-5.866-14.128l0,0
C676.748,699.625,671.617,697.675,666.487,697.675z"/>
<rect x="641.81" y="782.378" transform="matrix(0.7079 -0.7063 0.7063 0.7079 -359.213 699.7961)" style="fill:#FFFFFF;" width="49.355" height="3.657"/>
<path style="fill:#FFFFFF;" d="M562.988,628.959l30.83,20.544l-17.333-1.541c-0.109-0.01-0.217-0.014-0.325-0.014
c-1.417,0-2.72,0.823-3.321,2.128l-7.276,15.804L562.988,628.959L562.988,628.959z M558.822,621.788l4.087,58.603l13.252-28.786
l31.625,2.811L558.822,621.788L558.822,621.788z"/>
<polygon style="fill:#FFFFFF;" points="642.005,621.788 646.091,680.392 659.343,651.605 690.969,654.417 "/>
<polygon style="fill:#FFFFFF;" points="562.983,764.658 562.983,773.947 566.641,773.947 566.641,768.315 581.475,768.315
581.475,800.099 575.917,800.099 575.917,803.756 590.691,803.756 590.691,800.099 585.132,800.099 585.132,768.315
599.967,768.315 599.967,773.947 603.625,773.947 603.625,764.658 "/>
<rect x="549.759" y="725.438" transform="matrix(0.7079 -0.7063 0.7063 0.7079 -345.8851 618.1671)" style="fill:#FFFFFF;" width="49.355" height="3.657"/>
<path style="fill:#FFFFFF;" d="M587.155,702.414c-4.906,0-8.881,3.966-8.881,8.86c0-4.895-3.975-8.86-8.881-8.86
c4.906,0,8.881-3.965,8.881-8.86C578.274,698.448,582.249,702.414,587.155,702.414z"/>
<path style="fill:#FFFFFF;" d="M604.605,697.284c-3.701,0-6.699,2.991-6.699,6.684c0-3.692-2.998-6.684-6.699-6.684
c3.701,0,6.699-2.991,6.699-6.683C597.906,694.293,600.904,697.284,604.605,697.284z"/>
<path style="fill:#FFFFFF;" d="M609.641,713.839c-3.701,0-6.699,2.991-6.699,6.683c0-3.692-2.998-6.683-6.699-6.683
c3.701,0,6.699-2.991,6.699-6.683C602.942,710.848,605.941,713.839,609.641,713.839z"/>
<path style="fill:#FFFFFF;" d="M606.646,827.51c-1.596-1.595-4.186-1.597-5.784-0.004l-4.6,4.582l-1.703-1.702
c-1.596-1.595-4.186-1.596-5.784-0.004v0c-1.327,1.322-1.536,3.32-0.662,4.875l-28.945,28.835c-1.437,1.432-2.229,3.337-2.23,5.364
c-0.002,2.027,0.788,3.932,2.223,5.366c1.481,1.48,3.428,2.22,5.375,2.22c1.943,0,3.887-0.737,5.368-2.212l28.95-28.841
c1.558,0.874,3.56,0.668,4.887-0.654c1.598-1.592,1.6-4.176,0.004-5.771l-1.703-1.702l4.6-4.582
C608.24,831.689,608.242,829.105,606.646,827.51z M567.322,872.239c-1.538,1.533-4.041,1.532-5.577-0.004
c-0.743-0.742-1.151-1.728-1.151-2.776c0.001-1.048,0.411-2.034,1.155-2.775l28.831-28.721l5.566,5.561L567.322,872.239z"/>
</g>
<g id="_x35_">
<path style="fill:#667ECC;" d="M1235.358,604.798H813.294c-12.356,0-22.372-10.016-22.372-22.372v-70.052
c0-12.356,10.016-22.372,22.372-22.372h422.063c12.356,0,22.372,10.016,22.372,22.372v70.052
C1257.73,594.782,1247.713,604.798,1235.358,604.798z"/>
<rect x="819.753" y="524.646" style="fill:#FFFFFF;" width="4.13" height="49.645"/>
<rect x="827" y="530.391" style="fill:#FFFFFF;" width="24.397" height="14.075"/>
<rect x="827" y="548.63" style="fill:#FFFFFF;" width="33.397" height="14.075"/>
<rect x="1002.671" y="524.646" style="fill:#FFFFFF;" width="4.13" height="49.645"/>
<rect x="975.157" y="530.391" transform="matrix(-1 -1.224647e-16 1.224647e-16 -1 1974.7106 1074.8558)" style="fill:#FFFFFF;" width="24.397" height="14.075"/>
<rect x="966.156" y="548.63" transform="matrix(-1 -1.224647e-16 1.224647e-16 -1 1965.71 1111.3354)" style="fill:#FFFFFF;" width="33.397" height="14.075"/>
<rect x="911.212" y="524.646" style="fill:#FFFFFF;" width="4.13" height="49.645"/>
<rect x="898.449" y="530.391" style="fill:#FFFFFF;" width="29.655" height="14.075"/>
<rect x="892.979" y="548.63" style="fill:#FFFFFF;" width="40.595" height="14.075"/>
<rect x="1104.904" y="547.403" style="fill:#FFFFFF;" width="49.761" height="4.13"/>
<rect x="1127.06" y="542.414" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 1691.3209 -592.3848)" style="fill:#FFFFFF;" width="29.586" height="14.108"/>
<rect x="1103.32" y="542.414" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 1673.0387 -574.1025)" style="fill:#FFFFFF;" width="40.501" height="14.108"/>
<rect x="1032.735" y="524.615" style="fill:#FFFFFF;" width="49.76" height="4.13"/>
<rect x="1051.964" y="539.494" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 1613.3042 -520.2087)" style="fill:#FFFFFF;" width="29.586" height="14.108"/>
<rect x="1028.224" y="544.952" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 1600.4796 -496.4689)" style="fill:#FFFFFF;" width="40.501" height="14.108"/>
<rect x="1177.074" y="570.191" style="fill:#FFFFFF;" width="49.76" height="4.13"/>
<rect x="1196.302" y="545.335" transform="matrix(6.123234e-17 -1 1 6.123234e-17 658.7067 1763.4834)" style="fill:#FFFFFF;" width="29.586" height="14.108"/>
<rect x="1172.562" y="539.877" transform="matrix(6.123234e-17 -1 1 6.123234e-17 645.8821 1739.7435)" style="fill:#FFFFFF;" width="40.501" height="14.108"/>
</g>
<path id="_x34_" style="fill:#CE4ABF;" d="M745.718,462.985l5.401-27.742l-21.297-4.127c0.066-4.488-0.235-8.924-0.909-13.265
l20.49-7.042l-9.226-26.72l-20.517,7.051c-2.173-3.871-4.664-7.555-7.449-11.015l14.234-16.341l-21.381-18.537l-14.215,16.319
c-3.783-2.248-7.792-4.187-11.989-5.806l4.137-21.248l-27.807-5.389l-4.137,21.248c-4.499-0.066-8.944,0.234-13.296,0.906
l-7.058-20.442l-26.782,9.204l7.068,20.469c-3.88,2.168-7.573,4.654-11.04,7.432l-16.379-14.201l-18.581,21.331l16.357,14.182
c-2.254,3.774-4.197,7.774-5.82,11.961l-21.297-4.127l-5.401,27.742l21.297,4.127c-0.066,4.488,0.235,8.923,0.908,13.265
l-20.49,7.042l9.226,26.72l20.516-7.051c2.173,3.871,4.665,7.555,7.449,11.014l-14.234,16.341l21.381,18.538l14.215-16.319
c3.783,2.248,7.793,4.187,11.989,5.806l-4.137,21.248l27.807,5.388l4.137-21.248c4.499,0.066,8.944-0.234,13.296-0.906l7.058,20.442
l26.782-9.204l-7.068-20.469c3.88-2.168,7.573-4.654,11.04-7.431l16.379,14.201l18.581-21.331l-16.357-14.182
c2.253-3.774,4.197-7.775,5.82-11.961L745.718,462.985z M642.358,469.147c-51.359-10.864-36.463-87.335,15.231-78.221
C708.946,401.79,694.05,478.261,642.358,469.147z"/>
<path id="_x33_" style="fill:#7689D6;" d="M2068.479,1047.883l3.715-19.08l-14.647-2.839c0.045-3.087-0.162-6.137-0.625-9.123
l14.092-4.843l-6.345-18.377l-14.111,4.85c-1.495-2.662-3.208-5.196-5.123-7.576l9.79-11.239l-14.705-12.75l-9.777,11.224
c-2.602-1.546-5.359-2.88-8.246-3.993l2.845-14.614l-19.125-3.706l-2.845,14.613c-3.094-0.045-6.152,0.161-9.144,0.623l-4.854-14.06
l-18.42,6.331l4.861,14.078c-2.669,1.491-5.208,3.201-7.593,5.111l-11.265-9.767l-12.779,14.671l11.25,9.754
c-1.55,2.596-2.886,5.347-4.003,8.227l-14.647-2.839l-3.715,19.08l14.648,2.839c-0.045,3.087,0.161,6.137,0.625,9.123l-14.092,4.843
l6.345,18.377l14.111-4.85c1.495,2.662,3.208,5.196,5.123,7.575l-9.79,11.239l14.705,12.75l9.777-11.224
c2.602,1.546,5.36,2.88,8.246,3.993l-2.845,14.614l19.125,3.706l2.845-14.614c3.094,0.045,6.151-0.161,9.144-0.623l4.854,14.059
l18.42-6.331l-4.861-14.078c2.669-1.491,5.208-3.201,7.593-5.111l11.265,9.767l12.779-14.671l-11.25-9.754
c1.55-2.596,2.886-5.347,4.003-8.227L2068.479,1047.883z M1997.39,1052.121c-35.324-7.472-25.078-60.067,10.475-53.798
C2043.188,1005.795,2032.943,1058.39,1997.39,1052.121z"/>
<g id="_x32__18_">
<path style="fill:#662948;" d="M1484.887,321.948c0,0,10.305-19.486,41.203-13.469c30.898,6.017,37.894,45.248,48.257,63.16
c21.951,37.944,28.302,78.131,16.594,97.321c-13.003,21.313-41.283,12.719-82.647,8.557c-44.141-4.441-62.348-42.836-34.457-95.215
L1484.887,321.948z"/>
<path style="fill:#3A72AA;" d="M1497.016,412.991c-12.239,17.606-20.643,103.855-20.947,110.56
c-14.227-1.16-48.406-3.948-62.98-5.137c-3.872-31.627,38.623-96.094,76.983-103.472c0-0.01,0-0.01,0-0.01
C1494.396,413.503,1497.016,412.991,1497.016,412.991z"/>
<path style="fill:#F99370;" d="M1313.852,887.415l61.532-0.195c-0.081-9.813-2.546-29.642-0.506-40.822
c-0.002,0-26.586,3.212-26.586,3.212c0.694,5.384,2.238,17.06-6.731,19.782c-11.62,3.526-24.231,2.315-31.535,11.158
C1308.124,883.524,1310.315,887.426,1313.852,887.415z"/>
<path style="fill:#1D3F70;" d="M1377.167,881.303c-0.245,2.077-0.813,4.095-1.85,5.917l-62.131,0.197
c-3.021,0.01-5.217-2.95-4.224-5.797c2.338-6.652,10.069-16.298,38.704-17.883c0,0,10.471,1.746,26.481-1.198
C1374.145,862.539,1378.171,872.883,1377.167,881.303z"/>
<path style="fill:#E8C0A6;" d="M1327.312,879.374c-0.491-0.004-0.957-0.298-1.155-0.781c-2.295-5.597-6.407-7.299-6.581-7.368
c-3.257-5.026,6.117-1.472,8.91,6.417C1328.839,878.449,1328.187,879.397,1327.312,879.374z"/>
<path style="fill:#E8C0A6;" d="M1337.091,878.165c-0.532-0.004-1.024-0.348-1.191-0.881c-2.034-6.484-7.709-9.465-7.766-9.495
c-2.961-5.847,7.931,0.386,10.168,8.744C1338.566,877.328,1337.925,878.184,1337.091,878.165z"/>
<path style="fill:#E8C0A6;" d="M1348.217,876.765c-0.516-0.004-0.996-0.327-1.178-0.84c-2.371-6.732-7.811-10.385-7.866-10.422
c-2.285-6.13,7.613,1.28,10.241,9.589C1349.714,875.892,1349.067,876.786,1348.217,876.765z"/>
<path style="fill:#395B96;" d="M1309.065,881.519l68.17-0.216c-0.245,2.077-0.813,4.095-1.851,5.917l-62.131,0.197
C1310.191,887.431,1307.986,884.379,1309.065,881.519z"/>
<path style="fill:#EB5648;" d="M1510.604,654.578c-30.273-1.506-82.898-18.556-105.953,19.704
c-25.24,41.785-19.838,130.89-17.655,173.947c-33.328,10.421-51.118-0.398-51.118-0.398
c-13.474-88.211-58.548-289.248,97.206-260.19c-0.003-0.006,104.598,0.808,104.598,0.808
C1554.899,624.164,1539.019,654.204,1510.604,654.578z"/>
<path style="fill:#F99370;" d="M1387.633,887.985l61.532-0.195c-0.081-9.813-2.546-29.642-0.506-40.822
c-0.002,0-26.586,3.215-26.586,3.215c0.694,5.384,2.446,17.062-6.731,19.778c-11.644,3.447-24.231,2.315-31.535,11.158
C1381.905,884.094,1384.096,887.997,1387.633,887.985z"/>
<path style="fill:#1D3F70;" d="M1451.017,881.874c-0.244,2.077-0.813,4.095-1.851,5.917l-62.131,0.197
c-3.021,0.01-5.217-2.95-4.224-5.796c2.338-6.652,10.164-16.465,38.798-18.05c0,0,10.313,2.047,26.323-0.896
C1447.931,863.245,1452.021,873.455,1451.017,881.874z"/>
<path style="fill:#E8C0A6;" d="M1401.093,879.944c-0.491-0.004-0.957-0.297-1.155-0.781c-2.295-5.596-6.407-7.298-6.581-7.368
c-3.257-5.026,6.117-1.472,8.91,6.417C1402.62,879.019,1401.968,879.968,1401.093,879.944z"/>
<path style="fill:#E8C0A6;" d="M1410.872,878.736c-0.532-0.004-1.024-0.348-1.191-0.881c-2.034-6.484-7.709-9.465-7.766-9.494
c-2.961-5.847,7.932,0.386,10.168,8.744C1412.348,877.899,1411.706,878.754,1410.872,878.736z"/>
<path style="fill:#E8C0A6;" d="M1421.998,877.335c-0.516-0.004-0.997-0.327-1.178-0.84c-2.371-6.732-7.812-10.385-7.866-10.421
c-2.285-6.13,7.612,1.28,10.241,9.589C1423.495,876.462,1422.848,877.357,1421.998,877.335z"/>
<path style="fill:#395B96;" d="M1382.847,882.09l68.17-0.216c-0.244,2.077-0.813,4.095-1.851,5.917l-62.131,0.197
C1383.972,888.002,1381.767,884.95,1382.847,882.09z"/>
<path style="fill:#CE3932;" d="M1441.599,587.701l-9.456,64.992c-39.23,15.063-42.366,56.096-46.296,102.286
C1368.294,683.434,1370.43,611.268,1441.599,587.701z"/>
<path style="fill:#EB5648;" d="M1559.43,661.109c-33.398,16.386-72.768-5.476-95.407,39.242
c-18.295,39.165-8.797,114.354-4.123,148.444c-33.328,10.421-51.118-0.398-51.118-0.398
c-19.118-76.151-62.819-253.119,64.641-260.446c34.892-0.837,80.728-0.175,111.574-1.788
C1584.997,586.163,1600.728,640.848,1559.43,661.109z"/>
<path style="fill:#23659B;" d="M1442.99,496.795c-0.609-0.002-1.095-0.536-1.035-1.141c2.217-17.229,20.704-39.44,23.102-38.228
c1.625,2.12-19.639,20.472-21.018,38.423C1443.988,496.392,1443.527,496.799,1442.99,496.795z"/>
<path style="fill:#3A72AA;" d="M1585.755,568.192c2.552,19.27,1.138,27.395,1.138,27.395c-51.425,5.972-83.82,6.018-146.308-3.187
c40.431-117.755-49.093-42.536,56.431-179.409c0.25,0.023,25.608,2.512,25.838,2.529
C1552.048,437.784,1570.737,467.147,1585.755,568.192z"/>
<polygon style="fill:#163260;" points="1332.746,593.606 1471.186,594.684 1444.414,493.229 1306.811,492.158 "/>
<rect x="1398.505" y="527.86" transform="matrix(0.0078 -1 1 0.0078 793.9092 1994.3096)" style="fill:#234270;" width="6.875" height="138.444"/>
<polygon style="fill:#0B274F;" points="1442.334,493.213 1447.287,493.251 1473.496,594.201 1539.194,594.712 1539.141,601.587
1468.831,601.04 1468.879,594.891 "/>
<path style="fill:#F99370;" d="M1568.222,435.482c31.726,24.112,48.436,67.19,39.701,106.177
c-3.401,21.912-17.438,42.668-39.189,48.708c-10.748,4.192-60.785,10.679-58.318-7.004c0.825-4.24,4.613-5.775,8.278-6.881
c6.042-2.073,12.271-4.367,18.038-6.928c10.331-5.248,23.599-10.138,25.929-21.646c4.98-26.875-5.867-53.273-25.886-70.998
c0.036,0.021-0.095-0.081,0.202,0.137C1509.819,456.534,1540.899,415.223,1568.222,435.482z"/>
<path style="fill:#3A72AA;" d="M1586.176,585.694c-16.306,7.111-31.712,9.213-43.546,9.548c0,0,2.869-17.591-9.831-24.946
c72.288-53.927-30.17-77.659-9.831-157.568C1600.01,414.701,1678.657,531.842,1586.176,585.694z"/>
<path style="fill:#F99370;" d="M1479.836,594.25c0,0,6.334-21.46,43.122-18.695l7.559,19.09L1479.836,594.25z"/>
<path style="fill:#23659B;" d="M1551.174,594.714c-5.268,0.544-8.543,0.529-8.543,0.529s2.869-17.591-9.831-24.946
c0,0,3.155-2.012,7.286-5.592C1540.086,564.705,1553.986,575.055,1551.174,594.714z"/>
<path style="fill:#23659B;" d="M1539.406,567.709c-0.929,0.014-1.417-1.244-0.67-1.84c32.133-28.12,16.737-58.271-2.093-77.87
c-0.924-0.962,0.644-2.439,1.548-1.408C1558.218,506.879,1573.271,540.264,1539.406,567.709z"/>
<path style="fill:#23659B;" d="M1550.486,594.818c-5.939-3.514,16.773-1.607,34.121-9.624c0.536-0.213,1.146,0.044,1.362,0.58
c0.215,0.535-0.045,1.144-0.581,1.358C1567.47,594.301,1550.718,594.815,1550.486,594.818z"/>
<path style="fill:#F4D4BE;" d="M1529.838,445.002c-0.779,5.498-6.068,7.173-7.384,6.87c-18.473-8.047-5.484,17.012-13.128,19.494
c-6.429,2.087-7.126-6.263-12.713-6.763c-8.111-0.727-9.796,3.67-12.625,0.968c-3.939-3.763,5.72-10.204,4.541-16.892
c-0.828-4.695-10.21-0.056-10.011-9.706c0.22-9.479,11.207-23.596,11.556-24.032c0-0.01,0-0.01,0-0.01
c10.347-5.37,32.895-2.203,32.895-2.203s2.987-0.633,5.85,0.475C1530.172,420.922,1531.372,436.75,1529.838,445.002z"/>
<path style="fill:#F99370;" d="M1497.711,403.823l-0.697,9.167c0,0-0.928,8.815-1.057,25.217
c-0.18,23.057,5.55,20.417,13.593,4.371c4.419-8.817,13.418-29.849,13.418-29.849l1.345-16.122L1497.711,403.823z"/>
<path style="fill:#E57D61;" d="M1528.714,377.809c-0.109,0.435-0.234,0.875-0.383,1.296c-0.551-0.453-3.491,14.381-4.727,26.008
c-11.108,6.376-24.23,5.82-26.416,5.129c0.725-17.211,2.378-42.961,15.701-47.657
C1521.821,359.831,1531.195,368.67,1528.714,377.809z"/>
<path style="fill:#662948;" d="M1495.655,320.835c-33.357-4.256-39.065,48.285-16.525,67.809L1495.655,320.835z"/>
<path style="fill:#F99370;" d="M1534.727,358.193c1.52,11.495,1.52,24.963-4.983,34.811l-0.014,0.004
c-17.791,28.527-75.773,9.595-59.582-24.27c2.374-4.32,3.733-9.583,3.943-14.055
C1474.984,307.75,1530.391,318.032,1534.727,358.193z"/>
<path style="fill:#C16256;" d="M1490.751,363.965c-1.578-1.605,0.985-4.041,2.516-2.39
C1494.844,363.18,1492.281,365.616,1490.751,363.965z"/>
<path style="fill:#C16256;" d="M1476.06,365.798c-1.578-1.605,0.985-4.041,2.515-2.39
C1480.153,365.013,1477.59,367.449,1476.06,365.798z"/>
<path style="fill:#C16256;" d="M1484.701,378.884c-2.94,0.444-7.189-0.349-4.384-3.841c2.631-2.636,2.991-9.828,2.994-9.9
c0.025-0.842,1.365-0.795,1.314,0.059c-0.014,0.316-0.387,7.771-3.378,10.766c-0.339,0.341-0.692,0.808-0.447,1.101
c0.692,0.83,3.997,0.992,5.908-0.312c0.293-0.204,0.707-0.129,0.913,0.17C1488.102,378.301,1485.726,378.578,1484.701,378.884z"/>
<path style="fill:#E57D61;" d="M1534.727,358.193c1.52,11.495,1.521,24.963-4.983,34.811l-0.014,0.004
c3.022-7.397,2.021-21.627,2.021-21.627l-4.055-5.251c-41.851-0.885-47.759-32.441-47.759-32.441
C1497.945,308.646,1532.751,331.71,1534.727,358.193z"/>
<path style="fill:#662948;" d="M1483.176,329.757c0,0,5.981,31.071,43.849,33.243l10.231-10.413
C1542.186,325.618,1510.229,297.719,1483.176,329.757z"/>
<path style="fill:#F99370;" d="M1543.371,361.648c1.241,13.145-15.402,14.521-16.346,1.351
C1525.784,349.855,1542.427,348.478,1543.371,361.648z"/>
<path style="fill:#C16256;" d="M1539.481,358.218c0,0,0-0.001,0-0.001c-1.653-5.219-11.74,0.263-7.467,0.493
c0.019-0.23,4.489-2.855,5.858-0.536c-4.401,2.03-5.926,10.174-3.367,7.558c0.005-0.049,0.57-4.689,4.032-6.418
C1540.156,371.189,1541.776,362.972,1539.481,358.218z"/>
<path style="fill:#F4D4BE;" d="M1538.215,369.453c0.576,1.821-2.304,2.682-2.827,0.845
C1534.812,368.476,1537.693,367.615,1538.215,369.453z"/>
<path style="fill:#23659B;" d="M1495.652,477.293c0.026,3.805-5.964,3.758-5.878-0.046
C1489.749,473.443,1495.738,473.489,1495.652,477.293z"/>
<path style="fill:#23659B;" d="M1489.719,497.896c0.026,3.805-5.964,3.758-5.878-0.046
C1483.815,494.046,1489.805,494.092,1489.719,497.896z"/>
<path style="fill:#23659B;" d="M1489.574,516.477c0.026,3.805-5.964,3.758-5.878-0.046
C1483.67,512.627,1489.66,512.673,1489.574,516.477z"/>
<path style="fill:#CE3932;" d="M1386.25,833.438c0.472,9.223,0.746,14.79,0.746,14.79c-33.328,10.421-51.118-0.398-51.118-0.398
s-0.62-4.585-1.991-12.572C1334.283,835.459,1355.01,845.828,1386.25,833.438z"/>
<path style="fill:#CE3932;" d="M1459.9,848.796c-33.328,10.421-51.118-0.398-51.118-0.398s-1.083-4.279-2.909-11.664
c0,0,18.256,9.654,52.381,0.408C1459.202,844.437,1459.9,848.796,1459.9,848.796z"/>
<path style="fill:#E57D61;" d="M1481.847,594.049c-6.542,0.15,17.395-15.962,24.467-11.922c0.054,0.576-0.369,1.085-0.944,1.138
C1491.96,584.558,1482.976,594.017,1481.847,594.049z"/>
<path style="fill:#E57D61;" d="M1491.93,594.496c-4.947-2.153,10.5-5.655,18.15-7.657c0.563-0.114,1.118,0.255,1.231,0.821
c0.113,0.565-0.256,1.115-0.823,1.227C1502.343,590.495,1492.75,594.414,1491.93,594.496z"/>
<path style="fill:#C16256;" d="M1496.245,358.686c-0.161-0.001-0.322-0.054-0.457-0.161c-4.153-3.295-7.962-2.264-8.122-2.218
c-0.396,0.113-0.807-0.114-0.92-0.508c-0.114-0.393,0.11-0.803,0.504-0.918c0.187-0.056,4.681-1.315,9.464,2.481
c0.322,0.256,0.376,0.723,0.12,1.044C1496.686,358.591,1496.467,358.687,1496.245,358.686z"/>
<path style="fill:#C16256;" d="M1485.818,384.101c0.104-0.122,0.247-0.212,0.416-0.247c5.197-1.069,6.845-4.647,6.911-4.799
c0.167-0.375,0.605-0.545,0.98-0.38c0.375,0.164,0.548,0.599,0.384,0.975c-0.076,0.179-1.989,4.428-7.974,5.659
c-0.403,0.083-0.797-0.176-0.88-0.577C1485.608,384.499,1485.675,384.269,1485.818,384.101z"/>
<path style="fill:#C16256;" d="M1474.368,360.308c-0.144-0.001-0.288-0.044-0.415-0.13c-0.34-0.232-0.425-0.695-0.192-1.033
c0.099-0.143,2.445-3.502,6.313-3.323c0.41,0.019,0.728,0.367,0.709,0.777c-0.019,0.41-0.365,0.734-0.779,0.707
c-3.04-0.121-4.995,2.652-5.015,2.68C1474.842,360.197,1474.607,360.31,1474.368,360.308z"/>
</g>
<g id="_x31_">
<path style="fill:#F99370;" d="M805.507,1177.549c0,0,8.349,1.235,14.154-3.156l-7.872-3.122L805.507,1177.549z"/>
<path style="fill:#F99370;" d="M505.464,1669.707l-1.094-0.57c0,0.764-0.947,3.418-1.713,3.418l-32.357,0.665
c-0.766,0-1.237-1.439-1.237-2.203l-2.76-0.788c0-4.834,0.399-6.587,2.539-10.328c7.867-10.431-1.524-57.083-1.253-69.271
c-1.94-3.333,34.073-0.718,34.576-1.505c0.809,0.001,1.447,0.689,1.383,1.494l-4.238,53.307
C497.408,1654.047,505.193,1660.165,505.464,1669.707z"/>
<path style="fill:#F99370;" d="M665.504,1674.352h-64.451c-5.626-0.226-5.583-5.951-4.546-10.39
c3.549-16.002-2.724-52.746-3.887-69.203c-0.31-3.013,2.061-5.633,5.097-5.633c47.557-0.482,31.365-7.038,28.566,44.201
c-1.424,26.072,18.354,23.986,30.541,25.963C664.847,1659.869,678.265,1670.795,665.504,1674.352z"/>
<path style="fill:#103663;" d="M594.053,1667.681c0.174,3.153,1.741,6.749,5.401,6.671c0,0,66.854,0,66.854,0
c15.768-5.835-6.885-26.703-37.752-26.579c-14.141,3.872-30.992,1.192-30.992,1.192S593.013,1654.746,594.053,1667.681z"/>
<path style="fill:#103663;" d="M471.429,1674.353h28.574c13.925-1.897,3.975-26.551-14.287-27.301
C467.357,1646.297,457.515,1672.481,471.429,1674.353z"/>
<path style="fill:#E8C0A6;" d="M650.371,1665.326c0.561,0,1.093-0.334,1.317-0.886c2.57-6.325,7.202-8.26,7.398-8.339
c3.613-5.689-6.887-1.636-10.031,7.276C648.664,1664.281,649.389,1665.344,650.371,1665.326z"/>
<path style="fill:#E8C0A6;" d="M639.332,1663.997c0.607,0,1.168-0.391,1.357-0.999c2.271-7.325,8.664-10.71,8.729-10.743
c3.276-6.61-8.93,0.467-11.443,9.905C637.682,1663.052,638.396,1664.011,639.332,1663.997z"/>
<path style="fill:#E8C0A6;" d="M626.769,1662.456c0.588,0,1.137-0.367,1.342-0.952c2.652-7.607,8.778-11.75,8.84-11.79
C640.576,1639.948,620.571,1659.845,626.769,1662.456z"/>
<path style="fill:#E8C0A6;" d="M475.472,1656.203c7.937-4.804,20.113-0.475,20.159-0.127c8.288-1.782-11.311-8.761-20.878-2.514
C473.507,1654.235,474.079,1656.255,475.472,1656.203z"/>
<path style="fill:#0F2E54;" d="M670.986,1667.681h-76.933c0.283,2.343,0.932,4.618,2.11,6.671h70.145
C669.763,1674.358,672.213,1670.902,670.986,1667.681z"/>
<path style="fill:#0F2E54;" d="M471.429,1674.353h28.574c3.635,0.004,6.561-3.052,6.336-6.671h-41.247
C464.872,1671.3,467.794,1674.357,471.429,1674.353z"/>
<path style="fill:#CE3932;" d="M619.506,1180.145c62.916,47.915,140.915,28.285,185.551-5.044l4.155-18.833
c-102.955,7.959-128.1-42.705-193.883-65.712l5.728,20.759C621.058,1111.317,618.804,1178.61,619.506,1180.145z"/>
<path style="fill:#1D3F70;" d="M634.412,1631.681c-24.252,9.072-51.134,2.155-51.134,2.155c0.341-3.272,0.65-6.594,0.926-9.963
c9.158-113.218-21.5-272.787-24.65-284.402c0-0.008-0.008-0.016-0.008-0.016c-0.146-0.559-0.236-0.769-0.244-0.591
c-20.127,131.209-40.068,240.013-49.421,290.81c-23.895,11.146-48.138-4.803-48.138-4.803c-0.146-4.585-0.268-9.089-0.373-13.56
c-5.513-88.156,7.096-358.674,52.742-411.496c52.953,26.204,125.758,30.295,125.758,30.295
C652.047,1460.716,651.454,1570.937,634.412,1631.681z"/>
<path style="fill:#F99370;" d="M607.632,1087.698c3.018-15.923,10.346-44.568-1.657-51.629c-8.453-4.619-20.37,1.224-19.312,11.783
c0.9-0.374-1.356,28.847-4.504,36.535C557.268,1087.494,624.586,1122.792,607.632,1087.698z"/>
<path style="fill:#E57D61;" d="M586.641,1046.501c-0.012,0.448-0.009,0.905,0.021,1.351c0.652-0.289-0.387,15.868-2.319,27.396
c10.024,6.75,22.191,6.494,24.483,6.412c3.262-16.942,8.638-37.81-2.854-45.592C598.11,1031.029,586.706,1037.036,586.641,1046.501
z"/>
<path style="fill:#163356;" d="M618.227,1627.104c29.935-139.322-0.519-322.211-5.093-323.552
C610.791,1304.644,637.394,1488.444,618.227,1627.104z"/>
<path style="fill:#163356;" d="M486.717,1624.855l2.108-0.938c23.368-148.257,48.982-325.731,45.739-328.262
C531.05,1296.147,503.571,1473.11,486.717,1624.855z"/>
<path style="fill:#EB5648;" d="M485.194,1276.959c35.249,26.608,121.625,34.898,156.821,22.186
c11.864-5.012,13.523-44.88,12.147-84.033c-2.109-52.192-14.691-89.677-38.833-124.554c-0.297-0.121-5.055-2.399-7.686-2.844
c0.117,4.768-0.975,18.548-25.479-3.326c-0.109,0.094-9.236,0.395-9.236,0.395c-37.074,18.481-52.938,57.771-64.508,102.543
C490.671,1256.013,478.203,1266.592,485.194,1276.959z"/>
<path style="fill:#163356;" d="M594.019,1622.471l-9.816,1.401c9.158-113.218-21.5-272.787-24.65-284.402
c1.916,4.682,14.419,36.654,23.724,96.078C593.378,1500.075,596.528,1589.754,594.019,1622.471z"/>
<path style="fill:#CE3932;" d="M572.928,1084.783c8.067,13.725,43.871,35.207,42.402,5.774c-0.301-0.14-3.589-2.331-7.685-2.845
c0.055,0.773,1.458,21.017-25.485-3.329C582.19,1084.587,577.983,1083.14,572.928,1084.783z"/>
<path style="fill:#163356;" d="M583.279,1633.833c0,0,26.883,6.921,51.134-2.154l3.162-12.717c0,0-30.329,10.509-52.88-1.111
C584.264,1623.21,583.792,1628.542,583.279,1633.833z"/>
<path style="fill:#163356;" d="M461.366,1611.308c0.108,4.47,0.229,8.981,0.378,13.559c0,0,24.238,15.955,48.138,4.807
c0,0,0.783-4.174,2.186-11.821C512.069,1617.851,486.333,1625.512,461.366,1611.308z"/>
<path style="fill:#CE3932;" d="M602.164,1129.91c11.101,0,17.336-2.079,17.72-2.211l-1.38-4.011
c-0.133,0.046-13.486,4.431-36.054-0.073l-0.834,4.158C589.633,1129.372,596.528,1129.91,602.164,1129.91z"/>
<path style="fill:#CE3932;" d="M600.35,1146.228c13.878,0,22.153-3.219,22.626-3.408l-1.576-3.938
c-0.152,0.061-15.347,5.91-38.96,1.333l-0.812,4.162C588.637,1145.736,594.921,1146.228,600.35,1146.228z"/>
<path style="fill:#CE3932;" d="M603.015,1162.403c12.437,0,20.943-3.495,21.472-3.718l-1.651-3.908
c-0.184,0.075-18.414,7.509-40.103-0.048l-1.401,4.004C589.227,1161.484,596.639,1162.403,603.015,1162.403z"/>
<path style="fill:#8E4A35;" d="M630.927,1025.222c0,0-0.941-13.542-5.671-22.666l-7.571,4.203L630.927,1025.222z"/>
<path style="fill:#F99370;" d="M633.219,1064.868c-11.497,12.415-30.416,15.18-44.538,8.073l-0.005,0.001
c-1.052-0.579-2.052-1.219-3.013-1.932l-0.014,0.002c-45.638-41.3,9.541-115.652,39.662-54.833
C635.311,1033.227,651.615,1041.795,633.219,1064.868z"/>
<path style="fill:#C16256;" d="M612.697,1030.527c0.391-0.876-0.004-1.901-0.882-2.291c-0.879-0.39-1.908,0.004-2.3,0.88
c-0.391,0.876,0.003,1.902,0.882,2.291C611.276,1031.797,612.306,1031.403,612.697,1030.527z"/>
<path style="fill:#C16256;" d="M627.195,1027.328c0.392-0.876-0.004-1.902-0.882-2.291c-0.879-0.39-1.908,0.005-2.3,0.88
c-0.391,0.876,0.003,1.901,0.882,2.291C625.774,1028.598,626.803,1028.204,627.195,1027.328z"/>
<path style="fill:#C16256;" d="M623.441,1042.574c-1.013,0.173-2.056,0.171-2.949-0.064c-0.353-0.092-0.562-0.453-0.469-0.802
c0.092-0.353,0.459-0.557,0.805-0.467c2.242,0.59,5.31-0.671,5.688-1.689c0.132-0.358-0.358-0.681-0.793-0.887
c-3.835-1.822-6.701-8.733-6.82-9.026c-0.138-0.336,0.023-0.72,0.361-0.857c0.349-0.143,0.722,0.025,0.86,0.359
c0.028,0.067,2.792,6.732,6.167,8.336c1.84,0.875,1.636,2.058,1.463,2.527C627.302,1041.226,625.427,1042.235,623.441,1042.574z"/>
<path style="fill:#E57D61;" d="M623.074,1011.983c-9.69-4.468-26.558,0.936-34.959,1.306c0,0,2.533,14.232-11.43,25.191
c-0.538,13.236-2.409,19.099,11.996,34.462l-0.005,0.001c-1.052-0.579-2.052-1.219-3.013-1.932l-0.014,0.002
C541.723,1031.653,592.407,957.936,623.074,1011.983z"/>
<path style="fill:#8E4A35;" d="M628.714,983.066c-5.97-10.366-22.329-15.319-35.503-1.435
c-10.143,10.69-21.673,11.21-20.881,21.872c-20.905,10.812-9.049,33.785-9.049,33.785l10.666-1.825
c11.743-10.534,10.61-25.062,10.61-25.062l-0.003-0.002c2.741-0.008,5.614-0.275,8.129-1.057c7.794-2.424,19.431-4.006,28.51-1.198
C630.27,1010.951,635.386,994.651,628.714,983.066z"/>
<path style="fill:#F99370;" d="M562.203,1045.997c1.448,5.437,6.18,8.903,10.57,7.74c4.39-1.162,6.776-6.512,5.328-11.949
c-1.448-5.437-6.18-8.903-10.57-7.74C563.141,1035.21,560.755,1040.56,562.203,1045.997z"/>
<path style="fill:#C16256;" d="M572.359,1038.167c-0.175-0.057-4.326-1.413-6.503,0.818c-0.555,0.567-0.906,1.314-1.078,2.215
c-0.001,0.002,0,0.004-0.001,0.006c-0.016,0.084-0.041,0.16-0.054,0.247c0,0.001-0.001,0.003-0.001,0.004
c0,0.001,0.001,0.001,0,0.001c-0.243,1.672,0.125,3.871,1.109,6.584c0.111,0.302,0.423,0.477,0.732,0.424
c0.038-0.007,0.075-0.017,0.113-0.03c0.342-0.124,0.52-0.502,0.396-0.842c-0.799-2.199-1.154-4.002-1.09-5.381
c3.829,0.48,5.953,4.618,5.974,4.663c0.132,0.268,0.423,0.408,0.704,0.359c0.059-0.01,0.119-0.031,0.178-0.059
c0.328-0.158,0.462-0.551,0.304-0.878c-0.103-0.207-2.419-4.835-6.915-5.424c0.138-0.38,0.319-0.713,0.57-0.971
c1.6-1.64,5.111-0.5,5.144-0.49c0.35,0.113,0.718-0.074,0.834-0.417C572.89,1038.654,572.703,1038.283,572.359,1038.167z"/>
<path style="fill:#C16256;" d="M603.633,1028.581c-0.139,0.023-0.285,0.009-0.424-0.049c-0.394-0.164-0.581-0.614-0.417-1.007
c0.1-0.24,2.519-5.896,9.585-7.488c0.413-0.094,0.831,0.167,0.926,0.583c0.094,0.416-0.168,0.829-0.584,0.923
c-6.259,1.41-8.408,6.364-8.497,6.575C604.114,1028.371,603.886,1028.539,603.633,1028.581z"/>
<path style="fill:#C16256;" d="M627.479,1021.855c-0.237,0.039-0.488-0.031-0.672-0.213c-2.102-2.075-6.32-1.655-6.362-1.65
c-0.424,0.045-0.806-0.262-0.851-0.685c-0.046-0.424,0.261-0.807,0.686-0.85c0.202-0.022,4.987-0.506,7.616,2.088
c0.304,0.3,0.306,0.788,0.006,1.091C627.783,1021.757,627.634,1021.83,627.479,1021.855z"/>
<path style="fill:#F9F8F3;" d="M627.448,1050.274l-16.633-7.522c0,0-0.559,8.877,5.981,10.976
C623.047,1055.735,627.448,1050.274,627.448,1050.274z"/>
<path style="fill:#C16256;" d="M627.574,1051.036c-0.146,0.024-0.301,0.007-0.446-0.059l-16.633-7.522
c-0.389-0.176-0.562-0.633-0.386-1.022c0.177-0.388,0.637-0.561,1.024-0.384l16.633,7.522c0.389,0.176,0.562,0.633,0.385,1.022
C628.042,1050.836,627.82,1050.995,627.574,1051.036z"/>
<path style="fill:#FFFFFF;" d="M997.099,1032.813l-2.526-4.021l-50.202,31.397c0.742,3.044-0.484,6.347-3.276,8.116
c-3.424,2.17-8.157,1.139-10.36-2.259c-2.287-3.526-1.233-8.226,2.322-10.449c2.819-1.763,6.354-1.43,8.788,0.571l50.202-31.397
l-2.526-4.021l-65.443,12.385c-6.09,1.153-11.533,4.524-15.271,9.458l-19.722,26.035l10.526,16.753l10.526,16.753l32.082-6.364
c6.08-1.206,11.493-4.625,15.187-9.591L997.099,1032.813z"/>
<path style="fill:#3A72AA;" d="M443.166,1373.147l-2.076-3.304c-7.726-12.296-3.998-28.513,8.327-36.221l430.627-269.317
l30.054,47.832l-430.627,269.317C467.146,1389.162,450.892,1385.443,443.166,1373.147z"/>
<path style="fill:#255084;" d="M896.962,1111.318l-21.903-34.86c-2.738-4.358-1.417-10.106,2.951-12.838l0,0
c4.368-2.732,10.129-1.414,12.867,2.944l21.903,34.86c2.738,4.358,1.417,10.106-2.951,12.838l0,0
C905.462,1116.994,899.701,1115.676,896.962,1111.318z"/>
<path style="fill:#F99370;" d="M530.835,1282.703l-6.007,3.757c0,0,5.136,5.347,9.568,7.473c4.329,2.077,8.573,4.133,13.282,5.634
c6.636,8.437-18.558-0.451-21.63-0.84c-0.02,0.057-0.019,0.054-0.039,0.11c5.965,4.452,12.203,8.558,18.77,12.071
c4.844,10.338-20.133-5.809-22.995-7.144c-0.009,0.027-0.009,0.025-0.018,0.052c4.846,5.205,10.373,9.779,16.346,13.651
c3.958,10.89-19.972-7.49-22.333-9.78c-0.027,0.014-0.053,0.027-0.08,0.04c3.184,4.696,6.999,8.959,11.016,12.973
c2.316,10.83-16.082-8.322-17.947-10.651c-1.85-0.844-11.917-8.565-18.027-19.946c-2.002-3.729,7.761-13.068,11.98-12.665
c0,0,11.127,0.773,12.993,1.163C520.012,1278.869,526.314,1281.383,530.835,1282.703z"/>
<path style="fill:#F99370;" d="M812.819,1172.909c5.902,6.12,12.863-2.285,3.808-4.459l0.106,0.103
C813.487,1165.294,809.704,1169.931,812.819,1172.909z"/>
<path style="fill:#F99370;" d="M817.853,1170.165c7.942,5.648,11.846-5.541,3.392-5.043c0.183,0.036-0.762-0.293-0.592-0.251
C816.779,1162.298,814.394,1167.995,817.853,1170.165z"/>
<path style="fill:#F99370;" d="M823.219,1167.457l3.091,1.004c4.365,1.25,4.589-5.301,0.921-6.233c0.001,0-3.09-1.004-3.09-1.004
C819.776,1159.974,819.553,1166.525,823.219,1167.457z"/>
<path style="fill:#F99370;" d="M827.51,1164.378l3.091,1.004c4.366,1.25,4.589-5.301,0.922-6.233c0,0-3.09-1.005-3.09-1.005
C824.066,1156.895,823.843,1163.446,827.51,1164.378z"/>
<path style="fill:#CE3932;" d="M572.928,1084.783c0,0-58.397-5.466-110.422,48.513c-38.627,40.078-63.255,108.271,28.127,157.12
c0,0,3.065-9.812,12.089-12.98c0,0-16.469-38.608-11.919-59.345c5.032-22.94,33.343-44.871,55.096-71.864
C564.888,1122.666,572.928,1084.783,572.928,1084.783z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 49 KiB

@@ -0,0 +1,22 @@
<script lang="ts">
import { Button, type Size } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
child: Snippet<[{ onClose: () => void }]>;
size?: Size;
};
const { child, size = 'small' }: Props = $props();
let isOpen = $state(true);
const onReset = () => (isOpen = true);
const onClose = () => (isOpen = false);
</script>
{#if isOpen}
{@render child({ onClose })}
{:else}
<Button {size} color="secondary" onclick={onReset}>Reset</Button>
{/if}
@@ -0,0 +1,23 @@
<script lang="ts">
import { asComponentHref } from '$lib/utilities.js';
import { Button, Icon, Text, VStack, type IconLike } from '@immich/ui';
type Props = {
component: { name: string; icon: IconLike };
};
const { component }: Props = $props();
</script>
<Button
href={asComponentHref(component.name)}
variant="outline"
color="secondary"
class="border-light-300 h-24 w-44 rounded-2xl border "
size="large"
>
<VStack gap={2}>
<Icon icon={component.icon} size="1.5rem" aria-hidden />
<Text size="small">{component.name}</Text>
</VStack>
</Button>
@@ -0,0 +1,25 @@
<script lang="ts">
import type { Color } from '@immich/ui';
import type { Snippet } from 'svelte';
type Item = { color: Color; label: string };
type Props = {
child: Snippet<[Item]>;
};
const { child }: Props = $props();
const items: Item[] = [
{ color: 'primary', label: 'Primary' },
{ color: 'secondary', label: 'Secondary' },
{ color: 'success', label: 'Success' },
{ color: 'info', label: 'Info' },
{ color: 'warning', label: 'Warning' },
{ color: 'danger', label: 'Danger' },
];
</script>
{#each items as item (item.color)}
{@render child(item)}
{/each}
@@ -0,0 +1,59 @@
<script lang="ts">
import { type ExampleCardProps } from '$lib/constants.js';
import { Button, Card, CardBody, CardHeader, CardTitle, HStack, Theme } from '@immich/ui';
import { mdiEye, mdiXml } from '@mdi/js';
import { HighlightSvelte, LineNumbers } from 'svelte-highlight';
import atomOneDark from 'svelte-highlight/styles/atom-one-dark';
const { title, component: Component, code, theme }: ExampleCardProps = $props();
let viewMode = $state<'code' | 'preview'>('preview');
const handleToggle = () => {
viewMode = viewMode === 'code' ? 'preview' : 'code';
};
function getCardBodyClass(viewMode: string, theme?: Theme): string {
if (viewMode === 'code') {
return 'p-0 text-sm';
}
if (theme === Theme.Light) {
return 'bg-white dark:bg-white';
} else if (theme === Theme.Dark) {
return 'bg-black dark:bg-black';
}
return '';
}
const cardBodyClass = $derived(getCardBodyClass(viewMode, theme));
</script>
<svelte:head>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html atomOneDark}
</svelte:head>
<Card>
<CardHeader>
<div class="flex justify-between">
<CardTitle tag="h2">{title}</CardTitle>
<HStack gap={1}>
<Button leadingIcon={mdiEye} disabled={viewMode === 'preview'} onclick={handleToggle} size="small"
>Preview</Button
>
<Button leadingIcon={mdiXml} disabled={viewMode === 'code'} onclick={handleToggle} size="small">Code</Button>
</HStack>
</div>
</CardHeader>
<CardBody class={cardBodyClass}>
{#if viewMode === 'preview'}
<Component />
{:else}
<HighlightSvelte code={code.trim().replaceAll(/\t/gm, ' ')} let:highlighted>
<LineNumbers {highlighted} hideBorder wrapLines />
</HighlightSvelte>
{/if}
</CardBody>
</Card>
@@ -0,0 +1,17 @@
<script lang="ts">
import ExampleCard from '$lib/components/ComponentExampleCard.svelte';
import type { ExampleItem } from '$lib/constants.js';
import { Stack } from '@immich/ui';
type Props = {
examples: ExampleItem[];
};
const { examples }: Props = $props();
</script>
<Stack gap={4} class="mt-4">
{#each examples as { title, code, component, theme } (title)}
<ExampleCard {title} {code} {component} {theme} />
{/each}
</Stack>
@@ -0,0 +1,11 @@
<script lang="ts">
import ComponentLink from '$lib/components/ComponentLink.svelte';
import ComponentTipCard from '$lib/components/ComponentTipCard.svelte';
import { Text } from '@immich/ui';
</script>
<ComponentTipCard>
<Text>
This component can be used with the <ComponentLink name="Field" /> component to build forms
</Text>
</ComponentTipCard>
@@ -0,0 +1,12 @@
<script lang="ts">
import { asComponentHref } from '$lib/utilities.js';
import { Link } from '@immich/ui';
type Props = {
name: string;
};
const { name }: Props = $props();
</script>
<Link href={asComponentHref(name)}>{name}</Link>
@@ -0,0 +1,14 @@
<script lang="ts">
import { Alert } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
children: Snippet;
};
const { children }: Props = $props();
</script>
<Alert color="info" class="mt-4" title="Note">
{@render children()}
</Alert>
@@ -0,0 +1,16 @@
<script lang="ts">
import MarkdownPage from '$lib/components/MarkdownPage.svelte';
import { type ContainerSize } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
name: string;
description: string;
size?: ContainerSize;
children?: Snippet;
};
const { size, name, description, children }: Props = $props();
</script>
<MarkdownPage attributes={{ title: name, description }} {size} {children} />
@@ -0,0 +1,27 @@
<script lang="ts">
import type { Size } from '@immich/ui';
import type { Snippet } from 'svelte';
type Item = { size: Size; label: string };
type Props = {
child: Snippet<[Item]>;
fullSize?: boolean;
};
const { child, fullSize }: Props = $props();
const sizes: Item[] = [
{ size: 'tiny', label: 'Tiny' },
{ size: 'small', label: 'Small' },
{ size: 'medium', label: 'Medium' },
{ size: 'large', label: 'Large' },
{ size: 'giant', label: 'Giant' },
];
const items: Item[] = $derived(fullSize ? [...sizes, { size: 'full', label: 'Full' } as unknown as Item] : sizes);
</script>
{#each items as item (item.size)}
{@render child(item)}
{/each}
@@ -0,0 +1,26 @@
<script lang="ts">
import type { TextColor } from '@immich/ui';
import type { Snippet } from 'svelte';
type Item = { color: TextColor; label: string };
type Props = {
child: Snippet<[Item]>;
};
const { child }: Props = $props();
const items: Item[] = [
{ color: 'primary', label: 'Primary' },
{ color: 'secondary', label: 'Secondary' },
{ color: 'muted', label: 'Muted' },
{ color: 'success', label: 'Success' },
{ color: 'info', label: 'Info' },
{ color: 'warning', label: 'Warning' },
{ color: 'danger', label: 'Danger' },
];
</script>
{#each items as item (item.color)}
{@render child(item)}
{/each}
@@ -0,0 +1,15 @@
<script lang="ts">
import { Alert } from '@immich/ui';
import { mdiLightbulbOnOutline } from '@mdi/js';
import type { Snippet } from 'svelte';
type Props = {
children: Snippet;
};
const { children }: Props = $props();
</script>
<Alert color="success" class="mt-4" title="Tip" icon={mdiLightbulbOnOutline}>
{@render children()}
</Alert>
@@ -0,0 +1,22 @@
<script lang="ts">
import type { Variants } from '@immich/ui';
import type { Snippet } from 'svelte';
type Item = { variant: Variants; label: string };
type Props = {
child: Snippet<[Item]>;
};
const { child }: Props = $props();
const items: Item[] = [
{ variant: 'filled', label: 'Filled' },
{ variant: 'outline', label: 'Outline' },
{ variant: 'ghost', label: 'Ghost' },
];
</script>
{#each items as item (item.variant)}
{@render child(item)}
{/each}
@@ -0,0 +1,44 @@
<script lang="ts">
import { type Color, type Size } from '@immich/ui';
import { tv } from 'tailwind-variants';
type Props = {
color?: Color;
width?: Size | 'full';
height?: Size | 'full';
};
const { width = 'medium', height = 'medium', color = 'primary' }: Props = $props();
const styles = tv({
base: '',
variants: {
color: {
primary: 'bg-primary text-light',
secondary: 'bg-dark text-light',
success: 'bg-success text-light',
danger: 'bg-danger text-light',
warning: 'bg-warning text-light',
info: 'bg-info text-light',
},
width: {
tiny: 'w-4',
small: 'w-8',
medium: 'w-16',
large: 'w-32',
giant: 'w-64',
full: 'w-full',
},
height: {
tiny: 'h-4',
small: 'h-8',
medium: 'h-16',
large: 'h-32',
giant: 'h-64',
full: 'h-full',
},
},
});
</script>
<div class={styles({ width, height, color })}></div>
@@ -0,0 +1,13 @@
<script lang="ts">
import type { Snippet } from 'svelte';
type Props = {
children: Snippet;
};
const { children }: Props = $props();
</script>
<div class="flex flex-wrap gap-4">
{@render children()}
</div>
@@ -0,0 +1,19 @@
<script lang="ts">
type Props = { count?: number };
const { count = 1 }: Props = $props();
const lorem =
'Lorem ipsum dolor sit amet consectetur adipisicing elit. A ipsam tenetur accusantium impedit beatae omnis necessitatibus. Voluptatum blanditiis libero impedit, harum eius inventore nihil, officia voluptate dolorum error consequatur animi.';
const make = (value: number) => {
let content = '';
for (let i = 0; i < value; i++) {
content += lorem;
}
return content;
};
const text = $derived(make(count));
</script>
{text}
@@ -0,0 +1,31 @@
<script lang="ts">
import PageDescription from '$lib/components/PageDescription.svelte';
import { siteMetadata } from '$lib/constants.js';
import { Container, Heading, SiteMetadata, type BreadcrumbItem, type ContainerSize } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
attributes: { title: string; description: string };
size?: ContainerSize;
breadcrumbs?: BreadcrumbItem[];
children?: Snippet;
};
const { attributes, size = 'medium', children }: Props = $props();
const { title, description } = $derived(attributes);
const page = $derived({ title, description });
</script>
<SiteMetadata site={siteMetadata} {page} />
<Container {size} center class="my-4 flex flex-col p-4 lg:my-8">
<section class="mb-8">
<Heading tag="h1" size="giant">{page.title}</Heading>
{#if page.description}
<PageDescription>{page.description}</PageDescription>
{/if}
</section>
{@render children?.()}
</Container>
@@ -0,0 +1,12 @@
<script lang="ts">
import { Text } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
children: Snippet;
};
const { children }: Props = $props();
</script>
<Text color="muted" size="large" class="mt-2" {children} />
+323
View File
@@ -0,0 +1,323 @@
import { goto } from '$app/navigation';
import { asComponentHref } from '$lib/utilities.js';
import {
asText,
defaultProvider,
linkCommands,
MenuItemType,
toastManager,
type ActionItem,
type ActionItemHandler,
type CarouselImageItem,
type IconLike,
type MenuItems,
type Theme,
} from '@immich/ui';
import {
mdiAccountCircle,
mdiAccountCircleOutline,
mdiAlert,
mdiAlertOutline,
mdiApplication,
mdiApplicationOutline,
mdiBullhornVariant,
mdiBullhornVariantOutline,
mdiButtonCursor,
mdiButtonPointer,
mdiCalendar,
mdiCard,
mdiCardOutline,
mdiCheckboxMarked,
mdiCheckboxMultipleMarked,
mdiCheckboxMultipleMarkedOutline,
mdiCheckboxOutline,
mdiClock,
mdiClockOutline,
mdiCloseCircle,
mdiCloseCircleOutline,
mdiCodeBlockBraces,
mdiCodeBraces,
mdiContentCopy,
mdiDotsCircle,
mdiDotsVertical,
mdiDownload,
mdiFormatHeaderPound,
mdiFormDropdown,
mdiFormTextarea,
mdiFormTextbox,
mdiFormTextboxPassword,
mdiGauge,
mdiHelpBox,
mdiHelpBoxOutline,
mdiHomeCircle,
mdiHomeCircleOutline,
mdiImage,
mdiImageOutline,
mdiInformationSlabCircle,
mdiInformationSlabCircleOutline,
mdiKeyboardVariant,
mdiLanguageMarkdownOutline,
mdiLink,
mdiListBox,
mdiListBoxOutline,
mdiLockSmart,
mdiMenu,
mdiMessageAlert,
mdiMessageAlertOutline,
mdiNumeric,
mdiPalette,
mdiPanVertical,
mdiPartyPopper,
mdiPencilOutline,
mdiProgressHelper,
mdiShareVariant,
mdiSlashForward,
mdiSquare,
mdiSquareOutline,
mdiTable,
mdiTag,
mdiTagOutline,
mdiThemeLightDark,
mdiToggleSwitch,
mdiToggleSwitchOutline,
mdiTrashCanOutline,
mdiVectorSquare,
mdiViewCarousel,
mdiViewCarouselOutline,
mdiViewSequential,
mdiViewSequentialOutline,
mdiWindowMaximize,
} from '@mdi/js';
import { siGithub } from 'simple-icons';
import type { Component } from 'svelte';
export enum DisplayOption {
Both = 'both',
Light = 'light',
Dark = 'dark',
}
export type ExampleItem = {
title: string;
code: string;
component: Component;
theme?: Theme;
};
export type ExampleCardProps = ExampleItem;
export const siteMetadata = {
title: 'Immich UI',
description: 'A Svelte component library for Immich',
};
export type ComponentItem = {
name: string;
title?: string;
href?: string;
icon: IconLike;
activeIcon?: string;
items?: ComponentItem[];
};
export type ComponentGroup = {
title: string;
components: ComponentItem[];
};
export const components: ComponentItem[] = [
{ name: 'AnnouncementBanner', icon: mdiBullhornVariantOutline, activeIcon: mdiBullhornVariant },
{ name: 'AppShell', icon: mdiApplicationOutline, activeIcon: mdiApplication },
{ name: 'Alert', icon: mdiAlertOutline, activeIcon: mdiAlert },
{ name: 'Avatar', icon: mdiAccountCircleOutline, activeIcon: mdiAccountCircle },
{ name: 'Badge', icon: mdiTagOutline, activeIcon: mdiTag },
{ name: 'Breadcrumbs', icon: mdiSlashForward },
{
name: 'Button',
title: 'Buttons',
icon: mdiButtonCursor,
items: [
{ name: 'ActionButton', icon: mdiButtonPointer },
{ name: 'CloseButton', icon: mdiCloseCircleOutline, activeIcon: mdiCloseCircle },
{ name: 'IconButton', icon: mdiHomeCircleOutline, activeIcon: mdiHomeCircle },
{ name: 'ListButton', icon: mdiButtonPointer },
],
},
{ name: 'Card', icon: mdiCardOutline, activeIcon: mdiCard },
{ name: 'Checkbox', icon: mdiCheckboxOutline, activeIcon: mdiCheckboxMarked },
{ name: 'Code', icon: mdiCodeBraces },
{ name: 'CodeBlock', icon: mdiCodeBlockBraces },
{ name: 'Colors', icon: mdiPalette },
{ name: 'CommandPalette', icon: mdiMenu, activeIcon: mdiMenu },
{ name: 'Container', icon: mdiSquareOutline, activeIcon: mdiSquare },
{ name: 'ContextMenu', icon: mdiDotsVertical },
{
name: 'ControlBar',
icon: mdiApplicationOutline,
activeIcon: mdiApplication,
items: [{ name: 'ActionBar', icon: mdiCardOutline, activeIcon: mdiCard }],
},
{ name: 'DatePicker', icon: mdiCalendar },
{ name: 'Field', icon: mdiListBoxOutline, activeIcon: mdiListBox },
{ name: 'FormatBytes', icon: mdiNumeric },
{ name: 'GithubLink', icon: siGithub },
{ name: 'Heading', icon: mdiFormTextbox },
{ name: 'HelperText', icon: mdiHelpBoxOutline, activeIcon: mdiHelpBox },
{ name: 'Icon', icon: mdiVectorSquare },
{ name: 'ImageCarousel', icon: mdiViewCarouselOutline, activeIcon: mdiViewCarousel },
{ name: 'Input', icon: mdiFormTextbox },
{ name: 'Kbd', icon: mdiKeyboardVariant },
{ name: 'Link', icon: mdiLink },
{ name: 'LoadingSpinner', icon: mdiDotsCircle },
{ name: 'Logo', icon: mdiImageOutline, activeIcon: mdiImage },
{ name: 'Markdown', icon: mdiLanguageMarkdownOutline, activeIcon: mdiLanguageMarkdownOutline },
{ name: 'Meter', icon: mdiGauge },
{
name: 'Modal',
title: 'Modals',
icon: mdiWindowMaximize,
items: [
{ name: 'BasicModal', icon: mdiWindowMaximize },
{ name: 'ConfirmModal', icon: mdiCheckboxOutline },
{ name: 'FormModal', icon: mdiWindowMaximize },
],
},
{ name: 'MultiSelect', icon: mdiCheckboxMultipleMarkedOutline, activeIcon: mdiCheckboxMultipleMarked },
{ name: 'Navbar', icon: mdiMenu },
{ name: 'NumberInput', icon: mdiNumeric },
{ name: 'PasswordInput', icon: mdiFormTextboxPassword },
{ name: 'PinInput', icon: mdiLockSmart },
{ name: 'ProgressBar', icon: mdiProgressHelper },
{ name: 'Select', icon: mdiFormDropdown },
{ name: 'SupporterBadge', icon: mdiPartyPopper },
{ name: 'Switch', icon: mdiToggleSwitchOutline, activeIcon: mdiToggleSwitch },
{ name: 'Scrollable', icon: mdiPanVertical },
{ name: 'Stack', icon: mdiViewSequentialOutline, activeIcon: mdiViewSequential },
{ name: 'Table', icon: mdiTable },
{ name: 'Text', icon: mdiFormatHeaderPound },
{ name: 'Textarea', icon: mdiFormTextarea },
{ name: 'ThemeSwitcher', icon: mdiThemeLightDark },
{ name: 'TimeInput', icon: mdiClockOutline, activeIcon: mdiClock },
{ name: 'Toast', icon: mdiMessageAlertOutline, activeIcon: mdiMessageAlert },
{ name: 'Tooltip', icon: mdiInformationSlabCircleOutline, activeIcon: mdiInformationSlabCircle },
];
const onAction: ActionItemHandler = (item) => {
toastManager.show({
title: `Clicked ${item.title}`,
color: item.color ?? 'primary',
icon: item.icon,
});
};
export const ExampleActions = {
Copy: { title: 'Copy', icon: mdiContentCopy, onAction },
Share: { title: 'Share', icon: mdiShareVariant, onAction },
Edit: { title: 'Edit album', icon: mdiPencilOutline, onAction },
Download: { title: 'Download', icon: mdiDownload, onAction },
Delete: { title: 'Delete', icon: mdiTrashCanOutline, color: 'danger', onAction },
} satisfies Record<string, ActionItem>;
export const exampleActions: ActionItem[] = [
ExampleActions.Share,
ExampleActions.Copy,
ExampleActions.Download,
ExampleActions.Edit,
ExampleActions.Delete,
];
export const exampleMenuItems: MenuItems = [
ExampleActions.Share,
ExampleActions.Copy,
ExampleActions.Edit,
ExampleActions.Download,
MenuItemType.Divider,
ExampleActions.Delete,
];
export const carouselImageItems: CarouselImageItem[] = [
{
title: '1 year ago',
href: '#',
src: 'https://picsum.photos/id/1011/800/600',
alt: 'Sample image',
},
{
title: '2 years ago',
href: '#',
src: 'https://picsum.photos/id/1012/800/600',
alt: 'Sample image',
},
{
title: '3 years ago',
href: '#',
src: 'https://picsum.photos/id/1013/800/600',
alt: 'Sample image',
},
{
title: '4 years ago',
href: '#',
src: 'https://picsum.photos/id/1015/800/600',
alt: 'Sample image',
},
{
title: '5 years ago',
href: '#',
src: 'https://picsum.photos/id/1016/800/600',
alt: 'Sample image',
},
{
title: '6 years ago',
href: '#',
src: 'https://picsum.photos/id/1018/800/600',
alt: 'Sample image',
},
{
title: '7 years ago',
href: '#',
src: 'https://picsum.photos/id/1020/800/600',
alt: 'Sample image',
},
{
title: '8 years ago',
href: '#',
src: 'https://picsum.photos/id/1024/800/600',
alt: 'Sample image',
},
];
const asCommand = (component: ComponentItem): ActionItem => {
const href = asComponentHref(component.name);
return {
icon: component.icon,
iconClass: '',
title: component.name,
description: `View the ${component.name} component`,
onAction: () => goto(href),
extraText: asText(href),
};
};
export const getDocsProviders = () => {
const commands: ActionItem[] = [];
for (const component of components) {
commands.push(asCommand(component), ...(component.items ?? []).map((item) => asCommand(item)));
}
return [
defaultProvider({
name: 'Pages',
types: ['page', 'pages'],
actions: linkCommands([
{
title: 'Getting started',
description: 'Learn how to get started using @immich/ui in your project',
href: '/getting-started',
},
{ title: 'Introduction', description: 'A Svelte component library for Immich', href: '/introduction' },
{ title: 'Components', description: 'List of components', href: '/components' },
]),
}),
defaultProvider({ name: 'Components', types: ['component', 'components'], actions: commands }),
];
};
+15
View File
@@ -0,0 +1,15 @@
import { twMerge } from 'tailwind-merge';
export const cleanClass = (...classNames: unknown[]) => {
return twMerge(
classNames
.filter((className) => {
if (!className || typeof className === 'boolean') {
return false;
}
return typeof className === 'string';
})
.join(' '),
);
};
+4
View File
@@ -0,0 +1,4 @@
export const asSlug = (value: string) =>
value.replaceAll(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, '');
export const asComponentHref = (componentName: string) => `/components/${asSlug(componentName)}`;
@@ -0,0 +1,110 @@
<script lang="ts">
import { beforeNavigate } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { components } from '$lib/constants.js';
import { asComponentHref } from '$lib/utilities.js';
import {
AppShell,
AppShellHeader,
AppShellSidebar,
CommandPaletteButton,
commandPaletteManager,
ControlBar,
ControlBarHeader,
ControlBarOverflow,
IconButton,
Logo,
NavbarGroup,
NavbarItem,
SiteFooter,
ThemeSwitcher,
toastManager,
type NavbarProps,
} from '@immich/ui';
import { mdiCodeBraces, mdiCompass, mdiCompassOutline, mdiMenu, mdiNote, mdiNoteOutline } from '@mdi/js';
import { MediaQuery } from 'svelte/reactivity';
let { children } = $props();
const sidebar = new MediaQuery(`min-width: 850px`);
let open = $derived(sidebar.current);
beforeNavigate(() => {
if (!sidebar.current) {
open = false;
}
});
toastManager.setOptions({ class: 'top-[58px]' });
commandPaletteManager.enable();
commandPaletteManager.setTranslations({
command_palette_prompt_default: 'Quickly find components, links, and commands',
});
</script>
<AppShell>
<AppShellHeader>
<ControlBar static variant="ghost">
<ControlBarHeader class="flex-row items-center">
<IconButton
shape="round"
color="secondary"
variant="ghost"
size="medium"
aria-label="Main menu"
icon={mdiMenu}
onclick={() => (open = !open)}
class="md:hidden"
/>
<a href={resolve('/')}>
<Logo variant="inline" />
</a>
</ControlBarHeader>
<ControlBarOverflow>
<CommandPaletteButton />
<ThemeSwitcher size="medium" />
</ControlBarOverflow>
</ControlBar>
</AppShellHeader>
<AppShellSidebar bind:open>
<div class="my-4 me-4 mb-24">
<NavbarItem
icon={{ icon: mdiNoteOutline, flopped: true, flipped: true }}
activeIcon={{ icon: mdiNote, flopped: true, flipped: true }}
href="/introduction"
title="Introduction"
/>
<NavbarItem icon={mdiCompassOutline} activeIcon={mdiCompass} href="/getting-started" title="Getting started" />
<NavbarItem icon={mdiCodeBraces} href="/components" title="Components" />
<NavbarGroup title="Components" />
{#each components as component (component.name)}
{@const href = asComponentHref(component.name)}
<NavbarItem
{href}
isActive={() => page.url.pathname === href}
title={component.title ?? component.name}
icon={component.icon}
activeIcon={component.activeIcon}
items={component.items?.map(
({ name, ...item }) =>
({
title: name,
href: asComponentHref(name),
...item,
}) as NavbarProps,
)}
/>
{/each}
</div>
</AppShellSidebar>
<section class="flex h-full flex-col">
<div class="grow">
{@render children?.()}
</div>
<SiteFooter />
</section>
</AppShell>
@@ -0,0 +1,14 @@
<script lang="ts">
import ComponentCard from '$lib/components/ComponentCard.svelte';
import Grid from '$lib/components/Grid.svelte';
import MarkdownPage from '$lib/components/MarkdownPage.svelte';
import { components } from '$lib/constants.js';
</script>
<MarkdownPage attributes={{ title: 'Components', description: 'A list of all the Immich UI components' }} size="large">
<Grid>
{#each components as component (component.name)}
<ComponentCard {component} />
{/each}
</Grid>
</MarkdownPage>
@@ -0,0 +1,16 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentLink from '$lib/components/ComponentLink.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import ComponentTipCard from '$lib/components/ComponentTipCard.svelte';
import { Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
</script>
<ComponentPage name="ActionBar" description="A control bar for actions">
<ComponentTipCard>
<Text>See <ComponentLink name="ControlBar" /> for more examples of control bars</Text>
</ComponentTipCard>
<ComponentExamples examples={[{ title: 'Basic', code: basicExample, component: BasicExample }]} />
</ComponentPage>
@@ -0,0 +1,24 @@
<script lang="ts">
import { ExampleActions, exampleActions } from '$lib/constants.js';
import { ActionBar, ControlBarHeader, ControlBarTitle, Stack } from '@immich/ui';
const onClose = () => {};
</script>
<Stack>
<ActionBar {onClose} actions={exampleActions}>
<ControlBarHeader>
<ControlBarTitle>2 Selected</ControlBarTitle>
</ControlBarHeader>
</ActionBar>
<ActionBar
{onClose}
actions={[ExampleActions.Share, ExampleActions.Copy, ExampleActions.Download]}
overflowActions={[ExampleActions.Edit, ExampleActions.Delete]}
>
<ControlBarHeader>
<ControlBarTitle>2 Selected</ControlBarTitle>
</ControlBarHeader>
</ActionBar>
</Stack>
@@ -0,0 +1,16 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentLink from '$lib/components/ComponentLink.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import ComponentTipCard from '$lib/components/ComponentTipCard.svelte';
import { Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
</script>
<ComponentPage name="ActionButton" description="A button to use with actions">
<ComponentTipCard>
<Text>Also see <ComponentLink name="ActionBar" /></Text>
</ComponentTipCard>
<ComponentExamples examples={[{ title: 'Basic', code: basicExample, component: BasicExample }]} />
</ComponentPage>
@@ -0,0 +1,22 @@
<script lang="ts">
import { ExampleActions } from '$lib/constants.js';
import { ActionButton, Heading } from '@immich/ui';
</script>
<Heading size="small" class="mb-2">Icon buttons</Heading>
<div class="flex gap-2">
<ActionButton action={ExampleActions.Copy} />
<ActionButton action={ExampleActions.Edit} />
<ActionButton action={ExampleActions.Share} />
<ActionButton action={ExampleActions.Delete} />
<ActionButton action={ExampleActions.Download} variant="filled" color="primary" />
</div>
<Heading size="small" class="mt-6 mb-2">Buttons</Heading>
<div class="flex gap-2">
<ActionButton type="button" action={ExampleActions.Copy} />
<ActionButton type="button" action={ExampleActions.Edit} />
<ActionButton type="button" action={ExampleActions.Share} />
<ActionButton type="button" action={ExampleActions.Delete} />
<ActionButton type="button" action={ExampleActions.Download} variant="filled" color="primary" />
</div>
@@ -0,0 +1,23 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
import ColorExample from './ColorExample.svelte';
import colorExample from './ColorExample.svelte?raw';
import CustomIconExample from './CustomIconExample.svelte';
import customIconExample from './CustomIconExample.svelte?raw';
import EventExample from './EventExample.svelte';
import eventExample from './EventExample.svelte?raw';
</script>
<ComponentPage name="Alert" description="A component for displaying important messages to the user">
<ComponentExamples
examples={[
{ title: 'Basic', code: basicExample, component: BasicExample },
{ title: 'Color', code: colorExample, component: ColorExample },
{ title: 'Custom Icon', code: customIconExample, component: CustomIconExample },
{ title: 'Event', code: eventExample, component: EventExample },
]}
/>
</ComponentPage>
@@ -0,0 +1,14 @@
<script lang="ts">
import Lorem from '$lib/components/Lorem.svelte';
import { Alert, Stack } from '@immich/ui';
</script>
<Stack>
<Alert title="This is an alert" />
<Alert>
<p>This is an alert</p>
</Alert>
<Alert color="primary" title="This is a primary alert">
<Lorem />
</Alert>
</Stack>
@@ -0,0 +1,21 @@
<script lang="ts">
import ComponentColors from '$lib/components/ComponentColors.svelte';
import Lorem from '$lib/components/Lorem.svelte';
import { Alert, Stack } from '@immich/ui';
</script>
<Stack gap={4}>
<Stack gap={2}>
<ComponentColors>
{#snippet child({ color })}
<Alert {color} title="This is an alert" />
<Alert {color}>
<p>This is an alert</p>
</Alert>
<Alert {color} title="This is a {color} alert">
<Lorem />
</Alert>
{/snippet}
</ComponentColors>
</Stack>
</Stack>
@@ -0,0 +1,19 @@
<script lang="ts">
import Lorem from '$lib/components/Lorem.svelte';
import { Alert, Stack } from '@immich/ui';
import { mdiHelpCircleOutline } from '@mdi/js';
</script>
<Stack gap={4}>
<Alert icon={mdiHelpCircleOutline} title="This is an alert">
<Lorem />
</Alert>
<Alert icon={false} title="No icon">
<Lorem />
</Alert>
<Alert icon={false}>
<p>No icon</p>
</Alert>
</Stack>
@@ -0,0 +1,27 @@
<script lang="ts">
import { Alert, Button, Stack } from '@immich/ui';
let count = $state(0);
let open = $state(true);
const handleClose = () => (open = false);
const handleReset = () => {
open = true;
count++;
};
</script>
<Stack>
{#key count}
<Alert title="This is a closable alert" closable />
<Alert title="This alert disappears automatically" duration={2000} />
<Alert title="This alert disappears automatically and is closable" duration={2000} closable />
{#if open}
<Alert title="This is a controlled alert" controlled onClose={handleClose} />
{/if}
{/key}
<div>
<Button onclick={handleReset}>Reset</Button>
</div>
</Stack>
@@ -0,0 +1,23 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import AdvanceExample from './AdvanceExample.svelte';
import advanceExample from './AdvanceExample.svelte?raw';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
import ColorExample from './ColorExample.svelte';
import colorExample from './ColorExample.svelte?raw';
import SizeExample from './SizeExample.svelte';
import sizeExample from './SizeExample.svelte?raw';
</script>
<ComponentPage name="Banner" description="A banner component to display important messages across the top of the page">
<ComponentExamples
examples={[
{ title: 'Basic', code: basicExample, component: BasicExample },
{ title: 'Color', code: colorExample, component: ColorExample },
{ title: 'Size', code: sizeExample, component: SizeExample },
{ title: 'Advance', code: advanceExample, component: AdvanceExample },
]}
/>
</ComponentPage>
@@ -0,0 +1,31 @@
<script lang="ts">
import { AnnouncementBanner, Link, Stack, Text } from '@immich/ui';
import { DateTime } from 'luxon';
const nowPlus = (seconds: number) => DateTime.now().plus({ seconds });
</script>
<Stack>
<AnnouncementBanner>
{#snippet content()}
<Stack>
<Text color="secondary">Super important announcement with custom content</Text>
<Text color="secondary">
Read more <Link href="#">here</Link>
</Text>
</Stack>
{/snippet}
</AnnouncementBanner>
<AnnouncementBanner color="success" until={nowPlus(5)}>
This announcement expires after a few seconds
</AnnouncementBanner>
<AnnouncementBanner color="primary" since={nowPlus(3)}>
This announcement appears after a certain date
</AnnouncementBanner>
<AnnouncementBanner color="danger" since={nowPlus(8)} until={nowPlus(12)}>
This announcement appears after a certain date and expires after a few seconds
</AnnouncementBanner>
</Stack>
@@ -0,0 +1,13 @@
<script lang="ts">
import { AnnouncementBanner, Link, Stack } from '@immich/ui';
import { mdiCheckAll } from '@mdi/js';
</script>
<Stack>
<AnnouncementBanner>
Super important announcement &mdash; Read more <Link href="#">here</Link>
</AnnouncementBanner>
<AnnouncementBanner icon={mdiCheckAll}>Super important announcement (custom icon)</AnnouncementBanner>
<AnnouncementBanner icon={false}>Super important announcement (no icon)</AnnouncementBanner>
<AnnouncementBanner center>Super important announcement (content centered)</AnnouncementBanner>
</Stack>
@@ -0,0 +1,14 @@
<script lang="ts">
import ComponentTextColors from '$lib/components/ComponentTextColors.svelte';
import { AnnouncementBanner, Link, Stack } from '@immich/ui';
</script>
<Stack>
<ComponentTextColors>
{#snippet child({ color })}
<AnnouncementBanner {color}>
Super important announcement &mdash; Read more <Link href="#">here</Link> ({color})
</AnnouncementBanner>
{/snippet}
</ComponentTextColors>
</Stack>
@@ -0,0 +1,21 @@
<script lang="ts">
import { AnnouncementBanner, Link, Stack } from '@immich/ui';
</script>
<Stack>
<AnnouncementBanner size="tiny">
Super important announcement &mdash; Read more <Link href="#">here</Link> (tiny)
</AnnouncementBanner>
<AnnouncementBanner size="small">
Super important announcement &mdash; Read more <Link href="#">here</Link> (small)
</AnnouncementBanner>
<AnnouncementBanner size="medium">
Super important announcement &mdash; Read more <Link href="#">here</Link> (medium)
</AnnouncementBanner>
<AnnouncementBanner size="large">
Super important announcement &mdash; Read more <Link href="#">here</Link> (large)
</AnnouncementBanner>
<AnnouncementBanner size="giant">
Super important announcement &mdash; Read more <Link href="#">here</Link> (giant)
</AnnouncementBanner>
</Stack>
@@ -0,0 +1,24 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
import ControlBarExample from './ControlBarExample.svelte';
import controlBarExample from './ControlBarExample.svelte?raw';
import SidebarExample from './SidebarExample.svelte';
import sidebarExample from './SidebarExample.svelte?raw';
</script>
<ComponentPage
name="AppShell"
description="The AppShell component is a layout component that provides the basic skeleton for an application, including a
header, sidebar and scrollable content view"
>
<ComponentExamples
examples={[
{ title: 'Basic', code: basicExample, component: BasicExample },
{ title: 'Sidebar', code: sidebarExample, component: SidebarExample },
{ title: 'ControlBar', code: controlBarExample, component: ControlBarExample },
]}
/>
</ComponentPage>
@@ -0,0 +1,39 @@
<script lang="ts">
import { AppShell, AppShellHeader, AppShellSidebar, Heading, Stack } from '@immich/ui';
</script>
<Stack>
<div class="border">
<AppShell class="h-[400px]">
<AppShellHeader>
<div class="flex h-full items-center p-4">
<Heading size="tiny">Header</Heading>
</div>
</AppShellHeader>
<div class="p-4">
<Heading size="tiny">Content</Heading>
</div>
</AppShell>
</div>
<div class="border">
<AppShell class="h-[400px]">
<AppShellHeader>
<div class="p-4">
<Heading size="tiny">Header</Heading>
</div>
</AppShellHeader>
<AppShellSidebar>
<div class="p-4">
<Heading size="tiny">Sidebar</Heading>
</div>
</AppShellSidebar>
<div class="p-4">
<Heading size="tiny">Content</Heading>
</div>
</AppShell>
</div>
</Stack>
@@ -0,0 +1,73 @@
<script lang="ts">
import { carouselImageItems, ExampleActions, exampleActions } from '$lib/constants.js';
import {
ActionBar,
AppShell,
AppShellBar,
AppShellHeader,
AppShellSidebar,
Button,
ControlBarHeader,
ControlBarTitle,
Heading,
ImageCarousel,
NavbarItem,
Stack,
} from '@immich/ui';
import { mdiArrowLeft, mdiHome } from '@mdi/js';
let selectionActive = $state(false);
</script>
<Stack>
<div class="border">
<AppShell class="h-[400px]">
{#if selectionActive}
<AppShellBar>
<ActionBar actions={exampleActions} onClose={() => (selectionActive = false)} variant="filled">
<ControlBarHeader>
<ControlBarTitle>2 Selected</ControlBarTitle>
</ControlBarHeader>
</ActionBar>
</AppShellBar>
{/if}
<AppShellHeader>
<div class="p-4">
<Heading size="tiny">Header</Heading>
</div>
</AppShellHeader>
<AppShellSidebar class="pt-2">
<Stack>
<NavbarItem icon={mdiHome} title="Home" href="/" active />
<NavbarItem icon={mdiHome} title="Item 1" href="#" />
<NavbarItem icon={mdiHome} title="Item 2" href="#" />
<NavbarItem icon={mdiHome} title="Item 3" href="#" />
</Stack>
</AppShellSidebar>
<div class="p-4">
{#if !selectionActive}
<Button onclick={() => (selectionActive = true)}>Open control bar</Button>
{/if}
</div>
</AppShell>
</div>
<div class="border">
<AppShell class="h-[400px]">
<AppShellBar>
<ActionBar static closeIcon={mdiArrowLeft} onClose={() => {}} overflowActions={[ExampleActions.Delete]}>
<ControlBarHeader>
<ControlBarTitle>Mich's photos</ControlBarTitle>
</ControlBarHeader>
</ActionBar>
</AppShellBar>
<div class="p-2">
<ImageCarousel items={carouselImageItems} />
</div>
</AppShell>
</div>
</Stack>
@@ -0,0 +1,29 @@
<script lang="ts">
import { AppShell, AppShellHeader, AppShellSidebar, Button, Heading, NavbarItem, Stack } from '@immich/ui';
import { mdiHome } from '@mdi/js';
let open = $state(true);
</script>
<div class="border">
<AppShell class="h-[400px]">
<AppShellHeader>
<div class="p-4">
<Heading size="tiny">Header</Heading>
</div>
</AppShellHeader>
<AppShellSidebar class="pt-2" bind:open>
<Stack class="pe-4">
<NavbarItem icon={mdiHome} title="Home" href="/" active />
<NavbarItem icon={mdiHome} title="Item 1" href="#" />
<NavbarItem icon={mdiHome} title="Item 2" href="#" />
<NavbarItem icon={mdiHome} title="Item 3" href="#" />
</Stack>
</AppShellSidebar>
<div class="p-4">
<Button onclick={() => (open = !open)}>Toggle Sidebar</Button>
</div>
</AppShell>
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
import ColorExample from './ColorExample.svelte';
import colorExample from './ColorExample.svelte?raw';
import SizeExample from './SizeExample.svelte';
import sizeExample from './SizeExample.svelte?raw';
</script>
<ComponentPage name="Avatar" description="A component for displaying user initials">
<ComponentExamples
examples={[
{ title: 'Basic', code: basicExample, component: BasicExample },
{ title: 'Color', code: colorExample, component: ColorExample },
{ title: 'Size', code: sizeExample, component: SizeExample },
]}
/>
</ComponentPage>
@@ -0,0 +1,9 @@
<script lang="ts">
import { Avatar, HStack } from '@immich/ui';
</script>
<HStack>
<Avatar name="Immich User" />
</HStack>
<Avatar name="Mich Tester" color="orange" class="mt-4" />
@@ -0,0 +1,16 @@
<script lang="ts">
import { Avatar, HStack } from '@immich/ui';
</script>
<HStack wrap>
<Avatar color="primary" name="Immich User" />
<Avatar color="gray" name="Immich User" />
<Avatar color="purple" name="Immich User" />
<Avatar color="blue" name="Immich User" />
<Avatar color="green" name="Immich User" />
<Avatar color="yellow" name="Immich User" />
<Avatar color="amber" name="Immich User" />
<Avatar color="orange" name="Immich User" />
<Avatar color="red" name="Immich User" />
<Avatar color="pink" name="Immich User" />
</HStack>
@@ -0,0 +1,11 @@
<script lang="ts">
import { Avatar, HStack } from '@immich/ui';
</script>
<HStack>
<Avatar size="tiny" name="Immich User" />
<Avatar size="small" name="Immich User" />
<Avatar size="medium" name="Immich User" />
<Avatar size="large" name="Immich User" />
<Avatar size="giant" name="Immich User" />
</HStack>
@@ -0,0 +1,23 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import CloseExample from './CloseExample.svelte';
import closeExample from './CloseExample.svelte?raw';
import ColorExample from './ColorExample.svelte';
import colorExample from './ColorExample.svelte?raw';
import ShapeExample from './ShapeExample.svelte';
import shapeExample from './ShapeExample.svelte?raw';
import SizeExample from './SizeExample.svelte';
import sizeExample from './SizeExample.svelte?raw';
</script>
<ComponentPage name="Badge" description="A small, visual indicator for display information such as a status">
<ComponentExamples
examples={[
{ title: 'Size', code: sizeExample, component: SizeExample },
{ title: 'Color', code: colorExample, component: ColorExample },
{ title: 'Shape', code: shapeExample, component: ShapeExample },
{ title: 'Close', code: closeExample, component: CloseExample },
]}
/>
</ComponentPage>
@@ -0,0 +1,39 @@
<script lang="ts">
import Closable from '$lib/components/Closable.svelte';
import { Badge, IconButton, Stack, Text } from '@immich/ui';
import { mdiCheckAll } from '@mdi/js';
</script>
<Stack gap={4}>
<div>
<Text fontWeight="medium" class="mb-2">Default</Text>
<Closable>
{#snippet child({ onClose })}
<Badge {onClose}>Closable</Badge>
{/snippet}
</Closable>
</div>
<div>
<Text fontWeight="medium" class="mb-2">Close text</Text>
<Closable>
{#snippet child({ onClose })}
<Badge {onClose} translations={{ close: 'Clear failures' }}>Failures</Badge>
{/snippet}
</Closable>
</div>
<div>
<Text fontWeight="medium" class="mb-2">Custom button</Text>
<Closable>
{#snippet child({ onClose })}
<Badge>
100 messages
{#snippet close()}
<IconButton aria-label="Mark as read" size="tiny" icon={mdiCheckAll} onclick={onClose} />
{/snippet}
</Badge>
{/snippet}
</Closable>
</div>
</Stack>
@@ -0,0 +1,15 @@
<script lang="ts">
import ComponentColors from '$lib/components/ComponentColors.svelte';
import { Badge, Stack, Text } from '@immich/ui';
</script>
<Stack>
<ComponentColors>
{#snippet child({ color, label })}
<div>
<Text class="mb-1" fontWeight="bold">{label}</Text>
<Badge shape="semi-round" {color}>#immich</Badge>
</div>
{/snippet}
</ComponentColors>
</Stack>
@@ -0,0 +1,18 @@
<script lang="ts">
import { Badge, Stack, Text } from '@immich/ui';
</script>
<Stack>
<div>
<Text class="mb-1" fontWeight="bold">Rectangle</Text>
<Badge shape="rectangle">#immich</Badge>
</div>
<div>
<Text class="mb-1" fontWeight="bold">Semi-round</Text>
<Badge shape="semi-round">#immich</Badge>
</div>
<div>
<Text class="mb-1" fontWeight="bold">Round</Text>
<Badge shape="round">#immich</Badge>
</div>
</Stack>
@@ -0,0 +1,15 @@
<script lang="ts">
import ComponentSizes from '$lib/components/ComponentSizes.svelte';
import { Badge, Stack, Text } from '@immich/ui';
</script>
<Stack>
<ComponentSizes>
{#snippet child({ size, label })}
<div>
<Text class="mb-1" fontWeight="bold">{label}</Text>
<Badge {size}>#immich</Badge>
</div>
{/snippet}
</ComponentSizes>
</Stack>
@@ -0,0 +1,16 @@
<script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentLink from '$lib/components/ComponentLink.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte';
import ComponentTipCard from '$lib/components/ComponentTipCard.svelte';
import { Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
</script>
<ComponentPage name="BasicModal" description="A simplified modal with a close button">
<ComponentTipCard>
<Text>See <ComponentLink name="Modal" /> for more complex modals</Text>
</ComponentTipCard>
<ComponentExamples examples={[{ title: 'Basic', code: basicExample, component: BasicExample }]} />
</ComponentPage>

Some files were not shown because too many files have changed in this diff Show More