mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
@@ -181,7 +181,7 @@
|
||||
{#if dragStartTarget}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="fixed inset-0 flex h-full w-full flex-col items-center justify-center bg-gray-100/90 text-immich-dark-gray dark:bg-immich-dark-bg/90 dark:text-immich-gray"
|
||||
class="fixed inset-0 flex size-full flex-col items-center justify-center bg-gray-100/90 text-immich-dark-gray dark:bg-immich-dark-bg/90 dark:text-immich-gray"
|
||||
transition:fade={{ duration: 250 }}
|
||||
ondragover={onDragOver}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
{/snippet}
|
||||
|
||||
<div class="xl:hidden">
|
||||
<div class="w-fit h-14 dark:text-immich-dark-fg py-2">
|
||||
<div class="h-14 w-fit py-2 dark:text-immich-dark-fg">
|
||||
<GroupTab
|
||||
label={$t('show_albums')}
|
||||
filters={Object.keys(AlbumFilter)}
|
||||
@@ -52,7 +52,7 @@
|
||||
bind:albumGroupIds={albumGroups}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_albums_message')} onClick={() => createAlbumAndRedirect()} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_albums_message')} onClick={() => createAlbumAndRedirect()} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Albums>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
</script>
|
||||
|
||||
<!-- Filter Albums by Sharing Status (All, Owned, Shared) -->
|
||||
<div class="hidden xl:block h-10">
|
||||
<div class="hidden h-10 xl:block">
|
||||
<GroupTab
|
||||
label={$t('show_albums')}
|
||||
filters={Object.values(albumFilterNames)}
|
||||
@@ -123,7 +123,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Search Albums -->
|
||||
<div class="hidden xl:block h-10 xl:w-60 2xl:w-80">
|
||||
<div class="hidden h-10 xl:block xl:w-60 2xl:w-80">
|
||||
<SearchBar placeholder={$t('search_albums')} bind:name={searchQuery} showLoadingSpinner={false} />
|
||||
</div>
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
{#if getSelectedAlbumGroupOption($albumViewSettings) !== AlbumGroupBy.None}
|
||||
<span in:fly={{ x: -50, duration: 250 }}>
|
||||
<!-- Expand Album Groups -->
|
||||
<div class="hidden xl:flex gap-0">
|
||||
<div class="hidden gap-0 xl:flex">
|
||||
<div class="block">
|
||||
<IconButton
|
||||
title={$t('expand_all')}
|
||||
|
||||
+6
-6
@@ -339,7 +339,7 @@
|
||||
|
||||
<div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: Route.albums() }}>
|
||||
<div class="relative w-full shrink">
|
||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||
<main class="relative h-dvh overflow-hidden px-2 pt-(--navbar-height) max-md:pt-(--navbar-height-md) md:px-6">
|
||||
<Timeline
|
||||
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
|
||||
{album}
|
||||
@@ -416,13 +416,13 @@
|
||||
{/if}
|
||||
|
||||
{#if album.assetCount === 0}
|
||||
<section id="empty-album" class=" mt-50 flex place-content-center place-items-center">
|
||||
<section id="empty-album" class="mt-50 flex place-content-center place-items-center">
|
||||
<div class="w-75">
|
||||
<p class="uppercase text-xs dark:text-immich-dark-fg">{$t('add_photos')}</p>
|
||||
<p class="text-xs uppercase dark:text-immich-dark-fg">{$t('add_photos')}</p>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (viewMode = AlbumPageViewMode.SELECT_ASSETS)}
|
||||
class="mt-5 bg-subtle flex w-full place-items-center gap-6 rounded-2xl border px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 dark:hover:bg-gray-500/20 hover:text-immich-primary dark:border-none dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||
class="mt-5 flex w-full place-items-center gap-6 rounded-2xl border bg-subtle p-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:text-immich-dark-fg dark:hover:bg-gray-500/20 dark:hover:text-immich-dark-primary"
|
||||
>
|
||||
<span class="text-primary">
|
||||
<Icon icon={mdiPlus} size="24" />
|
||||
@@ -436,7 +436,7 @@
|
||||
</Timeline>
|
||||
|
||||
{#if showActivityStatus}
|
||||
<div class="absolute z-2 bottom-0 end-0 mb-6 me-12">
|
||||
<div class="absolute inset-e-0 bottom-0 z-2 me-12 mb-6">
|
||||
<ActivityStatus
|
||||
disabled={!album.isActivityEnabled}
|
||||
isLiked={activityManager.isLiked}
|
||||
@@ -617,7 +617,7 @@
|
||||
<div
|
||||
transition:fly={{ duration: 150 }}
|
||||
id="activity-panel"
|
||||
class="z-2 w-90 md:w-115 overflow-y-auto transition-all dark:border-l dark:border-s-immich-dark-gray"
|
||||
class="z-2 w-90 overflow-y-auto transition-all md:w-115 dark:border-l dark:border-s-immich-dark-gray"
|
||||
translate="yes"
|
||||
>
|
||||
<ActivityViewer disabled={!album.isActivityEnabled} albumUsers={album.albumUsers} albumId={album.id} />
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
}))}
|
||||
/>
|
||||
{:else if description}
|
||||
<p class="wrap-break-words whitespace-pre-line w-full text-black dark:text-white text-base">
|
||||
<p class="wrap-break-words w-full text-base whitespace-pre-line text-black dark:text-white">
|
||||
{description}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_archived_assets_message')} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_archived_assets_message')} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex gap-1 mt-2 place-items-center dark:bg-immich-dark-primary/10 bg-gray-200/50 p-2 rounded-lg bg-clip-padding border border-transparent relative supporter-effect"
|
||||
class="supporter-effect relative mt-2 flex place-items-center gap-1 rounded-lg border border-transparent bg-gray-200/50 bg-clip-padding p-2 dark:bg-immich-dark-primary/10"
|
||||
class:place-content-center={centered}
|
||||
>
|
||||
<Logo variant="icon" size={logoSize === 'sm' ? 'tiny' : 'small'} />
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
{#if hasPeople}
|
||||
<div class="mb-6 mt-2">
|
||||
<div class="mt-2 mb-6">
|
||||
<div class="flex justify-between">
|
||||
<p class="mb-4 font-medium dark:text-immich-dark-fg">{$t('people')}</p>
|
||||
<a
|
||||
@@ -50,10 +50,10 @@
|
||||
draggable="false">{$t('view_all')}</a
|
||||
>
|
||||
</div>
|
||||
<SingleGridRow class="grid grid-flow-col md:grid-auto-fill-28 grid-auto-fill-20 gap-x-4">
|
||||
<SingleGridRow class="grid grid-flow-col grid-auto-fill-20 gap-x-4 md:grid-auto-fill-28">
|
||||
{#snippet children({ itemCount })}
|
||||
{#each people.slice(0, itemCount) as person (person.id)}
|
||||
<a href={Route.viewPerson(person)} class="text-center relative">
|
||||
<a href={Route.viewPerson(person)} class="relative text-center">
|
||||
<ImageThumbnail
|
||||
circle
|
||||
shadow
|
||||
@@ -62,11 +62,11 @@
|
||||
widthStyle="100%"
|
||||
/>
|
||||
{#if person.isFavorite}
|
||||
<div class="absolute top-2 start-2">
|
||||
<div class="absolute inset-s-2 top-2">
|
||||
<Icon icon={mdiHeart} size="24" class="text-white" />
|
||||
</div>
|
||||
{/if}
|
||||
<p class="mt-2 text-ellipsis text-sm font-medium dark:text-white">{person.name}</p>
|
||||
<p class="mt-2 text-sm font-medium text-ellipsis dark:text-white">{person.name}</p>
|
||||
</a>
|
||||
{/each}
|
||||
{/snippet}
|
||||
@@ -75,7 +75,7 @@
|
||||
{/if}
|
||||
|
||||
{#if places.length > 0}
|
||||
<div class="mb-6 mt-2">
|
||||
<div class="mt-2 mb-6">
|
||||
<div class="flex justify-between">
|
||||
<p class="mb-4 font-medium dark:text-immich-dark-fg">{$t('places')}</p>
|
||||
<a
|
||||
@@ -84,7 +84,7 @@
|
||||
draggable="false">{$t('view_all')}</a
|
||||
>
|
||||
</div>
|
||||
<SingleGridRow class="grid grid-flow-col md:grid-auto-fill-36 grid-auto-fill-28 gap-x-4">
|
||||
<SingleGridRow class="grid grid-flow-col grid-auto-fill-28 gap-x-4 md:grid-auto-fill-36">
|
||||
{#snippet children({ itemCount })}
|
||||
{#each places.slice(0, itemCount) as item (item.data.id)}
|
||||
<a class="relative" href={Route.search({ city: item.value })} draggable="false">
|
||||
@@ -92,11 +92,11 @@
|
||||
<img
|
||||
src={getAssetMediaUrl({ id: item.data.id, size: AssetMediaSize.Thumbnail })}
|
||||
alt={item.value}
|
||||
class="object-cover aspect-square w-full"
|
||||
class="aspect-square w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="absolute bottom-2 w-full text-ellipsis px-1 text-center text-sm font-medium capitalize text-white backdrop-blur-[1px] hover:cursor-pointer"
|
||||
class="absolute bottom-2 w-full px-1 text-center text-sm font-medium text-ellipsis text-white capitalize backdrop-blur-[1px] hover:cursor-pointer"
|
||||
>
|
||||
{item.value}
|
||||
</span>
|
||||
@@ -108,6 +108,6 @@
|
||||
{/if}
|
||||
|
||||
{#if !hasPeople && places.length === 0}
|
||||
<EmptyPlaceholder text={$t('no_explore_results_message')} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_explore_results_message')} class="mx-auto mt-10" />
|
||||
{/if}
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_favorites_message')} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_favorites_message')} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<Sidebar>
|
||||
<SkipLink target={`#${headerId}`} text={$t('skip_to_folders')} breakpoint="md" />
|
||||
<section>
|
||||
<Text class="ps-4 mb-4" size="small">{$t('explorer')}</Text>
|
||||
<Text class="mb-4 ps-4" size="small">{$t('explorer')}</Text>
|
||||
<div class="h-full">
|
||||
<TreeItems
|
||||
icons={{ default: mdiFolderOutline, active: mdiFolder }}
|
||||
@@ -114,7 +114,7 @@
|
||||
</UserPageLayout>
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full">
|
||||
<div class="fixed inset-s-0 top-0 w-full">
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
removeAction={AssetAction.SET_VISIBILITY_TIMELINE}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_locked_photos_message')} title={$t('nothing_here_yet')} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_locked_photos_message')} title={$t('nothing_here_yet')} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -54,17 +54,17 @@
|
||||
|
||||
{#if featureFlagsManager.value.map}
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
<div class="isolate flex h-full w-full flex-col sm:flex-row">
|
||||
<div class="isolate flex size-full flex-col sm:flex-row">
|
||||
<div
|
||||
class={[
|
||||
'min-h-0',
|
||||
isTimelinePanelVisible ? 'h-1/2 w-full pb-2 sm:h-full sm:w-2/3 sm:pe-2 sm:pb-0' : 'h-full w-full',
|
||||
isTimelinePanelVisible ? 'h-1/2 w-full pb-2 sm:h-full sm:w-2/3 sm:pe-2 sm:pb-0' : 'size-full',
|
||||
]}
|
||||
>
|
||||
{#await import('$lib/components/shared-components/map/Map.svelte')}
|
||||
{#await delay(timeToLoadTheMap) then}
|
||||
<!-- show the loading spinner only if loading the map takes too much time -->
|
||||
<div class="flex items-center justify-center h-full w-full">
|
||||
<div class="flex size-full items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
@@ -94,8 +94,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<aside class="h-full w-full overflow-hidden bg-immich-bg dark:bg-immich-dark-bg flex flex-col contain-content">
|
||||
<div class="flex items-center justify-between border-b border-gray-200 dark:border-immich-dark-gray pb-1 pe-1">
|
||||
<aside class="flex size-full flex-col overflow-hidden bg-immich-bg contain-content dark:bg-immich-dark-bg">
|
||||
<div class="flex items-center justify-between border-b border-gray-200 pe-1 pb-1 dark:border-immich-dark-gray">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon icon={mdiImageMultiple} size="20" />
|
||||
<p class="text-sm font-medium text-immich-fg dark:text-immich-dark-fg">
|
||||
|
||||
+2
-2
@@ -46,9 +46,9 @@
|
||||
{#if !imageLoaded}
|
||||
<DelayedLoadingSpinner />
|
||||
{:else if imageLoaded}
|
||||
<div transition:fade={{ duration: assetViewerFadeDuration }} class="h-full w-full">
|
||||
<div transition:fade={{ duration: assetViewerFadeDuration }} class="size-full">
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-contain transition-all"
|
||||
class="size-full rounded-2xl object-contain transition-all"
|
||||
src={assetFileUrl}
|
||||
alt={$getAltText(asset)}
|
||||
draggable="false"
|
||||
|
||||
+2
-2
@@ -25,12 +25,12 @@
|
||||
</script>
|
||||
|
||||
{#if showVideo}
|
||||
<div class="h-full w-full bg-pink-9000" transition:fade={{ duration: assetViewerFadeDuration }}>
|
||||
<div class="bg-pink-9000 size-full" transition:fade={{ duration: assetViewerFadeDuration }}>
|
||||
<video
|
||||
bind:this={videoPlayer}
|
||||
autoplay={$autoPlayVideo}
|
||||
playsinline
|
||||
class="h-full w-full rounded-2xl object-contain transition-all"
|
||||
class="size-full rounded-2xl object-contain transition-all"
|
||||
src={getAssetPlaybackUrl({ id: asset.id })}
|
||||
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview })}
|
||||
draggable="false"
|
||||
|
||||
@@ -327,7 +327,7 @@
|
||||
/>
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="sticky top-0 z-1 dark">
|
||||
<div class="dark sticky top-0 z-1">
|
||||
<AssetSelectControlBar forceDark>
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CreateSharedLink />
|
||||
@@ -381,7 +381,7 @@
|
||||
{/snippet}
|
||||
|
||||
<div class="flex place-content-center place-items-center gap-2 overflow-hidden">
|
||||
<div class="w-12.5 dark">
|
||||
<div class="dark w-12.5">
|
||||
<IconButton
|
||||
shape="round"
|
||||
variant="ghost"
|
||||
@@ -394,8 +394,8 @@
|
||||
|
||||
{#each current.memory.assets as asset, index (asset.id)}
|
||||
<a class="relative w-full py-2" href={asHref(asset)} aria-label={$t('view')}>
|
||||
<span class="absolute start-0 h-0.5 w-full bg-gray-500"></span>
|
||||
<span class="absolute start-0 h-0.5 bg-white" style:width={`${toProgressPercentage(index)}%`}></span>
|
||||
<span class="absolute inset-s-0 h-0.5 w-full bg-gray-500"></span>
|
||||
<span class="absolute inset-s-0 h-0.5 bg-white" style:width={`${toProgressPercentage(index)}%`}></span>
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
@@ -406,7 +406,7 @@
|
||||
</div>
|
||||
|
||||
{#if currentTimelineAssets.some((asset) => asset.type === AssetTypeEnum.Video)}
|
||||
<div class="w-12.5 dark">
|
||||
<div class="dark w-12.5">
|
||||
<IconButton
|
||||
shape="round"
|
||||
variant="ghost"
|
||||
@@ -422,7 +422,7 @@
|
||||
|
||||
{#if galleryInView}
|
||||
<div
|
||||
class="fixed top-10 start-1/2 -translate-x-1/2 transition-opacity dark z-1"
|
||||
class="dark fixed inset-s-1/2 top-10 z-1 -translate-x-1/2 transition-opacity"
|
||||
class:opacity-0={!galleryInView}
|
||||
class:opacity-100={galleryInView}
|
||||
>
|
||||
@@ -444,26 +444,26 @@
|
||||
<!-- Viewer -->
|
||||
<section class="overflow-hidden pt-32 md:pt-20" bind:clientHeight={viewerHeight}>
|
||||
<div
|
||||
class="ms-[-100%] box-border flex h-[calc(100vh-224px)] md:h-[calc(100vh-180px)] w-[300%] items-center justify-center gap-10 overflow-hidden"
|
||||
class="ms-[-100%] box-border flex h-[calc(100vh-224px)] w-[300%] items-center justify-center gap-10 overflow-hidden md:h-[calc(100vh-180px)]"
|
||||
>
|
||||
<!-- PREVIOUS MEMORY -->
|
||||
<div class="h-1/2 w-[20vw] rounded-2xl {current.previousMemory ? 'opacity-25 hover:opacity-70' : 'opacity-0'}">
|
||||
<button
|
||||
type="button"
|
||||
class="relative h-full w-full rounded-2xl"
|
||||
class="relative size-full rounded-2xl"
|
||||
disabled={!current.previousMemory}
|
||||
onclick={handlePreviousMemory}
|
||||
>
|
||||
{#if current.previousMemory && current.previousMemory.assets.length > 0}
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
class="size-full rounded-2xl object-cover"
|
||||
src={getAssetMediaUrl({ id: current.previousMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
||||
alt={$t('previous_memory')}
|
||||
draggable="false"
|
||||
/>
|
||||
{:else}
|
||||
<enhanced:img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
class="size-full rounded-2xl object-cover"
|
||||
src="$lib/assets/no-thumbnail.png"
|
||||
sizes="min(271px,186px)"
|
||||
alt={$t('previous_memory')}
|
||||
@@ -472,8 +472,8 @@
|
||||
{/if}
|
||||
|
||||
{#if current.previousMemory}
|
||||
<div class="absolute bottom-4 end-4 text-start text-white">
|
||||
<p class="uppercase text-xs font-semibold text-gray-200">{$t('previous')}</p>
|
||||
<div class="absolute inset-e-4 bottom-4 text-start text-white">
|
||||
<p class="text-xs font-semibold text-gray-200 uppercase">{$t('previous')}</p>
|
||||
<p class="text-xl">{$memoryLaneTitle(current.previousMemory)}</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -484,7 +484,7 @@
|
||||
<div
|
||||
class="main-view relative flex h-full w-[70vw] place-content-center place-items-center rounded-2xl bg-black"
|
||||
>
|
||||
<div class="relative h-full w-full rounded-2xl bg-black">
|
||||
<div class="relative size-full rounded-2xl bg-black">
|
||||
{#key current.asset.id}
|
||||
{#if current.asset.isVideo}
|
||||
<MemoryVideoViewer
|
||||
@@ -499,7 +499,7 @@
|
||||
{/key}
|
||||
|
||||
<div
|
||||
class="absolute bottom-0 end-0 p-2 transition-all flex h-full justify-between flex-col items-end gap-2 dark"
|
||||
class="dark absolute inset-e-0 bottom-0 flex h-full flex-col items-end justify-between gap-2 p-2 transition-all"
|
||||
class:opacity-0={galleryInView}
|
||||
class:opacity-100={!galleryInView}
|
||||
>
|
||||
@@ -511,7 +511,7 @@
|
||||
color="secondary"
|
||||
aria-label={isSaved ? $t('unfavorite') : $t('favorite')}
|
||||
onclick={() => handleSaveMemory()}
|
||||
class="w-12 h-12"
|
||||
class="size-12"
|
||||
/>
|
||||
<!-- <IconButton
|
||||
icon={mdiShareVariantOutline}
|
||||
@@ -556,7 +556,7 @@
|
||||
</div>
|
||||
<!-- CONTROL BUTTONS -->
|
||||
{#if current.previous}
|
||||
<div class="absolute top-1/2 start-0 ms-4 dark">
|
||||
<div class="dark absolute inset-s-0 top-1/2 ms-4">
|
||||
<IconButton
|
||||
shape="round"
|
||||
aria-label={$t('previous_memory')}
|
||||
@@ -570,7 +570,7 @@
|
||||
{/if}
|
||||
|
||||
{#if current.next}
|
||||
<div class="absolute top-1/2 end-0 me-4 dark">
|
||||
<div class="dark absolute inset-e-0 top-1/2 me-4">
|
||||
<IconButton
|
||||
shape="round"
|
||||
aria-label={$t('next_memory')}
|
||||
@@ -583,7 +583,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="absolute start-8 top-4 text-sm font-medium text-white">
|
||||
<div class="absolute inset-s-8 top-4 text-sm font-medium text-white">
|
||||
<p>
|
||||
{fromISODateTimeUTC(current.memory.assets[0].localDateTime).toLocaleString(DateTime.DATE_FULL, {
|
||||
locale: $locale,
|
||||
@@ -603,20 +603,20 @@
|
||||
<div class="h-1/2 w-[20vw] rounded-2xl {current.nextMemory ? 'opacity-25 hover:opacity-70' : 'opacity-0'}">
|
||||
<button
|
||||
type="button"
|
||||
class="relative h-full w-full rounded-2xl"
|
||||
class="relative size-full rounded-2xl"
|
||||
onclick={handleNextMemory}
|
||||
disabled={!current.nextMemory}
|
||||
>
|
||||
{#if current.nextMemory && current.nextMemory.assets.length > 0}
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
class="size-full rounded-2xl object-cover"
|
||||
src={getAssetMediaUrl({ id: current.nextMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
||||
alt={$t('next_memory')}
|
||||
draggable="false"
|
||||
/>
|
||||
{:else}
|
||||
<enhanced:img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
class="size-full rounded-2xl object-cover"
|
||||
src="$lib/assets/no-thumbnail.png"
|
||||
sizes="min(271px,186px)"
|
||||
alt={$t('next_memory')}
|
||||
@@ -625,8 +625,8 @@
|
||||
{/if}
|
||||
|
||||
{#if current.nextMemory}
|
||||
<div class="absolute bottom-4 start-4 text-start text-white">
|
||||
<p class="uppercase text-xs font-semibold text-gray-200">{$t('up_next')}</p>
|
||||
<div class="absolute inset-s-4 bottom-4 text-start text-white">
|
||||
<p class="text-xs font-semibold text-gray-200 uppercase">{$t('up_next')}</p>
|
||||
<p class="text-xl">{$memoryLaneTitle(current.nextMemory)}</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -641,7 +641,7 @@
|
||||
<!-- GALLERY VIEWER -->
|
||||
<section class="bg-immich-dark-gray p-4">
|
||||
<div
|
||||
class="sticky mb-10 flex place-content-center place-items-center transition-all dark"
|
||||
class="dark sticky mb-10 flex place-content-center place-items-center transition-all"
|
||||
class:opacity-0={galleryInView}
|
||||
class:opacity-100={!galleryInView}
|
||||
>
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||
<main class="relative h-dvh overflow-hidden px-2 pt-(--navbar-height) max-md:pt-(--navbar-height-md) md:px-6">
|
||||
<Timeline enableRouting={true} {options} assetInteraction={assetMultiSelectManager} onEscape={handleEscape} />
|
||||
</main>
|
||||
|
||||
|
||||
@@ -315,9 +315,9 @@
|
||||
>
|
||||
{#snippet buttons()}
|
||||
{#if people.length > 0}
|
||||
<div class="flex gap-2 items-center justify-center">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<div class="hidden sm:block">
|
||||
<div class="w-40 lg:w-80 h-10">
|
||||
<div class="h-10 w-40 lg:w-80">
|
||||
<SearchPeople
|
||||
bind:this={searchPeopleElement}
|
||||
type="searchBar"
|
||||
@@ -344,7 +344,7 @@
|
||||
<PeopleInfiniteScroll people={showPeople} hasNextPage={!!nextPage && !searchName} {loadNextPage}>
|
||||
{#snippet children({ person })}
|
||||
<div
|
||||
class="p-2 rounded-xl hover:bg-gray-200 border-2 hover:border-immich-primary/50 hover:shadow-sm dark:hover:bg-immich-dark-primary/20 hover:dark:border-immich-dark-primary/25 border-transparent transition-all"
|
||||
class="rounded-xl border-2 border-transparent p-2 transition-all hover:border-immich-primary/50 hover:bg-gray-200 hover:shadow-sm hover:dark:border-immich-dark-primary/25 dark:hover:bg-immich-dark-primary/20"
|
||||
>
|
||||
<PeopleCard
|
||||
{person}
|
||||
@@ -355,7 +355,7 @@
|
||||
|
||||
<input
|
||||
type="text"
|
||||
class=" bg-white dark:bg-immich-dark-gray border-gray-100 placeholder-gray-400 text-center dark:border-gray-900 w-full rounded-2xl mt-2 py-2 text-sm text-primary"
|
||||
class="mt-2 w-full rounded-2xl border-gray-100 bg-white py-2 text-center text-sm text-primary placeholder-gray-400 dark:border-gray-900 dark:bg-immich-dark-gray"
|
||||
value={person.name}
|
||||
placeholder={$t('add_a_name')}
|
||||
use:shortcut={{ shortcut: { key: 'Enter' }, onShortcut: (e) => e.currentTarget.blur() }}
|
||||
@@ -370,7 +370,7 @@
|
||||
<div class="flex min-h-[calc(66vh-11rem)] w-full place-content-center items-center dark:text-white">
|
||||
<div class="flex flex-col content-center items-center text-center">
|
||||
<Icon icon={mdiAccountOff} size="3.5em" />
|
||||
<p class="mt-5 text-3xl font-medium max-w-lg line-clamp-2 overflow-hidden">
|
||||
<p class="mt-5 line-clamp-2 max-w-lg overflow-hidden text-3xl font-medium">
|
||||
{$t(searchName ? 'search_no_people_named' : 'search_no_people', { values: { name: searchName } })}
|
||||
</p>
|
||||
</div>
|
||||
@@ -381,7 +381,7 @@
|
||||
{#if selectHidden}
|
||||
<dialog
|
||||
transition:fly={{ y: innerHeight, duration: 150, easing: quintOut, opacity: 0 }}
|
||||
class="fixed inset-0 h-full w-full max-w-none max-h-none bg-light"
|
||||
class="fixed inset-0 size-full max-h-none max-w-none bg-light"
|
||||
aria-labelledby="manage-visibility-title"
|
||||
{@attach (dialog) => dialog.showModal()}
|
||||
>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
<div class="h-full overflow-y-auto">
|
||||
<div
|
||||
class="sticky top-0 z-1 flex h-16 w-full items-center justify-between border-b bg-white p-1 dark:border-immich-dark-gray dark:bg-black dark:text-immich-dark-fg md:p-8"
|
||||
class="sticky top-0 z-1 flex h-16 w-full items-center justify-between border-b bg-white p-1 md:p-8 dark:border-immich-dark-gray dark:bg-black dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<IconButton
|
||||
@@ -118,7 +118,7 @@
|
||||
icon={mdiClose}
|
||||
onclick={onClose}
|
||||
/>
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<p id={titleId} class="ms-2">{$t('show_and_hide_people')}</p>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-600">({totalPeopleCount.toLocaleString($locale)})</p>
|
||||
</div>
|
||||
@@ -152,7 +152,7 @@
|
||||
{@const hidden = overrides.get(person.id) ?? person.isHidden}
|
||||
<button
|
||||
type="button"
|
||||
class="group relative w-full h-full"
|
||||
class="group relative size-full"
|
||||
onclick={() => setHiddenOverride(person, !hidden)}
|
||||
aria-pressed={hidden}
|
||||
aria-label={person.name ? $t('hide_named_person', { values: { name: person.name } }) : $t('hide_person')}
|
||||
@@ -167,7 +167,7 @@
|
||||
preload={false}
|
||||
/>
|
||||
{#if person.name}
|
||||
<span class="absolute bottom-2 start-0 w-full select-text px-1 text-center font-medium text-white">
|
||||
<span class="absolute inset-s-0 bottom-2 w-full px-1 text-center font-medium text-white select-text">
|
||||
{person.name}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
draggable="false"
|
||||
onfocus={() => (showVerticalDots = true)}
|
||||
>
|
||||
<div class="w-full h-full rounded-xl brightness-95 filter">
|
||||
<div class="size-full rounded-xl brightness-95 filter">
|
||||
<ImageThumbnail
|
||||
shadow
|
||||
url={getPeopleThumbnailUrl(person)}
|
||||
@@ -57,7 +57,7 @@
|
||||
preload={false}
|
||||
/>
|
||||
{#if person.isFavorite}
|
||||
<div class="absolute top-4 start-4">
|
||||
<div class="absolute inset-s-4 top-4">
|
||||
<Icon icon={mdiHeart} size="24" class="text-white" />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -65,7 +65,7 @@
|
||||
</a>
|
||||
|
||||
{#if showVerticalDots}
|
||||
<div class="absolute top-2 end-2 z-1">
|
||||
<div class="absolute inset-e-2 top-2 z-1">
|
||||
<ButtonContextMenu
|
||||
buttonClass="icon-white-drop-shadow"
|
||||
color="secondary"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-10 gap-1">
|
||||
<div class="grid w-full grid-cols-2 gap-1 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-10">
|
||||
{#each people as person, index (person.id)}
|
||||
{#if hasNextPage && index === people.length - 1}
|
||||
<div bind:this={lastPersonContainer}>
|
||||
|
||||
+8
-8
@@ -332,7 +332,7 @@
|
||||
/>
|
||||
|
||||
<main
|
||||
class="relative z-0 h-dvh overflow-hidden px-2 md:px-6 md:pt-(--navbar-height-md) pt-(--navbar-height)"
|
||||
class="relative z-0 h-dvh overflow-hidden px-2 pt-(--navbar-height) md:px-6 md:pt-(--navbar-height-md)"
|
||||
use:scrollMemoryClearer={{
|
||||
routeStartsWith: Route.people(),
|
||||
beforeClear: () => {
|
||||
@@ -355,14 +355,14 @@
|
||||
{#if viewMode === PersonPageViewMode.VIEW_ASSETS}
|
||||
<!-- Person information block -->
|
||||
<div
|
||||
class="relative w-fit p-4 sm:px-6 pt-12"
|
||||
class="relative w-fit p-4 pt-12 sm:px-6"
|
||||
use:clickOutside={{
|
||||
onOutclick: handleCancelEditName,
|
||||
onEscape: handleCancelEditName,
|
||||
}}
|
||||
use:listNavigation={suggestionContainer}
|
||||
>
|
||||
<section class="flex w-64 sm:w-96 place-items-center border-black">
|
||||
<section class="flex w-64 place-items-center border-black sm:w-96">
|
||||
{#if isEditingName}
|
||||
<EditNameInput
|
||||
{person}
|
||||
@@ -388,8 +388,8 @@
|
||||
widthStyle="3.375rem"
|
||||
heightStyle="3.375rem"
|
||||
/>
|
||||
<div class="flex flex-col justify-center text-start px-4 text-primary">
|
||||
<p class="w-40 sm:w-72 font-medium truncate">{person.name || $t('add_a_name')}</p>
|
||||
<div class="flex flex-col justify-center px-4 text-start text-primary">
|
||||
<p class="w-40 truncate font-medium sm:w-72">{person.name || $t('add_a_name')}</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{$t('assets_count', { values: { count: numberOfAssets } })}
|
||||
</p>
|
||||
@@ -415,10 +415,10 @@
|
||||
{/if}
|
||||
</section>
|
||||
{#if isEditingName}
|
||||
<div class="absolute w-64 sm:w-96 z-1">
|
||||
<div class="absolute z-1 w-64 sm:w-96">
|
||||
{#if isSearchingPeople}
|
||||
<div
|
||||
class="flex border h-14 rounded-b-lg border-gray-400 dark:border-immich-dark-gray place-items-center bg-gray-200 p-2 dark:bg-gray-700"
|
||||
class="flex h-14 place-items-center rounded-b-lg border border-gray-400 bg-gray-200 p-2 dark:border-immich-dark-gray dark:bg-gray-700"
|
||||
>
|
||||
<div class="flex w-full place-items-center">
|
||||
<LoadingSpinner />
|
||||
@@ -429,7 +429,7 @@
|
||||
{#each suggestedPeople as person, index (person.id)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full border border-gray-200 dark:border-immich-dark-gray h-14 place-items-center bg-gray-100 p-2 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-[#232932] focus:bg-gray-300 focus:dark:bg-[#232932] {index ===
|
||||
class="flex h-14 w-full place-items-center border border-gray-200 bg-gray-100 p-2 hover:bg-gray-300 focus:bg-gray-300 dark:border-immich-dark-gray dark:bg-gray-700 hover:dark:bg-[#232932] focus:dark:bg-[#232932] {index ===
|
||||
suggestedPeople.length - 1
|
||||
? 'rounded-b-lg border-b'
|
||||
: ''}"
|
||||
|
||||
+2
-2
@@ -30,9 +30,9 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex w-full h-14 place-items-center {suggestedPeople.length > 0
|
||||
class="flex h-14 w-full place-items-center {suggestedPeople.length > 0
|
||||
? 'rounded-t-lg dark:border-immich-dark-gray'
|
||||
: 'rounded-lg'} bg-gray-100 p-2 dark:bg-gray-700 border border-gray-200 dark:border-immich-dark-gray"
|
||||
: 'rounded-lg'} border border-gray-200 bg-gray-100 p-2 dark:border-immich-dark-gray dark:bg-gray-700"
|
||||
>
|
||||
<ImageThumbnail circle shadow url={thumbnailData} altText={person.name} widthStyle="2rem" heightStyle="2rem" />
|
||||
<form class="ms-4 flex w-full justify-between gap-16" autocomplete="off" {onsubmit}>
|
||||
|
||||
+4
-4
@@ -33,7 +33,7 @@
|
||||
style:height={thumbnailSize ? thumbnailSize + 'px' : '100%'}
|
||||
>
|
||||
<div
|
||||
class="h-full w-full border-2 brightness-90 filter"
|
||||
class="size-full border-2 brightness-90 filter"
|
||||
class:rounded-full={circle}
|
||||
class:rounded-lg={!circle}
|
||||
class:border-transparent={!border}
|
||||
@@ -44,7 +44,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute start-0 top-0 h-full w-full bg-immich-primary/30 opacity-0"
|
||||
class="absolute inset-s-0 top-0 size-full bg-immich-primary/30 opacity-0"
|
||||
class:hover:opacity-100={selectable}
|
||||
class:rounded-full={circle}
|
||||
class:rounded-lg={!circle}
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
{#if selected}
|
||||
<div
|
||||
class="absolute start-0 top-0 h-full w-full bg-blue-500/80"
|
||||
class="absolute inset-s-0 top-0 size-full bg-blue-500/80"
|
||||
class:rounded-full={circle}
|
||||
class:rounded-lg={!circle}
|
||||
></div>
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
{#if person.name}
|
||||
<span
|
||||
class="w-100 text-white-shadow absolute bottom-2 start-0 w-full text-ellipsis px-1 text-center font-medium text-white hover:cursor-pointer"
|
||||
class="text-white-shadow absolute inset-s-0 bottom-2 w-full px-1 text-center font-medium text-ellipsis text-white hover:cursor-pointer"
|
||||
>
|
||||
{person.name}
|
||||
</span>
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@
|
||||
|
||||
<section
|
||||
transition:fly={{ y: 500, duration: 100, easing: quintOut }}
|
||||
class="absolute start-0 top-0 h-full w-full bg-light"
|
||||
class="absolute inset-s-0 top-0 size-full bg-light"
|
||||
>
|
||||
<ControlAppBar onClose={onBack}>
|
||||
{#snippet leading()}
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
{#if hasSelection}
|
||||
<div class="relative h-full">
|
||||
<div class="flex flex-col h-full justify-between">
|
||||
<div class="flex h-full flex-col justify-between">
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<Icon icon={mdiCallMerge} size="48" class="rotate-90 dark:text-white" />
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="w-40 sm:w-48 md:w-full h-14 flex gap-4 place-items-center">
|
||||
<div class="flex h-14 w-40 place-items-center gap-4 sm:w-48 md:w-full">
|
||||
<div class="md:w-96">
|
||||
<SearchPeople type="searchBar" placeholder={$t('search_people')} bind:searchName={name} bind:searchedPeopleLocal />
|
||||
</div>
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="immich-scrollbar overflow-y-auto rounded-3xl bg-gray-200 p-10 dark:bg-immich-dark-gray mt-6"
|
||||
class="mt-6 overflow-y-auto rounded-3xl bg-gray-200 p-10 immich-scrollbar dark:bg-immich-dark-gray"
|
||||
style:max-height={screenHeight - 400 + 'px'}
|
||||
>
|
||||
<div class="grid-col-2 grid gap-8 md:grid-cols-3 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10">
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@
|
||||
|
||||
<section
|
||||
transition:fly={{ y: 500, duration: 100, easing: quintOut }}
|
||||
class="absolute start-0 top-0 h-full w-full bg-light"
|
||||
class="absolute inset-s-0 top-0 size-full bg-light"
|
||||
>
|
||||
<ControlAppBar {onClose}>
|
||||
{#snippet leading()}
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<ImageCarousel {items} />
|
||||
{/if}
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => openFileUploadDialog()} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => openFileUploadDialog()} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => togglePlacesGroupCollapsing(group.id)}
|
||||
class="w-fit mt-2 pt-2 pe-2 mb-2 dark:text-immich-dark-fg"
|
||||
class="my-2 w-fit pe-2 pt-2 dark:text-immich-dark-fg"
|
||||
aria-expanded={!isCollapsed}
|
||||
>
|
||||
<Icon icon={mdiChevronRight} size="24" class="inline-block -mt-2.5 transition-all duration-250 {iconRotation}" />
|
||||
<span class="font-bold text-3xl text-black dark:text-white">{group.name}</span>
|
||||
<Icon icon={mdiChevronRight} size="24" class="-mt-2.5 inline-block transition-all duration-250 {iconRotation}" />
|
||||
<span class="text-3xl font-bold text-black dark:text-white">{group.name}</span>
|
||||
<span class="ms-1.5">({$t('places_count', { values: { count: places.length } })})</span>
|
||||
</button>
|
||||
<hr class="dark:border-immich-dark-gray" />
|
||||
@@ -47,12 +47,12 @@
|
||||
<img
|
||||
src={getAssetMediaUrl({ id: item.id, size: AssetMediaSize.Thumbnail })}
|
||||
alt={city}
|
||||
class="object-cover w-39 h-39"
|
||||
class="size-39 object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="absolute bottom-2 w-full text-ellipsis px-1 text-center text-sm font-medium capitalize text-white backdrop-blur-[1px] hover:cursor-pointer"
|
||||
class="absolute bottom-2 w-full px-1 text-center text-sm font-medium text-ellipsis text-white capitalize backdrop-blur-[1px] hover:cursor-pointer"
|
||||
>
|
||||
{city}
|
||||
</span>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</script>
|
||||
|
||||
<!-- Search Places -->
|
||||
<div class="hidden md:block h-10 xl:w-60 2xl:w-80">
|
||||
<div class="hidden h-10 md:block xl:w-60 2xl:w-80">
|
||||
<SearchBar placeholder={$t('search_places')} bind:name={searchQuery} showLoadingSpinner={false} />
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
{#if getSelectedPlacesGroupOption($placesViewSettings) !== PlacesGroupBy.None}
|
||||
<span in:fly={{ x: -50, duration: 250 }}>
|
||||
<!-- Expand Countries Groups -->
|
||||
<div class="hidden xl:flex gap-0">
|
||||
<div class="hidden gap-0 xl:flex">
|
||||
<div class="block">
|
||||
<IconButton
|
||||
title={$t('expand_all')}
|
||||
|
||||
@@ -243,20 +243,20 @@
|
||||
{#if terms}
|
||||
<section
|
||||
id="search-chips"
|
||||
class="mt-24 text-center w-full flex gap-5 place-content-center place-items-center flex-wrap px-24"
|
||||
class="mt-24 flex w-full flex-wrap place-content-center place-items-center gap-5 px-24 text-center"
|
||||
>
|
||||
{#each getObjectKeys(terms) as searchKey (searchKey)}
|
||||
{@const value = terms[searchKey]}
|
||||
<div class="flex place-content-center place-items-center items-stretch text-xs">
|
||||
<div
|
||||
class="flex items-center justify-center bg-immich-primary py-2 px-4 text-white dark:text-black dark:bg-immich-dark-primary
|
||||
class="flex items-center justify-center bg-immich-primary px-4 py-2 text-white dark:bg-immich-dark-primary dark:text-black
|
||||
{value === true ? 'rounded-full' : 'rounded-s-full'}"
|
||||
>
|
||||
{getHumanReadableSearchKey(searchKey as keyof SearchTerms)}
|
||||
</div>
|
||||
|
||||
{#if value !== true}
|
||||
<div class="bg-gray-300 py-2 px-4 dark:bg-gray-800 dark:text-white rounded-e-full">
|
||||
<div class="rounded-e-full bg-gray-300 px-4 py-2 dark:bg-gray-800 dark:text-white">
|
||||
{#if (searchKey === 'takenAfter' || searchKey === 'takenBefore') && typeof value === 'string'}
|
||||
{getHumanReadableDate(value)}
|
||||
{:else if searchKey === 'personIds' && Array.isArray(value)}
|
||||
@@ -282,7 +282,7 @@
|
||||
{/if}
|
||||
|
||||
<section
|
||||
class="mb-12 bg-immich-bg dark:bg-immich-dark-bg m-4 max-h-screen"
|
||||
class="m-4 mb-12 max-h-screen bg-immich-bg dark:bg-immich-dark-bg"
|
||||
bind:clientHeight={viewport.height}
|
||||
bind:clientWidth={viewport.width}
|
||||
bind:this={searchResultsElement}
|
||||
@@ -309,7 +309,7 @@
|
||||
{/if}
|
||||
|
||||
{#if isLoading}
|
||||
<div class="flex justify-center py-16 items-center">
|
||||
<div class="flex items-center justify-center py-16">
|
||||
<LoadingSpinner size="giant" />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -317,7 +317,7 @@
|
||||
|
||||
<section>
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full z-2">
|
||||
<div class="fixed inset-s-0 top-0 z-2 w-full">
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
@@ -368,7 +368,7 @@
|
||||
</AssetSelectControlBar>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="fixed top-0 start-0 w-full z-2">
|
||||
<div class="fixed inset-s-0 top-0 z-2 w-full">
|
||||
<ControlAppBar onClose={() => goto(previousRoute)} backIcon={mdiArrowLeft}>
|
||||
<div class="absolute bg-light"></div>
|
||||
<div class="w-full flex-1 ps-4">
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
{#snippet buttons()}
|
||||
<div class="hidden xl:block h-10">
|
||||
<div class="hidden h-10 xl:block">
|
||||
<GroupTab
|
||||
label={$t('show_shared_links')}
|
||||
{filters}
|
||||
@@ -85,7 +85,7 @@
|
||||
<Container center size="medium">
|
||||
{#if sharedLinks.length === 0}
|
||||
<div
|
||||
class="flex place-content-center place-items-center rounded-lg bg-gray-100 dark:bg-immich-dark-gray dark:text-immich-gray p-12"
|
||||
class="flex place-content-center place-items-center rounded-lg bg-gray-100 p-12 dark:bg-immich-dark-gray dark:text-immich-gray"
|
||||
>
|
||||
<p>{$t('you_dont_have_any_shared_links')}</p>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
let { sharedLink, preload = false, class: className = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="relative shrink-0 size-22">
|
||||
<div class="relative size-22 shrink-0">
|
||||
{#if sharedLink?.album}
|
||||
<AlbumCover album={sharedLink.album} class={className} {preload} />
|
||||
{:else if sharedLink.assets[0]}
|
||||
|
||||
@@ -61,11 +61,11 @@
|
||||
<svelte:element
|
||||
this={isExpired ? 'div' : 'a'}
|
||||
href={isExpired ? undefined : Route.viewSharedLink(sharedLink)}
|
||||
class="flex gap-4 w-full py-4"
|
||||
class="flex w-full gap-4 py-4"
|
||||
>
|
||||
<ShareCover class="transition-all duration-300 hover:shadow-lg" {sharedLink} />
|
||||
|
||||
<div class="flex flex-col gap-4 justify-between">
|
||||
<div class="flex flex-col justify-between gap-4">
|
||||
<div class="flex flex-col">
|
||||
<Text size="tiny" color={isExpired ? 'danger' : 'muted'} fontWeight="medium">
|
||||
{#if isExpired}
|
||||
@@ -102,8 +102,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</svelte:element>
|
||||
<div class="flex flex-auto flex-col place-content-center place-items-end text-end ms-4">
|
||||
<div class="sm:flex hidden">
|
||||
<div class="ms-4 flex flex-auto flex-col place-content-center place-items-end text-end">
|
||||
<div class="hidden sm:flex">
|
||||
<ActionButton action={Edit} />
|
||||
<ActionButton action={Copy} />
|
||||
<ActionButton action={Delete} />
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<UserPageLayout title={data.meta.title} actions={[CreateAlbum, ViewSharedLinks]}>
|
||||
<div class="flex flex-col">
|
||||
{#if data.partners.length > 0}
|
||||
<div class="mb-6 mt-2">
|
||||
<div class="mt-2 mb-6">
|
||||
<div>
|
||||
<p class="mb-4 font-medium dark:text-immich-dark-fg">{$t('partners')}</p>
|
||||
</div>
|
||||
@@ -69,7 +69,7 @@
|
||||
<hr class="mb-4 dark:border-immich-dark-gray" />
|
||||
{/if}
|
||||
|
||||
<div class="mb-6 mt-2">
|
||||
<div class="mt-2 mb-6">
|
||||
<div>
|
||||
<p class="mb-4 font-medium dark:text-immich-dark-fg">{$t('albums')}</p>
|
||||
</div>
|
||||
@@ -79,7 +79,7 @@
|
||||
<Albums sharedAlbums={data.sharedAlbums} userSettings={settings} showOwner>
|
||||
<!-- Empty List -->
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_shared_albums_message')} src={empty2Url} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_shared_albums_message')} src={empty2Url} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Albums>
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<Sidebar>
|
||||
<SkipLink target={`#${headerId}`} text={$t('skip_to_tags')} breakpoint="md" />
|
||||
<section>
|
||||
<Text class="ps-4 mb-4" size="small">{$t('explorer')}</Text>
|
||||
<Text class="mb-4 ps-4" size="small">{$t('explorer')}</Text>
|
||||
<div class="h-full">
|
||||
<TreeItems icons={{ default: mdiTag, active: mdiTag }} {tree} active={tag.path} {getLink} />
|
||||
</div>
|
||||
@@ -112,7 +112,7 @@
|
||||
|
||||
<section>
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full">
|
||||
<div class="fixed inset-s-0 top-0 w-full">
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
@@ -55,13 +55,13 @@
|
||||
assetInteraction={assetMultiSelectManager}
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
<p class="font-medium text-gray-500/60 dark:text-gray-300/60 p-4">
|
||||
<p class="p-4 font-medium text-gray-500/60 dark:text-gray-300/60">
|
||||
{$t('trashed_items_will_be_permanently_deleted_after', {
|
||||
values: { days: serverConfigManager.value.trashDays },
|
||||
})}
|
||||
</p>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('trash_no_results_message')} src={empty3Url} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('trash_no_results_message')} src={empty3Url} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<div class="sm:ms-8 flex flex-col gap-6">
|
||||
<div class="flex flex-col gap-6 sm:ms-8">
|
||||
<Field label={$t('theme_selection')} description={$t('theme_selection_description')}>
|
||||
<Switch
|
||||
checked={themeManager.preference === ThemePreference.System}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="sm:ms-8 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-4 sm:ms-8">
|
||||
<Field label={$t('password')} required>
|
||||
<PasswordInput bind:value={password} autocomplete="current-password" />
|
||||
</Field>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<div class="sm:ms-8 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-4 sm:ms-8">
|
||||
{#if currentSession}
|
||||
<div class="mb-6">
|
||||
<Text class="mb-2" fontWeight="medium" size="tiny" color="primary">
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="sm:ms-8 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-4 sm:ms-8">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('archive_size')}
|
||||
|
||||
@@ -66,9 +66,9 @@
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="sm:ms-4 md:ms-8 flex flex-col">
|
||||
<div class="flex flex-col sm:ms-4 md:ms-8">
|
||||
<SettingAccordion key="albums" title={$t('albums')} subtitle={$t('albums_feature_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('albums_default_sort_order')} description={$t('albums_default_sort_order_description')}>
|
||||
<Select
|
||||
options={[
|
||||
@@ -82,7 +82,7 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="folders" title={$t('folders')} subtitle={$t('folders_feature_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('enable')}>
|
||||
<Switch bind:checked={foldersEnabled} />
|
||||
</Field>
|
||||
@@ -96,7 +96,7 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="memories" title={$t('time_based_memories')} subtitle={$t('photos_from_previous_years')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('enable')}>
|
||||
<Switch bind:checked={memoriesEnabled} />
|
||||
</Field>
|
||||
@@ -108,7 +108,7 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="people" title={$t('people')} subtitle={$t('people_feature_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('enable')}>
|
||||
<Switch bind:checked={peopleEnabled} />
|
||||
</Field>
|
||||
@@ -122,7 +122,7 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="rating" title={$t('rating')} subtitle={$t('rating_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('enable')}>
|
||||
<Switch bind:checked={ratingsEnabled} />
|
||||
</Field>
|
||||
@@ -130,7 +130,7 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="shared-links" title={$t('shared_links')} subtitle={$t('shared_links_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('enable')}>
|
||||
<Switch bind:checked={sharedLinksEnabled} />
|
||||
</Field>
|
||||
@@ -144,7 +144,7 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="tags" title={$t('tags')} subtitle={$t('tag_feature_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('enable')}>
|
||||
<Switch bind:checked={tagsEnabled} />
|
||||
</Field>
|
||||
@@ -158,14 +158,14 @@
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion key="cast" title={$t('cast')} subtitle={$t('cast_description')}>
|
||||
<div class="sm:ms-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 flex flex-col gap-4 sm:ms-4">
|
||||
<Field label={$t('gcast_enabled')} description={$t('gcast_enabled_description')}>
|
||||
<Switch bind:checked={gCastEnabled} />
|
||||
</Field>
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<div class="mt-4 flex justify-end">
|
||||
<Button shape="round" type="submit" size="small" onclick={() => handleSave()}>{$t('save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" {onsubmit}>
|
||||
<div class="sm:ms-8 flex flex-col gap-6">
|
||||
<div class="flex flex-col gap-6 sm:ms-8">
|
||||
<Field label={$t('enable')} description={$t('notification_toggle_setting_description')}>
|
||||
<Switch bind:checked={emailNotificationsEnabled} />
|
||||
</Field>
|
||||
@@ -53,7 +53,7 @@
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<div class="mt-4 flex justify-end">
|
||||
<Button shape="round" type="submit" size="small" onclick={() => handleSave()}>{$t('save')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<div class="sm:ms-8 flex justify-end">
|
||||
<div class="flex justify-end sm:ms-8">
|
||||
{#if loading}
|
||||
<div class="flex place-content-center place-items-center">
|
||||
<LoadingSpinner />
|
||||
|
||||
@@ -120,8 +120,8 @@
|
||||
<section class="my-4">
|
||||
{#if partners.length > 0}
|
||||
{#each partners as partner (partner.user.id)}
|
||||
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 mt-6 bg-slate-50 dark:bg-gray-900 p-5">
|
||||
<div class="flex gap-4 rounded-lg pb-4 transition-all justify-between">
|
||||
<div class="mt-6 rounded-2xl border border-gray-200 bg-slate-50 p-5 dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="flex justify-between gap-4 rounded-lg pb-4 transition-all">
|
||||
<div class="flex gap-4">
|
||||
<UserAvatar user={partner.user} size="md" />
|
||||
<div class="text-start">
|
||||
@@ -147,7 +147,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="dark:text-gray-200 text-immich-dark-gray">
|
||||
<div class="text-immich-dark-gray dark:text-gray-200">
|
||||
<!-- I am sharing my assets with this user -->
|
||||
{#if partner.sharedByMe}
|
||||
<hr class="my-4 border border-gray-200 dark:border-gray-700" />
|
||||
@@ -158,11 +158,11 @@
|
||||
>{$t('partner_can_access', { values: { partner: partner.user.name } })}</Text
|
||||
>
|
||||
<ul class="text-sm">
|
||||
<li class="flex gap-2 place-items-center py-1 mt-2">
|
||||
<li class="mt-2 flex place-items-center gap-2 py-1">
|
||||
<Icon icon={mdiCheck} />
|
||||
{$t('partner_can_access_assets')}
|
||||
</li>
|
||||
<li class="flex gap-2 place-items-center py-1">
|
||||
<li class="flex place-items-center gap-2 py-1">
|
||||
<Icon icon={mdiCheck} />
|
||||
{$t('partner_can_access_location')}
|
||||
</li>
|
||||
@@ -188,7 +188,7 @@
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="flex justify-end mt-5">
|
||||
<div class="mt-5 flex justify-end">
|
||||
<Button shape="round" size="small" onclick={() => handleCreatePartners()}>{$t('add_partner')}</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</script>
|
||||
|
||||
<form autocomplete="off" onsubmit={handleSubmit}>
|
||||
<div class="flex flex-col gap-6 place-items-center place-content-center">
|
||||
<div class="flex flex-col place-content-center place-items-center gap-6">
|
||||
<Heading>{$t('change_pin_code')}</Heading>
|
||||
<Field label={$t('current_pin_code')}>
|
||||
<PinInput bind:value={currentPinCode} />
|
||||
@@ -53,7 +53,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2 mt-4">
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<Button shape="round" color="secondary" type="button" size="small" onclick={resetForm}>
|
||||
{$t('clear')}
|
||||
</Button>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
|
||||
<Text size="small" color="muted">{subtitle}</Text>
|
||||
<div class="flex items-center mt-2 max-w-[300px]">
|
||||
<div class="mt-2 flex max-w-[300px] items-center">
|
||||
<Combobox label={title} hideLabel={true} {selectedOption} {options} placeholder={comboboxPlaceholder} {onSelect} />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<OnEvents {onApiKeyCreate} {onApiKeyUpdate} {onApiKeyDelete} />
|
||||
|
||||
<section class="my-4">
|
||||
<div class="sm:ms-8 flex flex-col gap-2" in:fade={{ duration: 500 }}>
|
||||
<div class="flex flex-col gap-2 sm:ms-8" in:fade={{ duration: 500 }}>
|
||||
<div class="mb-2 flex justify-end">
|
||||
<Button leadingIcon={Create.icon} shape="round" size="small" onclick={() => Create.onAction(Create)}>
|
||||
{Create.title}
|
||||
@@ -56,7 +56,7 @@
|
||||
<TableCell>{key.name}</TableCell>
|
||||
<TableCell>
|
||||
<Text
|
||||
class="font-mono overflow-hidden line-clamp-3"
|
||||
class="line-clamp-3 overflow-hidden font-mono"
|
||||
size="small"
|
||||
title={JSON.stringify(key.permissions, null, 2)}>{key.permissions}</Text
|
||||
>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" onsubmit={preventDefault(bubble('submit'))}>
|
||||
<div class="sm:ms-8 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-4 sm:ms-8">
|
||||
<Field label={$t('user_id')} disabled>
|
||||
<Input bind:value={editedUser.id} />
|
||||
</Field>
|
||||
|
||||
@@ -118,17 +118,17 @@
|
||||
<!-- PRODUCT KEY INFO CARD -->
|
||||
{#if isServerProduct}
|
||||
<div
|
||||
class="bg-gray-50 border border-immich-dark-primary/20 dark:bg-immich-dark-primary/15 p-6 pe-12 rounded-xl flex place-content-center gap-4"
|
||||
class="flex place-content-center gap-4 rounded-xl border border-immich-dark-primary/20 bg-gray-50 p-6 pe-12 dark:bg-immich-dark-primary/15"
|
||||
>
|
||||
<Icon icon={mdiKey} size="56" class="text-primary" />
|
||||
|
||||
<div>
|
||||
<p class="text-primary font-semibold text-lg">
|
||||
<p class="text-lg font-semibold text-primary">
|
||||
{$t('purchase_server_title')}
|
||||
</p>
|
||||
|
||||
{#if authManager.user.isAdmin && serverPurchaseInfo?.activatedAt}
|
||||
<p class="dark:text-white text-sm mt-1 col-start-2">
|
||||
<p class="col-start-2 mt-1 text-sm dark:text-white">
|
||||
{$t('purchase_activated_time', {
|
||||
values: {
|
||||
date: new Date(serverPurchaseInfo.activatedAt).toLocaleString($locale, dateFormats.settings),
|
||||
@@ -142,7 +142,7 @@
|
||||
</div>
|
||||
|
||||
{#if authManager.user.isAdmin}
|
||||
<div class="text-right mt-4">
|
||||
<div class="mt-4 text-right">
|
||||
<Button shape="round" size="small" color="danger" onclick={removeServerProductKey}
|
||||
>{$t('purchase_button_remove_key')}</Button
|
||||
>
|
||||
@@ -150,16 +150,16 @@
|
||||
{/if}
|
||||
{:else}
|
||||
<div
|
||||
class="bg-gray-50 border border-immich-dark-primary/20 dark:bg-immich-dark-primary/15 p-6 pe-12 rounded-xl flex place-content-center gap-4"
|
||||
class="flex place-content-center gap-4 rounded-xl border border-immich-dark-primary/20 bg-gray-50 p-6 pe-12 dark:bg-immich-dark-primary/15"
|
||||
>
|
||||
<Icon icon={mdiKey} size="56" class="text-primary" />
|
||||
|
||||
<div>
|
||||
<p class="text-primary font-semibold text-lg">
|
||||
<p class="text-lg font-semibold text-primary">
|
||||
{$t('purchase_individual_title')}
|
||||
</p>
|
||||
{#if authManager.user.license?.activatedAt}
|
||||
<p class="dark:text-white text-sm mt-1 col-start-2">
|
||||
<p class="col-start-2 mt-1 text-sm dark:text-white">
|
||||
{$t('purchase_activated_time', {
|
||||
values: {
|
||||
date: new Date(authManager.user.license?.activatedAt).toLocaleString($locale, dateFormats.settings),
|
||||
@@ -170,7 +170,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right mt-4">
|
||||
<div class="mt-4 text-right">
|
||||
<Button shape="round" size="small" color="danger" onclick={removeIndividualProductKey}
|
||||
>{$t('purchase_button_remove_key')}</Button
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</script>
|
||||
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
<div class="w-full max-w-xl m-auto">
|
||||
<div class="m-auto w-full max-w-xl">
|
||||
<div class="mt-5">
|
||||
<UtilitiesMenu />
|
||||
</div>
|
||||
|
||||
@@ -20,24 +20,24 @@
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="border border-gray-300 dark:border-immich-dark-gray rounded-3xl pt-1 pb-6 dark:text-white">
|
||||
<div class="rounded-3xl border border-gray-300 pt-1 pb-6 dark:border-immich-dark-gray dark:text-white">
|
||||
<Text size="tiny" color="muted" fontWeight="medium" class="p-4">{$t('organize_your_library')}</Text>
|
||||
|
||||
{#each links as link (link.href)}
|
||||
<a href={link.href} class="w-full hover:bg-gray-100 dark:hover:bg-immich-dark-gray flex items-center gap-4 p-4">
|
||||
<a href={link.href} class="flex w-full items-center gap-4 p-4 hover:bg-gray-100 dark:hover:bg-immich-dark-gray">
|
||||
<span><Icon icon={link.icon} class="text-primary" size="24" /> </span>
|
||||
{link.label}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
<br />
|
||||
<div class="border border-gray-300 dark:border-immich-dark-gray rounded-3xl pt-1 pb-6 dark:text-white">
|
||||
<div class="rounded-3xl border border-gray-300 pt-1 pb-6 dark:border-immich-dark-gray dark:text-white">
|
||||
<Text size="tiny" color="muted" fontWeight="medium" class="p-4">{$t('download')}</Text>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => modalManager.show(ObtainiumConfigModal, {})}
|
||||
class="w-full hover:bg-gray-100 dark:hover:bg-immich-dark-gray flex items-center gap-4 p-4"
|
||||
class="flex w-full items-center gap-4 p-4 hover:bg-gray-100 dark:hover:bg-immich-dark-gray"
|
||||
>
|
||||
<span>
|
||||
<Icon icon={mdiLinkEdit} class="text-immich-primary dark:text-immich-dark-primary" size="24" />
|
||||
@@ -47,7 +47,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => modalManager.show(AppDownloadModal, {})}
|
||||
class="w-full hover:bg-gray-100 dark:hover:bg-immich-dark-gray flex items-center gap-4 p-4"
|
||||
class="flex w-full items-center gap-4 p-4 hover:bg-gray-100 dark:hover:bg-immich-dark-gray"
|
||||
>
|
||||
<span>
|
||||
<Icon icon={mdiCellphoneArrowDownVariant} class="text-immich-primary dark:text-immich-dark-primary" size="24" />
|
||||
|
||||
+8
-8
@@ -265,14 +265,14 @@
|
||||
handleResolve(duplicates[duplicatesIndex].duplicateId, duplicateAssetIds, trashIds)}
|
||||
onStack={(assets) => handleStack(duplicates[duplicatesIndex].duplicateId, assets)}
|
||||
/>
|
||||
<div class="max-w-5xl mx-auto mb-16">
|
||||
<div class="flex mb-4 sm:px-6 w-full place-content-center justify-between items-center place-items-center">
|
||||
<div class="mx-auto mb-16 max-w-5xl">
|
||||
<div class="mb-4 flex w-full place-content-center place-items-center items-center justify-between sm:px-6">
|
||||
<div class="flex text-xs text-black">
|
||||
<Button
|
||||
size="small"
|
||||
leadingIcon={mdiPageFirst}
|
||||
color="primary"
|
||||
class="flex place-items-center rounded-s-full gap-2 px-2 sm:px-4"
|
||||
class="flex place-items-center gap-2 rounded-s-full px-2 sm:px-4"
|
||||
onclick={handleFirst}
|
||||
disabled={duplicatesIndex === 0}
|
||||
>
|
||||
@@ -282,14 +282,14 @@
|
||||
size="small"
|
||||
leadingIcon={mdiChevronLeft}
|
||||
color="primary"
|
||||
class="flex place-items-center rounded-e-full gap-2 px-2 sm:px-4"
|
||||
class="flex place-items-center gap-2 rounded-e-full px-2 sm:px-4"
|
||||
onclick={handlePrevious}
|
||||
disabled={duplicatesIndex === 0}
|
||||
>
|
||||
{$t('previous')}
|
||||
</Button>
|
||||
</div>
|
||||
<p class="border px-3 md:px-6 py-1 dark:bg-subtle rounded-lg text-xs md:text-sm">
|
||||
<p class="rounded-lg border px-3 py-1 text-xs md:px-6 md:text-sm dark:bg-subtle">
|
||||
{duplicatesIndex + 1} / {duplicates.length.toLocaleString($locale)}
|
||||
</p>
|
||||
<div class="flex text-xs text-black">
|
||||
@@ -297,7 +297,7 @@
|
||||
size="small"
|
||||
trailingIcon={mdiChevronRight}
|
||||
color="primary"
|
||||
class="flex place-items-center rounded-s-full gap-2 px-2 sm:px-4"
|
||||
class="flex place-items-center gap-2 rounded-s-full px-2 sm:px-4"
|
||||
onclick={handleNext}
|
||||
disabled={duplicatesIndex === duplicates.length - 1}
|
||||
>
|
||||
@@ -307,7 +307,7 @@
|
||||
size="small"
|
||||
trailingIcon={mdiPageLast}
|
||||
color="primary"
|
||||
class="flex place-items-center rounded-e-full gap-2 px-2 sm:px-4"
|
||||
class="flex place-items-center gap-2 rounded-e-full px-2 sm:px-4"
|
||||
onclick={handleLast}
|
||||
disabled={duplicatesIndex === duplicates.length - 1}
|
||||
>
|
||||
@@ -318,7 +318,7 @@
|
||||
</div>
|
||||
{/key}
|
||||
{:else}
|
||||
<p class="text-center text-lg dark:text-white flex place-items-center place-content-center">
|
||||
<p class="flex place-content-center place-items-center text-center text-lg dark:text-white">
|
||||
{$t('no_duplicates_found')}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
+10
-10
@@ -100,12 +100,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="min-w-60 transition-colors border rounded-lg flex-1">
|
||||
<div class="min-w-60 flex-1 rounded-lg border transition-colors">
|
||||
<div class="relative w-full">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => onSelectAsset(asset)}
|
||||
class="block relative w-full"
|
||||
class="relative block w-full"
|
||||
aria-pressed={isSelected}
|
||||
aria-label={$t('keep')}
|
||||
>
|
||||
@@ -113,20 +113,20 @@
|
||||
<img
|
||||
src={getAssetMediaUrl({ id: asset.id })}
|
||||
alt={$getAltText(toTimelineAsset(asset))}
|
||||
class="h-60 object-cover w-full rounded-t-md"
|
||||
class="h-60 w-full rounded-t-md object-cover"
|
||||
draggable="false"
|
||||
/>
|
||||
|
||||
<!-- FAVORITE ICON -->
|
||||
{#if asset.isFavorite}
|
||||
<div class="absolute bottom-2 start-2">
|
||||
<div class="absolute inset-s-2 bottom-2">
|
||||
<Icon icon={mdiHeart} size="24" class="text-white" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- OVERLAY CHIP -->
|
||||
<div
|
||||
class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors {isSelected
|
||||
class="absolute inset-e-3 bottom-1 rounded-xl px-4 py-1 text-xs transition-colors {isSelected
|
||||
? 'bg-green-400/90'
|
||||
: 'bg-red-300/90'} text-black"
|
||||
>
|
||||
@@ -134,14 +134,14 @@
|
||||
</div>
|
||||
|
||||
<!-- EXTERNAL LIBRARY / STACK COUNT CHIP -->
|
||||
<div class="absolute top-2 end-3">
|
||||
<div class="absolute inset-e-3 top-2">
|
||||
{#if isFromExternalLibrary}
|
||||
<div class="bg-immich-primary/90 px-2 py-1 rounded-xl text-xs text-white">
|
||||
<div class="rounded-xl bg-immich-primary/90 px-2 py-1 text-xs text-white">
|
||||
{$t('external')}
|
||||
</div>
|
||||
{/if}
|
||||
{#if asset.stack?.assetCount}
|
||||
<div class="bg-immich-primary/90 px-2 py-1 my-0.5 rounded-xl text-xs text-white">
|
||||
<div class="my-0.5 rounded-xl bg-immich-primary/90 px-2 py-1 text-xs text-white">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="me-1">{asset.stack.assetCount}</div>
|
||||
<Icon icon={mdiImageMultipleOutline} size="18" />
|
||||
@@ -154,7 +154,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => onViewAsset(asset)}
|
||||
class="absolute rounded-full top-1 start-1 text-gray-200 p-2 hover:text-white bg-black/35 hover:bg-black/50"
|
||||
class="absolute inset-s-1 top-1 rounded-full bg-black/35 p-2 text-gray-200 hover:bg-black/50 hover:text-white"
|
||||
title={$t('view')}
|
||||
>
|
||||
<Icon aria-label={$t('view')} icon={mdiMagnifyPlus} flipped size="18" />
|
||||
@@ -162,7 +162,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid place-items-start gap-y-2 py-2 text-sm transition-colors rounded-b-lg {isSelected
|
||||
class="grid place-items-start gap-y-2 rounded-b-lg py-2 text-sm transition-colors {isSelected
|
||||
? 'bg-success/15 dark:bg-[#001a06]'
|
||||
: 'bg-transparent'}"
|
||||
>
|
||||
|
||||
+4
-4
@@ -105,8 +105,8 @@
|
||||
]}
|
||||
/>
|
||||
|
||||
<div class="rounded-3xl border dark:border-2 border-gray-300 dark:border-gray-700 max-w-256 mx-auto mb-4 py-6 px-0.2">
|
||||
<div class="flex flex-wrap gap-y-6 mb-4 px-6 w-full place-content-end justify-between">
|
||||
<div class="px-0.2 mx-auto mb-4 max-w-5xl rounded-3xl border border-gray-300 py-6 dark:border-2 dark:border-gray-700">
|
||||
<div class="mb-4 flex w-full flex-wrap place-content-end justify-between gap-y-6 px-6">
|
||||
<!-- MARK ALL BUTTONS -->
|
||||
<div class="flex text-xs text-black">
|
||||
<Button class="rounded-s-full" size="small" color="primary" leadingIcon={mdiCheck} onclick={onSelectAll}
|
||||
@@ -128,7 +128,7 @@
|
||||
size="small"
|
||||
leadingIcon={mdiCheck}
|
||||
color="success"
|
||||
class="flex place-items-center rounded-s-full gap-2"
|
||||
class="flex place-items-center gap-2 rounded-s-full"
|
||||
onclick={handleResolve}
|
||||
>
|
||||
{$t('keep_all')}
|
||||
@@ -158,7 +158,7 @@
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto p-2">
|
||||
<div class="flex flex-nowrap gap-1 place-items-start justify-center min-w-full w-fit mx-auto">
|
||||
<div class="mx-auto flex w-fit min-w-full flex-nowrap place-items-start justify-center gap-1">
|
||||
{#each assets as asset (asset.id)}
|
||||
<DuplicateAsset {assets} {asset} {onSelectAsset} isSelected={selectedAssetIds.has(asset.id)} {onViewAsset} />
|
||||
{/each}
|
||||
|
||||
+3
-3
@@ -13,13 +13,13 @@
|
||||
let { icon, children, borderBottom = true, highlight = false, title }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-[25px_1fr] w-full px-1 py-0.5" class:border-b={borderBottom} {title}>
|
||||
<div class="grid w-full grid-cols-[25px_1fr] px-1 py-0.5" class:border-b={borderBottom} {title}>
|
||||
<Icon {icon} size="18" class="text-dark/25 {highlight ? 'text-primary/75' : ''}" />
|
||||
<div class="justify-self-end text-end rounded px-1 transition-colors w-full overflow-hidden">
|
||||
<div class="w-full justify-self-end overflow-hidden rounded-sm px-1 text-end transition-colors">
|
||||
<Text
|
||||
size="tiny"
|
||||
fontWeight={highlight ? 'semi-bold' : 'normal'}
|
||||
class={`${highlight ? 'text-primary' : ''} text-ellipsis w-full overflow-hidden`}
|
||||
class={`${highlight ? 'text-primary' : ''} w-full overflow-hidden text-ellipsis`}
|
||||
>
|
||||
{@render children?.()}
|
||||
</Text>
|
||||
|
||||
@@ -134,16 +134,16 @@
|
||||
|
||||
<UserPageLayout title={data.meta.title} scrollbar={true}>
|
||||
{#snippet buttons()}
|
||||
<div class="flex gap-2 justify-end place-items-center">
|
||||
<Text class="hidden md:block mr-4" size="tiny" color="muted">{$t('geolocation_instruction_location')}</Text>
|
||||
<div class="border flex place-items-center place-content-center px-2 py-1 bg-primary/10 rounded-2xl">
|
||||
<Text class="hidden md:inline-block font-mono mr-5 ml-2" color="muted" size="tiny">
|
||||
<div class="flex place-items-center justify-end gap-2">
|
||||
<Text class="mr-4 hidden md:block" size="tiny" color="muted">{$t('geolocation_instruction_location')}</Text>
|
||||
<div class="flex place-content-center place-items-center rounded-2xl border bg-primary/10 px-2 py-1">
|
||||
<Text class="mr-5 ml-2 hidden font-mono md:inline-block" color="muted" size="tiny">
|
||||
{$t('selected_gps_coordinates')}
|
||||
</Text>
|
||||
<Text
|
||||
title="latitude, longitude"
|
||||
class="rounded-3xl font-mono text-sm text-primary px-2 py-1 transition-all duration-100 ease-in-out {locationUpdated
|
||||
? 'bg-primary/90 text-light font-semibold scale-105'
|
||||
class="rounded-3xl px-2 py-1 font-mono text-sm text-primary transition-all duration-100 ease-in-out {locationUpdated
|
||||
? 'scale-105 bg-primary/90 font-semibold text-light'
|
||||
: ''}"
|
||||
>
|
||||
{#if point}
|
||||
@@ -182,7 +182,7 @@
|
||||
{/snippet}
|
||||
|
||||
{#if isLoading}
|
||||
<div class="h-full w-full flex items-center justify-center">
|
||||
<div class="flex size-full items-center justify-center">
|
||||
<LoadingSpinner size="giant" />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -200,17 +200,17 @@
|
||||
>
|
||||
{#snippet customThumbnailLayout(asset: TimelineAsset)}
|
||||
{#if hasGps(asset)}
|
||||
<div class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors bg-success text-black">
|
||||
<div class="absolute inset-e-3 bottom-1 rounded-xl bg-success px-4 py-1 text-xs text-black transition-colors">
|
||||
{asset.city || $t('gps')}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors bg-danger text-light">
|
||||
<div class="absolute inset-e-3 bottom-1 rounded-xl bg-danger px-4 py-1 text-xs text-light transition-colors">
|
||||
{$t('gps_missing')}
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => {}} class="mt-10 mx-auto" />
|
||||
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => {}} class="mx-auto mt-10" />
|
||||
{/snippet}
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
+2
-2
@@ -64,13 +64,13 @@
|
||||
<OnEvents {onAssetsDelete} />
|
||||
|
||||
<UserPageLayout title={data.meta.title} scrollbar={true}>
|
||||
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
|
||||
<div class="grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
|
||||
{#if assets && assets.length > 0}
|
||||
{#each assets as asset (asset.id)}
|
||||
<LargeAssetData {asset} {onViewAsset} />
|
||||
{/each}
|
||||
{:else}
|
||||
<p class="text-center text-lg dark:text-white flex place-items-center place-content-center">
|
||||
<p class="flex place-content-center place-items-center text-center text-lg dark:text-white">
|
||||
{$t('no_assets_to_show')}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
+5
-5
@@ -17,21 +17,21 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="w-full aspect-square rounded-xl border-4 transition-colors font-semibold text-xs bg-gray-200 dark:bg-gray-800 border-gray-200 dark:border-gray-800"
|
||||
class="aspect-square w-full rounded-xl border-4 border-gray-200 bg-gray-200 text-xs font-semibold transition-colors dark:border-gray-800 dark:bg-gray-800"
|
||||
bind:clientWidth={boxWidth}
|
||||
title={assetData}
|
||||
>
|
||||
<div class="relative w-full h-full overflow-hidden rounded-lg">
|
||||
<div class="relative size-full overflow-hidden rounded-lg">
|
||||
<Thumbnail asset={toTimelineAsset(asset)} readonly onClick={() => onViewAsset(asset)} thumbnailSize={boxWidth} />
|
||||
|
||||
{#if !!asset.libraryId}
|
||||
<div class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors bg-red-500">External</div>
|
||||
<div class="absolute inset-e-3 bottom-1 rounded-xl bg-red-500 px-4 py-1 text-xs transition-colors">External</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-center mt-4 px-4 text-sm font-normal truncate" title={asset.originalFileName}>
|
||||
<div class="mt-4 truncate px-4 text-center text-sm font-normal" title={asset.originalFileName}>
|
||||
{asset.originalFileName}
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-primary text-xl font-semibold py-3">{getFileSize(asset, 1)}</p>
|
||||
<p class="py-3 text-xl font-semibold text-primary">{getFileSize(asset, 1)}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
<OnEvents {onWorkflowCreate} {onWorkflowUpdate} {onWorkflowDelete} />
|
||||
|
||||
{#snippet chipItem(title: string)}
|
||||
<span class="rounded-xl border border-gray-200/80 px-3 py-1.5 text-sm dark:border-gray-600 bg-light">
|
||||
<span class="rounded-xl border border-gray-200/80 bg-light px-3 py-1.5 text-sm dark:border-gray-600">
|
||||
<span class="font-medium text-dark">{title}</span>
|
||||
</span>
|
||||
{/snippet}
|
||||
@@ -160,14 +160,14 @@
|
||||
text={$t('workflows_help_text')}
|
||||
onClick={() => Create.onAction(Create)}
|
||||
src={emptyWorkflows}
|
||||
class="mt-10 mx-auto"
|
||||
class="mx-auto mt-10"
|
||||
/>
|
||||
{:else}
|
||||
<div class="my-6 grid gap-6">
|
||||
{#each workflows as workflow (workflow.id)}
|
||||
<Card class="border border-light-200">
|
||||
<CardHeader
|
||||
class={`flex flex-row px-8 py-6 gap-4 sm:items-center sm:gap-6 ${
|
||||
class={`flex flex-row gap-4 px-8 py-6 sm:items-center sm:gap-6 ${
|
||||
workflow.enabled
|
||||
? 'bg-linear-to-r from-green-50 to-white dark:from-green-800/50 dark:to-green-950/45'
|
||||
: 'bg-neutral-50 dark:bg-neutral-900'
|
||||
@@ -175,8 +175,7 @@
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
class="rounded-full {workflow.enabled ? 'h-3 w-3 bg-success' : 'h-3 w-3 rounded-full bg-muted'}"
|
||||
<span class="rounded-full {workflow.enabled ? 'size-3 bg-success' : 'size-3 rounded-full bg-muted'}"
|
||||
></span>
|
||||
<CardTitle>{workflow.name}</CardTitle>
|
||||
</div>
|
||||
@@ -186,7 +185,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="text-right hidden sm:block">
|
||||
<div class="hidden text-right sm:block">
|
||||
<Text size="tiny">{$t('created_at')}</Text>
|
||||
<Text size="small" fontWeight="medium">
|
||||
{formatTimestamp(workflow.createdAt)}
|
||||
@@ -206,7 +205,7 @@
|
||||
<CardBody class="space-y-6">
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<!-- Trigger Section -->
|
||||
<div class="rounded-2xl border p-4 bg-light-50 border-light-200">
|
||||
<div class="rounded-2xl border border-light-200 bg-light-50 p-4">
|
||||
<div class="mb-3">
|
||||
<Text size="tiny" color="muted" fontWeight="medium">{$t('trigger')}</Text>
|
||||
</div>
|
||||
@@ -214,7 +213,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<div class="rounded-2xl border p-4 bg-light-50 border-light-200">
|
||||
<div class="rounded-2xl border border-light-200 bg-light-50 p-4">
|
||||
<div class="mb-3">
|
||||
<Text size="tiny" color="muted" fontWeight="medium">{$t('filters')}</Text>
|
||||
</div>
|
||||
@@ -232,7 +231,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Actions Section -->
|
||||
<div class="rounded-2xl border p-4 bg-light-50 border-light-200">
|
||||
<div class="rounded-2xl border border-light-200 bg-light-50 p-4">
|
||||
<div class="mb-3">
|
||||
<Text size="tiny" color="muted" fontWeight="medium">{$t('actions')}</Text>
|
||||
</div>
|
||||
@@ -254,7 +253,7 @@
|
||||
</div>
|
||||
|
||||
{#if expandedWorkflows.has(workflow.id)}
|
||||
<VStack gap={2} class="w-full rounded-2xl border bg-light-50 p-4 border-light-200 ">
|
||||
<VStack gap={2} class="w-full rounded-2xl border border-light-200 bg-light-50 p-4">
|
||||
<CodeBlock code={getJson(workflow)} lineNumbers />
|
||||
<Button
|
||||
leadingIcon={mdiClose}
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
</script>
|
||||
|
||||
{#snippet cardOrder(index: number)}
|
||||
<div class="h-8 w-8 rounded-lg flex place-items-center place-content-center shrink-0 border bg-light-50">
|
||||
<div class="flex size-8 shrink-0 place-content-center place-items-center rounded-lg border bg-light-50">
|
||||
<Text size="small" class="font-mono font-bold">
|
||||
{index + 1}
|
||||
</Text>
|
||||
@@ -338,7 +338,7 @@
|
||||
<div class="w-full border-t-2 border-dashed border-light-200"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-xs uppercase">
|
||||
<Text class="bg-white dark:bg-black px-2" fontWeight="semi-bold" size="tiny" color="muted">{$t('then')}</Text>
|
||||
<Text class="bg-white px-2 dark:bg-black" fontWeight="semi-bold" size="tiny" color="muted">{$t('then')}</Text>
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
@@ -347,7 +347,7 @@
|
||||
<button
|
||||
type="button"
|
||||
{onclick}
|
||||
class="w-full p-8 rounded-lg border-2 border-dashed hover:border-light-400 hover:bg-light-50 transition-all flex flex-col items-center justify-center gap-2"
|
||||
class="flex w-full flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed p-8 transition-all hover:border-light-400 hover:bg-light-50"
|
||||
>
|
||||
<Icon icon={mdiPlus} size="32" />
|
||||
<Text size="small" fontWeight="medium">{title}</Text>
|
||||
@@ -384,7 +384,7 @@
|
||||
<CardBody>
|
||||
<VStack gap={4}>
|
||||
<div
|
||||
class="relative overflow-hidden border p-4 w-full rounded-xl"
|
||||
class="relative w-full overflow-hidden rounded-xl border p-4"
|
||||
class:bg-primary-50={editWorkflow.enabled}
|
||||
>
|
||||
<Field
|
||||
@@ -468,12 +468,12 @@
|
||||
isDragging: draggedFilterIndex === index,
|
||||
isDragOver: dragOverFilterIndex === index,
|
||||
})}
|
||||
class="mb-4 cursor-move rounded-2xl border-2 p-4 transition-all bg-light-50 border-dashed hover:border-light-300"
|
||||
class="mb-4 cursor-move rounded-2xl border-2 border-dashed bg-light-50 p-4 transition-all hover:border-light-300"
|
||||
>
|
||||
<div class="flex items-start gap-4">
|
||||
{@render cardOrder(index)}
|
||||
<div class="flex-1">
|
||||
<h1 class="font-bold text-lg mb-3">{filter.title}</h1>
|
||||
<h1 class="mb-3 text-lg font-bold">{filter.title}</h1>
|
||||
<SchemaFormFields
|
||||
schema={filter.schema}
|
||||
bind:config={filterConfigs}
|
||||
@@ -537,12 +537,12 @@
|
||||
isDragging: draggedActionIndex === index,
|
||||
isDragOver: dragOverActionIndex === index,
|
||||
})}
|
||||
class="mb-4 cursor-move rounded-2xl border-2 p-4 transition-all bg-light-50 border-dashed hover:border-light-300"
|
||||
class="mb-4 cursor-move rounded-2xl border-2 border-dashed bg-light-50 p-4 transition-all hover:border-light-300"
|
||||
>
|
||||
<div class="flex items-start gap-4">
|
||||
{@render cardOrder(index)}
|
||||
<div class="flex-1">
|
||||
<h1 class="font-bold text-lg mb-3">{action.title}</h1>
|
||||
<h1 class="mb-3 text-lg font-bold">{action.title}</h1>
|
||||
<SchemaFormFields
|
||||
schema={action.schema}
|
||||
bind:config={actionConfigs}
|
||||
@@ -587,7 +587,7 @@
|
||||
|
||||
{#snippet trailing()}
|
||||
<HStack gap={4}>
|
||||
<HStack gap={1} class="border rounded-lg p-1 border-light-300">
|
||||
<HStack gap={1} class="rounded-lg border border-light-300 p-1">
|
||||
<Button
|
||||
size="small"
|
||||
variant={viewMode === 'visual' ? 'outline' : 'ghost'}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
{#each Object.entries(components) as [key, component] (key)}
|
||||
{@const label = component.title || component.label || key}
|
||||
|
||||
<div class="flex flex-col gap-1 border bg-light p-4 rounded-xl">
|
||||
<div class="flex flex-col gap-1 rounded-xl border bg-light p-4">
|
||||
<!-- Select component -->
|
||||
{#if component.type === 'select'}
|
||||
{#if isPickerField(component.subType)}
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<div class="relative h-12 w-0.5">
|
||||
<div class="absolute inset-0 bg-linear-to-b from-primary/30 via-primary/50 to-primary/30"></div>
|
||||
{#if animated}
|
||||
<div class="absolute inset-0 bg-linear-to-b from-transparent via-primary to-transparent flow-pulse"></div>
|
||||
<div class="flow-pulse absolute inset-0 bg-linear-to-b from-transparent via-primary to-transparent"></div>
|
||||
{/if}
|
||||
<div class="absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2">
|
||||
<div class="h-2 w-2 rounded-full bg-primary shadow-sm shadow-primary/50"></div>
|
||||
<div class="absolute top-0 left-1/2 -translate-1/2">
|
||||
<div class="size-2 rounded-full bg-primary shadow-sm shadow-primary/50"></div>
|
||||
</div>
|
||||
<div class="absolute left-1/2 bottom-0 -translate-x-1/2 translate-y-1/2">
|
||||
<div class="h-2 w-2 rounded-full bg-primary shadow-sm shadow-primary/50"></div>
|
||||
<div class="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2">
|
||||
<div class="size-2 rounded-full bg-primary shadow-sm shadow-primary/50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<VStack gap={2}>
|
||||
<div class="w-full h-[600px] rounded-lg overflow-hidden border {editorClass}">
|
||||
<div class="h-[600px] w-full overflow-hidden rounded-lg border {editorClass}">
|
||||
<JSONEditor {content} onChange={handleChange} mainMenuBar={false} mode={Mode.text} />
|
||||
</div>
|
||||
</VStack>
|
||||
|
||||
+3
-3
@@ -22,13 +22,13 @@
|
||||
<img
|
||||
src={getAssetMediaUrl({ id: item.albumThumbnailAssetId })}
|
||||
alt={item.albumName}
|
||||
class="h-12 w-12 rounded-lg object-cover"
|
||||
class="size-12 rounded-lg object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<div class="h-12 w-12 rounded-lg"></div>
|
||||
<div class="size-12 rounded-lg"></div>
|
||||
{/if}
|
||||
{:else if !isAlbum && 'name' in item}
|
||||
<img src={getPeopleThumbnailUrl(item)} alt={item.name} class="h-12 w-12 rounded-full object-cover" />
|
||||
<img src={getPeopleThumbnailUrl(item)} alt={item.name} class="size-12 rounded-full object-cover" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
|
||||
@@ -82,15 +82,15 @@
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
bind:this={containerEl}
|
||||
class="hidden sm:block fixed w-64 hover:cursor-grab select-none"
|
||||
class="fixed hidden w-64 select-none hover:cursor-grab sm:block"
|
||||
style="left: {position.x}px; top: {position.y}px;"
|
||||
class:cursor-grabbing={isDragging}
|
||||
onmousedown={handleMouseDown}
|
||||
>
|
||||
<div
|
||||
class="rounded-xl border-transparent border-2 hover:shadow-xl hover:border-dashed bg-light-50 shadow-sm p-4 hover:border-light-300 transition-all"
|
||||
class="rounded-xl border-2 border-transparent bg-light-50 p-4 shadow-sm transition-all hover:border-dashed hover:border-light-300 hover:shadow-xl"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4 cursor-grab select-none">
|
||||
<div class="mb-4 flex cursor-grab items-center justify-between select-none">
|
||||
<Text size="small" fontWeight="semi-bold">{$t('workflow_summary')}</Text>
|
||||
<div class="flex items-center gap-1">
|
||||
<IconButton
|
||||
@@ -110,23 +110,23 @@
|
||||
|
||||
<div class="space-y-2">
|
||||
<!-- Trigger -->
|
||||
<div class="rounded-lg bg-light-100 border p-3">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<div class="rounded-lg border bg-light-100 p-3">
|
||||
<div class="mb-1 flex items-center gap-2">
|
||||
<Icon icon={mdiFlashOutline} size="18" class="text-primary" />
|
||||
<Text size="tiny" fontWeight="semi-bold">{$t('trigger')}</Text>
|
||||
</div>
|
||||
<p class="text-sm truncate pl-5">{getTriggerName(trigger.type)}</p>
|
||||
<p class="truncate pl-5 text-sm">{getTriggerName(trigger.type)}</p>
|
||||
</div>
|
||||
|
||||
<!-- Connector -->
|
||||
<div class="flex justify-center">
|
||||
<div class="w-0.5 h-3 bg-light-400"></div>
|
||||
<div class="h-3 w-0.5 bg-light-400"></div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
{#if filters.length > 0}
|
||||
<div class="rounded-lg bg-light-100 border p-3">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<div class="rounded-lg border bg-light-100 p-3">
|
||||
<div class="mb-2 flex items-center gap-2">
|
||||
<Icon icon={mdiFilterOutline} size="18" class="text-warning" />
|
||||
<Text size="tiny" fontWeight="semi-bold">{$t('filters')}</Text>
|
||||
</div>
|
||||
@@ -134,10 +134,10 @@
|
||||
{#each filters as filter, index (index)}
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="shrink-0 h-4 w-4 rounded-full bg-light-200 text-[10px] font-medium flex items-center justify-center"
|
||||
class="flex size-4 shrink-0 items-center justify-center rounded-full bg-light-200 text-[10px] font-medium"
|
||||
>{index + 1}</span
|
||||
>
|
||||
<p class="text-sm truncate">{filter.title}</p>
|
||||
<p class="truncate text-sm">{filter.title}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -145,14 +145,14 @@
|
||||
|
||||
<!-- Connector -->
|
||||
<div class="flex justify-center">
|
||||
<div class="w-0.5 h-3 bg-light-400"></div>
|
||||
<div class="h-3 w-0.5 bg-light-400"></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Actions -->
|
||||
{#if actions.length > 0}
|
||||
<div class="rounded-lg bg-light-100 border p-3">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<div class="rounded-lg border bg-light-100 p-3">
|
||||
<div class="mb-2 flex items-center gap-2">
|
||||
<Icon icon={mdiPlayCircleOutline} size="18" class="text-success" />
|
||||
<Text size="tiny" fontWeight="semi-bold">{$t('actions')}</Text>
|
||||
</div>
|
||||
@@ -160,10 +160,10 @@
|
||||
{#each actions as action, index (index)}
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="shrink-0 h-4 w-4 rounded-full bg-light-200 text-[10px] font-medium flex items-center justify-center"
|
||||
class="flex size-4 shrink-0 items-center justify-center rounded-full bg-light-200 text-[10px] font-medium"
|
||||
>{index + 1}</span
|
||||
>
|
||||
<p class="text-sm truncate">{action.title}</p>
|
||||
<p class="truncate text-sm">{action.title}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -175,7 +175,7 @@
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="hidden sm:flex fixed right-6 bottom-6 h-14 w-14 items-center justify-center rounded-full bg-primary text-light shadow-lg hover:bg-primary/90 transition-colors"
|
||||
class="fixed right-6 bottom-6 hidden size-14 items-center justify-center rounded-full bg-primary text-light shadow-lg transition-colors hover:bg-primary/90 sm:flex"
|
||||
title={$t('workflow_summary')}
|
||||
onclick={() => (isOpen = true)}
|
||||
>
|
||||
|
||||
@@ -58,15 +58,15 @@
|
||||
<button
|
||||
type="button"
|
||||
{onclick}
|
||||
class="group rounded-xl p-4 w-full text-left cursor-pointer border-2 {selected
|
||||
class="group w-full cursor-pointer rounded-xl border-2 p-4 text-left {selected
|
||||
? 'border-primary text-primary'
|
||||
: 'border-light-100 hover:border-light-200 text-light-400 hover:text-light-700'}"
|
||||
: 'border-light-100 text-light-400 hover:border-light-200 hover:text-light-700'}"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="rounded-xl p-2 {selected
|
||||
? 'bg-primary text-light'
|
||||
: 'text-light-100 bg-light-300 group-hover:bg-light-500'}"
|
||||
: 'bg-light-300 text-light-100 group-hover:bg-light-500'}"
|
||||
>
|
||||
<Icon icon={getTriggerIcon(trigger.type)} size="24" />
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</script>
|
||||
|
||||
<AuthPageLayout>
|
||||
<div class="flex flex-col place-items-center text-center gap-12">
|
||||
<div class="flex flex-col place-items-center gap-12 text-center">
|
||||
<Heading size="large" color="primary" tag="h1">{$t('welcome_to_immich')}</Heading>
|
||||
<Stack>
|
||||
<Button href={Route.register()} size="large" shape="round">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{#if downloadManager.isDownloading}
|
||||
<div
|
||||
transition:fly={{ x: -100, duration: 350 }}
|
||||
class="fixed bottom-10 start-2 max-h-67.5 w-79 z-60 rounded-2xl border dark:border-white/10 p-4 shadow-lg bg-subtle"
|
||||
class="fixed inset-s-2 bottom-10 z-60 max-h-67.5 w-79 rounded-2xl border bg-subtle p-4 shadow-lg dark:border-white/10"
|
||||
>
|
||||
<Heading size="tiny">{$t('downloading')}</Heading>
|
||||
<div class="my-2 mb-2 flex max-h-50 flex-col overflow-y-auto text-sm">
|
||||
@@ -34,14 +34,14 @@
|
||||
<div class="h-2.5 w-full rounded-full bg-neutral-200 dark:bg-neutral-600">
|
||||
<div class="h-2.5 rounded-full bg-primary" style={`width: ${download.percentage}%`}></div>
|
||||
</div>
|
||||
<p class="min-w-16 whitespace-nowrap text-right">
|
||||
<p class="min-w-16 text-right whitespace-nowrap">
|
||||
<span class="text-primary">
|
||||
{(download.percentage / 100).toLocaleString($locale, { style: 'percent' })}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute end-4">
|
||||
<div class="absolute inset-e-4">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
variant="outline"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-dvh w-dvw">
|
||||
<div class="flex h-dvh w-dvw flex-col">
|
||||
<section>
|
||||
<div class="flex place-items-center border-b px-6 py-4 dark:border-b-immich-dark-gray">
|
||||
<Link href="/photos">
|
||||
@@ -40,11 +40,11 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="flex flex-1 w-full place-content-center place-items-center overflow-hidden bg-black/30">
|
||||
<div class="flex w-full flex-1 place-content-center place-items-center overflow-hidden bg-black/30">
|
||||
<div class="max-w-[95vw]">
|
||||
<Card color="secondary">
|
||||
<CardHeader class="flex-row justify-between gap-12">
|
||||
<CardTitle tag="h1" size="medium" class="text-primary flex place-items-center gap-4">
|
||||
<CardTitle tag="h1" size="medium" class="flex place-items-center gap-4 text-primary">
|
||||
<Icon icon={mdiAlarmLight} color="red" size="32" />
|
||||
{$t('error_title')}
|
||||
</CardTitle>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</script>
|
||||
|
||||
{#if showing}
|
||||
<div class="absolute start-0 top-0 h-[3px] w-dvw bg-white">
|
||||
<div class="absolute inset-s-0 top-0 h-[3px] w-dvw bg-white">
|
||||
<span class="absolute h-[3px] bg-immich-primary" style:width={`${$progress}%`}></span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div
|
||||
in:fade={{ duration: 250 }}
|
||||
out:fade={{ duration: 100 }}
|
||||
class="flex flex-col rounded-xl text-xs p-2 gap-1 border border-gray-300 dark:border-subtle bg-primary/10"
|
||||
class="flex flex-col gap-1 rounded-xl border border-gray-300 bg-primary/10 p-2 text-xs dark:border-subtle"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center justify-center">
|
||||
@@ -93,9 +93,9 @@
|
||||
</div>
|
||||
|
||||
{#if uploadAsset.state === UploadState.STARTED}
|
||||
<div class="text-black relative mt-[5px] h-4.5 w-full rounded-md bg-gray-300 dark:bg-gray-700">
|
||||
<div class="relative mt-[5px] h-4.5 w-full rounded-md bg-gray-300 text-black dark:bg-gray-700">
|
||||
<div class="h-4.5 rounded-md bg-immich-primary transition-all" style={`width: ${uploadAsset.progress}%`}></div>
|
||||
<p class="absolute top-0.5 h-full w-full text-center text-white text-[10px]">
|
||||
<p class="absolute top-0.5 size-full text-center text-[10px] text-white">
|
||||
{#if uploadAsset.message === $t('asset_hashing')}
|
||||
{uploadAsset.message}
|
||||
{:else}
|
||||
|
||||
@@ -37,16 +37,16 @@
|
||||
}
|
||||
uploadAssetsStore.reset();
|
||||
}}
|
||||
class="fixed bottom-6 end-16"
|
||||
class="fixed inset-e-16 bottom-6"
|
||||
>
|
||||
{#if showDetail}
|
||||
<div
|
||||
in:scale={{ duration: 250, easing: quartInOut }}
|
||||
class="w-81 rounded-xl border border-gray-200 dark:border-subtle p-4 text-sm shadow-xs bg-subtle"
|
||||
class="w-81 rounded-xl border border-gray-200 bg-subtle p-4 text-sm shadow-xs dark:border-subtle"
|
||||
>
|
||||
<div class="place-item-center mb-4 flex justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<p class="immich-form-label text-xm">
|
||||
<p class="text-xm immich-form-label">
|
||||
{$t('upload_progress', {
|
||||
values: {
|
||||
remaining: $remainingUploads,
|
||||
@@ -55,7 +55,7 @@
|
||||
},
|
||||
})}
|
||||
</p>
|
||||
<p class="immich-form-label text-xs">
|
||||
<p class="text-xs immich-form-label">
|
||||
{$t('upload_status_uploaded')}
|
||||
<span class="text-success">{$stats.success.toLocaleString($locale)}</span>
|
||||
-
|
||||
@@ -101,7 +101,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if showOptions}
|
||||
<div class="immich-scrollbar mb-4 max-h-100 overflow-y-auto rounded-lg">
|
||||
<div class="mb-4 max-h-100 overflow-y-auto rounded-lg immich-scrollbar">
|
||||
<div class="flex h-6.5 place-items-center gap-1">
|
||||
<label class="immich-form-label" for="upload-concurrency">{$t('upload_concurrency')}</label>
|
||||
</div>
|
||||
@@ -119,7 +119,7 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="immich-scrollbar flex max-h-[400px] flex-col gap-2 overflow-y-auto rounded-lg">
|
||||
<div class="flex max-h-[400px] flex-col gap-2 overflow-y-auto rounded-lg immich-scrollbar">
|
||||
{#each $uploadAssetsStore as uploadAsset (uploadAsset.id)}
|
||||
<UploadAssetPreview {uploadAsset} />
|
||||
{/each}
|
||||
@@ -131,7 +131,7 @@
|
||||
type="button"
|
||||
in:scale={{ duration: 250, easing: quartInOut }}
|
||||
onclick={() => (showDetail = true)}
|
||||
class="absolute -start-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-primary p-5 text-xs text-light"
|
||||
class="absolute -inset-s-4 -top-4 flex size-10 place-content-center place-items-center rounded-full bg-primary p-5 text-xs text-light"
|
||||
>
|
||||
{$remainingUploads.toLocaleString($locale)}
|
||||
</button>
|
||||
@@ -140,7 +140,7 @@
|
||||
type="button"
|
||||
in:scale={{ duration: 250, easing: quartInOut }}
|
||||
onclick={() => (showDetail = true)}
|
||||
class="absolute -end-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-danger p-5 text-xs text-light"
|
||||
class="absolute -inset-e-4 -top-4 flex size-10 place-content-center place-items-center rounded-full bg-danger p-5 text-xs text-light"
|
||||
>
|
||||
{$stats.errors.toLocaleString($locale)}
|
||||
</button>
|
||||
@@ -149,7 +149,7 @@
|
||||
type="button"
|
||||
in:scale={{ duration: 250, easing: quartInOut }}
|
||||
onclick={() => (showDetail = true)}
|
||||
class="flex h-16 w-16 place-content-center place-items-center rounded-full bg-subtle p-5 text-sm text-primary shadow-lg"
|
||||
class="flex size-16 place-content-center place-items-center rounded-full bg-subtle p-5 text-sm text-primary shadow-lg"
|
||||
>
|
||||
<div class="animate-pulse">
|
||||
<Icon icon={mdiCloudUploadOutline} size="30" />
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
fullWidth
|
||||
text={$t('no_libraries_message')}
|
||||
onClick={() => goto(Route.newLibrary())}
|
||||
class="mt-10 mx-auto"
|
||||
class="mx-auto mt-10"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
actions={[Scan, Edit, Delete]}
|
||||
>
|
||||
<Container size="large" center>
|
||||
<div class="grid gap-4 grid-cols-1 lg:grid-cols-2 w-full">
|
||||
<div class="grid w-full grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<Heading tag="h1" size="large" class="col-span-full my-4">{library.name}</Heading>
|
||||
<div class="flex flex-col lg:flex-row gap-4 col-span-full">
|
||||
<div class="col-span-full flex flex-col gap-4 lg:flex-row">
|
||||
<ServerStatisticsCard icon={mdiCameraIris} title={$t('photos')} valuePromise={photosPromise} />
|
||||
<ServerStatisticsCard icon={mdiPlayCircle} title={$t('videos')} valuePromise={videosPromise} />
|
||||
<ServerStatisticsCard icon={mdiChartPie} title={$t('usage')} valuePromise={usagePromise} />
|
||||
@@ -86,7 +86,7 @@
|
||||
<td>
|
||||
<Code>{folder}</Code>
|
||||
</td>
|
||||
<td class="flex gap-2 justify-end">
|
||||
<td class="flex justify-end gap-2">
|
||||
<TableButton action={Edit} />
|
||||
<TableButton action={Delete} />
|
||||
</td>
|
||||
@@ -106,7 +106,7 @@
|
||||
<td>
|
||||
<Code>{exclusionPattern}</Code>
|
||||
</td>
|
||||
<td class="flex gap-2 justify-end">
|
||||
<td class="flex justify-end gap-2">
|
||||
<TableButton action={Edit} />
|
||||
<TableButton action={Delete} />
|
||||
</td>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
const commonClasses = 'flex place-items-center justify-between w-full py-2 sm:py-4 pe-4 ps-6';
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col overflow-hidden rounded-2xl bg-gray-100 dark:bg-immich-dark-gray sm:flex-row sm:rounded-9">
|
||||
<div class="sm:rounded-9 flex flex-col overflow-hidden rounded-2xl bg-gray-100 sm:flex-row dark:bg-immich-dark-gray">
|
||||
<div class="flex w-full flex-col">
|
||||
{#if queue.isPaused}
|
||||
<QueueCardBadge color="warning">{$t('paused')}</QueueCardBadge>
|
||||
@@ -94,7 +94,7 @@
|
||||
</div>
|
||||
|
||||
{#if subtitle}
|
||||
<div class="whitespace-pre-line text-sm dark:text-white">{subtitle}</div>
|
||||
<div class="text-sm whitespace-pre-line dark:text-white">{subtitle}</div>
|
||||
{/if}
|
||||
|
||||
{#if description}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
let { color, children }: Props = $props();
|
||||
|
||||
const styles = tv({
|
||||
base: 'w-full p-2 text-center text-sm ',
|
||||
base: 'w-full p-2 text-center text-sm',
|
||||
variants: {
|
||||
color: {
|
||||
success: 'bg-green-500/70 text-gray-900 dark:bg-green-700/90 dark:text-gray-100',
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
let { color, disabled = false, onClick = () => {}, children }: Props = $props();
|
||||
|
||||
const styles = tv({
|
||||
base: 'flex h-full w-full flex-col place-content-center place-items-center gap-2 px-8 py-2 text-xs text-gray-600 transition-colors dark:text-gray-200 ',
|
||||
base: 'flex size-full flex-col place-content-center place-items-center gap-2 px-8 py-2 text-xs text-gray-600 transition-colors dark:text-gray-200',
|
||||
variants: {
|
||||
color: {
|
||||
'light-gray': 'bg-gray-300/80 dark:bg-gray-700',
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-7 mt-10">
|
||||
<div class="mt-10 flex flex-col gap-7">
|
||||
{#each queueList as [queueName, props] (queueName)}
|
||||
{@const queue = queues.find(({ name }) => name === queueName)}
|
||||
{#if queue}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
>
|
||||
<div>
|
||||
<Container size="large" center>
|
||||
<div class="mb-1 mt-4 flex items-center gap-2">
|
||||
<div class="mt-4 mb-1 flex items-center gap-2">
|
||||
<Heading tag="h1" size="large">{item.title}</Heading>
|
||||
{#if queue.isPaused}
|
||||
<Badge color="warning">
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
<Text color="muted" class="mb-4">{item.subtitle}</Text>
|
||||
|
||||
<div class="flex gap-1 mb-4">
|
||||
<div class="mb-4 flex gap-1">
|
||||
<Badge>{$t('active_count', { values: { count: queue.statistics.active } })}</Badge>
|
||||
<Badge>{$t('waiting_count', { values: { count: queue.statistics.waiting + queue.statistics.paused } })}</Badge>
|
||||
{#if queue.statistics.failed > 0}
|
||||
|
||||
@@ -61,11 +61,11 @@
|
||||
<TableCell class="w-1/4"><span class="skeleton-loader inline-block h-4 w-24"></span></TableCell>
|
||||
{/snippet}
|
||||
|
||||
<div class="flex flex-col gap-5 my-4">
|
||||
<div class="my-4 flex flex-col gap-5">
|
||||
<div>
|
||||
<Text class="mb-2" fontWeight="medium">{$t('total_usage')}</Text>
|
||||
|
||||
<div class="hidden justify-between lg:flex gap-4">
|
||||
<div class="hidden justify-between gap-4 lg:flex">
|
||||
<ServerStatisticsCard icon={mdiCameraIris} title={$t('photos')} valuePromise={photosPromise} />
|
||||
<ServerStatisticsCard icon={mdiPlayCircle} title={$t('videos')} valuePromise={videosPromise} />
|
||||
<ServerStatisticsCard icon={mdiChartPie} title={$t('storage')} valuePromise={storagePromise} />
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
<div class="relative text-center font-mono text-2xl font-medium">
|
||||
{#await statsPromise}
|
||||
<span class="text-gray-300 dark:text-gray-600 shimmer-text">{zeros(0)}</span>
|
||||
<span class="shimmer-text text-gray-300 dark:text-gray-600">{zeros(0)}</span>
|
||||
{:then stats}
|
||||
<span class="text-light-300">{zeros(stats.photos)}</span><span class="text-primary">{stats.photos}</span>
|
||||
{:catch}
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
<div class="relative text-center font-mono text-2xl font-medium">
|
||||
{#await statsPromise}
|
||||
<span class="text-gray-300 dark:text-gray-600 shimmer-text">{zeros(0)}</span>
|
||||
<span class="shimmer-text text-gray-300 dark:text-gray-600">{zeros(0)}</span>
|
||||
{:then stats}
|
||||
<span class="text-light-300">{zeros(stats.videos)}</span><span class="text-primary">{stats.videos}</span>
|
||||
{:catch}
|
||||
@@ -113,15 +113,15 @@
|
||||
|
||||
<div class="relative flex text-center font-mono text-2xl font-medium">
|
||||
{#await statsPromise}
|
||||
<span class="text-gray-300 dark:text-gray-600 shimmer-text">{zeros(0)}</span>
|
||||
<span class="shimmer-text text-gray-300 dark:text-gray-600">{zeros(0)}</span>
|
||||
{:then stats}
|
||||
{@const storageUsageWithUnit = getStorageUsageWithUnit(stats.usage)}
|
||||
<span class="text-light-300">{zeros(storageUsageWithUnit[0])}</span><span class="text-primary"
|
||||
>{storageUsageWithUnit[0]}</span
|
||||
>
|
||||
|
||||
<div class="absolute -end-1.5 -bottom-4">
|
||||
<Code color="muted" class="text-xs font-light font-mono">{storageUsageWithUnit[1]}</Code>
|
||||
<div class="absolute -inset-e-1.5 -bottom-4">
|
||||
<Code color="muted" class="font-mono text-xs font-light">{storageUsageWithUnit[1]}</Code>
|
||||
</div>
|
||||
{:catch}
|
||||
<span class="text-gray-300 dark:text-gray-600">{zeros(0)}</span>
|
||||
@@ -133,7 +133,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text class="mb-2 mt-4" fontWeight="medium">{$t('user_usage_detail')}</Text>
|
||||
<Text class="mt-4 mb-2" fontWeight="medium">{$t('user_usage_detail')}</Text>
|
||||
<Table striped size="small">
|
||||
<TableHeader>
|
||||
<TableHeading class="w-1/4">{$t('user')}</TableHeading>
|
||||
|
||||
@@ -210,7 +210,7 @@
|
||||
<AdminPageLayout breadcrumbs={[{ title: data.meta.title }]} actions={[CopyToClipboard, Download, Upload]}>
|
||||
<Container size="large" center>
|
||||
{#if featureFlagsManager.value.configFile}
|
||||
<Alert color="warning" class="text-dark my-4" title={$t('admin.config_set_by_file')} />
|
||||
<Alert color="warning" class="my-4 text-dark" title={$t('admin.config_set_by_file')} />
|
||||
{/if}
|
||||
<div>
|
||||
<SearchBar placeholder={$t('search_settings')} bind:name={searchQuery} showLoadingSpinner={false} />
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
{#if configToEdit.oauth.enabled}
|
||||
<hr />
|
||||
|
||||
<div class="flex items-center gap-2 justify-between">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<Text size="small">{$t('admin.unlink_all_oauth_accounts_description')}</Text>
|
||||
<Button size="small" onclick={handleUnlinkAllOAuthAccounts}
|
||||
>{$t('admin.unlink_all_oauth_accounts')}</Button
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
isEdited={configToEdit.machineLearning.clip.modelName !== config.machineLearning.clip.modelName}
|
||||
>
|
||||
{#snippet descriptionSnippet()}
|
||||
<p class="immich-form-label pb-2 text-sm">
|
||||
<p class="pb-2 text-sm immich-form-label">
|
||||
<FormatMessage key="admin.machine_learning_clip_model_description">
|
||||
{#snippet children({ message })}
|
||||
<a target="_blank" href="https://huggingface.co/immich-app"><u>{message}</u></a>
|
||||
@@ -259,7 +259,7 @@
|
||||
title={$t('admin.machine_learning_ocr')}
|
||||
subtitle={$t('admin.machine_learning_ocr_description')}
|
||||
>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<div class="mt-4 ml-4 flex flex-col gap-4">
|
||||
<SettingSwitch
|
||||
title={$t('admin.machine_learning_ocr_enabled')}
|
||||
subtitle={$t('admin.machine_learning_ocr_enabled_description')}
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
isEdited={configToEdit.notifications.smtp.from !== config.notifications.smtp.from}
|
||||
/>
|
||||
|
||||
<div class="flex gap-2 place-items-center">
|
||||
<div class="flex place-items-center gap-2">
|
||||
<Button
|
||||
size="small"
|
||||
shape="round"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<div>
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" onsubmit={(event) => event.preventDefault()}>
|
||||
<div class="mt-4 ms-4">
|
||||
<div class="ms-4 mt-4">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.server_external_domain_settings')}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<div class="mb-4 w-full">
|
||||
<div class="flex h-6.5 place-items-center gap-1">
|
||||
<label class="font-medium text-primary text-sm" for="{name}-select">
|
||||
<label class="text-sm font-medium text-primary" for="{name}-select">
|
||||
{label}
|
||||
</label>
|
||||
|
||||
@@ -46,13 +46,13 @@
|
||||
</div>
|
||||
|
||||
{#if desc}
|
||||
<p class="immich-form-label pb-2 text-sm" id="{name}-desc">
|
||||
<p class="pb-2 text-sm immich-form-label" id="{name}-desc">
|
||||
{desc}
|
||||
</p>
|
||||
{/if}
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each options as option (option.value)}
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
size="tiny"
|
||||
id="{option.value}-checkbox"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<div class="mb-4 w-full">
|
||||
<div class="flex h-6.5 place-items-center gap-1">
|
||||
<label class="font-medium text-primary text-sm" for="{name}-select">{label}</label>
|
||||
<label class="text-sm font-medium text-primary" for="{name}-select">{label}</label>
|
||||
|
||||
{#if isEdited}
|
||||
<div
|
||||
@@ -53,7 +53,7 @@
|
||||
</div>
|
||||
|
||||
{#if desc}
|
||||
<p class="immich-form-label pb-2 text-sm" id="{name}-desc">
|
||||
<p class="pb-2 text-sm immich-form-label" id="{name}-desc">
|
||||
{desc}
|
||||
</p>
|
||||
{/if}
|
||||
@@ -63,12 +63,12 @@
|
||||
icon={mdiChevronDown}
|
||||
size="1.2em"
|
||||
aria-hidden
|
||||
class="pointer-events-none end-1 relative col-start-1 row-start-1 self-center justify-self-end {disabled
|
||||
class="pointer-events-none relative inset-e-1 col-start-1 row-start-1 self-center justify-self-end {disabled
|
||||
? 'text-immich-bg'
|
||||
: 'text-immich-fg dark:text-immich-bg'}"
|
||||
/>
|
||||
<select
|
||||
class="immich-form-input w-full appearance-none row-start-1 col-start-1 pe-6!"
|
||||
class="col-start-1 row-start-1 immich-form-input w-full appearance-none pe-6!"
|
||||
{disabled}
|
||||
aria-describedby={desc ? `${name}-desc` : undefined}
|
||||
{name}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<div class="mb-4 w-full">
|
||||
<div class="flex h-6.5 place-items-center gap-1">
|
||||
<label class="font-medium text-primary text-sm" for={label}>{label}</label>
|
||||
<label class="text-sm font-medium text-primary" for={label}>{label}</label>
|
||||
{#if required}
|
||||
<div class="text-red-400">*</div>
|
||||
{/if}
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
|
||||
{#if description}
|
||||
<p class="immich-form-label pb-2 text-sm" id="{label}-desc">
|
||||
<p class="pb-2 text-sm immich-form-label" id="{label}-desc">
|
||||
{description}
|
||||
</p>
|
||||
{:else}
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</Field>
|
||||
|
||||
<Field label={$t('admin.require_password_change_on_login')}>
|
||||
<Switch id="require-password-change" bind:checked={shouldChangePassword} class="text-sm text-start" />
|
||||
<Switch id="require-password-change" bind:checked={shouldChangePassword} class="text-start text-sm" />
|
||||
</Field>
|
||||
|
||||
<Field label={$t('name')} required>
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
<Alert color="danger" class="my-4" title={$t('user_has_been_deleted')} icon={mdiTrashCanOutline} />
|
||||
{/if}
|
||||
|
||||
<div class="grid gap-4 grid-cols-1 lg:grid-cols-2 w-full">
|
||||
<div class="col-span-full flex flex-col gap-4 my-4">
|
||||
<div class="grid w-full grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<div class="col-span-full my-4 flex flex-col gap-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<UserAvatar {user} size="md" />
|
||||
<Heading tag="h1" size="large">{user.name}</Heading>
|
||||
@@ -114,7 +114,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-full">
|
||||
<div class="flex flex-col lg:flex-row gap-4 w-full">
|
||||
<div class="flex w-full flex-col gap-4 lg:flex-row">
|
||||
<ServerStatisticsCard
|
||||
icon={mdiCameraIris}
|
||||
title={$t('photos')}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
let { title, state }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center justify-between">
|
||||
<Text class="text-sm" fontWeight="medium">{title}</Text>
|
||||
<Icon icon={state ? mdiCheck : mdiClose} class={state ? 'text-primary' : 'text-danger'} size="24" />
|
||||
</div>
|
||||
|
||||
@@ -155,10 +155,10 @@
|
||||
|
||||
{#if featureFlagsManager.value.oauth}
|
||||
{#if featureFlagsManager.value.passwordLogin}
|
||||
<div class="inline-flex w-full items-center justify-center my-4">
|
||||
<div class="my-4 inline-flex w-full items-center justify-center">
|
||||
<hr class="my-4 h-px w-3/4 border-0 bg-gray-200 dark:bg-gray-600" />
|
||||
<span
|
||||
class="absolute start-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white uppercase"
|
||||
class="absolute inset-s-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 uppercase dark:bg-neutral-900 dark:text-white"
|
||||
>
|
||||
{$t('or')}
|
||||
</span>
|
||||
|
||||
@@ -160,15 +160,15 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<section id="onboarding-page" class="min-w-dvw flex min-h-dvh p-4">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" bg-gray-300 dark:bg-gray-600 rounded-md h-2">
|
||||
<section id="onboarding-page" class="flex min-h-dvh min-w-dvw p-4">
|
||||
<div class="flex w-full flex-col">
|
||||
<div class="h-2 rounded-md bg-gray-300 dark:bg-gray-600">
|
||||
<div
|
||||
class="progress-bar bg-primary h-2 rounded-md transition-all duration-200 ease-out"
|
||||
class="progress-bar h-2 rounded-md bg-primary transition-all duration-200 ease-out"
|
||||
style="width: {(onboardingProgress / onboardingStepCount) * 100}%"
|
||||
></div>
|
||||
</div>
|
||||
<div class="py-8 flex place-content-center place-items-center m-auto w-[min(100%,800px)]">
|
||||
<div class="m-auto flex w-[min(100%,800px)] place-content-center place-items-center py-8">
|
||||
<OnboardingCard
|
||||
title={onboardingSteps[index].title}
|
||||
icon={onboardingSteps[index].icon}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Stack gap={2}>
|
||||
<div class="flex items-start gap-4 p-6 my-10 bg-gray-100 dark:bg-gray-800/40 rounded-xl border border-gray-700/50">
|
||||
<Icon icon={mdiAlertCircleOutline} size="36" class="text-warning shrink-0 mt-0.5" />
|
||||
<div class="text-gray-800 dark:text-gray-300 leading-relaxed">
|
||||
<div class="my-10 flex items-start gap-4 rounded-xl border border-gray-700/50 bg-gray-100 p-6 dark:bg-gray-800/40">
|
||||
<Icon icon={mdiAlertCircleOutline} size="36" class="mt-0.5 shrink-0 text-warning" />
|
||||
<div class="leading-relaxed text-gray-800 dark:text-gray-300">
|
||||
<FormatMessage key="admin.backup_onboarding_description">
|
||||
{#snippet children({ message })}
|
||||
<Link href="https://www.backblaze.com/blog/the-3-2-1-backup-strategy/">
|
||||
@@ -32,10 +32,10 @@
|
||||
<div class="space-y-6">
|
||||
{#each messageKeys as keyString, index (index)}
|
||||
<div class="flex items-start gap-6">
|
||||
<div class="shrink-0 w-12 h-12 rounded-full bg-primary/90 flex items-center justify-center">
|
||||
<span class="text-light text-xl font-semibold">{3 - index}</span>
|
||||
<div class="flex size-12 shrink-0 items-center justify-center rounded-full bg-primary/90">
|
||||
<span class="text-xl font-semibold text-light">{3 - index}</span>
|
||||
</div>
|
||||
<div class="leading-relaxed pt-2">
|
||||
<div class="pt-2 leading-relaxed">
|
||||
<FormatMessage key={keyString as Translations} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 leading-relaxed mt-4">
|
||||
<div class="mt-4 text-sm/relaxed text-gray-600 dark:text-gray-400">
|
||||
<p>
|
||||
<FormatMessage key="admin.backup_onboarding_footer">
|
||||
{#snippet children({ message })}
|
||||
|
||||
@@ -30,16 +30,16 @@
|
||||
|
||||
<div
|
||||
id="onboarding-card"
|
||||
class="flex w-full max-w-4xl flex-col gap-4 rounded-3xl border-2 border-gray-500 px-8 py-8 dark:border-gray-700 dark:bg-immich-dark-gray text-black dark:text-immich-dark-fg bg-gray-50"
|
||||
class="flex w-full max-w-4xl flex-col gap-4 rounded-3xl border-2 border-gray-500 bg-gray-50 p-8 text-black dark:border-gray-700 dark:bg-immich-dark-gray dark:text-immich-dark-fg"
|
||||
in:fade={{ duration: 250 }}
|
||||
>
|
||||
{#if title || icon}
|
||||
<div class="flex gap-2 items-center justify-center w-fit">
|
||||
<div class="flex w-fit items-center justify-center gap-2">
|
||||
{#if icon}
|
||||
<Icon {icon} size="30" class="text-primary" />
|
||||
{/if}
|
||||
{#if title}
|
||||
<p class="text-xl text-primary font-medium">
|
||||
<p class="text-xl font-medium text-primary">
|
||||
{title}
|
||||
</p>
|
||||
{/if}
|
||||
@@ -49,11 +49,11 @@
|
||||
|
||||
<div class="flex pt-4">
|
||||
{#if previousTitle}
|
||||
<div class="w-full flex place-content-start">
|
||||
<div class="flex w-full place-content-start">
|
||||
<Button
|
||||
shape="round"
|
||||
leadingIcon={mdiArrowLeft}
|
||||
class="flex gap-2 place-content-center"
|
||||
class="flex place-content-center gap-2"
|
||||
onclick={() => {
|
||||
onLeave?.();
|
||||
onPrevious?.();
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
<div class="gap-4">
|
||||
<Logo variant="icon" size="giant" class="mb-2" />
|
||||
<p class="font-medium mb-6 text-6xl text-primary">
|
||||
<p class="mb-6 text-6xl font-medium text-primary">
|
||||
{$t('onboarding_welcome_user', { values: { user: authManager.user.name } })}
|
||||
</p>
|
||||
<p class="text-3xl pb-6 font-light">
|
||||
<p class="pb-6 text-3xl font-light">
|
||||
{userRole == OnboardingRole.SERVER
|
||||
? $t('onboarding_server_welcome_description')
|
||||
: $t('onboarding_user_welcome_description')}
|
||||
|
||||
@@ -10,26 +10,24 @@
|
||||
<div class="flex gap-4">
|
||||
<button
|
||||
type="button"
|
||||
class="w-1/2 aspect-square bg-light dark:bg-dark rounded-3xl transition-all shadow-sm hover:shadow-xl border-[3px] border-immich-primary dark:border dark:border-transparent"
|
||||
class="aspect-square w-1/2 rounded-3xl border-[3px] border-immich-primary bg-light shadow-sm transition-all hover:shadow-xl dark:border dark:border-transparent dark:bg-dark"
|
||||
onclick={() => themeManager.setPreference(ThemePreference.Light)}
|
||||
>
|
||||
<div
|
||||
class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-primary"
|
||||
>
|
||||
<div class="flex size-full flex-col place-content-center place-items-center justify-around text-immich-primary">
|
||||
<Icon icon={sunPath} viewBox={sunViewBox} size="96" />
|
||||
<p class="font-semibold text-4xl">{$t('light')}</p>
|
||||
<p class="text-4xl font-semibold">{$t('light')}</p>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="w-1/2 aspect-square bg-dark dark:bg-light rounded-3xl transition-all shadow-sm hover:shadow-xl dark:border-[3px] dark:border-immich-dark-primary border border-transparent"
|
||||
class="aspect-square w-1/2 rounded-3xl border border-transparent bg-dark shadow-sm transition-all hover:shadow-xl dark:border-[3px] dark:border-immich-dark-primary dark:bg-light"
|
||||
onclick={() => themeManager.setPreference(ThemePreference.Dark)}
|
||||
>
|
||||
<div
|
||||
class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-dark-primary"
|
||||
class="flex size-full flex-col place-content-center place-items-center justify-around text-immich-dark-primary"
|
||||
>
|
||||
<Icon icon={moonPath} viewBox={moonViewBox} size="96" />
|
||||
<p class="font-semibold text-4xl">{$t('dark')}</p>
|
||||
<p class="text-4xl font-semibold">{$t('dark')}</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<AuthPageLayout withHeader={false}>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="w-96 flex flex-col gap-6 items-center justify-center">
|
||||
<div class="flex w-96 flex-col items-center justify-center gap-6">
|
||||
{#if hasPinCode}
|
||||
{#if isVerified}
|
||||
<div in:fade={{ duration: 200 }}>
|
||||
@@ -59,7 +59,7 @@
|
||||
<div class="text-primary">
|
||||
<Icon icon={mdiLockSmart} size="64" />
|
||||
</div>
|
||||
<p class="text-center text-sm mb-4" style="text-wrap: pretty;">
|
||||
<p class="mb-4 text-center text-sm" style="text-wrap: pretty;">
|
||||
{$t('new_pin_code_subtitle')}
|
||||
</p>
|
||||
<PinCodeCreateForm showLabel={false} onCreated={() => (hasPinCode = true)} />
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
withHeader={$status?.action === MaintenanceAction.Start || $status?.action === MaintenanceAction.End}
|
||||
withBackdrop={$status?.action === MaintenanceAction.Start}
|
||||
>
|
||||
<div class="flex flex-col place-items-center text-center gap-8">
|
||||
<div class="flex flex-col place-items-center gap-8 text-center">
|
||||
{#if $status?.action === MaintenanceAction.RestoreDatabase}
|
||||
<Heading size="large" color="primary" tag="h1">{$t('maintenance_action_restore')}</Heading>
|
||||
{#if $status.error}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user