mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	chore(web): sort tailwindcss class automatically (#3330)
This commit is contained in:
		
							parent
							
								
									f28fc8fa5c
								
							
						
					
					
						commit
						7316ad5a72
					
				
							
								
								
									
										82
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										82
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -53,6 +53,7 @@
 | 
			
		||||
        "postcss": "^8.4.21",
 | 
			
		||||
        "prettier": "^2.8.4",
 | 
			
		||||
        "prettier-plugin-svelte": "^2.10.1",
 | 
			
		||||
        "prettier-plugin-tailwindcss": "^0.4.1",
 | 
			
		||||
        "svelte": "^4.0.5",
 | 
			
		||||
        "svelte-check": "^3.4.3",
 | 
			
		||||
        "svelte-jester": "^2.3.2",
 | 
			
		||||
@ -10146,6 +10147,80 @@
 | 
			
		||||
        "svelte": "^3.2.0 || ^4.0.0-next.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/prettier-plugin-tailwindcss": {
 | 
			
		||||
      "version": "0.4.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.4.1.tgz",
 | 
			
		||||
      "integrity": "sha512-hwn2EiJmv8M+AW4YDkbjJ6HlZCTzLyz1QlySn9sMuKV/Px0fjwldlB7tol8GzdgqtkdPtzT3iJ4UzdnYXP25Ag==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12.17.0"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "@ianvs/prettier-plugin-sort-imports": "*",
 | 
			
		||||
        "@prettier/plugin-pug": "*",
 | 
			
		||||
        "@shopify/prettier-plugin-liquid": "*",
 | 
			
		||||
        "@shufo/prettier-plugin-blade": "*",
 | 
			
		||||
        "@trivago/prettier-plugin-sort-imports": "*",
 | 
			
		||||
        "prettier": "^2.2 || ^3.0",
 | 
			
		||||
        "prettier-plugin-astro": "*",
 | 
			
		||||
        "prettier-plugin-css-order": "*",
 | 
			
		||||
        "prettier-plugin-import-sort": "*",
 | 
			
		||||
        "prettier-plugin-jsdoc": "*",
 | 
			
		||||
        "prettier-plugin-marko": "*",
 | 
			
		||||
        "prettier-plugin-organize-attributes": "*",
 | 
			
		||||
        "prettier-plugin-organize-imports": "*",
 | 
			
		||||
        "prettier-plugin-style-order": "*",
 | 
			
		||||
        "prettier-plugin-svelte": "*",
 | 
			
		||||
        "prettier-plugin-twig-melody": "*"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependenciesMeta": {
 | 
			
		||||
        "@ianvs/prettier-plugin-sort-imports": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "@prettier/plugin-pug": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "@shopify/prettier-plugin-liquid": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "@shufo/prettier-plugin-blade": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "@trivago/prettier-plugin-sort-imports": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-astro": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-css-order": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-import-sort": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-jsdoc": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-marko": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-organize-attributes": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-organize-imports": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-style-order": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-svelte": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        },
 | 
			
		||||
        "prettier-plugin-twig-melody": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/pretty-format": {
 | 
			
		||||
      "version": "27.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
 | 
			
		||||
@ -19248,6 +19323,13 @@
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {}
 | 
			
		||||
    },
 | 
			
		||||
    "prettier-plugin-tailwindcss": {
 | 
			
		||||
      "version": "0.4.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.4.1.tgz",
 | 
			
		||||
      "integrity": "sha512-hwn2EiJmv8M+AW4YDkbjJ6HlZCTzLyz1QlySn9sMuKV/Px0fjwldlB7tol8GzdgqtkdPtzT3iJ4UzdnYXP25Ag==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {}
 | 
			
		||||
    },
 | 
			
		||||
    "pretty-format": {
 | 
			
		||||
      "version": "27.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,7 @@
 | 
			
		||||
    "postcss": "^8.4.21",
 | 
			
		||||
    "prettier": "^2.8.4",
 | 
			
		||||
    "prettier-plugin-svelte": "^2.10.1",
 | 
			
		||||
    "prettier-plugin-tailwindcss": "^0.4.1",
 | 
			
		||||
    "svelte": "^4.0.5",
 | 
			
		||||
    "svelte-check": "^3.4.3",
 | 
			
		||||
    "svelte-jester": "^2.3.2",
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ input:focus-visible {
 | 
			
		||||
 | 
			
		||||
@layer utilities {
 | 
			
		||||
  .immich-form-input {
 | 
			
		||||
    @apply bg-slate-200 p-4 rounded-xl focus:border-immich-primary text-sm dark:bg-gray-600 dark:text-immich-dark-fg disabled:bg-gray-400 dark:disabled:bg-gray-800 disabled:cursor-not-allowed disabled:text-gray-200;
 | 
			
		||||
    @apply rounded-xl bg-slate-200 p-4 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-200 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .immich-form-label {
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<button
 | 
			
		||||
  class="h-full w-full py-2 flex gap-2 flex-col place-items-center place-content-center px-8 text-gray-600 transition-colors hover:bg-immich-primary hover:text-white dark:text-gray-200 dark:hover:bg-immich-dark-primary text-xs dark:hover:text-black {colorClasses[
 | 
			
		||||
  class="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 hover:bg-immich-primary hover:text-white dark:text-gray-200 dark:hover:bg-immich-dark-primary dark:hover:text-black {colorClasses[
 | 
			
		||||
    color
 | 
			
		||||
  ]}"
 | 
			
		||||
  on:click
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,6 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="w-full text-center text-sm p-2 {colorClasses[color]}">
 | 
			
		||||
<div class="w-full p-2 text-center text-sm {colorClasses[color]}">
 | 
			
		||||
  <slot />
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -34,9 +34,9 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
  class="flex sm:flex-row flex-col bg-gray-100 dark:bg-immich-dark-gray rounded-2xl sm:rounded-[35px] overflow-hidden"
 | 
			
		||||
  class="flex flex-col overflow-hidden rounded-2xl bg-gray-100 dark:bg-immich-dark-gray sm:flex-row sm:rounded-[35px]"
 | 
			
		||||
>
 | 
			
		||||
  <div class="flex flex-col w-full">
 | 
			
		||||
  <div class="flex w-full flex-col">
 | 
			
		||||
    {#if queueStatus.isPaused}
 | 
			
		||||
      <JobTileStatus color="warning">Paused</JobTileStatus>
 | 
			
		||||
    {:else if queueStatus.isActive}
 | 
			
		||||
@ -44,8 +44,8 @@
 | 
			
		||||
    {/if}
 | 
			
		||||
    <div class="flex flex-col gap-2 p-5 sm:p-7 md:p-9">
 | 
			
		||||
      <div class="flex items-center gap-4 text-xl font-semibold text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        <span class="flex gap-2 items-center">
 | 
			
		||||
          <svelte:component this={icon} size="1.25em" class="shrink-0 hidden sm:block" />
 | 
			
		||||
        <span class="flex items-center gap-2">
 | 
			
		||||
          <svelte:component this={icon} size="1.25em" class="hidden shrink-0 sm:block" />
 | 
			
		||||
          {title.toUpperCase()}
 | 
			
		||||
        </span>
 | 
			
		||||
        <div class="flex gap-2">
 | 
			
		||||
@ -63,7 +63,7 @@
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      {#if subtitle}
 | 
			
		||||
        <div class="text-sm dark:text-white whitespace-pre-line">{subtitle}</div>
 | 
			
		||||
        <div class="whitespace-pre-line text-sm dark:text-white">{subtitle}</div>
 | 
			
		||||
      {/if}
 | 
			
		||||
 | 
			
		||||
      {#if slots?.description}
 | 
			
		||||
@ -72,9 +72,9 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      {/if}
 | 
			
		||||
 | 
			
		||||
      <div class="flex w-full max-w-md mt-2 flex-col sm:flex-row">
 | 
			
		||||
      <div class="mt-2 flex w-full max-w-md flex-col sm:flex-row">
 | 
			
		||||
        <div
 | 
			
		||||
          class="{commonClasses} bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-immich-dark-gray rounded-t-lg sm:rounded-l-lg sm:rounded-r-none"
 | 
			
		||||
          class="{commonClasses} rounded-t-lg bg-immich-primary text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray sm:rounded-l-lg sm:rounded-r-none"
 | 
			
		||||
        >
 | 
			
		||||
          <p>Active</p>
 | 
			
		||||
          <p class="text-2xl">
 | 
			
		||||
@ -83,7 +83,7 @@
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div
 | 
			
		||||
          class="{commonClasses} bg-gray-200 text-immich-dark-bg dark:bg-gray-700 dark:text-immich-gray rounded-b-lg sm:rounded-r-lg sm:rounded-l-none flex-row-reverse"
 | 
			
		||||
          class="{commonClasses} flex-row-reverse rounded-b-lg bg-gray-200 text-immich-dark-bg dark:bg-gray-700 dark:text-immich-gray sm:rounded-l-none sm:rounded-r-lg"
 | 
			
		||||
        >
 | 
			
		||||
          <p class="text-2xl">
 | 
			
		||||
            {waitingCount.toLocaleString($locale)}
 | 
			
		||||
@ -93,7 +93,7 @@
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="flex sm:flex-col flex-row sm:w-32 w-full overflow-hidden">
 | 
			
		||||
  <div class="flex w-full flex-row overflow-hidden sm:w-32 sm:flex-col">
 | 
			
		||||
    {#if !isIdle}
 | 
			
		||||
      {#if waitingCount > 0}
 | 
			
		||||
        <JobTileButton color="gray" on:click={() => dispatch('command', { command: JobCommand.Empty, force: false })}>
 | 
			
		||||
 | 
			
		||||
@ -29,20 +29,20 @@
 | 
			
		||||
  <div>
 | 
			
		||||
    <p class="text-sm dark:text-immich-dark-fg">TOTAL USAGE</p>
 | 
			
		||||
 | 
			
		||||
    <div class="mt-5 justify-between lg:flex hidden">
 | 
			
		||||
    <div class="mt-5 hidden justify-between lg:flex">
 | 
			
		||||
      <StatsCard logo={CameraIris} title="PHOTOS" value={stats.photos} />
 | 
			
		||||
      <StatsCard logo={PlayCircle} title="VIDEOS" value={stats.videos} />
 | 
			
		||||
      <StatsCard logo={Memory} title="STORAGE" value={statsUsage} unit={statsUsageUnit} />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="mt-5 lg:hidden flex">
 | 
			
		||||
      <div class="bg-immich-gray dark:bg-immich-dark-gray rounded-3xl p-5 flex flex-col justify-between">
 | 
			
		||||
    <div class="mt-5 flex lg:hidden">
 | 
			
		||||
      <div class="flex flex-col justify-between rounded-3xl bg-immich-gray p-5 dark:bg-immich-dark-gray">
 | 
			
		||||
        <div class="flex flex-wrap gap-x-12">
 | 
			
		||||
          <div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
            <CameraIris size="25" />
 | 
			
		||||
            <p>PHOTOS</p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="relative text-center font-mono font-semibold text-2xl">
 | 
			
		||||
          <div class="relative text-center font-mono text-2xl font-semibold">
 | 
			
		||||
            <span class="text-[#DCDADA] dark:text-[#525252]">{zeros(stats.photos)}</span><span
 | 
			
		||||
              class="text-immich-primary dark:text-immich-dark-primary">{stats.photos}</span
 | 
			
		||||
            >
 | 
			
		||||
@ -54,7 +54,7 @@
 | 
			
		||||
            <p>VIDEOS</p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="relative text-center font-mono font-semibold text-2xl">
 | 
			
		||||
          <div class="relative text-center font-mono text-2xl font-semibold">
 | 
			
		||||
            <span class="text-[#DCDADA] dark:text-[#525252]">{zeros(stats.videos)}</span><span
 | 
			
		||||
              class="text-immich-primary dark:text-immich-dark-primary">{stats.videos}</span
 | 
			
		||||
            >
 | 
			
		||||
@ -66,11 +66,11 @@
 | 
			
		||||
            <p>STORAGE</p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="relative text-center font-mono font-semibold text-2xl flex">
 | 
			
		||||
          <div class="relative flex text-center font-mono text-2xl font-semibold">
 | 
			
		||||
            <span class="text-[#DCDADA] dark:text-[#525252]">{zeros(statsUsage)}</span><span
 | 
			
		||||
              class="text-immich-primary dark:text-immich-dark-primary">{statsUsage}</span
 | 
			
		||||
            >
 | 
			
		||||
            <span class="text-center my-auto ml-2 text-base font-light text-gray-400">{statsUsageUnit}</span>
 | 
			
		||||
            <span class="my-auto ml-2 text-center text-base font-light text-gray-400">{statsUsageUnit}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
@ -79,28 +79,28 @@
 | 
			
		||||
 | 
			
		||||
  <div>
 | 
			
		||||
    <p class="text-sm dark:text-immich-dark-fg">USER USAGE DETAIL</p>
 | 
			
		||||
    <table class="text-left w-full mt-5">
 | 
			
		||||
    <table class="mt-5 w-full text-left">
 | 
			
		||||
      <thead
 | 
			
		||||
        class="border rounded-md mb-4 bg-gray-50 dark:bg-immich-dark-gray dark:border-immich-dark-gray flex text-immich-primary dark:text-immich-dark-primary w-full h-12"
 | 
			
		||||
        class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
 | 
			
		||||
      >
 | 
			
		||||
        <tr class="flex w-full place-items-center">
 | 
			
		||||
          <th class="text-center w-1/4 font-medium text-sm">User</th>
 | 
			
		||||
          <th class="text-center w-1/4 font-medium text-sm">Photos</th>
 | 
			
		||||
          <th class="text-center w-1/4 font-medium text-sm">Videos</th>
 | 
			
		||||
          <th class="text-center w-1/4 font-medium text-sm">Size</th>
 | 
			
		||||
          <th class="w-1/4 text-center text-sm font-medium">User</th>
 | 
			
		||||
          <th class="w-1/4 text-center text-sm font-medium">Photos</th>
 | 
			
		||||
          <th class="w-1/4 text-center text-sm font-medium">Videos</th>
 | 
			
		||||
          <th class="w-1/4 text-center text-sm font-medium">Size</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody
 | 
			
		||||
        class="overflow-y-auto rounded-md w-full max-h-[320px] block border dark:border-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
        class="block max-h-[320px] w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
      >
 | 
			
		||||
        {#each stats.usageByUser as user (user.userId)}
 | 
			
		||||
          <tr
 | 
			
		||||
            class="text-center flex place-items-center w-full h-[50px] even:bg-immich-bg even:dark:bg-immich-dark-gray/50 odd:bg-immich-gray odd:dark:bg-immich-dark-gray/75"
 | 
			
		||||
            class="flex h-[50px] w-full place-items-center text-center odd:bg-immich-gray even:bg-immich-bg odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50"
 | 
			
		||||
          >
 | 
			
		||||
            <td class="text-sm px-2 w-1/4 text-ellipsis">{user.userFirstName} {user.userLastName}</td>
 | 
			
		||||
            <td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos.toLocaleString($locale)}</td>
 | 
			
		||||
            <td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos.toLocaleString($locale)}</td>
 | 
			
		||||
            <td class="text-sm px-2 w-1/4 text-ellipsis">{asByteUnitString(user.usage, $locale)}</td>
 | 
			
		||||
            <td class="w-1/4 text-ellipsis px-2 text-sm">{user.userFirstName} {user.userLastName}</td>
 | 
			
		||||
            <td class="w-1/4 text-ellipsis px-2 text-sm">{user.photos.toLocaleString($locale)}</td>
 | 
			
		||||
            <td class="w-1/4 text-ellipsis px-2 text-sm">{user.videos.toLocaleString($locale)}</td>
 | 
			
		||||
            <td class="w-1/4 text-ellipsis px-2 text-sm">{asByteUnitString(user.usage, $locale)}</td>
 | 
			
		||||
          </tr>
 | 
			
		||||
        {/each}
 | 
			
		||||
      </tbody>
 | 
			
		||||
 | 
			
		||||
@ -15,13 +15,13 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="w-[250px] h-[140px] bg-immich-gray dark:bg-immich-dark-gray rounded-3xl p-5 flex flex-col justify-between">
 | 
			
		||||
<div class="flex h-[140px] w-[250px] flex-col justify-between rounded-3xl bg-immich-gray p-5 dark:bg-immich-dark-gray">
 | 
			
		||||
  <div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
    <svelte:component this={logo} size="40" />
 | 
			
		||||
    <p>{title}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="relative text-center font-mono font-semibold text-2xl">
 | 
			
		||||
  <div class="relative text-center font-mono text-2xl font-semibold">
 | 
			
		||||
    <span class="text-[#DCDADA] dark:text-[#525252]">{zeros()}</span><span
 | 
			
		||||
      class="text-immich-primary dark:text-immich-dark-primary">{value}</span
 | 
			
		||||
    >
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@
 | 
			
		||||
  {#await getConfigs() then}
 | 
			
		||||
    <div in:fade={{ duration: 500 }}>
 | 
			
		||||
      <form autocomplete="off" on:submit|preventDefault>
 | 
			
		||||
        <div class="flex flex-col gap-4 ml-4 mt-4">
 | 
			
		||||
        <div class="ml-4 mt-4 flex flex-col gap-4">
 | 
			
		||||
          <SettingInputField
 | 
			
		||||
            inputType={SettingInputFieldType.NUMBER}
 | 
			
		||||
            label="CONSTANT RATE FACTOR (-crf)"
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@
 | 
			
		||||
    <div in:fade={{ duration: 500 }}>
 | 
			
		||||
      <form autocomplete="off" on:submit|preventDefault>
 | 
			
		||||
        {#each jobNames as jobName}
 | 
			
		||||
          <div class="flex flex-col gap-4 ml-4 mt-4">
 | 
			
		||||
          <div class="ml-4 mt-4 flex flex-col gap-4">
 | 
			
		||||
            <SettingInputField
 | 
			
		||||
              inputType={SettingInputFieldType.NUMBER}
 | 
			
		||||
              label="{api.getJobName(jobName)} Concurrency"
 | 
			
		||||
 | 
			
		||||
@ -107,7 +107,7 @@
 | 
			
		||||
<div class="mt-2">
 | 
			
		||||
  {#await getConfigs() then}
 | 
			
		||||
    <div in:fade={{ duration: 500 }}>
 | 
			
		||||
      <form autocomplete="off" on:submit|preventDefault class="flex flex-col mx-4 gap-4 py-4">
 | 
			
		||||
      <form autocomplete="off" on:submit|preventDefault class="mx-4 flex flex-col gap-4 py-4">
 | 
			
		||||
        <p class="text-sm dark:text-immich-dark-fg">
 | 
			
		||||
          For more details about this feature, refer to the <a
 | 
			
		||||
            href="http://immich.app/docs/administration/oauth#mobile-redirect-uri"
 | 
			
		||||
 | 
			
		||||
@ -96,7 +96,7 @@
 | 
			
		||||
  {#await getConfigs() then}
 | 
			
		||||
    <div in:fade={{ duration: 500 }}>
 | 
			
		||||
      <form autocomplete="off" on:submit|preventDefault>
 | 
			
		||||
        <div class="flex flex-col gap-4 ml-4 mt-4">
 | 
			
		||||
        <div class="ml-4 mt-4 flex flex-col gap-4">
 | 
			
		||||
          <div class="ml-4">
 | 
			
		||||
            <SettingSwitch
 | 
			
		||||
              title="ENABLED"
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,8 @@
 | 
			
		||||
  const toggle = () => (isOpen = !isOpen);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="border-b-[1px] border-gray-200 dark:border-gray-700 py-4">
 | 
			
		||||
  <div class="flex justify-between place-items-center">
 | 
			
		||||
<div class="border-b-[1px] border-gray-200 py-4 dark:border-gray-700">
 | 
			
		||||
  <div class="flex place-items-center justify-between">
 | 
			
		||||
    <div>
 | 
			
		||||
      <h2 class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        {title}
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
    <button
 | 
			
		||||
      on:click={toggle}
 | 
			
		||||
      aria-expanded={isOpen}
 | 
			
		||||
      class="immich-circle-icon-button hover:bg-immich-primary/10 dark:text-immich-dark-fg hover:dark:bg-immich-dark-primary/20 rounded-full p-3 flex place-items-center place-content-center transition-all"
 | 
			
		||||
      class="immich-circle-icon-button flex place-content-center place-items-center rounded-full p-3 transition-all hover:bg-immich-primary/10 dark:text-immich-dark-fg hover:dark:bg-immich-dark-primary/20"
 | 
			
		||||
    >
 | 
			
		||||
      <svg
 | 
			
		||||
        style="tran"
 | 
			
		||||
 | 
			
		||||
@ -7,12 +7,12 @@
 | 
			
		||||
  export let showResetToDefault = true;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="flex justify-between gap-2 mt-8">
 | 
			
		||||
<div class="mt-8 flex justify-between gap-2">
 | 
			
		||||
  <div class="left">
 | 
			
		||||
    {#if showResetToDefault}
 | 
			
		||||
      <button
 | 
			
		||||
        on:click={() => dispatch('reset-to-default')}
 | 
			
		||||
        class="text-sm dark:text-immich-dark-primary hover:dark:text-immich-dark-primary/75 text-immich-primary hover:text-immich-primary/75 font-medium bg-none"
 | 
			
		||||
        class="bg-none text-sm font-medium text-immich-primary hover:text-immich-primary/75 dark:text-immich-dark-primary hover:dark:text-immich-dark-primary/75"
 | 
			
		||||
      >
 | 
			
		||||
        Reset to default
 | 
			
		||||
      </button>
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="w-full">
 | 
			
		||||
  <div class={`flex place-items-center gap-1 h-[26px]`}>
 | 
			
		||||
  <div class={`flex h-[26px] place-items-center gap-1`}>
 | 
			
		||||
    <label class={`immich-form-label text-sm`} for={label}>{label}</label>
 | 
			
		||||
    {#if required}
 | 
			
		||||
      <div class="text-red-400">*</div>
 | 
			
		||||
@ -37,7 +37,7 @@
 | 
			
		||||
    {#if isEdited}
 | 
			
		||||
      <div
 | 
			
		||||
        transition:fly={{ x: 10, duration: 200, easing: quintOut }}
 | 
			
		||||
        class="bg-orange-100 px-2 rounded-full text-orange-900 text-[10px]"
 | 
			
		||||
        class="rounded-full bg-orange-100 px-2 text-[10px] text-orange-900"
 | 
			
		||||
      >
 | 
			
		||||
        Unsaved change
 | 
			
		||||
      </div>
 | 
			
		||||
@ -45,13 +45,13 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {#if desc}
 | 
			
		||||
    <p class="immich-form-label text-xs pb-2" id="{label}-desc">
 | 
			
		||||
    <p class="immich-form-label pb-2 text-xs" id="{label}-desc">
 | 
			
		||||
      {desc}
 | 
			
		||||
    </p>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <input
 | 
			
		||||
    class="immich-form-input pb-2 w-full"
 | 
			
		||||
    class="immich-form-input w-full pb-2"
 | 
			
		||||
    aria-describedby={desc ? `${label}-desc` : undefined}
 | 
			
		||||
    aria-labelledby="{label}-label"
 | 
			
		||||
    id={label}
 | 
			
		||||
 | 
			
		||||
@ -15,13 +15,13 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="w-full">
 | 
			
		||||
  <div class={`flex place-items-center gap-1 h-[26px]`}>
 | 
			
		||||
  <div class={`flex h-[26px] place-items-center gap-1`}>
 | 
			
		||||
    <label class={`immich-form-label text-sm`} for="{name}-select">{label}</label>
 | 
			
		||||
 | 
			
		||||
    {#if isEdited}
 | 
			
		||||
      <div
 | 
			
		||||
        transition:fly={{ x: 10, duration: 200, easing: quintOut }}
 | 
			
		||||
        class="bg-orange-100 px-2 rounded-full text-orange-900 text-[10px]"
 | 
			
		||||
        class="rounded-full bg-orange-100 px-2 text-[10px] text-orange-900"
 | 
			
		||||
      >
 | 
			
		||||
        Unsaved change
 | 
			
		||||
      </div>
 | 
			
		||||
@ -29,13 +29,13 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {#if desc}
 | 
			
		||||
    <p class="immich-form-label text-xs pb-2" id="{name}-desc">
 | 
			
		||||
    <p class="immich-form-label pb-2 text-xs" id="{name}-desc">
 | 
			
		||||
      {desc}
 | 
			
		||||
    </p>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <select
 | 
			
		||||
    class="immich-form-input pb-2 w-full"
 | 
			
		||||
    class="immich-form-input w-full pb-2"
 | 
			
		||||
    aria-describedby={desc ? `${name}-desc` : undefined}
 | 
			
		||||
    {name}
 | 
			
		||||
    id="{name}-select"
 | 
			
		||||
 | 
			
		||||
@ -9,16 +9,16 @@
 | 
			
		||||
  export let isEdited = false;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="flex justify-between place-items-center">
 | 
			
		||||
<div class="flex place-items-center justify-between">
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="flex place-items-center gap-1 h-[26px]">
 | 
			
		||||
    <div class="flex h-[26px] place-items-center gap-1">
 | 
			
		||||
      <label class="immich-form-label text-sm" for={title}>
 | 
			
		||||
        {title}
 | 
			
		||||
      </label>
 | 
			
		||||
      {#if isEdited}
 | 
			
		||||
        <div
 | 
			
		||||
          transition:fly={{ x: 10, duration: 200, easing: quintOut }}
 | 
			
		||||
          class="bg-orange-100 px-2 rounded-full text-orange-900 text-[10px]"
 | 
			
		||||
          class="rounded-full bg-orange-100 px-2 text-[10px] text-orange-900"
 | 
			
		||||
        >
 | 
			
		||||
          Unsaved change
 | 
			
		||||
        </div>
 | 
			
		||||
@ -28,8 +28,8 @@
 | 
			
		||||
    <p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <label class="relative inline-block flex-none w-[36px] h-[10px]">
 | 
			
		||||
    <input class="opacity-0 w-0 h-0 disabled::cursor-not-allowed" type="checkbox" bind:checked on:click {disabled} />
 | 
			
		||||
  <label class="relative inline-block h-[10px] w-[36px] flex-none">
 | 
			
		||||
    <input class="disabled::cursor-not-allowed h-0 w-0 opacity-0" type="checkbox" bind:checked on:click {disabled} />
 | 
			
		||||
 | 
			
		||||
    {#if disabled}
 | 
			
		||||
      <span class="slider-disable" />
 | 
			
		||||
 | 
			
		||||
@ -133,7 +133,7 @@
 | 
			
		||||
<section class="dark:text-immich-dark-fg">
 | 
			
		||||
  {#await getConfigs() then}
 | 
			
		||||
    <div id="directory-path-builder" class="m-4">
 | 
			
		||||
      <h3 class="font-medium text-immich-primary dark:text-immich-dark-primary text-base">Variables</h3>
 | 
			
		||||
      <h3 class="text-base font-medium text-immich-primary dark:text-immich-dark-primary">Variables</h3>
 | 
			
		||||
 | 
			
		||||
      <section class="support-date">
 | 
			
		||||
        {#await getSupportDateTimeFormat()}
 | 
			
		||||
@ -150,9 +150,9 @@
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <div class="mt-4 flex flex-col">
 | 
			
		||||
        <h3 class="font-medium text-immich-primary dark:text-immich-dark-primary text-base">Template</h3>
 | 
			
		||||
        <h3 class="text-base font-medium text-immich-primary dark:text-immich-dark-primary">Template</h3>
 | 
			
		||||
 | 
			
		||||
        <div class="text-xs my-2">
 | 
			
		||||
        <div class="my-2 text-xs">
 | 
			
		||||
          <h4>PREVIEW</h4>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
@ -167,17 +167,17 @@
 | 
			
		||||
          <code>{user.storageLabel || user.id}</code> is the user's Storage Label
 | 
			
		||||
        </p>
 | 
			
		||||
 | 
			
		||||
        <p class="text-xs p-4 bg-gray-200 dark:bg-gray-700 dark:text-immich-dark-fg py-2 rounded-lg mt-2">
 | 
			
		||||
        <p class="mt-2 rounded-lg bg-gray-200 p-4 py-2 text-xs dark:bg-gray-700 dark:text-immich-dark-fg">
 | 
			
		||||
          <span class="text-immich-fg/25 dark:text-immich-dark-fg/50"
 | 
			
		||||
            >UPLOAD_LOCATION/{user.storageLabel || user.id}</span
 | 
			
		||||
          >/{parsedTemplate()}.jpg
 | 
			
		||||
        </p>
 | 
			
		||||
 | 
			
		||||
        <form autocomplete="off" class="flex flex-col" on:submit|preventDefault>
 | 
			
		||||
          <div class="flex flex-col my-2">
 | 
			
		||||
          <div class="my-2 flex flex-col">
 | 
			
		||||
            <label class="text-xs" for="presets">PRESET</label>
 | 
			
		||||
            <select
 | 
			
		||||
              class="text-sm bg-slate-200 p-2 rounded-lg mt-2 dark:bg-gray-600 hover:cursor-pointer"
 | 
			
		||||
              class="mt-2 rounded-lg bg-slate-200 p-2 text-sm hover:cursor-pointer dark:bg-gray-600"
 | 
			
		||||
              name="presets"
 | 
			
		||||
              id="preset-select"
 | 
			
		||||
              bind:value={selectedPreset}
 | 
			
		||||
@ -202,7 +202,7 @@
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div id="migration-info" class="text-sm mt-4">
 | 
			
		||||
          <div id="migration-info" class="mt-4 text-sm">
 | 
			
		||||
            <p>
 | 
			
		||||
              Template changes will only apply to new assets. To retroactively apply the template to previously uploaded
 | 
			
		||||
              assets, run the <a href="/admin/jobs-status" class="text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
 | 
			
		||||
@ -9,18 +9,18 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="text-xs mt-2">
 | 
			
		||||
<div class="mt-2 text-xs">
 | 
			
		||||
  <h4>DATE & TIME</h4>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="text-xs bg-gray-200 dark:bg-gray-700 dark:text-immich-dark-fg p-4 mt-2 rounded-lg">
 | 
			
		||||
<div class="mt-2 rounded-lg bg-gray-200 p-4 text-xs dark:bg-gray-700 dark:text-immich-dark-fg">
 | 
			
		||||
  <div class="mb-2 text-gray-600 dark:text-immich-dark-fg">
 | 
			
		||||
    <p>Asset's creation timestamp is used for the datetime information</p>
 | 
			
		||||
    <p>Sample time 2022-09-04T20:03:05.250</p>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="flex gap-[50px]">
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">YEAR</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">YEAR</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {#each options.yearOptions as yearFormat}
 | 
			
		||||
          <li>{'{{'}{yearFormat}{'}}'} - {getLuxonExample(yearFormat)}</li>
 | 
			
		||||
@ -29,7 +29,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">MONTH</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">MONTH</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {#each options.monthOptions as monthFormat}
 | 
			
		||||
          <li>{'{{'}{monthFormat}{'}}'} - {getLuxonExample(monthFormat)}</li>
 | 
			
		||||
@ -38,7 +38,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">DAY</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">DAY</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {#each options.dayOptions as dayFormat}
 | 
			
		||||
          <li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
 | 
			
		||||
@ -47,7 +47,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">HOUR</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">HOUR</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {#each options.hourOptions as dayFormat}
 | 
			
		||||
          <li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
 | 
			
		||||
@ -56,7 +56,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">MINUTE</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">MINUTE</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {#each options.minuteOptions as dayFormat}
 | 
			
		||||
          <li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
 | 
			
		||||
@ -65,7 +65,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">SECOND</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">SECOND</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {#each options.secondOptions as dayFormat}
 | 
			
		||||
          <li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
 | 
			
		||||
 | 
			
		||||
@ -1,25 +1,25 @@
 | 
			
		||||
<div class="text-xs mt-4">
 | 
			
		||||
<div class="mt-4 text-xs">
 | 
			
		||||
  <h4>OTHER VARIABLES</h4>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="text-xs bg-gray-200 dark:bg-gray-700 dark:text-immich-dark-fg p-4 mt-2 rounded-lg">
 | 
			
		||||
<div class="mt-2 rounded-lg bg-gray-200 p-4 text-xs dark:bg-gray-700 dark:text-immich-dark-fg">
 | 
			
		||||
  <div class="flex gap-[50px]">
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">FILE NAME</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">FILE NAME</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        <li>{`{{filename}}`}</li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">FILE EXTENSION</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">FILE EXTENSION</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        <li>{`{{ext}}`}</li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-immich-primary font-medium dark:text-immich-dark-primary">FILE TYPE</p>
 | 
			
		||||
      <p class="font-medium text-immich-primary dark:text-immich-dark-primary">FILE TYPE</p>
 | 
			
		||||
      <ul>
 | 
			
		||||
        <li>{`{{filetype}}`} - VID or IMG</li>
 | 
			
		||||
        <li>{`{{filetypefull}}`} - VIDEO or IMAGE</li>
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@
 | 
			
		||||
 | 
			
		||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
			
		||||
<div
 | 
			
		||||
  class="group hover:cursor-pointer mt-4 border-[3px] border-transparent dark:hover:border-immich-dark-primary/75 hover:border-immich-primary/75 rounded-3xl p-5 relative"
 | 
			
		||||
  class="group relative mt-4 rounded-3xl border-[3px] border-transparent p-5 hover:cursor-pointer hover:border-immich-primary/75 dark:hover:border-immich-dark-primary/75"
 | 
			
		||||
  on:click={() => dispatchClick('click', album)}
 | 
			
		||||
  on:keydown={() => dispatchClick('click', album)}
 | 
			
		||||
  data-testid="album-card"
 | 
			
		||||
@ -69,7 +69,7 @@
 | 
			
		||||
  {#if showContextMenu}
 | 
			
		||||
    <div
 | 
			
		||||
      id={`icon-${album.id}`}
 | 
			
		||||
      class="absolute top-6 right-6 z-10"
 | 
			
		||||
      class="absolute right-6 top-6 z-10"
 | 
			
		||||
      on:click|stopPropagation|preventDefault={showAlbumContextMenu}
 | 
			
		||||
      data-testid="context-button-parent"
 | 
			
		||||
    >
 | 
			
		||||
@ -79,16 +79,16 @@
 | 
			
		||||
    </div>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <div class={`aspect-square relative`}>
 | 
			
		||||
  <div class={`relative aspect-square`}>
 | 
			
		||||
    <img
 | 
			
		||||
      src={imageData}
 | 
			
		||||
      alt={album.id}
 | 
			
		||||
      class={`object-cover h-full w-full transition-all z-0 rounded-3xl duration-300 hover:shadow-lg`}
 | 
			
		||||
      class={`z-0 h-full w-full rounded-3xl object-cover transition-all duration-300 hover:shadow-lg`}
 | 
			
		||||
      data-testid="album-image"
 | 
			
		||||
      draggable="false"
 | 
			
		||||
    />
 | 
			
		||||
    <div
 | 
			
		||||
      class="w-full h-full absolute top-0 rounded-3xl {isSharingView
 | 
			
		||||
      class="absolute top-0 h-full w-full rounded-3xl {isSharingView
 | 
			
		||||
        ? 'group-hover:bg-yellow-800/25'
 | 
			
		||||
        : 'group-hover:bg-indigo-800/25'} "
 | 
			
		||||
    />
 | 
			
		||||
@ -96,14 +96,14 @@
 | 
			
		||||
 | 
			
		||||
  <div class="mt-4">
 | 
			
		||||
    <p
 | 
			
		||||
      class="text-xl font-semibold dark:text-immich-dark-primary text-immich-primary w-full truncate"
 | 
			
		||||
      class="w-full truncate text-xl font-semibold text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
      data-testid="album-name"
 | 
			
		||||
      title={album.albumName}
 | 
			
		||||
    >
 | 
			
		||||
      {album.albumName}
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <span class="text-sm flex gap-2 dark:text-immich-dark-fg" data-testid="album-details">
 | 
			
		||||
    <span class="flex gap-2 text-sm dark:text-immich-dark-fg" data-testid="album-details">
 | 
			
		||||
      {#if showItemCount}
 | 
			
		||||
        <p>
 | 
			
		||||
          {album.assetCount.toLocaleString($locale)}
 | 
			
		||||
 | 
			
		||||
@ -298,7 +298,7 @@
 | 
			
		||||
        {#if isPublicShared && !isOwned}
 | 
			
		||||
          <a
 | 
			
		||||
            data-sveltekit-preload-data="hover"
 | 
			
		||||
            class="flex gap-2 place-items-center hover:cursor-pointer ml-6"
 | 
			
		||||
            class="ml-6 flex place-items-center gap-2 hover:cursor-pointer"
 | 
			
		||||
            href="https://immich.app"
 | 
			
		||||
          >
 | 
			
		||||
            <ImmichLogo height={30} width={30} />
 | 
			
		||||
@ -377,7 +377,7 @@
 | 
			
		||||
    </ControlAppBar>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <section class="flex flex-col my-[160px] px-6 sm:px-12 md:px-24 lg:px-40">
 | 
			
		||||
  <section class="my-[160px] flex flex-col px-6 sm:px-12 md:px-24 lg:px-40">
 | 
			
		||||
    <input
 | 
			
		||||
      on:keydown={(e) => {
 | 
			
		||||
        if (e.key == 'Enter') {
 | 
			
		||||
@ -387,9 +387,9 @@
 | 
			
		||||
      }}
 | 
			
		||||
      on:focus={() => (isEditingTitle = true)}
 | 
			
		||||
      on:blur={() => (isEditingTitle = false)}
 | 
			
		||||
      class={`transition-all text-6xl text-immich-primary dark:text-immich-dark-primary w-[99%] border-b-2 border-transparent outline-none ${
 | 
			
		||||
      class={`w-[99%] border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary ${
 | 
			
		||||
        isOwned ? 'hover:border-gray-400' : 'hover:border-transparent'
 | 
			
		||||
      } focus:outline-none focus:border-b-2 focus:border-immich-primary dark:focus:border-immich-dark-primary bg-immich-bg dark:bg-immich-dark-bg dark:focus:bg-immich-dark-gray`}
 | 
			
		||||
      } bg-immich-bg focus:border-b-2 focus:border-immich-primary focus:outline-none dark:bg-immich-dark-bg dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray`}
 | 
			
		||||
      type="text"
 | 
			
		||||
      bind:value={album.albumName}
 | 
			
		||||
      disabled={!isOwned}
 | 
			
		||||
@ -397,14 +397,14 @@
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
    {#if album.assetCount > 0}
 | 
			
		||||
      <span class="flex gap-2 my-4 text-sm text-gray-500 font-medium" data-testid="album-details">
 | 
			
		||||
      <span class="my-4 flex gap-2 text-sm font-medium text-gray-500" data-testid="album-details">
 | 
			
		||||
        <p class="">{getDateRange()}</p>
 | 
			
		||||
        <p>·</p>
 | 
			
		||||
        <p>{album.assetCount} items</p>
 | 
			
		||||
      </span>
 | 
			
		||||
    {/if}
 | 
			
		||||
    {#if album.shared}
 | 
			
		||||
      <div class="flex my-6 gap-x-1">
 | 
			
		||||
      <div class="my-6 flex gap-x-1">
 | 
			
		||||
        {#each album.sharedUsers as user (user.id)}
 | 
			
		||||
          <button on:click={() => (isShowShareInfoModal = true)}>
 | 
			
		||||
            <UserAvatar {user} size="md" autoColor />
 | 
			
		||||
@ -415,7 +415,7 @@
 | 
			
		||||
          style:display={isOwned ? 'block' : 'none'}
 | 
			
		||||
          on:click={() => (isShowShareUserSelection = true)}
 | 
			
		||||
          title="Add more users"
 | 
			
		||||
          class="h-12 w-12 border bg-white transition-colors hover:bg-gray-300 text-3xl flex place-items-center place-content-center rounded-full"
 | 
			
		||||
          class="flex h-12 w-12 place-content-center place-items-center rounded-full border bg-white text-3xl transition-colors hover:bg-gray-300"
 | 
			
		||||
          >+</button
 | 
			
		||||
        >
 | 
			
		||||
      </div>
 | 
			
		||||
@ -430,7 +430,7 @@
 | 
			
		||||
          <p class="text-xs dark:text-immich-dark-fg">ADD PHOTOS</p>
 | 
			
		||||
          <button
 | 
			
		||||
            on:click={() => (isShowAssetSelection = true)}
 | 
			
		||||
            class="w-full py-8 border bg-immich-bg dark:bg-immich-dark-gray text-immich-fg dark:text-immich-dark-fg dark:hover:text-immich-dark-primary rounded-md mt-5 flex place-items-center gap-6 px-8 transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none"
 | 
			
		||||
            class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
 | 
			
		||||
          >
 | 
			
		||||
            <span class="text-text-immich-primary dark:text-immich-dark-primary"><Plus size="24" /> </span>
 | 
			
		||||
            <span class="text-lg">Select photos</span>
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  transition:fly={{ y: 500, duration: 100, easing: quintOut }}
 | 
			
		||||
  class="absolute top-0 left-0 w-full h-full bg-immich-bg dark:bg-immich-dark-bg z-[9999]"
 | 
			
		||||
  class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg"
 | 
			
		||||
>
 | 
			
		||||
  <ControlAppBar
 | 
			
		||||
    on:close-button-click={() => {
 | 
			
		||||
@ -56,14 +56,14 @@
 | 
			
		||||
    <svelte:fragment slot="trailing">
 | 
			
		||||
      <button
 | 
			
		||||
        on:click={handleSelectFromComputerClicked}
 | 
			
		||||
        class="text-immich-primary dark:text-immich-dark-primary text-sm hover:bg-immich-primary/10 dark:hover:bg-immich-dark-primary/25 transition-all px-6 py-2 rounded-lg font-medium"
 | 
			
		||||
        class="rounded-lg px-6 py-2 text-sm font-medium text-immich-primary transition-all hover:bg-immich-primary/10 dark:text-immich-dark-primary dark:hover:bg-immich-dark-primary/25"
 | 
			
		||||
      >
 | 
			
		||||
        Select from computer
 | 
			
		||||
      </button>
 | 
			
		||||
      <Button size="sm" rounded="lg" disabled={$selectedAssets.size === 0} on:click={addSelectedAssets}>Done</Button>
 | 
			
		||||
    </svelte:fragment>
 | 
			
		||||
  </ControlAppBar>
 | 
			
		||||
  <section class="pt-[100px] pl-[70px] grid h-screen bg-immich-bg dark:bg-immich-dark-bg">
 | 
			
		||||
  <section class="grid h-screen bg-immich-bg pl-[70px] pt-[100px] dark:bg-immich-dark-bg">
 | 
			
		||||
    <AssetGrid isAlbumSelectionMode={true} />
 | 
			
		||||
  </section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@ -73,19 +73,19 @@
 | 
			
		||||
{#if !selectedRemoveUser}
 | 
			
		||||
  <BaseModal on:close={() => dispatch('close')}>
 | 
			
		||||
    <svelte:fragment slot="title">
 | 
			
		||||
      <span class="flex gap-2 place-items-center">
 | 
			
		||||
      <span class="flex place-items-center gap-2">
 | 
			
		||||
        <p class="font-medium text-immich-fg dark:text-immich-dark-fg">Options</p>
 | 
			
		||||
      </span>
 | 
			
		||||
    </svelte:fragment>
 | 
			
		||||
 | 
			
		||||
    <section class="max-h-[400px] overflow-y-auto immich-scrollbar pb-4">
 | 
			
		||||
    <section class="immich-scrollbar max-h-[400px] overflow-y-auto pb-4">
 | 
			
		||||
      {#each album.sharedUsers as user}
 | 
			
		||||
        <div
 | 
			
		||||
          class="flex gap-4 p-5 place-items-center justify-between w-full transition-colors hover:bg-gray-50 dark:hover:bg-gray-700"
 | 
			
		||||
          class="flex w-full place-items-center justify-between gap-4 p-5 transition-colors hover:bg-gray-50 dark:hover:bg-gray-700"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="flex gap-4 place-items-center">
 | 
			
		||||
          <div class="flex place-items-center gap-4">
 | 
			
		||||
            <UserAvatar {user} size="md" autoColor />
 | 
			
		||||
            <p class="font-medium text-sm">{user.firstName} {user.lastName}</p>
 | 
			
		||||
            <p class="text-sm font-medium">{user.firstName} {user.lastName}</p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div id={`icon-${user.id}`} class="flex place-items-center">
 | 
			
		||||
@ -108,7 +108,7 @@
 | 
			
		||||
            {:else if user.id == currentUser?.id}
 | 
			
		||||
              <button
 | 
			
		||||
                on:click={() => (selectedRemoveUser = user)}
 | 
			
		||||
                class="text-sm text-immich-primary dark:text-immich-dark-primary font-medium transition-colors hover:text-immich-primary/75"
 | 
			
		||||
                class="text-sm font-medium text-immich-primary transition-colors hover:text-immich-primary/75 dark:text-immich-dark-primary"
 | 
			
		||||
                >Leave</button
 | 
			
		||||
              >
 | 
			
		||||
            {/if}
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  transition:fly={{ y: 500, duration: 100, easing: quintOut }}
 | 
			
		||||
  class="absolute top-0 left-0 w-full h-full py-[160px] bg-immich-bg dark:bg-immich-dark-bg z-[9999]"
 | 
			
		||||
  class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg py-[160px] dark:bg-immich-dark-bg"
 | 
			
		||||
>
 | 
			
		||||
  <ControlAppBar on:close-button-click={() => dispatch('close')}>
 | 
			
		||||
    <svelte:fragment slot="leading">
 | 
			
		||||
@ -42,7 +42,7 @@
 | 
			
		||||
    </svelte:fragment>
 | 
			
		||||
  </ControlAppBar>
 | 
			
		||||
 | 
			
		||||
  <section class="flex flex-wrap gap-14 px-20 overflow-y-auto">
 | 
			
		||||
  <section class="flex flex-wrap gap-14 overflow-y-auto px-20">
 | 
			
		||||
    <!-- Image grid -->
 | 
			
		||||
    <div class="flex flex-wrap gap-[2px]">
 | 
			
		||||
      {#each album.assets as asset}
 | 
			
		||||
 | 
			
		||||
@ -55,22 +55,22 @@
 | 
			
		||||
 | 
			
		||||
<BaseModal on:close={() => dispatch('close')}>
 | 
			
		||||
  <svelte:fragment slot="title">
 | 
			
		||||
    <span class="flex gap-2 place-items-center">
 | 
			
		||||
    <span class="flex place-items-center gap-2">
 | 
			
		||||
      <ImmichLogo width={24} />
 | 
			
		||||
      <p class="font-medium">Invite to album</p>
 | 
			
		||||
    </span>
 | 
			
		||||
  </svelte:fragment>
 | 
			
		||||
 | 
			
		||||
  <div class="max-h-[300px] overflow-y-auto immich-scrollbar">
 | 
			
		||||
  <div class="immich-scrollbar max-h-[300px] overflow-y-auto">
 | 
			
		||||
    {#if selectedUsers.length > 0}
 | 
			
		||||
      <div class="flex gap-4 py-2 px-5 overflow-x-auto place-items-center mb-2">
 | 
			
		||||
      <div class="mb-2 flex place-items-center gap-4 overflow-x-auto px-5 py-2">
 | 
			
		||||
        <p class="font-medium">To</p>
 | 
			
		||||
 | 
			
		||||
        {#each selectedUsers as user}
 | 
			
		||||
          {#key user.id}
 | 
			
		||||
            <button
 | 
			
		||||
              on:click={() => deselectUser(user)}
 | 
			
		||||
              class="flex gap-1 place-items-center border border-gray-400 rounded-full p-1 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
 | 
			
		||||
              class="flex place-items-center gap-1 rounded-full border border-gray-400 p-1 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
 | 
			
		||||
            >
 | 
			
		||||
              <UserAvatar {user} size="sm" autoColor />
 | 
			
		||||
              <p class="text-xs font-medium">{user.firstName} {user.lastName}</p>
 | 
			
		||||
@ -81,17 +81,17 @@
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    {#if users.length > 0}
 | 
			
		||||
      <p class="text-xs font-medium px-5">SUGGESTIONS</p>
 | 
			
		||||
      <p class="px-5 text-xs font-medium">SUGGESTIONS</p>
 | 
			
		||||
 | 
			
		||||
      <div class="my-4">
 | 
			
		||||
        {#each users as user}
 | 
			
		||||
          <button
 | 
			
		||||
            on:click={() => selectUser(user)}
 | 
			
		||||
            class="w-full flex place-items-center gap-4 py-4 px-5 hover:bg-gray-200 dark:hover:bg-gray-700 transition-all"
 | 
			
		||||
            class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
 | 
			
		||||
          >
 | 
			
		||||
            {#if selectedUsers.includes(user)}
 | 
			
		||||
              <span
 | 
			
		||||
                class="bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-immich-dark-bg rounded-full w-12 h-12 border flex place-items-center place-content-center text-3xl dark:border-immich-dark-gray"
 | 
			
		||||
                class="flex h-12 w-12 place-content-center place-items-center rounded-full border bg-immich-primary text-3xl text-white dark:border-immich-dark-gray dark:bg-immich-dark-primary dark:text-immich-dark-bg"
 | 
			
		||||
                >✓</span
 | 
			
		||||
              >
 | 
			
		||||
            {:else}
 | 
			
		||||
@ -111,7 +111,7 @@
 | 
			
		||||
        {/each}
 | 
			
		||||
      </div>
 | 
			
		||||
    {:else}
 | 
			
		||||
      <p class="text-sm p-5">
 | 
			
		||||
      <p class="p-5 text-sm">
 | 
			
		||||
        Looks like you have shared this album with all users or you don't have any user to share with.
 | 
			
		||||
      </p>
 | 
			
		||||
    {/if}
 | 
			
		||||
@ -124,9 +124,9 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <hr />
 | 
			
		||||
  <div id="shared-buttons" class="flex my-4 justify-around place-items-center place-content-center">
 | 
			
		||||
  <div id="shared-buttons" class="my-4 flex place-content-center place-items-center justify-around">
 | 
			
		||||
    <button
 | 
			
		||||
      class="flex flex-col gap-2 place-items-center place-content-center hover:cursor-pointer"
 | 
			
		||||
      class="flex flex-col place-content-center place-items-center gap-2 hover:cursor-pointer"
 | 
			
		||||
      on:click={onSharedLinkClick}
 | 
			
		||||
    >
 | 
			
		||||
      <Link size={24} />
 | 
			
		||||
@ -135,7 +135,7 @@
 | 
			
		||||
 | 
			
		||||
    {#if sharedLinks.length}
 | 
			
		||||
      <button
 | 
			
		||||
        class="flex flex-col gap-2 place-items-center place-content-center hover:cursor-pointer"
 | 
			
		||||
        class="flex flex-col place-content-center place-items-center gap-2 hover:cursor-pointer"
 | 
			
		||||
        on:click={() => goto(AppRoute.SHARED_LINKS)}
 | 
			
		||||
      >
 | 
			
		||||
        <ShareCircle size={24} />
 | 
			
		||||
 | 
			
		||||
@ -25,20 +25,20 @@
 | 
			
		||||
 | 
			
		||||
<button
 | 
			
		||||
  on:click={() => dispatcher('album')}
 | 
			
		||||
  class="w-full flex gap-4 px-6 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
 | 
			
		||||
  class="flex w-full gap-4 px-6 py-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
 | 
			
		||||
>
 | 
			
		||||
  <div class="h-12 w-12 rounded-xl bg-slate-300">
 | 
			
		||||
    {#if album.albumThumbnailAssetId}
 | 
			
		||||
      <img
 | 
			
		||||
        src={api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)}
 | 
			
		||||
        alt={album.albumName}
 | 
			
		||||
        class={`object-cover h-full w-full transition-all z-0 rounded-xl duration-300 hover:shadow-lg`}
 | 
			
		||||
        class={`z-0 h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg`}
 | 
			
		||||
        data-testid="album-image"
 | 
			
		||||
        draggable="false"
 | 
			
		||||
      />
 | 
			
		||||
    {/if}
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="h-12 flex flex-col items-start justify-center">
 | 
			
		||||
  <div class="flex h-12 flex-col items-start justify-center">
 | 
			
		||||
    <span>{albumNameArray[0]}<b>{albumNameArray[1]}</b>{albumNameArray[2]}</span>
 | 
			
		||||
    <span class="flex gap-1 text-sm">
 | 
			
		||||
      {#if variant === 'simple'}
 | 
			
		||||
 | 
			
		||||
@ -46,12 +46,12 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
  class="h-16 flex justify-between place-items-center px-3 transition-transform duration-200 z-[1001] bg-gradient-to-b from-black/40"
 | 
			
		||||
  class="z-[1001] flex h-16 place-items-center justify-between bg-gradient-to-b from-black/40 px-3 transition-transform duration-200"
 | 
			
		||||
>
 | 
			
		||||
  <div class="text-white">
 | 
			
		||||
    <CircleIconButton isOpacity={true} logo={ArrowLeft} on:click={() => dispatch('goBack')} />
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="text-white flex gap-2 justify-end w-[calc(100%-3rem)] overflow-hidden">
 | 
			
		||||
  <div class="flex w-[calc(100%-3rem)] justify-end gap-2 overflow-hidden text-white">
 | 
			
		||||
    {#if showMotionPlayButton}
 | 
			
		||||
      {#if isMotionPhotoPlaying}
 | 
			
		||||
        <CircleIconButton
 | 
			
		||||
 | 
			
		||||
@ -245,9 +245,9 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  id="immich-asset-viewer"
 | 
			
		||||
  class="fixed h-screen w-screen left-0 top-0 overflow-y-hidden bg-black z-[1001] grid grid-rows-[64px_1fr] grid-cols-4"
 | 
			
		||||
  class="fixed left-0 top-0 z-[1001] grid h-screen w-screen grid-cols-4 grid-rows-[64px_1fr] overflow-y-hidden bg-black"
 | 
			
		||||
>
 | 
			
		||||
  <div class="col-start-1 col-span-4 row-start-1 row-span-1 z-[1000] transition-transform">
 | 
			
		||||
  <div class="z-[1000] col-span-4 col-start-1 row-span-1 row-start-1 transition-transform">
 | 
			
		||||
    <AssetViewerNavBar
 | 
			
		||||
      {asset}
 | 
			
		||||
      isMotionPhotoPlaying={shouldPlayMotionPhoto}
 | 
			
		||||
@ -270,17 +270,17 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {#if showNavigation}
 | 
			
		||||
    <div class="row-start-2 row-span-1 col-start-1 column-span-1 justify-self-start mb-[60px] z-[999]">
 | 
			
		||||
    <div class="column-span-1 z-[999] col-start-1 row-span-1 row-start-2 mb-[60px] justify-self-start">
 | 
			
		||||
      <NavigationArea on:click={navigateAssetBackward}><ChevronLeft size="36" /></NavigationArea>
 | 
			
		||||
    </div>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <div class="row-start-1 row-span-full col-start-1 col-span-4">
 | 
			
		||||
  <div class="col-span-4 col-start-1 row-span-full row-start-1">
 | 
			
		||||
    {#key asset.id}
 | 
			
		||||
      {#if !asset.resized}
 | 
			
		||||
        <div class="h-full w-full flex justify-center">
 | 
			
		||||
        <div class="flex h-full w-full justify-center">
 | 
			
		||||
          <div
 | 
			
		||||
            class="h-full bg-gray-100 dark:bg-immich-dark-gray flex items-center justify-center aspect-square px-auto"
 | 
			
		||||
            class="px-auto flex aspect-square h-full items-center justify-center bg-gray-100 dark:bg-immich-dark-gray"
 | 
			
		||||
          >
 | 
			
		||||
            <ImageBrokenVariant size="25%" />
 | 
			
		||||
          </div>
 | 
			
		||||
@ -303,7 +303,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {#if showNavigation}
 | 
			
		||||
    <div class="row-start-2 row-span-1 col-start-4 col-span-1 justify-self-end mb-[60px] z-[999]">
 | 
			
		||||
    <div class="z-[999] col-span-1 col-start-4 row-span-1 row-start-2 mb-[60px] justify-self-end">
 | 
			
		||||
      <NavigationArea on:click={navigateAssetForward}><ChevronRight size="36" /></NavigationArea>
 | 
			
		||||
    </div>
 | 
			
		||||
  {/if}
 | 
			
		||||
@ -312,7 +312,7 @@
 | 
			
		||||
    <div
 | 
			
		||||
      transition:fly={{ duration: 150 }}
 | 
			
		||||
      id="detail-panel"
 | 
			
		||||
      class="bg-immich-bg w-[360px] z-[1002] row-span-full transition-all overflow-y-auto dark:bg-immich-dark-bg dark:border-l dark:border-l-immich-dark-gray"
 | 
			
		||||
      class="z-[1002] row-span-full w-[360px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg"
 | 
			
		||||
      translate="yes"
 | 
			
		||||
    >
 | 
			
		||||
      <DetailPanel
 | 
			
		||||
 | 
			
		||||
@ -80,20 +80,20 @@
 | 
			
		||||
<section class="p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
 | 
			
		||||
  <div class="flex place-items-center gap-2">
 | 
			
		||||
    <button
 | 
			
		||||
      class="rounded-full p-3 flex place-items-center place-content-center hover:bg-gray-200 transition-colors dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
			
		||||
      class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
			
		||||
      on:click={() => dispatch('close')}
 | 
			
		||||
    >
 | 
			
		||||
      <Close size="24" />
 | 
			
		||||
    </button>
 | 
			
		||||
 | 
			
		||||
    <p class="text-immich-fg dark:text-immich-dark-fg text-lg">Info</p>
 | 
			
		||||
    <p class="text-lg text-immich-fg dark:text-immich-dark-fg">Info</p>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <section class="mx-4 mt-10">
 | 
			
		||||
    <textarea
 | 
			
		||||
      bind:this={textarea}
 | 
			
		||||
      class="max-h-[500px]
 | 
			
		||||
      text-base text-black bg-transparent dark:text-white border-b focus:border-b-2 border-gray-500 w-full focus:border-immich-primary dark:focus:border-immich-dark-primary transition-all resize-none overflow-hidden outline-none disabled:border-none"
 | 
			
		||||
      w-full resize-none overflow-hidden border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary"
 | 
			
		||||
      placeholder={$page?.data?.user?.id !== asset.ownerId ? '' : 'Add a description'}
 | 
			
		||||
      style:display={$page?.data?.user?.id !== asset.ownerId && textarea?.value == '' ? 'none' : 'block'}
 | 
			
		||||
      on:focusin={handleFocusIn}
 | 
			
		||||
@ -108,7 +108,7 @@
 | 
			
		||||
    <section class="px-4 py-4 text-sm">
 | 
			
		||||
      <h2>PEOPLE</h2>
 | 
			
		||||
 | 
			
		||||
      <div class="flex flex-wrap gap-2 mt-4">
 | 
			
		||||
      <div class="mt-4 flex flex-wrap gap-2">
 | 
			
		||||
        {#each people as person (person.id)}
 | 
			
		||||
          <a href="/people/{person.id}" class="w-[90px]" on:click={() => dispatch('close-viewer')}>
 | 
			
		||||
            <ImageThumbnail
 | 
			
		||||
@ -120,7 +120,7 @@
 | 
			
		||||
              heightStyle="90px"
 | 
			
		||||
              thumbhash={null}
 | 
			
		||||
            />
 | 
			
		||||
            <p class="font-medium mt-1 truncate">{person.name}</p>
 | 
			
		||||
            <p class="mt-1 truncate font-medium">{person.name}</p>
 | 
			
		||||
          </a>
 | 
			
		||||
        {/each}
 | 
			
		||||
      </div>
 | 
			
		||||
@ -178,7 +178,7 @@
 | 
			
		||||
          <p class="break-all">
 | 
			
		||||
            {getAssetFilename(asset)}
 | 
			
		||||
          </p>
 | 
			
		||||
          <div class="flex text-sm gap-2">
 | 
			
		||||
          <div class="flex gap-2 text-sm">
 | 
			
		||||
            {#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
 | 
			
		||||
              {#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
 | 
			
		||||
                <p>
 | 
			
		||||
@ -200,7 +200,7 @@
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
          <p>{asset.exifInfo.make || ''} {asset.exifInfo.model || ''}</p>
 | 
			
		||||
          <div class="flex text-sm gap-2">
 | 
			
		||||
          <div class="flex gap-2 text-sm">
 | 
			
		||||
            <p>{`ƒ/${asset.exifInfo.fNumber.toLocaleString($locale)}` || ''}</p>
 | 
			
		||||
 | 
			
		||||
            {#if asset.exifInfo.exposureTime}
 | 
			
		||||
@ -227,10 +227,10 @@
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
          <p>{asset.exifInfo.city}</p>
 | 
			
		||||
          <div class="flex text-sm gap-2">
 | 
			
		||||
          <div class="flex gap-2 text-sm">
 | 
			
		||||
            <p>{asset.exifInfo.state}</p>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="flex text-sm gap-2">
 | 
			
		||||
          <div class="flex gap-2 text-sm">
 | 
			
		||||
            <p>{asset.exifInfo.country}</p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@ -258,7 +258,7 @@
 | 
			
		||||
<section class="p-2 dark:text-immich-dark-fg">
 | 
			
		||||
  <div class="px-4 py-4">
 | 
			
		||||
    {#if albums.length > 0}
 | 
			
		||||
      <p class="text-sm pb-4">APPEARS IN</p>
 | 
			
		||||
      <p class="pb-4 text-sm">APPEARS IN</p>
 | 
			
		||||
    {/if}
 | 
			
		||||
    {#each albums as album}
 | 
			
		||||
      <a data-sveltekit-preload-data="hover" href={`/albums/${album.id}`}>
 | 
			
		||||
@ -271,14 +271,14 @@
 | 
			
		||||
          <div>
 | 
			
		||||
            <img
 | 
			
		||||
              alt={album.albumName}
 | 
			
		||||
              class="w-[50px] h-[50px] object-cover rounded"
 | 
			
		||||
              class="h-[50px] w-[50px] rounded object-cover"
 | 
			
		||||
              src={album.albumThumbnailAssetId &&
 | 
			
		||||
                api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Jpeg)}
 | 
			
		||||
              draggable="false"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="mt-auto mb-auto">
 | 
			
		||||
          <div class="mb-auto mt-auto">
 | 
			
		||||
            <p class="dark:text-immich-dark-primary">{album.albumName}</p>
 | 
			
		||||
            <div class="flex gap-2 text-sm">
 | 
			
		||||
              <p>{album.assetCount} items</p>
 | 
			
		||||
 | 
			
		||||
@ -15,25 +15,25 @@
 | 
			
		||||
{#if $isDownloading}
 | 
			
		||||
  <div
 | 
			
		||||
    transition:fly={{ x: -100, duration: 350 }}
 | 
			
		||||
    class="w-[315px] max-h-[270px] bg-immich-bg border rounded-2xl shadow-sm absolute bottom-10 left-2 p-4 z-[10000] text-sm"
 | 
			
		||||
    class="absolute bottom-10 left-2 z-[10000] max-h-[270px] w-[315px] rounded-2xl border bg-immich-bg p-4 text-sm shadow-sm"
 | 
			
		||||
  >
 | 
			
		||||
    <p class="text-gray-500 text-xs mb-2">DOWNLOADING</p>
 | 
			
		||||
    <div class="max-h-[200px] my-2 overflow-y-auto mb-2 flex flex-col text-sm">
 | 
			
		||||
    <p class="mb-2 text-xs text-gray-500">DOWNLOADING</p>
 | 
			
		||||
    <div class="my-2 mb-2 flex max-h-[200px] flex-col overflow-y-auto text-sm">
 | 
			
		||||
      {#each Object.keys($downloadAssets) as downloadKey (downloadKey)}
 | 
			
		||||
        {@const download = $downloadAssets[downloadKey]}
 | 
			
		||||
        <div class="mb-2 flex place-items-center" transition:slide>
 | 
			
		||||
          <div class="w-full pr-10">
 | 
			
		||||
            <div class="font-medium text-xs flex gap-2 place-items-center justify-between">
 | 
			
		||||
            <div class="flex place-items-center justify-between gap-2 text-xs font-medium">
 | 
			
		||||
              <p class="truncate">■ {downloadKey}</p>
 | 
			
		||||
              {#if download.total}
 | 
			
		||||
                <p class="whitespace-nowrap">{asByteUnitString(download.total, $locale)}</p>
 | 
			
		||||
              {/if}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex place-items-center gap-2">
 | 
			
		||||
              <div class="w-full bg-gray-200 rounded-full h-[7px] dark:bg-gray-700">
 | 
			
		||||
                <div class="bg-immich-primary h-[7px] rounded-full" style={`width: ${download.percentage}%`} />
 | 
			
		||||
              <div class="h-[7px] w-full rounded-full bg-gray-200 dark:bg-gray-700">
 | 
			
		||||
                <div class="h-[7px] rounded-full bg-immich-primary" style={`width: ${download.percentage}%`} />
 | 
			
		||||
              </div>
 | 
			
		||||
              <p class="whitespace-nowrap min-w-[4em] text-right">
 | 
			
		||||
              <p class="min-w-[4em] whitespace-nowrap text-right">
 | 
			
		||||
                <span class="text-immich-primary">{download.percentage}%</span>
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,8 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
			
		||||
<div class="group h-full flex place-items-center" on:click on:keydown>
 | 
			
		||||
  <button class="rounded-full p-3 transition text-gray-500 mx-4 group-hover:text-white group-hover:bg-gray-500">
 | 
			
		||||
<div class="group flex h-full place-items-center" on:click on:keydown>
 | 
			
		||||
  <button class="mx-4 rounded-full p-3 text-gray-500 transition group-hover:bg-gray-500 group-hover:text-white">
 | 
			
		||||
    <slot />
 | 
			
		||||
  </button>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@
 | 
			
		||||
<div
 | 
			
		||||
  bind:this={element}
 | 
			
		||||
  transition:fade={{ duration: 150 }}
 | 
			
		||||
  class="flex place-items-center place-content-center h-full select-none"
 | 
			
		||||
  class="flex h-full select-none place-content-center place-items-center"
 | 
			
		||||
>
 | 
			
		||||
  {#await loadAssetData()}
 | 
			
		||||
    <LoadingSpinner />
 | 
			
		||||
@ -113,7 +113,7 @@
 | 
			
		||||
        transition:fade={{ duration: 150 }}
 | 
			
		||||
        src={assetData}
 | 
			
		||||
        alt={asset.id}
 | 
			
		||||
        class="object-contain h-full w-full"
 | 
			
		||||
        class="h-full w-full object-contain"
 | 
			
		||||
        draggable="false"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div transition:fade={{ duration: 150 }} class="flex place-items-center place-content-center h-full select-none">
 | 
			
		||||
<div transition:fade={{ duration: 150 }} class="flex h-full select-none place-content-center place-items-center">
 | 
			
		||||
  <video
 | 
			
		||||
    controls
 | 
			
		||||
    class="h-full object-contain"
 | 
			
		||||
@ -35,7 +35,7 @@
 | 
			
		||||
  </video>
 | 
			
		||||
 | 
			
		||||
  {#if isVideoLoading}
 | 
			
		||||
    <div class="absolute flex place-items-center place-content-center">
 | 
			
		||||
    <div class="absolute flex place-content-center place-items-center">
 | 
			
		||||
      <LoadingSpinner />
 | 
			
		||||
    </div>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@
 | 
			
		||||
  on:image-load|once={() => (complete = true)}
 | 
			
		||||
/>
 | 
			
		||||
{#if hidden}
 | 
			
		||||
  <div class="absolute top-1/2 left-1/2 transform translate-x-[-50%] translate-y-[-50%]">
 | 
			
		||||
  <div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
 | 
			
		||||
    <EyeOffOutline size="2em" />
 | 
			
		||||
  </div>
 | 
			
		||||
{/if}
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
    style:height={heightStyle}
 | 
			
		||||
    src={thumbHashToDataURL(Buffer.from(thumbhash, 'base64'))}
 | 
			
		||||
    alt={altText}
 | 
			
		||||
    class="absolute object-cover top-0"
 | 
			
		||||
    class="absolute top-0 object-cover"
 | 
			
		||||
    class:rounded-lg={curve}
 | 
			
		||||
    class:shadow-lg={shadow}
 | 
			
		||||
    class:rounded-full={circle}
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,7 @@
 | 
			
		||||
  <div
 | 
			
		||||
    style:width="{width}px"
 | 
			
		||||
    style:height="{height}px"
 | 
			
		||||
    class="relative group overflow-hidden {disabled
 | 
			
		||||
    class="group relative overflow-hidden {disabled
 | 
			
		||||
      ? 'bg-gray-300'
 | 
			
		||||
      : 'bg-immich-primary/20 dark:bg-immich-dark-primary/20'}"
 | 
			
		||||
    class:cursor-not-allowed={disabled}
 | 
			
		||||
@ -80,7 +80,7 @@
 | 
			
		||||
    on:keydown={thumbnailKeyDownHandler}
 | 
			
		||||
  >
 | 
			
		||||
    {#if intersecting}
 | 
			
		||||
      <div class="absolute w-full h-full z-20">
 | 
			
		||||
      <div class="absolute z-20 h-full w-full">
 | 
			
		||||
        <!-- Select asset button  -->
 | 
			
		||||
        {#if !readonly && (mouseOver || selected || selectionCandidate)}
 | 
			
		||||
          <button
 | 
			
		||||
@ -103,12 +103,12 @@
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div
 | 
			
		||||
        class="h-full w-full bg-gray-100 dark:bg-immich-dark-gray absolute select-none transition-transform"
 | 
			
		||||
        class="absolute h-full w-full select-none bg-gray-100 transition-transform dark:bg-immich-dark-gray"
 | 
			
		||||
        class:scale-[0.85]={selected}
 | 
			
		||||
      >
 | 
			
		||||
        <!-- Gradient overlay on hover -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="absolute w-full h-full bg-gradient-to-b from-black/25 via-[transparent_25%] opacity-0 group-hover:opacity-100 transition-opacity z-10"
 | 
			
		||||
          class="absolute z-10 h-full w-full bg-gradient-to-b from-black/25 via-[transparent_25%] opacity-0 transition-opacity group-hover:opacity-100"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <!-- Favorite asset star -->
 | 
			
		||||
@ -133,13 +133,13 @@
 | 
			
		||||
            thumbhash={asset.thumbhash}
 | 
			
		||||
          />
 | 
			
		||||
        {:else}
 | 
			
		||||
          <div class="w-full h-full p-4 flex items-center justify-center">
 | 
			
		||||
          <div class="flex h-full w-full items-center justify-center p-4">
 | 
			
		||||
            <ImageBrokenVariant size="48" />
 | 
			
		||||
          </div>
 | 
			
		||||
        {/if}
 | 
			
		||||
 | 
			
		||||
        {#if asset.type === AssetTypeEnum.Video}
 | 
			
		||||
          <div class="absolute w-full h-full top-0">
 | 
			
		||||
          <div class="absolute top-0 h-full w-full">
 | 
			
		||||
            <VideoThumbnail
 | 
			
		||||
              url={api.getAssetFileUrl(asset.id, false, true, publicSharedKey)}
 | 
			
		||||
              enablePlayback={mouseOver}
 | 
			
		||||
@ -149,7 +149,7 @@
 | 
			
		||||
        {/if}
 | 
			
		||||
 | 
			
		||||
        {#if asset.type === AssetTypeEnum.Image && asset.livePhotoVideoId}
 | 
			
		||||
          <div class="absolute w-full h-full top-0">
 | 
			
		||||
          <div class="absolute top-0 h-full w-full">
 | 
			
		||||
            <VideoThumbnail
 | 
			
		||||
              url={api.getAssetFileUrl(asset.livePhotoVideoId, false, true, publicSharedKey)}
 | 
			
		||||
              pauseIcon={MotionPauseOutline}
 | 
			
		||||
@ -162,7 +162,7 @@
 | 
			
		||||
      </div>
 | 
			
		||||
      {#if selectionCandidate}
 | 
			
		||||
        <div
 | 
			
		||||
          class="absolute w-full h-full top-0 bg-immich-primary opacity-40"
 | 
			
		||||
          class="absolute top-0 h-full w-full bg-immich-primary opacity-40"
 | 
			
		||||
          in:fade={{ duration: 100 }}
 | 
			
		||||
          out:fade={{ duration: 100 }}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="absolute right-0 top-0 text-white text-xs font-medium flex gap-1 place-items-center z-20">
 | 
			
		||||
<div class="absolute right-0 top-0 z-20 flex place-items-center gap-1 text-xs font-medium text-white">
 | 
			
		||||
  {#if showTime}
 | 
			
		||||
    <span class="pt-2">
 | 
			
		||||
      {Duration.fromObject({ seconds: remainingSeconds }).toFormat('m:ss')}
 | 
			
		||||
@ -38,7 +38,7 @@
 | 
			
		||||
 | 
			
		||||
  <!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
			
		||||
  <span
 | 
			
		||||
    class="pt-2 pr-2"
 | 
			
		||||
    class="pr-2 pt-2"
 | 
			
		||||
    on:mouseenter={() => {
 | 
			
		||||
      if (playbackOnIconHover) {
 | 
			
		||||
        enablePlayback = true;
 | 
			
		||||
@ -67,7 +67,7 @@
 | 
			
		||||
{#if enablePlayback}
 | 
			
		||||
  <video
 | 
			
		||||
    bind:this={player}
 | 
			
		||||
    class="w-full h-full object-cover"
 | 
			
		||||
    class="h-full w-full object-cover"
 | 
			
		||||
    muted
 | 
			
		||||
    autoplay
 | 
			
		||||
    src={url}
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<span
 | 
			
		||||
  class="inline-block h-min whitespace-nowrap px-4 pt-[0.55em] pb-[0.55em] text-center align-baseline text-xs leading-none {colorClasses[
 | 
			
		||||
  class="inline-block h-min whitespace-nowrap px-4 pb-[0.55em] pt-[0.55em] text-center align-baseline text-xs leading-none {colorClasses[
 | 
			
		||||
    color
 | 
			
		||||
  ]}"
 | 
			
		||||
  class:rounded-md={rounded === true}
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@
 | 
			
		||||
  {disabled}
 | 
			
		||||
  {title}
 | 
			
		||||
  on:click
 | 
			
		||||
  class="inline-flex justify-center items-center transition-colors disabled:cursor-not-allowed disabled:opacity-60 {colorClasses[
 | 
			
		||||
  class="inline-flex items-center justify-center transition-colors disabled:cursor-not-allowed disabled:opacity-60 {colorClasses[
 | 
			
		||||
    color
 | 
			
		||||
  ]} {sizeClasses[size]}"
 | 
			
		||||
  class:rounded-lg={rounded === 'lg'}
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@
 | 
			
		||||
  style:background-color={backgroundColor}
 | 
			
		||||
  style:--immich-icon-button-hover-color={hoverColor}
 | 
			
		||||
  class:dark:text-immich-dark-fg={!forceDark}
 | 
			
		||||
  class="rounded-full p-3 flex place-items-center place-content-center transition-all
 | 
			
		||||
  class="flex place-content-center place-items-center rounded-full p-3 transition-all
 | 
			
		||||
	{isOpacity ? 'hover:bg-immich-bg/30' : 'immich-circle-icon-button hover:dark:text-immich-dark-gray'}
 | 
			
		||||
  {forceDark && 'hover:text-black'}
 | 
			
		||||
  {hideMobile && 'hidden sm:flex'}"
 | 
			
		||||
 | 
			
		||||
@ -33,18 +33,18 @@
 | 
			
		||||
  {#if showMenu}
 | 
			
		||||
    <div
 | 
			
		||||
      transition:fly={{ y: -30, x: 30, duration: 200 }}
 | 
			
		||||
      class="absolute top-5 right-0 min-w-[250px] bg-gray-100 dark:bg-gray-700 rounded-2xl py-4 shadow-lg dark:text-white text-black z-50 text-md flex flex-col"
 | 
			
		||||
      class="text-md absolute right-0 top-5 z-50 flex min-w-[250px] flex-col rounded-2xl bg-gray-100 py-4 text-black shadow-lg dark:bg-gray-700 dark:text-white"
 | 
			
		||||
    >
 | 
			
		||||
      {#each options as option, index (option)}
 | 
			
		||||
        <button
 | 
			
		||||
          class="hover:bg-gray-300 dark:hover:bg-gray-800 p-4 transition-all grid grid-cols-[20px,1fr] place-items-center gap-2"
 | 
			
		||||
          class="grid grid-cols-[20px,1fr] place-items-center gap-2 p-4 transition-all hover:bg-gray-300 dark:hover:bg-gray-800"
 | 
			
		||||
          on:click={() => handleSelectOption(index)}
 | 
			
		||||
        >
 | 
			
		||||
          {#if value == option}
 | 
			
		||||
            <div class="text-immich-primary dark:text-immich-dark-primary font-medium">
 | 
			
		||||
            <div class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
              <Check size="18" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="justify-self-start text-immich-primary dark:text-immich-dark-primary font-medium">
 | 
			
		||||
            <p class="justify-self-start font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
              {option}
 | 
			
		||||
            </p>
 | 
			
		||||
          {:else}
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
  class="flex place-items-center max-w-lg rounded-lg border dark:border-transparent p-2 bg-gray-100 dark:bg-gray-700"
 | 
			
		||||
  class="flex max-w-lg place-items-center rounded-lg border bg-gray-100 p-2 dark:border-transparent dark:bg-gray-700"
 | 
			
		||||
  use:clickOutside
 | 
			
		||||
  on:outclick={() => dispatch('cancel')}
 | 
			
		||||
>
 | 
			
		||||
@ -28,14 +28,14 @@
 | 
			
		||||
    heightStyle="2rem"
 | 
			
		||||
  />
 | 
			
		||||
  <form
 | 
			
		||||
    class="ml-4 flex justify-between w-full gap-16"
 | 
			
		||||
    class="ml-4 flex w-full justify-between gap-16"
 | 
			
		||||
    autocomplete="off"
 | 
			
		||||
    on:submit|preventDefault={() => dispatch('change', name)}
 | 
			
		||||
  >
 | 
			
		||||
    <!-- svelte-ignore a11y-autofocus -->
 | 
			
		||||
    <input
 | 
			
		||||
      autofocus
 | 
			
		||||
      class="gap-2 w-full bg-gray-100 dark:bg-gray-700 dark:text-white"
 | 
			
		||||
      class="w-full gap-2 bg-gray-100 dark:bg-gray-700 dark:text-white"
 | 
			
		||||
      type="text"
 | 
			
		||||
      placeholder="New name or nickname"
 | 
			
		||||
      bind:value={name}
 | 
			
		||||
 | 
			
		||||
@ -25,12 +25,12 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  transition:fly={{ y: 500, duration: 100, easing: quintOut }}
 | 
			
		||||
  class="absolute top-0 left-0 w-full h-full bg-immich-bg dark:bg-immich-dark-bg z-[9999]"
 | 
			
		||||
  class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg"
 | 
			
		||||
>
 | 
			
		||||
  <ControlAppBar on:close-button-click={onClose}>
 | 
			
		||||
    <svelte:fragment slot="leading">Select feature photo</svelte:fragment>
 | 
			
		||||
  </ControlAppBar>
 | 
			
		||||
  <section class="pt-[100px] pl-[70px] bg-immich-bg dark:bg-immich-dark-bg">
 | 
			
		||||
  <section class="bg-immich-bg pl-[70px] pt-[100px] dark:bg-immich-dark-bg">
 | 
			
		||||
    <AssetSelectionViewer {assets} on:select={handleSelectedAsset} />
 | 
			
		||||
  </section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@ -18,14 +18,14 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<button
 | 
			
		||||
  class="relative transition-all rounded-lg"
 | 
			
		||||
  class="relative rounded-lg transition-all"
 | 
			
		||||
  on:click={handleOnClicked}
 | 
			
		||||
  disabled={!selectable}
 | 
			
		||||
  style:width={thumbnailSize ? thumbnailSize + 'px' : '100%'}
 | 
			
		||||
  style:height={thumbnailSize ? thumbnailSize + 'px' : '100%'}
 | 
			
		||||
>
 | 
			
		||||
  <div
 | 
			
		||||
    class="filter w-full h-full brightness-90 border-2"
 | 
			
		||||
    class="h-full w-full border-2 brightness-90 filter"
 | 
			
		||||
    class:rounded-full={circle}
 | 
			
		||||
    class:rounded-lg={!circle}
 | 
			
		||||
    class:border-transparent={!border}
 | 
			
		||||
@ -42,7 +42,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div
 | 
			
		||||
    class="absolute top-0 left-0 w-full h-full bg-immich-primary/30 opacity-0"
 | 
			
		||||
    class="absolute left-0 top-0 h-full w-full bg-immich-primary/30 opacity-0"
 | 
			
		||||
    class:hover:opacity-100={selectable}
 | 
			
		||||
    class:rounded-full={circle}
 | 
			
		||||
    class:rounded-lg={!circle}
 | 
			
		||||
@ -50,7 +50,7 @@
 | 
			
		||||
 | 
			
		||||
  {#if selected}
 | 
			
		||||
    <div
 | 
			
		||||
      class="absolute top-0 left-0 w-full h-full bg-blue-500/80"
 | 
			
		||||
      class="absolute left-0 top-0 h-full w-full bg-blue-500/80"
 | 
			
		||||
      class:rounded-full={circle}
 | 
			
		||||
      class:rounded-lg={!circle}
 | 
			
		||||
    />
 | 
			
		||||
@ -58,7 +58,7 @@
 | 
			
		||||
 | 
			
		||||
  {#if person.name}
 | 
			
		||||
    <span
 | 
			
		||||
      class="absolute bottom-2 left-0 w-full text-center font-medium text-white text-ellipsis w-100 px-1 hover:cursor-pointer backdrop-blur-[1px]"
 | 
			
		||||
      class="w-100 absolute bottom-2 left-0 w-full text-ellipsis px-1 text-center font-medium text-white backdrop-blur-[1px] hover:cursor-pointer"
 | 
			
		||||
    >
 | 
			
		||||
      {person.name}
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  transition:fly={{ y: 500, duration: 100, easing: quintOut }}
 | 
			
		||||
  class="absolute top-0 left-0 w-full h-full bg-immich-bg dark:bg-immich-dark-bg z-[9999]"
 | 
			
		||||
  class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg"
 | 
			
		||||
>
 | 
			
		||||
  <ControlAppBar on:close-button-click={onClose}>
 | 
			
		||||
    <svelte:fragment slot="leading">
 | 
			
		||||
@ -99,12 +99,12 @@
 | 
			
		||||
      >
 | 
			
		||||
    </svelte:fragment>
 | 
			
		||||
  </ControlAppBar>
 | 
			
		||||
  <section class="pt-[100px] px-[70px] bg-immich-bg dark:bg-immich-dark-bg">
 | 
			
		||||
  <section class="bg-immich-bg px-[70px] pt-[100px] dark:bg-immich-dark-bg">
 | 
			
		||||
    <section id="merge-face-selector relative">
 | 
			
		||||
      <div class="place-items-center place-content-center mb-10 h-[200px]">
 | 
			
		||||
        <p class="uppercase mb-4 dark:text-white text-center">Choose matching faces to merge</p>
 | 
			
		||||
      <div class="mb-10 h-[200px] place-content-center place-items-center">
 | 
			
		||||
        <p class="mb-4 text-center uppercase dark:text-white">Choose matching faces to merge</p>
 | 
			
		||||
 | 
			
		||||
        <div class="grid grid-flow-col-dense place-items-center place-content-center gap-4">
 | 
			
		||||
        <div class="grid grid-flow-col-dense place-content-center place-items-center gap-4">
 | 
			
		||||
          {#each selectedPeople as person (person.id)}
 | 
			
		||||
            <div animate:flip={{ duration: 250, easing: quintOut }}>
 | 
			
		||||
              <FaceThumbnail border circle {person} selectable thumbnailSize={120} on:click={() => onSelect(person)} />
 | 
			
		||||
@ -118,10 +118,10 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div
 | 
			
		||||
        class="p-10 overflow-y-auto rounded-3xl bg-gray-200 dark:bg-immich-dark-gray"
 | 
			
		||||
        class="overflow-y-auto rounded-3xl bg-gray-200 p-10 dark:bg-immich-dark-gray"
 | 
			
		||||
        style:max-height={screenHeight - 200 - 200 + 'px'}
 | 
			
		||||
      >
 | 
			
		||||
        <div class="grid grid-col-2 md:grid-cols-3 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10 gap-8">
 | 
			
		||||
        <div class="grid-col-2 grid gap-8 md:grid-cols-3 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10">
 | 
			
		||||
          {#each unselectedPeople as person (person.id)}
 | 
			
		||||
            <FaceThumbnail {person} on:click={() => onSelect(person)} circle border selectable />
 | 
			
		||||
          {/each}
 | 
			
		||||
 | 
			
		||||
@ -11,19 +11,19 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  transition:fly={{ y: 500, duration: 100, easing: quintOut }}
 | 
			
		||||
  class="absolute top-0 left-0 w-full h-full bg-immich-bg dark:bg-immich-dark-bg z-[9999]"
 | 
			
		||||
  class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg"
 | 
			
		||||
>
 | 
			
		||||
  <div
 | 
			
		||||
    class="absolute border-b dark:border-immich-dark-gray flex justify-between place-items-center dark:text-immich-dark-fg w-full h-16"
 | 
			
		||||
    class="absolute flex h-16 w-full place-items-center justify-between border-b dark:border-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex items-center justify-between p-8 w-full">
 | 
			
		||||
    <div class="flex w-full items-center justify-between p-8">
 | 
			
		||||
      <div class="flex items-center">
 | 
			
		||||
        <CircleIconButton logo={Close} on:click={() => dispatch('closeClick')} />
 | 
			
		||||
        <p class="ml-4">Show & hide faces</p>
 | 
			
		||||
      </div>
 | 
			
		||||
      <IconButton on:click={() => dispatch('doneClick')}>Done</IconButton>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="absolute top-16 h-[calc(100%-theme(spacing.16))] w-full immich-scrollbar p-4 pb-8">
 | 
			
		||||
    <div class="immich-scrollbar absolute top-16 h-[calc(100%-theme(spacing.16))] w-full p-4 pb-8">
 | 
			
		||||
      <slot />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,7 @@
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<form on:submit|preventDefault={registerAdmin} method="post" class="flex flex-col gap-5 mt-5">
 | 
			
		||||
<form on:submit|preventDefault={registerAdmin} method="post" class="mt-5 flex flex-col gap-5">
 | 
			
		||||
  <div class="flex flex-col gap-2">
 | 
			
		||||
    <label class="immich-form-label" for="email">Admin Email</label>
 | 
			
		||||
    <input class="immich-form-input" id="email" name="email" type="email" autocomplete="email" required />
 | 
			
		||||
 | 
			
		||||
@ -17,13 +17,13 @@
 | 
			
		||||
 | 
			
		||||
<FullScreenModal on:clickOutside={() => handleCancel()}>
 | 
			
		||||
  <div
 | 
			
		||||
    class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 | 
			
		||||
    class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
  >
 | 
			
		||||
    <div
 | 
			
		||||
      class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
      class="flex flex-col place-content-center place-items-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
    >
 | 
			
		||||
      <KeyVariant size="4em" />
 | 
			
		||||
      <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
 | 
			
		||||
      <h1 class="text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        {title}
 | 
			
		||||
      </h1>
 | 
			
		||||
    </div>
 | 
			
		||||
@ -34,7 +34,7 @@
 | 
			
		||||
        <input class="immich-form-input" id="name" name="name" type="text" bind:value={apiKey.name} />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="flex w-full px-4 gap-4 mt-8">
 | 
			
		||||
      <div class="mt-8 flex w-full gap-4 px-4">
 | 
			
		||||
        <Button color="gray" fullwidth on:click={() => handleCancel()}>{cancelText}</Button>
 | 
			
		||||
        <Button type="submit" fullwidth>{submitText}</Button>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@ -31,13 +31,13 @@
 | 
			
		||||
 | 
			
		||||
<FullScreenModal>
 | 
			
		||||
  <div
 | 
			
		||||
    class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 | 
			
		||||
    class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
  >
 | 
			
		||||
    <div
 | 
			
		||||
      class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
      class="flex flex-col place-content-center place-items-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
    >
 | 
			
		||||
      <KeyVariant size="4em" />
 | 
			
		||||
      <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">API Key</h1>
 | 
			
		||||
      <h1 class="text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">API Key</h1>
 | 
			
		||||
 | 
			
		||||
      <p class="text-sm dark:text-immich-dark-fg">
 | 
			
		||||
        This value will only be shown once. Please be sure to copy it before closing the window.
 | 
			
		||||
@ -49,7 +49,7 @@
 | 
			
		||||
      <textarea class="immich-form-input" id="secret" name="secret" readonly={true} value={secret} />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="flex w-full px-4 gap-4 mt-8">
 | 
			
		||||
    <div class="mt-8 flex w-full gap-4 px-4">
 | 
			
		||||
      {#if canCopyImagesToClipboard}
 | 
			
		||||
        <Button on:click={() => handleCopy()} fullwidth>Copy to Clipboard</Button>
 | 
			
		||||
      {/if}
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<form on:submit|preventDefault={changePassword} method="post" class="flex flex-col gap-5 mt-5">
 | 
			
		||||
<form on:submit|preventDefault={changePassword} method="post" class="mt-5 flex flex-col gap-5">
 | 
			
		||||
  <div class="flex flex-col gap-2">
 | 
			
		||||
    <label class="immich-form-label" for="password">New Password</label>
 | 
			
		||||
    <input
 | 
			
		||||
@ -74,11 +74,11 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {#if error}
 | 
			
		||||
    <p class="text-red-400 text-sm">{error}</p>
 | 
			
		||||
    <p class="text-sm text-red-400">{error}</p>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  {#if success}
 | 
			
		||||
    <p class="text-immich-primary text-sm">{success}</p>
 | 
			
		||||
    <p class="text-sm text-immich-primary">{success}</p>
 | 
			
		||||
  {/if}
 | 
			
		||||
  <div class="my-5 flex w-full">
 | 
			
		||||
    <Button type="submit" size="lg" fullwidth>Change password</Button>
 | 
			
		||||
 | 
			
		||||
@ -78,12 +78,12 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
  class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 | 
			
		||||
  class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
>
 | 
			
		||||
  <div class="flex flex-col place-items-center place-content-center gap-4 px-4">
 | 
			
		||||
  <div class="flex flex-col place-content-center place-items-center gap-4 px-4">
 | 
			
		||||
    <ImmichLogo class="text-center" height="100" width="100" />
 | 
			
		||||
    <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">Create new user</h1>
 | 
			
		||||
    <p class="text-sm border rounded-md p-4 font-mono text-gray-600 dark:border-immich-dark-bg dark:text-gray-300">
 | 
			
		||||
    <h1 class="text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">Create new user</h1>
 | 
			
		||||
    <p class="rounded-md border p-4 font-mono text-sm text-gray-600 dark:border-immich-dark-bg dark:text-gray-300">
 | 
			
		||||
      Please provide your user with the password, they will have to change it on their first sign in.
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
@ -122,11 +122,11 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {#if error}
 | 
			
		||||
      <p class="text-red-400 ml-4 text-sm">{error}</p>
 | 
			
		||||
      <p class="ml-4 text-sm text-red-400">{error}</p>
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    {#if success}
 | 
			
		||||
      <p class="text-immich-primary ml-4 text-sm">{success}</p>
 | 
			
		||||
      <p class="ml-4 text-sm text-immich-primary">{success}</p>
 | 
			
		||||
    {/if}
 | 
			
		||||
    <div class="flex w-full p-4">
 | 
			
		||||
      <Button type="submit" disabled={isCreatingUser} fullwidth>Create</Button>
 | 
			
		||||
 | 
			
		||||
@ -67,13 +67,13 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
  class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 | 
			
		||||
  class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
>
 | 
			
		||||
  <div
 | 
			
		||||
    class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
    class="flex flex-col place-content-center place-items-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
  >
 | 
			
		||||
    <AccountEditOutline size="4em" />
 | 
			
		||||
    <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">Edit user</h1>
 | 
			
		||||
    <h1 class="text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">Edit user</h1>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <form on:submit|preventDefault={editUser} autocomplete="off">
 | 
			
		||||
@ -134,13 +134,13 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {#if error}
 | 
			
		||||
      <p class="text-red-400 ml-4 text-sm">{error}</p>
 | 
			
		||||
      <p class="ml-4 text-sm text-red-400">{error}</p>
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    {#if success}
 | 
			
		||||
      <p class="text-immich-primary ml-4 text-sm">{success}</p>
 | 
			
		||||
      <p class="ml-4 text-sm text-immich-primary">{success}</p>
 | 
			
		||||
    {/if}
 | 
			
		||||
    <div class="flex w-full px-4 gap-4 mt-8">
 | 
			
		||||
    <div class="mt-8 flex w-full gap-4 px-4">
 | 
			
		||||
      {#if canResetPassword}
 | 
			
		||||
        <Button color="light-red" fullwidth on:click={() => (isShowResetPasswordConfirmation = true)}
 | 
			
		||||
          >Reset password</Button
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if authConfig.passwordLoginEnabled}
 | 
			
		||||
  <form on:submit|preventDefault={login} class="flex flex-col gap-5 mt-5">
 | 
			
		||||
  <form on:submit|preventDefault={login} class="mt-5 flex flex-col gap-5">
 | 
			
		||||
    {#if errorMessage}
 | 
			
		||||
      <p class="text-red-400" transition:fade>
 | 
			
		||||
        {errorMessage}
 | 
			
		||||
@ -128,10 +128,10 @@
 | 
			
		||||
 | 
			
		||||
{#if authConfig.enabled}
 | 
			
		||||
  {#if authConfig.passwordLoginEnabled}
 | 
			
		||||
    <div class="inline-flex items-center justify-center w-full">
 | 
			
		||||
      <hr class="w-3/4 h-px my-4 bg-gray-200 border-0 dark:bg-gray-600" />
 | 
			
		||||
    <div class="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 px-3 font-medium text-gray-900 -translate-x-1/2 left-1/2 dark:text-white bg-white dark:bg-immich-dark-gray"
 | 
			
		||||
        class="absolute left-1/2 -translate-x-1/2 bg-white px-3 font-medium text-gray-900 dark:bg-immich-dark-gray dark:text-white"
 | 
			
		||||
      >
 | 
			
		||||
        or
 | 
			
		||||
      </span>
 | 
			
		||||
@ -162,5 +162,5 @@
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
{#if !authConfig.enabled && !authConfig.passwordLoginEnabled}
 | 
			
		||||
  <p class="text-center dark:text-immich-dark-fg p-4">Login has been disabled.</p>
 | 
			
		||||
  <p class="p-4 text-center dark:text-immich-dark-fg">Login has been disabled.</p>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@
 | 
			
		||||
  <slot name="header" />
 | 
			
		||||
</header>
 | 
			
		||||
<main
 | 
			
		||||
  class="grid md:grid-cols-[theme(spacing.64)_auto] grid-cols-[theme(spacing.18)_auto] relative pt-[var(--navbar-height)] h-screen overflow-hidden bg-immich-bg dark:bg-immich-dark-bg"
 | 
			
		||||
  class="relative grid h-screen grid-cols-[theme(spacing.18)_auto] overflow-hidden bg-immich-bg pt-[var(--navbar-height)] dark:bg-immich-dark-bg md:grid-cols-[theme(spacing.64)_auto]"
 | 
			
		||||
>
 | 
			
		||||
  <slot name="sidebar">
 | 
			
		||||
    <SideBar />
 | 
			
		||||
@ -26,13 +26,13 @@
 | 
			
		||||
    {#if title}
 | 
			
		||||
      <section class="relative">
 | 
			
		||||
        <div
 | 
			
		||||
          class="absolute border-b dark:border-immich-dark-gray flex justify-between place-items-center dark:text-immich-dark-fg w-full p-4 h-16"
 | 
			
		||||
          class="absolute flex h-16 w-full place-items-center justify-between border-b p-4 dark:border-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
        >
 | 
			
		||||
          <p class="font-medium">{title}</p>
 | 
			
		||||
          <slot name="buttons" />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="absolute overflow-y-auto top-16 h-[calc(100%-theme(spacing.16))] w-full immich-scrollbar p-4 pb-8">
 | 
			
		||||
        <div class="immich-scrollbar absolute top-16 h-[calc(100%-theme(spacing.16))] w-full overflow-y-auto p-4 pb-8">
 | 
			
		||||
          <slot />
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
@ -20,9 +20,9 @@
 | 
			
		||||
 | 
			
		||||
<FullScreenModal on:clickOutside={() => dispatch('close')}>
 | 
			
		||||
  <div
 | 
			
		||||
    class="flex flex-col gap-8 border bg-white dark:bg-immich-dark-gray dark:border-immich-dark-gray p-8 shadow-sm w-96 max-w-lg rounded-3xl"
 | 
			
		||||
    class="flex w-96 max-w-lg flex-col gap-8 rounded-3xl border bg-white p-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray"
 | 
			
		||||
  >
 | 
			
		||||
    <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium self-center">Map Settings</h1>
 | 
			
		||||
    <h1 class="self-center text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">Map Settings</h1>
 | 
			
		||||
 | 
			
		||||
    <form
 | 
			
		||||
      on:submit|preventDefault={() => dispatch('save', settings)}
 | 
			
		||||
@ -32,8 +32,8 @@
 | 
			
		||||
      <SettingSwitch title="Only favorites" bind:checked={settings.onlyFavorites} />
 | 
			
		||||
      {#if customDateRange}
 | 
			
		||||
        <div in:fly={{ y: 10, duration: 200 }} class="flex flex-col gap-4">
 | 
			
		||||
          <div class="flex justify-between items-center gap-8">
 | 
			
		||||
            <label class="immich-form-label text-sm shrink-0" for="date-after">Date after</label>
 | 
			
		||||
          <div class="flex items-center justify-between gap-8">
 | 
			
		||||
            <label class="immich-form-label shrink-0 text-sm" for="date-after">Date after</label>
 | 
			
		||||
            <input
 | 
			
		||||
              class="immich-form-input w-40"
 | 
			
		||||
              type="date"
 | 
			
		||||
@ -42,8 +42,8 @@
 | 
			
		||||
              bind:value={settings.dateAfter}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="flex justify-between items-center gap-8">
 | 
			
		||||
            <label class="immich-form-label text-sm shrink-0" for="date-before">Date before</label>
 | 
			
		||||
          <div class="flex items-center justify-between gap-8">
 | 
			
		||||
            <label class="immich-form-label shrink-0 text-sm" for="date-before">Date before</label>
 | 
			
		||||
            <input class="immich-form-input w-40" type="date" id="date-before" bind:value={settings.dateBefore} />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="flex justify-center text-xs">
 | 
			
		||||
@ -104,7 +104,7 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      {/if}
 | 
			
		||||
 | 
			
		||||
      <div class="flex w-full gap-4 mt-4">
 | 
			
		||||
      <div class="mt-4 flex w-full gap-4">
 | 
			
		||||
        <Button color="gray" size="sm" fullwidth on:click={() => dispatch('close')}>Cancel</Button>
 | 
			
		||||
        <Button type="submit" size="sm" fullwidth>Save</Button>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@ -111,12 +111,12 @@
 | 
			
		||||
      </svelte:fragment>
 | 
			
		||||
 | 
			
		||||
      {#if !galleryInView}
 | 
			
		||||
        <div class="flex place-items-center place-content-center overflow-hidden gap-2">
 | 
			
		||||
        <div class="flex place-content-center place-items-center gap-2 overflow-hidden">
 | 
			
		||||
          <CircleIconButton logo={paused ? Play : Pause} forceDark on:click={() => (paused = !paused)} />
 | 
			
		||||
 | 
			
		||||
          {#each currentMemory.assets as _, i}
 | 
			
		||||
            <button class="relative w-full py-2" on:click={() => goto(`?memory=${memoryIndex}&asset=${i}`)}>
 | 
			
		||||
              <span class="absolute left-0 w-full h-[2px] bg-gray-500" />
 | 
			
		||||
              <span class="absolute left-0 h-[2px] w-full bg-gray-500" />
 | 
			
		||||
              {#await resetPromise}
 | 
			
		||||
                <span class="absolute left-0 h-[2px] bg-white" style:width={`${i < assetIndex ? 100 : 0}%`} />
 | 
			
		||||
              {:then}
 | 
			
		||||
@ -139,7 +139,7 @@
 | 
			
		||||
 | 
			
		||||
    {#if galleryInView}
 | 
			
		||||
      <div
 | 
			
		||||
        class="sticky top-20 flex place-content-center place-items-center z-30 transition-opacity"
 | 
			
		||||
        class="sticky top-20 z-30 flex place-content-center place-items-center transition-opacity"
 | 
			
		||||
        class:opacity-0={!galleryInView}
 | 
			
		||||
        class:opacity-100={galleryInView}
 | 
			
		||||
      >
 | 
			
		||||
@ -149,28 +149,28 @@
 | 
			
		||||
      </div>
 | 
			
		||||
    {/if}
 | 
			
		||||
    <!-- Viewer -->
 | 
			
		||||
    <section class="pt-20 overflow-hidden">
 | 
			
		||||
    <section class="overflow-hidden pt-20">
 | 
			
		||||
      <div
 | 
			
		||||
        class="flex w-[300%] h-[calc(100vh_-_180px)] items-center justify-center box-border ml-[-100%] gap-10 overflow-hidden"
 | 
			
		||||
        class="ml-[-100%] box-border flex h-[calc(100vh_-_180px)] w-[300%] items-center justify-center gap-10 overflow-hidden"
 | 
			
		||||
      >
 | 
			
		||||
        <!-- PREVIOUS MEMORY -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="rounded-2xl w-[20vw] h-1/2"
 | 
			
		||||
          class="h-1/2 w-[20vw] rounded-2xl"
 | 
			
		||||
          class:opacity-25={previousMemory}
 | 
			
		||||
          class:opacity-0={!previousMemory}
 | 
			
		||||
          class:hover:opacity-70={previousMemory}
 | 
			
		||||
        >
 | 
			
		||||
          <button class="rounded-2xl h-full w-full relative" disabled={!previousMemory} on:click={toPreviousMemory}>
 | 
			
		||||
          <button class="relative h-full w-full rounded-2xl" disabled={!previousMemory} on:click={toPreviousMemory}>
 | 
			
		||||
            <img
 | 
			
		||||
              class="rounded-2xl h-full w-full object-cover"
 | 
			
		||||
              class="h-full w-full rounded-2xl object-cover"
 | 
			
		||||
              src={previousMemory ? api.getAssetThumbnailUrl(previousMemory.assets[0].id, 'JPEG') : noThumbnailUrl}
 | 
			
		||||
              alt=""
 | 
			
		||||
              draggable="false"
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            {#if previousMemory}
 | 
			
		||||
              <div class="absolute right-4 bottom-4 text-white text-left">
 | 
			
		||||
                <p class="font-semibold text-xs text-gray-200">PREVIOUS</p>
 | 
			
		||||
              <div class="absolute bottom-4 right-4 text-left text-white">
 | 
			
		||||
                <p class="text-xs font-semibold text-gray-200">PREVIOUS</p>
 | 
			
		||||
                <p class="text-xl">{previousMemory.title}</p>
 | 
			
		||||
              </div>
 | 
			
		||||
            {/if}
 | 
			
		||||
@ -179,19 +179,19 @@
 | 
			
		||||
 | 
			
		||||
        <!-- CURRENT MEMORY -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="main-view rounded-2xl h-full relative w-[70vw] bg-black flex place-items-center place-content-center"
 | 
			
		||||
          class="main-view relative flex h-full w-[70vw] place-content-center place-items-center rounded-2xl bg-black"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="bg-black w-full h-full rounded-2xl">
 | 
			
		||||
          <div class="h-full w-full rounded-2xl bg-black">
 | 
			
		||||
            <!-- CONTROL BUTTONS -->
 | 
			
		||||
            <div class="absolute h-full flex justify-between w-full">
 | 
			
		||||
              <div class="flex h-full flex-col place-content-center place-items-center ml-4">
 | 
			
		||||
            <div class="absolute flex h-full w-full justify-between">
 | 
			
		||||
              <div class="ml-4 flex h-full flex-col place-content-center place-items-center">
 | 
			
		||||
                <div class="inline-block">
 | 
			
		||||
                  {#if canGoBack}
 | 
			
		||||
                    <CircleIconButton logo={ChevronLeft} backgroundColor="#202123" on:click={toPrevious} />
 | 
			
		||||
                  {/if}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="flex h-full flex-col place-content-center place-items-center mr-4">
 | 
			
		||||
              <div class="mr-4 flex h-full flex-col place-content-center place-items-center">
 | 
			
		||||
                <div class="inline-block">
 | 
			
		||||
                  {#if canGoForward}
 | 
			
		||||
                    <CircleIconButton logo={ChevronRight} backgroundColor="#202123" on:click={toNext} />
 | 
			
		||||
@ -203,14 +203,14 @@
 | 
			
		||||
            {#key currentAsset.id}
 | 
			
		||||
              <img
 | 
			
		||||
                transition:fade
 | 
			
		||||
                class="rounded-2xl w-full h-full object-contain transition-all"
 | 
			
		||||
                class="h-full w-full rounded-2xl object-contain transition-all"
 | 
			
		||||
                src={api.getAssetThumbnailUrl(currentAsset.id, 'JPEG')}
 | 
			
		||||
                alt=""
 | 
			
		||||
                draggable="false"
 | 
			
		||||
              />
 | 
			
		||||
            {/key}
 | 
			
		||||
 | 
			
		||||
            <div class="absolute top-4 left-8 text-white text-sm font-medium">
 | 
			
		||||
            <div class="absolute left-8 top-4 text-sm font-medium text-white">
 | 
			
		||||
              <p>
 | 
			
		||||
                {DateTime.fromISO(currentMemory.assets[0].fileCreatedAt).toLocaleString(DateTime.DATE_FULL)}
 | 
			
		||||
              </p>
 | 
			
		||||
@ -224,22 +224,22 @@
 | 
			
		||||
 | 
			
		||||
        <!-- NEXT MEMORY -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="rounded-xl w-[20vw] h-1/2"
 | 
			
		||||
          class="h-1/2 w-[20vw] rounded-xl"
 | 
			
		||||
          class:opacity-25={nextMemory}
 | 
			
		||||
          class:opacity-0={!nextMemory}
 | 
			
		||||
          class:hover:opacity-70={nextMemory}
 | 
			
		||||
        >
 | 
			
		||||
          <button class="rounded-2xl h-full w-full relative" on:click={toNextMemory} disabled={!nextMemory}>
 | 
			
		||||
          <button class="relative h-full w-full rounded-2xl" on:click={toNextMemory} disabled={!nextMemory}>
 | 
			
		||||
            <img
 | 
			
		||||
              class="rounded-2xl h-full w-full object-cover"
 | 
			
		||||
              class="h-full w-full rounded-2xl object-cover"
 | 
			
		||||
              src={nextMemory ? api.getAssetThumbnailUrl(nextMemory.assets[0].id, 'JPEG') : noThumbnailUrl}
 | 
			
		||||
              alt=""
 | 
			
		||||
              draggable="false"
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            {#if nextMemory}
 | 
			
		||||
              <div class="absolute left-4 bottom-4 text-white text-left">
 | 
			
		||||
                <p class="font-semibold text-xs text-gray-200">UP NEXT</p>
 | 
			
		||||
              <div class="absolute bottom-4 left-4 text-left text-white">
 | 
			
		||||
                <p class="text-xs font-semibold text-gray-200">UP NEXT</p>
 | 
			
		||||
                <p class="text-xl">{nextMemory.title}</p>
 | 
			
		||||
              </div>
 | 
			
		||||
            {/if}
 | 
			
		||||
@ -252,7 +252,7 @@
 | 
			
		||||
 | 
			
		||||
    <section class="bg-immich-dark-gray pl-4">
 | 
			
		||||
      <div
 | 
			
		||||
        class="sticky flex place-content-center place-items-center mb-10 mt-4 transition-all"
 | 
			
		||||
        class="sticky mb-10 mt-4 flex place-content-center place-items-center transition-all"
 | 
			
		||||
        class:opacity-0={galleryInView}
 | 
			
		||||
        class:opacity-100={!galleryInView}
 | 
			
		||||
      >
 | 
			
		||||
 | 
			
		||||
@ -155,7 +155,7 @@
 | 
			
		||||
 | 
			
		||||
    <!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
			
		||||
    <div
 | 
			
		||||
      class="flex flex-col mt-5"
 | 
			
		||||
      class="mt-5 flex flex-col"
 | 
			
		||||
      on:mouseenter={() => {
 | 
			
		||||
        isMouseOverGroup = true;
 | 
			
		||||
        assetMouseEventHandler(dateGroupTitle, null);
 | 
			
		||||
@ -167,7 +167,7 @@
 | 
			
		||||
    >
 | 
			
		||||
      <!-- Date group title -->
 | 
			
		||||
      <p
 | 
			
		||||
        class="font-medium text-xs md:text-sm text-immich-fg dark:text-immich-dark-fg mb-2 flex place-items-center h-6"
 | 
			
		||||
        class="mb-2 flex h-6 place-items-center text-xs font-medium text-immich-fg dark:text-immich-dark-fg md:text-sm"
 | 
			
		||||
        style="width: {geometry[groupIndex].containerWidth}px"
 | 
			
		||||
      >
 | 
			
		||||
        {#if (hoveredDateGroup == dateGroupTitle && isMouseOverGroup) || $selectedGroup.has(dateGroupTitle)}
 | 
			
		||||
@ -185,7 +185,7 @@
 | 
			
		||||
          </div>
 | 
			
		||||
        {/if}
 | 
			
		||||
 | 
			
		||||
        <span class="first-letter:capitalize truncate" title={dateGroupTitle}>
 | 
			
		||||
        <span class="truncate first-letter:capitalize" title={dateGroupTitle}>
 | 
			
		||||
          {dateGroupTitle}
 | 
			
		||||
        </span>
 | 
			
		||||
      </p>
 | 
			
		||||
 | 
			
		||||
@ -283,7 +283,7 @@
 | 
			
		||||
<!-- Right margin MUST be equal to the width of immich-scrubbable-scrollbar -->
 | 
			
		||||
<section
 | 
			
		||||
  id="asset-grid"
 | 
			
		||||
  class="overflow-y-auto ml-4 mb-4 mr-[60px] scrollbar-hidden"
 | 
			
		||||
  class="scrollbar-hidden mb-4 ml-4 mr-[60px] overflow-y-auto"
 | 
			
		||||
  bind:clientHeight={viewportHeight}
 | 
			
		||||
  bind:clientWidth={viewportWidth}
 | 
			
		||||
  bind:this={assetGridElement}
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@
 | 
			
		||||
  <section
 | 
			
		||||
    id="memory-lane"
 | 
			
		||||
    bind:this={memoryLaneElement}
 | 
			
		||||
    class="relative overflow-x-hidden whitespace-nowrap mt-5 transition-all"
 | 
			
		||||
    class="relative mt-5 overflow-x-hidden whitespace-nowrap transition-all"
 | 
			
		||||
    bind:offsetWidth
 | 
			
		||||
    on:scroll={onScroll}
 | 
			
		||||
  >
 | 
			
		||||
@ -46,7 +46,7 @@
 | 
			
		||||
        {#if canScrollLeft}
 | 
			
		||||
          <div class="absolute left-4 top-[6rem] z-20" transition:fade={{ duration: 200 }}>
 | 
			
		||||
            <button
 | 
			
		||||
              class="rounded-full opacity-50 hover:opacity-100 p-2 border border-gray-500 bg-gray-100 text-gray-500"
 | 
			
		||||
              class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
 | 
			
		||||
              on:click={scrollLeft}
 | 
			
		||||
            >
 | 
			
		||||
              <ChevronLeft size="36" /></button
 | 
			
		||||
@ -56,7 +56,7 @@
 | 
			
		||||
        {#if canScrollRight}
 | 
			
		||||
          <div class="absolute right-4 top-[6rem] z-20" transition:fade={{ duration: 200 }}>
 | 
			
		||||
            <button
 | 
			
		||||
              class="rounded-full opacity-50 hover:opacity-100 p-2 border border-gray-500 bg-gray-100 text-gray-500"
 | 
			
		||||
              class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
 | 
			
		||||
              on:click={scrollRight}
 | 
			
		||||
            >
 | 
			
		||||
              <ChevronRight size="36" /></button
 | 
			
		||||
@ -69,18 +69,18 @@
 | 
			
		||||
    <div class="inline-block" bind:offsetWidth={innerWidth}>
 | 
			
		||||
      {#each $memoryStore as memory, i (memory.title)}
 | 
			
		||||
        <button
 | 
			
		||||
          class="memory-card relative inline-block mr-8 rounded-xl aspect-video h-[215px]"
 | 
			
		||||
          class="memory-card relative mr-8 inline-block aspect-video h-[215px] rounded-xl"
 | 
			
		||||
          on:click={() => goto(`/memory?memory=${i}`)}
 | 
			
		||||
        >
 | 
			
		||||
          <img
 | 
			
		||||
            class="rounded-xl h-full w-full object-cover"
 | 
			
		||||
            class="h-full w-full rounded-xl object-cover"
 | 
			
		||||
            src={api.getAssetThumbnailUrl(memory.assets[0].id, 'JPEG')}
 | 
			
		||||
            alt={memory.title}
 | 
			
		||||
            draggable="false"
 | 
			
		||||
          />
 | 
			
		||||
          <p class="absolute bottom-2 left-4 text-lg text-white z-10">{memory.title}</p>
 | 
			
		||||
          <p class="absolute bottom-2 left-4 z-10 text-lg text-white">{memory.title}</p>
 | 
			
		||||
          <div
 | 
			
		||||
            class="absolute top-0 left-0 w-full h-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent z-0 hover:bg-black/20 transition-all"
 | 
			
		||||
            class="absolute left-0 top-0 z-0 h-full w-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20"
 | 
			
		||||
          />
 | 
			
		||||
        </button>
 | 
			
		||||
      {/each}
 | 
			
		||||
 | 
			
		||||
@ -86,7 +86,7 @@
 | 
			
		||||
      <svelte:fragment slot="leading">
 | 
			
		||||
        <a
 | 
			
		||||
          data-sveltekit-preload-data="hover"
 | 
			
		||||
          class="flex gap-2 place-items-center hover:cursor-pointer ml-6"
 | 
			
		||||
          class="ml-6 flex place-items-center gap-2 hover:cursor-pointer"
 | 
			
		||||
          href="https://immich.app"
 | 
			
		||||
        >
 | 
			
		||||
          <ImmichLogo height="30" width="30" />
 | 
			
		||||
@ -105,7 +105,7 @@
 | 
			
		||||
      </svelte:fragment>
 | 
			
		||||
    </ControlAppBar>
 | 
			
		||||
  {/if}
 | 
			
		||||
  <section class="flex flex-col my-[160px] px-6 sm:px-12 md:px-24 lg:px-40">
 | 
			
		||||
  <section class="my-[160px] flex flex-col px-6 sm:px-12 md:px-24 lg:px-40">
 | 
			
		||||
    <GalleryViewer {assets} {sharedLink} bind:selectedAssets viewFrom="shared-link-page" />
 | 
			
		||||
  </section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@ -49,23 +49,23 @@
 | 
			
		||||
 | 
			
		||||
<BaseModal on:close={() => dispatch('close')}>
 | 
			
		||||
  <svelte:fragment slot="title">
 | 
			
		||||
    <span class="flex gap-2 place-items-center">
 | 
			
		||||
    <span class="flex place-items-center gap-2">
 | 
			
		||||
      <p class="font-medium">
 | 
			
		||||
        Add to {#if shared}Shared {/if} Album
 | 
			
		||||
      </p>
 | 
			
		||||
    </span>
 | 
			
		||||
  </svelte:fragment>
 | 
			
		||||
 | 
			
		||||
  <div class="max-h-[400px] flex flex-col mb-2">
 | 
			
		||||
  <div class="mb-2 flex max-h-[400px] flex-col">
 | 
			
		||||
    {#if loading}
 | 
			
		||||
      {#each { length: 3 } as _}
 | 
			
		||||
        <div class="animate-pulse flex gap-4 px-6 py-2">
 | 
			
		||||
          <div class="h-12 w-12 bg-slate-200 rounded-xl" />
 | 
			
		||||
        <div class="flex animate-pulse gap-4 px-6 py-2">
 | 
			
		||||
          <div class="h-12 w-12 rounded-xl bg-slate-200" />
 | 
			
		||||
          <div class="flex flex-col items-start justify-center gap-2">
 | 
			
		||||
            <span class="animate-pulse w-36 h-4 bg-slate-200" />
 | 
			
		||||
            <span class="h-4 w-36 animate-pulse bg-slate-200" />
 | 
			
		||||
            <div class="flex animate-pulse gap-1">
 | 
			
		||||
              <span class="w-8 h-3 bg-slate-200" />
 | 
			
		||||
              <span class="w-20 h-3 bg-slate-200" />
 | 
			
		||||
              <span class="h-3 w-8 bg-slate-200" />
 | 
			
		||||
              <span class="h-3 w-20 bg-slate-200" />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@ -73,17 +73,17 @@
 | 
			
		||||
    {:else}
 | 
			
		||||
      <!-- svelte-ignore a11y-autofocus -->
 | 
			
		||||
      <input
 | 
			
		||||
        class="px-6 py-2 text-2xl border-b-4 bg-immich-bg border-immich-bg focus:border-immich-primary dark:bg-immich-dark-gray dark:border-immich-dark-gray dark:focus:border-immich-dark-primary"
 | 
			
		||||
        class="border-b-4 border-immich-bg bg-immich-bg px-6 py-2 text-2xl focus:border-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:focus:border-immich-dark-primary"
 | 
			
		||||
        placeholder="Search"
 | 
			
		||||
        autofocus
 | 
			
		||||
        bind:value={search}
 | 
			
		||||
      />
 | 
			
		||||
      <div class="overflow-y-auto immich-scrollbar">
 | 
			
		||||
      <div class="immich-scrollbar overflow-y-auto">
 | 
			
		||||
        <button
 | 
			
		||||
          on:click={handleNew}
 | 
			
		||||
          class="w-full flex gap-4 px-6 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors items-center"
 | 
			
		||||
          class="flex w-full items-center gap-4 px-6 py-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="h-12 w-12 flex justify-center items-center">
 | 
			
		||||
          <div class="flex h-12 w-12 items-center justify-center">
 | 
			
		||||
            <Plus size="30" />
 | 
			
		||||
          </div>
 | 
			
		||||
          <p class="">
 | 
			
		||||
@ -92,14 +92,14 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        {#if filteredAlbums.length > 0}
 | 
			
		||||
          {#if !shared && search.length === 0}
 | 
			
		||||
            <p class="text-xs px-5 py-3">RECENT</p>
 | 
			
		||||
            <p class="px-5 py-3 text-xs">RECENT</p>
 | 
			
		||||
            {#each recentAlbums as album (album.id)}
 | 
			
		||||
              <AlbumListItem variant="simple" {album} on:album={() => handleSelect(album)} />
 | 
			
		||||
            {/each}
 | 
			
		||||
          {/if}
 | 
			
		||||
 | 
			
		||||
          {#if !shared}
 | 
			
		||||
            <p class="text-xs px-5 py-3">
 | 
			
		||||
            <p class="px-5 py-3 text-xs">
 | 
			
		||||
              {#if search.length === 0}ALL {/if}ALBUMS
 | 
			
		||||
            </p>
 | 
			
		||||
          {/if}
 | 
			
		||||
@ -107,9 +107,9 @@
 | 
			
		||||
            <AlbumListItem {album} searchQuery={search} on:album={() => handleSelect(album)} />
 | 
			
		||||
          {/each}
 | 
			
		||||
        {:else if albums.length > 0}
 | 
			
		||||
          <p class="text-sm px-5 py-1">It looks like you do not have any albums with this name yet.</p>
 | 
			
		||||
          <p class="px-5 py-1 text-sm">It looks like you do not have any albums with this name yet.</p>
 | 
			
		||||
        {:else}
 | 
			
		||||
          <p class="text-sm px-5 py-1">It looks like you do not have any albums yet.</p>
 | 
			
		||||
          <p class="px-5 py-1 text-sm">It looks like you do not have any albums yet.</p>
 | 
			
		||||
        {/if}
 | 
			
		||||
      </div>
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
@ -31,14 +31,14 @@
 | 
			
		||||
  id="immich-modal"
 | 
			
		||||
  style:z-index={zIndex}
 | 
			
		||||
  transition:fade={{ duration: 100, easing: quintOut }}
 | 
			
		||||
  class="fixed top-0 left-0 w-full h-full bg-black/50 flex place-items-center place-content-center overflow-hidden"
 | 
			
		||||
  class="fixed left-0 top-0 flex h-full w-full place-content-center place-items-center overflow-hidden bg-black/50"
 | 
			
		||||
>
 | 
			
		||||
  <div
 | 
			
		||||
    use:clickOutside
 | 
			
		||||
    on:outclick={() => dispatch('close')}
 | 
			
		||||
    class="bg-immich-bg dark:bg-immich-dark-gray dark:text-immich-dark-fg w-[450px] min-h-[200px] max-h-[600px] rounded-lg shadow-md"
 | 
			
		||||
    class="max-h-[600px] min-h-[200px] w-[450px] rounded-lg bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex justify-between place-items-center px-5 py-3">
 | 
			
		||||
    <div class="flex place-items-center justify-between px-5 py-3">
 | 
			
		||||
      <div>
 | 
			
		||||
        <slot name="title">
 | 
			
		||||
          <p>Modal Title</p>
 | 
			
		||||
 | 
			
		||||
@ -26,23 +26,23 @@
 | 
			
		||||
 | 
			
		||||
<FullScreenModal on:clickOutside={handleCancel}>
 | 
			
		||||
  <div
 | 
			
		||||
    class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 | 
			
		||||
    class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
  >
 | 
			
		||||
    <div
 | 
			
		||||
      class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
      class="flex flex-col place-content-center place-items-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
 | 
			
		||||
    >
 | 
			
		||||
      <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium pb-2">
 | 
			
		||||
      <h1 class="pb-2 text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        {title}
 | 
			
		||||
      </h1>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
      <div class="px-4 py-5 text-md text-center">
 | 
			
		||||
      <div class="text-md px-4 py-5 text-center">
 | 
			
		||||
        <slot name="prompt">
 | 
			
		||||
          <p>{prompt}</p>
 | 
			
		||||
        </slot>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="flex w-full px-4 gap-4 mt-4">
 | 
			
		||||
      <div class="mt-4 flex w-full gap-4 px-4">
 | 
			
		||||
        {#if !hideCancelButton}
 | 
			
		||||
          <Button color={cancelColor} fullwidth on:click={handleCancel}>
 | 
			
		||||
            {cancelText}
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@
 | 
			
		||||
<div
 | 
			
		||||
  transition:slide={{ duration: 200, easing: quintOut }}
 | 
			
		||||
  bind:this={menuElement}
 | 
			
		||||
  class="absolute w-[200px] z-[99999] rounded-lg overflow-hidden shadow-lg"
 | 
			
		||||
  class="absolute z-[99999] w-[200px] overflow-hidden rounded-lg shadow-lg"
 | 
			
		||||
  style="left: {left}px; top: {top}px;"
 | 
			
		||||
  role="menu"
 | 
			
		||||
  use:clickOutside
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
<button
 | 
			
		||||
  on:click
 | 
			
		||||
  class="bg-slate-100 hover:bg-gray-200 text-immich-fg dark:text-immich-dark-bg p-4 w-full text-left text-sm font-medium focus:outline-none focus:ring-inset focus:ring-2"
 | 
			
		||||
  class="w-full bg-slate-100 p-4 text-left text-sm font-medium text-immich-fg hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-inset dark:text-immich-dark-bg"
 | 
			
		||||
  role="menuitem"
 | 
			
		||||
>
 | 
			
		||||
  {#if text}
 | 
			
		||||
 | 
			
		||||
@ -40,14 +40,14 @@
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div in:fly={{ y: 10, duration: 200 }} class="fixed top-0 w-full bg-transparent z-[100]">
 | 
			
		||||
<div in:fly={{ y: 10, duration: 200 }} class="fixed top-0 z-[100] w-full bg-transparent">
 | 
			
		||||
  <div
 | 
			
		||||
    id="asset-selection-app-bar"
 | 
			
		||||
    class={`grid grid-cols-3 justify-between ${appBarBorder} rounded-lg p-2 mx-2 mt-2 transition-all place-items-center ${tailwindClasses} dark:bg-immich-dark-gray ${
 | 
			
		||||
    class={`grid grid-cols-3 justify-between ${appBarBorder} mx-2 mt-2 place-items-center rounded-lg p-2 transition-all ${tailwindClasses} dark:bg-immich-dark-gray ${
 | 
			
		||||
      forceDark && 'bg-immich-dark-gray text-white'
 | 
			
		||||
    }`}
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex place-items-center gap-6 dark:text-immich-dark-fg justify-self-start">
 | 
			
		||||
    <div class="flex place-items-center gap-6 justify-self-start dark:text-immich-dark-fg">
 | 
			
		||||
      {#if showBackButton}
 | 
			
		||||
        <CircleIconButton
 | 
			
		||||
          on:click={() => dispatch('close-button-click')}
 | 
			
		||||
@ -65,7 +65,7 @@
 | 
			
		||||
      <slot />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="flex place-items-center gap-1 mr-4 justify-self-end">
 | 
			
		||||
    <div class="mr-4 flex place-items-center gap-1 justify-self-end">
 | 
			
		||||
      <slot name="trailing" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
@ -140,7 +140,7 @@
 | 
			
		||||
 | 
			
		||||
<BaseModal on:close={() => dispatch('close')}>
 | 
			
		||||
  <svelte:fragment slot="title">
 | 
			
		||||
    <span class="flex gap-2 place-items-center">
 | 
			
		||||
    <span class="flex place-items-center gap-2">
 | 
			
		||||
      <Link size={24} />
 | 
			
		||||
      {#if editingLink}
 | 
			
		||||
        <p class="font-medium text-immich-fg dark:text-immich-dark-fg">Edit link</p>
 | 
			
		||||
@ -175,10 +175,10 @@
 | 
			
		||||
      {/if}
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    <div class="mt-4 mb-2">
 | 
			
		||||
    <div class="mb-2 mt-4">
 | 
			
		||||
      <p class="text-xs">LINK OPTIONS</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="p-4 bg-gray-100 dark:bg-black/40 rounded-lg">
 | 
			
		||||
    <div class="rounded-lg bg-gray-100 p-4 dark:bg-black/40">
 | 
			
		||||
      <div class="flex flex-col">
 | 
			
		||||
        <div class="mb-2">
 | 
			
		||||
          <SettingInputField inputType={SettingInputFieldType.TEXT} label="Description" bind:value={description} />
 | 
			
		||||
@ -198,11 +198,11 @@
 | 
			
		||||
 | 
			
		||||
        <div class="text-sm">
 | 
			
		||||
          {#if editingLink}
 | 
			
		||||
            <p class="my-2 immich-form-label">
 | 
			
		||||
            <p class="immich-form-label my-2">
 | 
			
		||||
              <SettingSwitch bind:checked={shouldChangeExpirationTime} title={'Change expiration time'} />
 | 
			
		||||
            </p>
 | 
			
		||||
          {:else}
 | 
			
		||||
            <p class="my-2 immich-form-label">Expire after</p>
 | 
			
		||||
            <p class="immich-form-label my-2">Expire after</p>
 | 
			
		||||
          {/if}
 | 
			
		||||
 | 
			
		||||
          <DropdownButton
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@
 | 
			
		||||
{#if dragStartTarget}
 | 
			
		||||
  <!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
			
		||||
  <div
 | 
			
		||||
    class="fixed inset-0 w-full h-full z-[1000] flex flex-col items-center justify-center bg-gray-100/90 dark:bg-immich-dark-bg/90 text-immich-dark-gray dark:text-immich-gray"
 | 
			
		||||
    class="fixed inset-0 z-[1000] 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"
 | 
			
		||||
    transition:fade={{ duration: 250 }}
 | 
			
		||||
    on:dragover={(e) => {
 | 
			
		||||
      // Prevent browser from opening the dropped file.
 | 
			
		||||
@ -34,7 +34,7 @@
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
    }}
 | 
			
		||||
  >
 | 
			
		||||
    <ImmichLogo class="animate-bounce w-48 m-16" />
 | 
			
		||||
    <ImmichLogo class="m-16 w-48 animate-bounce" />
 | 
			
		||||
    <div class="text-2xl">Drop files anywhere to upload</div>
 | 
			
		||||
  </div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@
 | 
			
		||||
    {disabled}
 | 
			
		||||
    on:click={toggle}
 | 
			
		||||
    aria-expanded={isOpen}
 | 
			
		||||
    class="bg-gray-200 w-full flex p-2 rounded-lg dark:bg-gray-600 place-items-center justify-between disabled:cursor-not-allowed dark:disabled:bg-gray-300 disabled:bg-gray-600"
 | 
			
		||||
    class="flex w-full place-items-center justify-between rounded-lg bg-gray-200 p-2 disabled:cursor-not-allowed disabled:bg-gray-600 dark:bg-gray-600 dark:disabled:bg-gray-300"
 | 
			
		||||
  >
 | 
			
		||||
    <div>
 | 
			
		||||
      {selected}
 | 
			
		||||
@ -49,14 +49,14 @@
 | 
			
		||||
  </button>
 | 
			
		||||
 | 
			
		||||
  {#if isOpen}
 | 
			
		||||
    <div class="flex flex-col mt-2 absolute w-full">
 | 
			
		||||
    <div class="absolute mt-2 flex w-full flex-col">
 | 
			
		||||
      {#each options.options as option}
 | 
			
		||||
        <button
 | 
			
		||||
          on:click={() => {
 | 
			
		||||
            selected = option;
 | 
			
		||||
            isOpen = false;
 | 
			
		||||
          }}
 | 
			
		||||
          class="bg-gray-200 dark:bg-gray-500 dark:hover:bg-gray-700 w-full flex p-2 hover:bg-gray-300 transition-all"
 | 
			
		||||
          class="flex w-full bg-gray-200 p-2 transition-all hover:bg-gray-300 dark:bg-gray-500 dark:hover:bg-gray-700"
 | 
			
		||||
        >
 | 
			
		||||
          {option}
 | 
			
		||||
        </button>
 | 
			
		||||
 | 
			
		||||
@ -13,16 +13,16 @@
 | 
			
		||||
  <div
 | 
			
		||||
    on:click={actionHandler}
 | 
			
		||||
    on:keydown={actionHandler}
 | 
			
		||||
    class="border dark:border-immich-dark-gray {hoverClasses} p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
 | 
			
		||||
    class="border dark:border-immich-dark-gray {hoverClasses} m-auto mt-10 flex w-[50%] flex-col place-content-center place-items-center rounded-3xl bg-gray-50 p-5 dark:bg-immich-dark-gray"
 | 
			
		||||
  >
 | 
			
		||||
    <img src={empty1Url} {alt} width="500" draggable="false" />
 | 
			
		||||
    <p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">{text}</p>
 | 
			
		||||
    <p class="text-immich-text-gray-500 text-center dark:text-immich-dark-fg">{text}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
{:else}
 | 
			
		||||
  <div
 | 
			
		||||
    class="border dark:border-immich-dark-gray p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
 | 
			
		||||
    class="m-auto mt-10 flex w-[50%] flex-col place-content-center place-items-center rounded-3xl border bg-gray-50 p-5 dark:border-immich-dark-gray dark:bg-immich-dark-gray"
 | 
			
		||||
  >
 | 
			
		||||
    <img src={empty1Url} {alt} width="500" draggable="false" />
 | 
			
		||||
    <p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">{text}</p>
 | 
			
		||||
    <p class="text-immich-text-gray-500 text-center dark:text-immich-dark-fg">{text}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
<section
 | 
			
		||||
  in:fade={{ duration: 100 }}
 | 
			
		||||
  out:fade={{ duration: 100 }}
 | 
			
		||||
  class="fixed left-0 top-0 w-screen h-screen bg-black/40 z-[990] flex place-items-center place-content-center"
 | 
			
		||||
  class="fixed left-0 top-0 z-[990] flex h-screen w-screen place-content-center place-items-center bg-black/40"
 | 
			
		||||
>
 | 
			
		||||
  <div class="z-[9999]" use:clickOutside on:outclick={() => dispatch('clickOutside')}>
 | 
			
		||||
    <slot />
 | 
			
		||||
 | 
			
		||||
@ -5,20 +5,20 @@
 | 
			
		||||
  export let showMessage = $$slots.message;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<section class="min-h-screen w-screen flex place-items-center place-content-center p-4">
 | 
			
		||||
<section class="flex min-h-screen w-screen place-content-center place-items-center p-4">
 | 
			
		||||
  <div
 | 
			
		||||
    class="flex flex-col gap-4 border bg-white dark:bg-immich-dark-gray dark:border-immich-dark-gray p-8 shadow-sm w-full max-w-lg rounded-3xl"
 | 
			
		||||
    class="flex w-full max-w-lg flex-col gap-4 rounded-3xl border bg-white p-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex flex-col place-items-center place-content-center gap-4 py-4">
 | 
			
		||||
    <div class="flex flex-col place-content-center place-items-center gap-4 py-4">
 | 
			
		||||
      <ImmichLogo class="h-24 w-24" />
 | 
			
		||||
      <h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
 | 
			
		||||
      <h1 class="text-2xl font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        {title}
 | 
			
		||||
      </h1>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {#if showMessage}
 | 
			
		||||
      <div
 | 
			
		||||
        class="text-sm rounded-xl p-4 text-immich-primary dark:text-immich-dark-primary font-medium bg-immich-primary/5 dark:border-immich-dark-bg w-full border-immich-primary border-2"
 | 
			
		||||
        class="w-full rounded-xl border-2 border-immich-primary bg-immich-primary/5 p-4 text-sm font-medium text-immich-primary dark:border-immich-dark-bg dark:text-immich-dark-primary"
 | 
			
		||||
      >
 | 
			
		||||
        <slot name="message" />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if assets.length > 0}
 | 
			
		||||
  <div class="flex flex-wrap gap-1 w-full pb-20" bind:clientWidth={viewWidth}>
 | 
			
		||||
  <div class="flex w-full flex-wrap gap-1 pb-20" bind:clientWidth={viewWidth}>
 | 
			
		||||
    {#each assets as asset (asset.id)}
 | 
			
		||||
      <div animate:flip={{ duration: 500 }}>
 | 
			
		||||
        <Thumbnail
 | 
			
		||||
 | 
			
		||||
@ -98,7 +98,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if assets.length > 0}
 | 
			
		||||
  <div class="flex flex-wrap gap-1 w-full pb-20" bind:clientWidth={viewWidth}>
 | 
			
		||||
  <div class="flex w-full flex-wrap gap-1 pb-20" bind:clientWidth={viewWidth}>
 | 
			
		||||
    {#each assets as asset (asset.id)}
 | 
			
		||||
      <div animate:flip={{ duration: 500 }}>
 | 
			
		||||
        <Thumbnail
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@
 | 
			
		||||
  $: if (map) map.setView(center, zoom);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div bind:this={container} class="w-full h-full" class:map-dark={allowDarkMode}>
 | 
			
		||||
<div bind:this={container} class="h-full w-full" class:map-dark={allowDarkMode}>
 | 
			
		||||
  {#if map}
 | 
			
		||||
    <slot />
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<div>
 | 
			
		||||
  <svg
 | 
			
		||||
    role="status"
 | 
			
		||||
    class={`w-[24px] h-[24px] text-gray-400 animate-spin dark:text-gray-600 fill-immich-primary`}
 | 
			
		||||
    class={`h-[24px] w-[24px] animate-spin fill-immich-primary text-gray-400 dark:text-gray-600`}
 | 
			
		||||
    viewBox="0 0 100 101"
 | 
			
		||||
    fill="none"
 | 
			
		||||
    xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
 | 
			
		||||
@ -17,15 +17,15 @@
 | 
			
		||||
  in:fade={{ duration: 100 }}
 | 
			
		||||
  out:fade={{ duration: 100 }}
 | 
			
		||||
  id="account-info-panel"
 | 
			
		||||
  class="absolute right-[25px] top-[75px] bg-gray-200 dark:bg-immich-dark-gray dark:border dark:border-immich-dark-gray shadow-lg rounded-3xl w-[360px] z-[100]"
 | 
			
		||||
  class="absolute right-[25px] top-[75px] z-[100] w-[360px] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray"
 | 
			
		||||
>
 | 
			
		||||
  <div
 | 
			
		||||
    class="flex flex-col items-center justify-center gap-4 bg-white dark:bg-immich-dark-primary/10 rounded-3xl mx-4 mt-4 p-4"
 | 
			
		||||
    class="mx-4 mt-4 flex flex-col items-center justify-center gap-4 rounded-3xl bg-white p-4 dark:bg-immich-dark-primary/10"
 | 
			
		||||
  >
 | 
			
		||||
    <UserAvatar size="lg" {user} />
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <p class="text-lg text-immich-primary dark:text-immich-dark-primary font-medium text-center">
 | 
			
		||||
      <p class="text-center text-lg font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        {user.firstName}
 | 
			
		||||
        {user.lastName}
 | 
			
		||||
      </p>
 | 
			
		||||
@ -34,7 +34,7 @@
 | 
			
		||||
 | 
			
		||||
    <a href={AppRoute.USER_SETTINGS} on:click={() => dispatch('close')}>
 | 
			
		||||
      <Button color="dark-gray" size="sm" shadow={false} border>
 | 
			
		||||
        <div class="flex gap-2 place-items-center place-content-center px-2">
 | 
			
		||||
        <div class="flex place-content-center place-items-center gap-2 px-2">
 | 
			
		||||
          <Cog size="18" />
 | 
			
		||||
          Account Settings
 | 
			
		||||
        </div>
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
 | 
			
		||||
  <div class="mb-4 flex flex-col">
 | 
			
		||||
    <button
 | 
			
		||||
      class="py-3 w-full font-medium flex place-items-center gap-2 hover:bg-immich-primary/10 text-gray-500 dark:text-gray-300 place-content-center"
 | 
			
		||||
      class="flex w-full place-content-center place-items-center gap-2 py-3 font-medium text-gray-500 hover:bg-immich-primary/10 dark:text-gray-300"
 | 
			
		||||
      on:click={() => dispatch('logout')}
 | 
			
		||||
    >
 | 
			
		||||
      <Logout size={24} />
 | 
			
		||||
 | 
			
		||||
@ -33,23 +33,23 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<section id="dashboard-navbar" class="fixed h-[var(--navbar-height)] w-screen z-[900] text-sm">
 | 
			
		||||
<section id="dashboard-navbar" class="fixed z-[900] h-[var(--navbar-height)] w-screen text-sm">
 | 
			
		||||
  <div
 | 
			
		||||
    class="grid h-full md:grid-cols-[theme(spacing.64)_auto] grid-cols-[theme(spacing.18)_auto] border-b dark:border-b-immich-dark-gray items-center py-2 bg-immich-bg dark:bg-immich-dark-bg"
 | 
			
		||||
    class="grid h-full grid-cols-[theme(spacing.18)_auto] items-center border-b bg-immich-bg py-2 dark:border-b-immich-dark-gray dark:bg-immich-dark-bg md:grid-cols-[theme(spacing.64)_auto]"
 | 
			
		||||
  >
 | 
			
		||||
    <a data-sveltekit-preload-data="hover" class="flex gap-2 md:mx-6 mx-4 place-items-center" href={AppRoute.PHOTOS}>
 | 
			
		||||
    <a data-sveltekit-preload-data="hover" class="mx-4 flex place-items-center gap-2 md:mx-6" href={AppRoute.PHOTOS}>
 | 
			
		||||
      <ImmichLogo height="35" width="35" />
 | 
			
		||||
      <h1 class="font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary md:block hidden">
 | 
			
		||||
      <h1 class="hidden font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary md:block">
 | 
			
		||||
        IMMICH
 | 
			
		||||
      </h1>
 | 
			
		||||
    </a>
 | 
			
		||||
    <div class="flex justify-between gap-16 pr-6">
 | 
			
		||||
      <div class="w-full max-w-5xl flex-1 pl-4 sm:block hidden">
 | 
			
		||||
      <div class="hidden w-full max-w-5xl flex-1 pl-4 sm:block">
 | 
			
		||||
        <SearchBar grayTheme={true} />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <section class="flex gap-4 place-items-center justify-end max-sm:w-full">
 | 
			
		||||
        <a href={AppRoute.SEARCH} id="search-button" class="sm:hidden pl-4">
 | 
			
		||||
      <section class="flex place-items-center justify-end gap-4 max-sm:w-full">
 | 
			
		||||
        <a href={AppRoute.SEARCH} id="search-button" class="pl-4 sm:hidden">
 | 
			
		||||
          <IconButton title="Search">
 | 
			
		||||
            <div class="flex gap-2">
 | 
			
		||||
              <Magnify size="1.5em" />
 | 
			
		||||
@ -64,7 +64,7 @@
 | 
			
		||||
            <LinkButton on:click={() => dispatch('uploadClicked')}>
 | 
			
		||||
              <div class="flex gap-2">
 | 
			
		||||
                <TrayArrowUp size="1.5em" />
 | 
			
		||||
                <span class="md:block hidden">Upload</span>
 | 
			
		||||
                <span class="hidden md:block">Upload</span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </LinkButton>
 | 
			
		||||
          </div>
 | 
			
		||||
@ -72,18 +72,18 @@
 | 
			
		||||
 | 
			
		||||
        {#if user.isAdmin}
 | 
			
		||||
          <a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_USER_MANAGEMENT}>
 | 
			
		||||
            <div class="sm:block hidden">
 | 
			
		||||
            <div class="hidden sm:block">
 | 
			
		||||
              <LinkButton>
 | 
			
		||||
                <span
 | 
			
		||||
                  class={$page.url.pathname.includes('/admin')
 | 
			
		||||
                    ? 'text-immich-primary dark:text-immich-dark-primary underline item'
 | 
			
		||||
                    ? 'item text-immich-primary underline dark:text-immich-dark-primary'
 | 
			
		||||
                    : ''}
 | 
			
		||||
                >
 | 
			
		||||
                  Administration
 | 
			
		||||
                </span>
 | 
			
		||||
              </LinkButton>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="sm:hidden block">
 | 
			
		||||
            <div class="block sm:hidden">
 | 
			
		||||
              <IconButton title="Administration">
 | 
			
		||||
                <Cog
 | 
			
		||||
                  size="1.5em"
 | 
			
		||||
@ -94,7 +94,7 @@
 | 
			
		||||
              </IconButton>
 | 
			
		||||
              <hr
 | 
			
		||||
                class={$page.url.pathname.includes('/admin')
 | 
			
		||||
                  ? 'block border-1 w-2/3 mx-auto border-immich-primary dark:border-immich-dark-primary'
 | 
			
		||||
                  ? 'border-1 mx-auto block w-2/3 border-immich-primary dark:border-immich-dark-primary'
 | 
			
		||||
                  : 'hidden'}
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
@ -117,7 +117,7 @@
 | 
			
		||||
            <div
 | 
			
		||||
              in:fade={{ delay: 500, duration: 150 }}
 | 
			
		||||
              out:fade={{ delay: 200, duration: 150 }}
 | 
			
		||||
              class="absolute -bottom-12 right-5 border bg-gray-500 dark:bg-immich-dark-gray text-[12px] text-gray-100 p-2 rounded-md shadow-md dark:border-immich-dark-gray"
 | 
			
		||||
              class="absolute -bottom-12 right-5 rounded-md border bg-gray-500 p-2 text-[12px] text-gray-100 shadow-md dark:border-immich-dark-gray dark:bg-immich-dark-gray"
 | 
			
		||||
            >
 | 
			
		||||
              <p>{user.firstName} {user.lastName}</p>
 | 
			
		||||
              <p>{user.email}</p>
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,6 @@
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="absolute top-0 left-0 w-screen h-[3px] bg-white z-[999999999]">
 | 
			
		||||
  <span class="absolute bg-immich-primary h-[3px]" style:width={`${$progress}%`} />
 | 
			
		||||
<div class="absolute left-0 top-0 z-[999999999] h-[3px] w-screen bg-white">
 | 
			
		||||
  <span class="absolute h-[3px] bg-immich-primary" style:width={`${$progress}%`} />
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -74,12 +74,12 @@
 | 
			
		||||
  transition:fade={{ duration: 250 }}
 | 
			
		||||
  style:background-color={backgroundColor()}
 | 
			
		||||
  style:border={borderStyle()}
 | 
			
		||||
  class="min-h-[80px] w-[300px] rounded-2xl z-[999999] shadow-md p-4 mb-4 hover:cursor-pointer"
 | 
			
		||||
  class="z-[999999] mb-4 min-h-[80px] w-[300px] rounded-2xl p-4 shadow-md hover:cursor-pointer"
 | 
			
		||||
  on:click={handleClick}
 | 
			
		||||
  on:keydown={handleClick}
 | 
			
		||||
>
 | 
			
		||||
  <div class="flex justify-between">
 | 
			
		||||
    <div class="flex gap-2 place-items-center">
 | 
			
		||||
    <div class="flex place-items-center gap-2">
 | 
			
		||||
      <svelte:component this={icon} color={primaryColor()} size="20" />
 | 
			
		||||
      <h2 style:color={primaryColor()} class="font-medium" data-testid="title">
 | 
			
		||||
        {notificationInfo.type.toString()}
 | 
			
		||||
@ -90,7 +90,7 @@
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <p class="whitespace-pre-wrap text-sm pl-[28px] pr-[16px]" data-testid="message">
 | 
			
		||||
  <p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
 | 
			
		||||
    {notificationInfo.message}
 | 
			
		||||
  </p>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -65,21 +65,21 @@
 | 
			
		||||
 | 
			
		||||
<BaseModal on:close>
 | 
			
		||||
  <svelte:fragment slot="title">
 | 
			
		||||
    <span class="flex gap-2 place-items-center">
 | 
			
		||||
    <span class="flex place-items-center gap-2">
 | 
			
		||||
      <p class="font-medium">Set profile picture</p>
 | 
			
		||||
    </span>
 | 
			
		||||
  </svelte:fragment>
 | 
			
		||||
  <div class="flex justify-center place-items-center items-center">
 | 
			
		||||
  <div class="flex place-items-center items-center justify-center">
 | 
			
		||||
    <div
 | 
			
		||||
      class="w-1/2 aspect-square rounded-full overflow-hidden relative flex border-immich-primary dark:border-immich-dark-primary border-4 bg-immich-dark-primary dark:bg-immich-primary"
 | 
			
		||||
      class="relative flex aspect-square w-1/2 overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"
 | 
			
		||||
    >
 | 
			
		||||
      <PhotoViewer bind:element={imgElement} {asset} />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <span class="p-4 flex justify-end">
 | 
			
		||||
  <span class="flex justify-end p-4">
 | 
			
		||||
    <Button on:click={handleSetProfilePicture}>
 | 
			
		||||
      <p>Set as profile picture</p>
 | 
			
		||||
    </Button>
 | 
			
		||||
  </span>
 | 
			
		||||
  <div class="max-h-[400px] flex flex-col mb-2" />
 | 
			
		||||
  <div class="mb-2 flex max-h-[400px] flex-col" />
 | 
			
		||||
</BaseModal>
 | 
			
		||||
 | 
			
		||||
@ -94,7 +94,7 @@
 | 
			
		||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
			
		||||
<div
 | 
			
		||||
  id="immich-scrubbable-scrollbar"
 | 
			
		||||
  class="fixed right-0 bg-immich-bg z-[100] hover:cursor-row-resize select-none"
 | 
			
		||||
  class="fixed right-0 z-[100] select-none bg-immich-bg hover:cursor-row-resize"
 | 
			
		||||
  style:width={isDragging ? '100vw' : '60px'}
 | 
			
		||||
  style:background-color={isDragging ? 'transparent' : 'transparent'}
 | 
			
		||||
  on:mouseenter={() => (isHover = true)}
 | 
			
		||||
@ -109,7 +109,7 @@
 | 
			
		||||
>
 | 
			
		||||
  {#if isHover}
 | 
			
		||||
    <div
 | 
			
		||||
      class="border-b-2 border-immich-primary dark:border-immich-dark-primary w-[100px] right-0 pr-6 py-1 text-sm pl-1 font-medium absolute bg-immich-bg dark:bg-immich-dark-gray z-[100] pointer-events-none rounded-tl-md shadow-lg dark:text-immich-dark-fg"
 | 
			
		||||
      class="pointer-events-none absolute right-0 z-[100] w-[100px] rounded-tl-md border-b-2 border-immich-primary bg-immich-bg py-1 pl-1 pr-6 text-sm font-medium shadow-lg dark:border-immich-dark-primary dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
      style:top={currentMouseYLocation + 'px'}
 | 
			
		||||
    >
 | 
			
		||||
      {hoveredDate?.toLocaleString('default', { month: 'short' })}
 | 
			
		||||
@ -120,7 +120,7 @@
 | 
			
		||||
  <!-- Scroll Position Indicator Line -->
 | 
			
		||||
  {#if !isDragging}
 | 
			
		||||
    <div
 | 
			
		||||
      class="absolute right-0 w-10 h-[2px] bg-immich-primary dark:bg-immich-dark-primary"
 | 
			
		||||
      class="absolute right-0 h-[2px] w-10 bg-immich-primary dark:bg-immich-dark-primary"
 | 
			
		||||
      style:top={scrollbarPosition + 'px'}
 | 
			
		||||
    />
 | 
			
		||||
  {/if}
 | 
			
		||||
@ -139,7 +139,7 @@
 | 
			
		||||
        {#if segment.height > 8}
 | 
			
		||||
          <div
 | 
			
		||||
            aria-label={segment.timeGroup + ' ' + segment.count}
 | 
			
		||||
            class="absolute right-0 pr-5 z-10 text-xs font-medium dark:text-immich-dark-fg"
 | 
			
		||||
            class="absolute right-0 z-10 pr-5 text-xs font-medium dark:text-immich-dark-fg"
 | 
			
		||||
          >
 | 
			
		||||
            {groupDate.getFullYear()}
 | 
			
		||||
          </div>
 | 
			
		||||
@ -147,7 +147,7 @@
 | 
			
		||||
      {:else if segment.height > 5}
 | 
			
		||||
        <div
 | 
			
		||||
          aria-label={segment.timeGroup + ' ' + segment.count}
 | 
			
		||||
          class="absolute right-0 rounded-full h-[4px] w-[4px] mr-3 bg-gray-300 block"
 | 
			
		||||
          class="absolute right-0 mr-3 block h-[4px] w-[4px] rounded-full bg-gray-300"
 | 
			
		||||
        />
 | 
			
		||||
      {/if}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -66,9 +66,9 @@
 | 
			
		||||
      name="q"
 | 
			
		||||
      class="w-full transition-all {grayTheme
 | 
			
		||||
        ? 'dark:bg-immich-dark-gray'
 | 
			
		||||
        : 'dark:bg-immich-dark-bg'} text-immich-fg/75 dark:text-immich-dark-fg px-14 py-4 {showBigSearchBar
 | 
			
		||||
        ? 'rounded-t-3xl bg-white  border border-gray-200 dark:border-gray-800'
 | 
			
		||||
        : 'rounded-3xl bg-gray-200 border border-transparent'}"
 | 
			
		||||
        : 'dark:bg-immich-dark-bg'} px-14 py-4 text-immich-fg/75 dark:text-immich-dark-fg {showBigSearchBar
 | 
			
		||||
        ? 'rounded-t-3xl border  border-gray-200 bg-white dark:border-gray-800'
 | 
			
		||||
        : 'rounded-3xl border border-transparent bg-gray-200'}"
 | 
			
		||||
      placeholder="Search your photos"
 | 
			
		||||
      required
 | 
			
		||||
      pattern="^(?!m:$).*$"
 | 
			
		||||
@ -79,7 +79,7 @@
 | 
			
		||||
    <div class="absolute inset-y-0 right-0 flex items-center pr-4">
 | 
			
		||||
      <button
 | 
			
		||||
        type="reset"
 | 
			
		||||
        class="dark:text-immich-dark-fg/75 hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 rounded-full p-2 active:bg-immich-primary/10 dark:active:bg-immich-dark-primary/[.35]"
 | 
			
		||||
        class="rounded-full p-2 hover:bg-immich-primary/5 active:bg-immich-primary/10 dark:text-immich-dark-fg/75 dark:hover:bg-immich-dark-primary/25 dark:active:bg-immich-dark-primary/[.35]"
 | 
			
		||||
      >
 | 
			
		||||
        <Close size="1.5em" />
 | 
			
		||||
      </button>
 | 
			
		||||
@ -89,23 +89,23 @@
 | 
			
		||||
  {#if showBigSearchBar}
 | 
			
		||||
    <div
 | 
			
		||||
      transition:fly={{ y: 25, duration: 250 }}
 | 
			
		||||
      class="w-full pb-5 absolute bg-white transition-all rounded-b-3xl shadow-2xl border border-gray-200 dark:bg-immich-dark-gray dark:border-gray-800 dark:text-gray-300"
 | 
			
		||||
      class="absolute w-full rounded-b-3xl border border-gray-200 bg-white pb-5 shadow-2xl transition-all dark:border-gray-800 dark:bg-immich-dark-gray dark:text-gray-300"
 | 
			
		||||
    >
 | 
			
		||||
      <div class="px-5 pt-5 text-xs">
 | 
			
		||||
        <p>
 | 
			
		||||
          Smart search is enabled by default, to search for metadata use the syntax <span
 | 
			
		||||
            class="font-mono p-2 font-semibold text-immich-primary dark:text-immich-dark-primary bg-gray-100 rounded-lg dark:bg-gray-900 leading-7"
 | 
			
		||||
            class="rounded-lg bg-gray-100 p-2 font-mono font-semibold leading-7 text-immich-primary dark:bg-gray-900 dark:text-immich-dark-primary"
 | 
			
		||||
            >m:your-search-term</span
 | 
			
		||||
          >
 | 
			
		||||
        </p>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      {#if $savedSearchTerms.length > 0}
 | 
			
		||||
        <div class="px-5 pt-5 text-xs flex justify-between">
 | 
			
		||||
        <div class="flex justify-between px-5 pt-5 text-xs">
 | 
			
		||||
          <p>RECENT SEARCHES</p>
 | 
			
		||||
          <button
 | 
			
		||||
            type="button"
 | 
			
		||||
            class="text-immich-primary dark:text-immich-dark-primary font-semibold p-2 hover:bg-immich-primary/25 rounded-lg"
 | 
			
		||||
            class="rounded-lg p-2 font-semibold text-immich-primary hover:bg-immich-primary/25 dark:text-immich-dark-primary"
 | 
			
		||||
            on:click={clearSearchTerm}>Clear all</button
 | 
			
		||||
          >
 | 
			
		||||
        </div>
 | 
			
		||||
@ -114,7 +114,7 @@
 | 
			
		||||
      {#each $savedSearchTerms as savedSearchTerm, i (i)}
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          class="w-full hover:bg-gray-100 dark:hover:bg-gray-500/10 px-5 py-3 cursor-pointer flex gap-3 text-black dark:text-gray-300"
 | 
			
		||||
          class="flex w-full cursor-pointer gap-3 px-5 py-3 text-black hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-500/10"
 | 
			
		||||
          on:click={() => {
 | 
			
		||||
            value = savedSearchTerm;
 | 
			
		||||
            onSearch(false);
 | 
			
		||||
 | 
			
		||||
@ -19,35 +19,35 @@
 | 
			
		||||
<div
 | 
			
		||||
  on:click={onButtonClicked}
 | 
			
		||||
  on:keydown={onButtonClicked}
 | 
			
		||||
  class="flex gap-4 justify-between place-items-center w-full transition-[padding] delay-100 duration-100 py-3 rounded-r-full hover:bg-immich-gray dark:hover:bg-immich-dark-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:text-immich-dark-primary hover:cursor-pointer
 | 
			
		||||
  class="flex w-full place-items-center justify-between gap-4 rounded-r-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-immich-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary
 | 
			
		||||
    {isSelected
 | 
			
		||||
    ? 'bg-immich-primary/10 dark:bg-immich-dark-primary/10 text-immich-primary dark:text-immich-dark-primary hover:bg-immich-primary/25'
 | 
			
		||||
    ? 'bg-immich-primary/10 text-immich-primary hover:bg-immich-primary/25 dark:bg-immich-dark-primary/10 dark:text-immich-dark-primary'
 | 
			
		||||
    : ''}
 | 
			
		||||
		pl-5 group-hover:sm:px-5 md:px-5
 | 
			
		||||
  "
 | 
			
		||||
>
 | 
			
		||||
  <div class="flex gap-4 place-items-center w-full overflow-hidden truncate">
 | 
			
		||||
  <div class="flex w-full place-items-center gap-4 overflow-hidden truncate">
 | 
			
		||||
    <svelte:component this={logo} size="1.5em" class="shrink-0 {flippedLogo ? '-scale-x-100' : ''}" />
 | 
			
		||||
    <p class="font-medium text-sm">{title}</p>
 | 
			
		||||
    <p class="text-sm font-medium">{title}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div
 | 
			
		||||
    class="transition-[height] group-hover:sm:overflow-visible md:overflow-visible overflow-hidden duration-100 delay-1000 sm:group-hover:h-auto md:h-auto h-0"
 | 
			
		||||
    class="h-0 overflow-hidden transition-[height] delay-1000 duration-100 sm:group-hover:h-auto group-hover:sm:overflow-visible md:h-auto md:overflow-visible"
 | 
			
		||||
  >
 | 
			
		||||
    {#if $$slots.moreInformation}
 | 
			
		||||
      <div
 | 
			
		||||
        class="relative flex justify-center select-none cursor-default"
 | 
			
		||||
        class="relative flex cursor-default select-none justify-center"
 | 
			
		||||
        on:mouseenter={() => (showMoreInformation = true)}
 | 
			
		||||
        on:mouseleave={() => (showMoreInformation = false)}
 | 
			
		||||
      >
 | 
			
		||||
        <div class="hover:cursor-help p-1 text-gray-600 dark:text-gray-400">
 | 
			
		||||
        <div class="p-1 text-gray-600 hover:cursor-help dark:text-gray-400">
 | 
			
		||||
          <InformationOutline />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        {#if showMoreInformation}
 | 
			
		||||
          <div class="absolute right-6 top-0">
 | 
			
		||||
            <div
 | 
			
		||||
              class="flex place-items-center place-content-center whitespace-nowrap rounded-3xl shadow-lg py-3 px-6 bg-immich-bg text-immich-fg dark:bg-gray-600 dark:text-immich-dark-fg text-xs border dark:border-immich-dark-gray"
 | 
			
		||||
              class="flex place-content-center place-items-center whitespace-nowrap rounded-3xl border bg-immich-bg px-6 py-3 text-xs text-immich-fg shadow-lg dark:border-immich-dark-gray dark:bg-gray-600 dark:text-immich-dark-fg"
 | 
			
		||||
              class:hidden={!showMoreInformation}
 | 
			
		||||
              transition:fade={{ duration: 200 }}
 | 
			
		||||
            >
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
<section
 | 
			
		||||
  id="sidebar"
 | 
			
		||||
  class="group flex flex-col gap-1 pt-8 bg-immich-bg dark:bg-immich-dark-bg transition-all duration-200 z-10 w-18 md:w-64 hover:sm:pr-6 md:pr-6 hover:sm:w-64 hover:sm:shadow-2xl hover:md:shadow-none hover:md:border-none hover:sm:border-r hover:sm:dark:border-r-immich-dark-gray relative overflow-y-auto immich-scrollbar"
 | 
			
		||||
  class="immich-scrollbar group relative z-10 flex w-18 flex-col gap-1 overflow-y-auto bg-immich-bg pt-8 transition-all duration-200 dark:bg-immich-dark-bg hover:sm:w-64 hover:sm:border-r hover:sm:pr-6 hover:sm:shadow-2xl hover:sm:dark:border-r-immich-dark-gray md:w-64 md:pr-6 hover:md:border-none hover:md:shadow-none"
 | 
			
		||||
>
 | 
			
		||||
  <slot />
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@ -80,9 +80,9 @@
 | 
			
		||||
    </SideBarButton>
 | 
			
		||||
  </a>
 | 
			
		||||
 | 
			
		||||
  <div class="text-xs dark:text-immich-dark-fg transition-all duration-200">
 | 
			
		||||
    <p class="p-6 hidden md:block group-hover:sm:block">LIBRARY</p>
 | 
			
		||||
    <hr class="mt-8 mb-[31px] mx-4 block md:hidden group-hover:sm:hidden" />
 | 
			
		||||
  <div class="text-xs transition-all duration-200 dark:text-immich-dark-fg">
 | 
			
		||||
    <p class="hidden p-6 group-hover:sm:block md:block">LIBRARY</p>
 | 
			
		||||
    <hr class="mx-4 mb-[31px] mt-8 block group-hover:sm:hidden md:hidden" />
 | 
			
		||||
  </div>
 | 
			
		||||
  <a data-sveltekit-preload-data="hover" href={AppRoute.FAVORITES} draggable="false">
 | 
			
		||||
    <SideBarButton
 | 
			
		||||
 | 
			
		||||
@ -51,16 +51,16 @@
 | 
			
		||||
 | 
			
		||||
<div class="dark:text-immich-dark-fg">
 | 
			
		||||
  <div class="storage-status grid grid-cols-[64px_auto]">
 | 
			
		||||
    <div class="pl-5 pr-6 text-immich-primary dark:text-immich-dark-primary pb-[2.15rem] group-hover:sm:pb-0 md:pb-0">
 | 
			
		||||
    <div class="pb-[2.15rem] pl-5 pr-6 text-immich-primary dark:text-immich-dark-primary group-hover:sm:pb-0 md:pb-0">
 | 
			
		||||
      <Cloud size={'24'} />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="hidden md:block group-hover:sm:block">
 | 
			
		||||
    <div class="hidden group-hover:sm:block md:block">
 | 
			
		||||
      <p class="text-sm font-medium text-immich-primary dark:text-immich-dark-primary">Storage</p>
 | 
			
		||||
      {#if serverInfo}
 | 
			
		||||
        <div class="w-full bg-gray-200 rounded-full h-[7px] dark:bg-gray-700 my-2">
 | 
			
		||||
        <div class="my-2 h-[7px] w-full rounded-full bg-gray-200 dark:bg-gray-700">
 | 
			
		||||
          <!-- style={`width: ${$downloadAssets[fileName]}%`} -->
 | 
			
		||||
          <div
 | 
			
		||||
            class="bg-immich-primary dark:bg-immich-dark-primary h-[7px] rounded-full"
 | 
			
		||||
            class="h-[7px] rounded-full bg-immich-primary dark:bg-immich-dark-primary"
 | 
			
		||||
            style="width: {getStorageUsagePercentage()}%"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
@ -76,16 +76,16 @@
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div>
 | 
			
		||||
    <hr class="ml-5 my-4 dark:border-immich-dark-gray" />
 | 
			
		||||
    <hr class="my-4 ml-5 dark:border-immich-dark-gray" />
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="server-status grid grid-cols-[64px_auto]">
 | 
			
		||||
    <div class="pl-5 pr-6 text-immich-primary dark:text-immich-dark-primary pb-11 md:pb-0 group-hover:sm:pb-0">
 | 
			
		||||
    <div class="pb-11 pl-5 pr-6 text-immich-primary dark:text-immich-dark-primary group-hover:sm:pb-0 md:pb-0">
 | 
			
		||||
      <Dns size={'24'} />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="text-xs hidden md:block group-hover:sm:block">
 | 
			
		||||
    <div class="hidden text-xs group-hover:sm:block md:block">
 | 
			
		||||
      <p class="text-sm font-medium text-immich-primary dark:text-immich-dark-primary">Server</p>
 | 
			
		||||
 | 
			
		||||
      <div class="flex justify-items-center justify-between mt-2">
 | 
			
		||||
      <div class="mt-2 flex justify-between justify-items-center">
 | 
			
		||||
        <p>Status</p>
 | 
			
		||||
 | 
			
		||||
        {#if isServerOk}
 | 
			
		||||
@ -95,7 +95,7 @@
 | 
			
		||||
        {/if}
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="flex justify-items-center justify-between mt-2">
 | 
			
		||||
      <div class="mt-2 flex justify-between justify-items-center">
 | 
			
		||||
        <p>Version</p>
 | 
			
		||||
        <p class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
          {serverVersion}
 | 
			
		||||
 | 
			
		||||
@ -20,11 +20,11 @@
 | 
			
		||||
 | 
			
		||||
<IconButton on:click={toggleTheme} title="Toggle theme">
 | 
			
		||||
  {#if $colorTheme === 'light'}
 | 
			
		||||
    <svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
    <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
      ><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /></svg
 | 
			
		||||
    >
 | 
			
		||||
  {:else}
 | 
			
		||||
    <svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
    <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
      ><path
 | 
			
		||||
        d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
 | 
			
		||||
        fill-rule="evenodd"
 | 
			
		||||
 | 
			
		||||
@ -15,12 +15,12 @@
 | 
			
		||||
<div
 | 
			
		||||
  in:fade={{ duration: 250 }}
 | 
			
		||||
  out:fade={{ duration: 100 }}
 | 
			
		||||
  class="text-xs mt-3 rounded-lg bg-immich-bg grid grid-cols-[70px_auto] gap-2 h-[70px]"
 | 
			
		||||
  class="mt-3 grid h-[70px] grid-cols-[70px_auto] gap-2 rounded-lg bg-immich-bg text-xs"
 | 
			
		||||
>
 | 
			
		||||
  <div class="relative">
 | 
			
		||||
    {#if showFallbackImage}
 | 
			
		||||
      <div in:fade={{ duration: 250 }}>
 | 
			
		||||
        <ImmichLogo class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg" />
 | 
			
		||||
        <ImmichLogo class="h-[70px] w-[70px] rounded-bl-lg rounded-tl-lg object-cover" />
 | 
			
		||||
      </div>
 | 
			
		||||
    {:else}
 | 
			
		||||
      <img
 | 
			
		||||
@ -34,30 +34,30 @@
 | 
			
		||||
        }}
 | 
			
		||||
        src={previewURL}
 | 
			
		||||
        alt="Preview of asset"
 | 
			
		||||
        class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg"
 | 
			
		||||
        class="h-[70px] w-[70px] rounded-bl-lg rounded-tl-lg object-cover"
 | 
			
		||||
        draggable="false"
 | 
			
		||||
      />
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    <div class="bottom-0 left-0 absolute w-full h-[25px] bg-immich-primary/30">
 | 
			
		||||
    <div class="absolute bottom-0 left-0 h-[25px] w-full bg-immich-primary/30">
 | 
			
		||||
      <p
 | 
			
		||||
        class="absolute bottom-1 right-1 object-right-bottom text-gray-50/95 font-semibold stroke-immich-primary uppercase"
 | 
			
		||||
        class="absolute bottom-1 right-1 stroke-immich-primary object-right-bottom font-semibold uppercase text-gray-50/95"
 | 
			
		||||
      >
 | 
			
		||||
        .{getFilenameExtension(uploadAsset.file.name)}
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="p-2 pr-4 flex flex-col justify-between">
 | 
			
		||||
  <div class="flex flex-col justify-between p-2 pr-4">
 | 
			
		||||
    <input
 | 
			
		||||
      disabled
 | 
			
		||||
      class="bg-gray-100 border w-full p-1 rounded-md text-[10px] px-2"
 | 
			
		||||
      class="w-full rounded-md border bg-gray-100 p-1 px-2 text-[10px]"
 | 
			
		||||
      value={`[${asByteUnitString(uploadAsset.file.size, $locale)}] ${uploadAsset.file.name}`}
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
    <div class="w-full bg-gray-300 h-[15px] rounded-md mt-[5px] text-white relative">
 | 
			
		||||
      <div class="bg-immich-primary h-[15px] rounded-md transition-all" style={`width: ${uploadAsset.progress}%`} />
 | 
			
		||||
      <p class="absolute h-full w-full text-center top-0 text-[10px]">
 | 
			
		||||
    <div class="relative mt-[5px] h-[15px] w-full rounded-md bg-gray-300 text-white">
 | 
			
		||||
      <div class="h-[15px] rounded-md bg-immich-primary transition-all" style={`width: ${uploadAsset.progress}%`} />
 | 
			
		||||
      <p class="absolute top-0 h-full w-full text-center text-[10px]">
 | 
			
		||||
        {uploadAsset.progress}/100
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -33,24 +33,24 @@
 | 
			
		||||
        type: NotificationType.Info,
 | 
			
		||||
      });
 | 
			
		||||
    }}
 | 
			
		||||
    class="absolute right-6 bottom-6 z-[10000]"
 | 
			
		||||
    class="absolute bottom-6 right-6 z-[10000]"
 | 
			
		||||
  >
 | 
			
		||||
    {#if showDetail}
 | 
			
		||||
      <div
 | 
			
		||||
        in:scale={{ duration: 250, easing: quartInOut }}
 | 
			
		||||
        class="bg-gray-200 p-4 text-sm w-[300px] rounded-lg shadow-sm border"
 | 
			
		||||
        class="w-[300px] rounded-lg border bg-gray-200 p-4 text-sm shadow-sm"
 | 
			
		||||
      >
 | 
			
		||||
        <div class="flex justify-between place-item-center mb-4">
 | 
			
		||||
        <div class="place-item-center mb-4 flex justify-between">
 | 
			
		||||
          <p class="text-xs text-gray-500">UPLOADING {$uploadAssetsStore.length}</p>
 | 
			
		||||
          <button
 | 
			
		||||
            on:click={() => (showDetail = false)}
 | 
			
		||||
            class="w-[20px] h-[20px] bg-gray-50 rounded-full flex place-items-center place-content-center transition-colors hover:bg-gray-100"
 | 
			
		||||
            class="flex h-[20px] w-[20px] place-content-center place-items-center rounded-full bg-gray-50 transition-colors hover:bg-gray-100"
 | 
			
		||||
          >
 | 
			
		||||
            <WindowMinimize />
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="max-h-[400px] overflow-y-auto pr-2 rounded-lg immich-scrollbar">
 | 
			
		||||
        <div class="immich-scrollbar max-h-[400px] overflow-y-auto rounded-lg pr-2">
 | 
			
		||||
          {#each $uploadAssetsStore as uploadAsset}
 | 
			
		||||
            {#key uploadAsset.id}
 | 
			
		||||
              <UploadAssetPreview {uploadAsset} />
 | 
			
		||||
@ -63,14 +63,14 @@
 | 
			
		||||
        <button
 | 
			
		||||
          in:scale={{ duration: 250, easing: quartInOut }}
 | 
			
		||||
          on:click={() => (showDetail = true)}
 | 
			
		||||
          class="absolute -top-4 -left-4 text-xs rounded-full w-10 h-10 p-5 flex place-items-center place-content-center bg-immich-primary text-gray-200"
 | 
			
		||||
          class="absolute -left-4 -top-4 flex h-10 w-10 place-content-center place-items-center rounded-full bg-immich-primary p-5 text-xs text-gray-200"
 | 
			
		||||
        >
 | 
			
		||||
          {$uploadAssetsStore.length}
 | 
			
		||||
        </button>
 | 
			
		||||
        <button
 | 
			
		||||
          in:scale={{ duration: 250, easing: quartInOut }}
 | 
			
		||||
          on:click={() => (showDetail = true)}
 | 
			
		||||
          class="bg-gray-300 p-5 rounded-full w-16 h-16 flex place-items-center place-content-center text-sm shadow-lg"
 | 
			
		||||
          class="flex h-16 w-16 place-content-center place-items-center rounded-full bg-gray-300 p-5 text-sm shadow-lg"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="animate-pulse">
 | 
			
		||||
            <CloudUploadOutline size="30" color="#4250af" />
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<figure
 | 
			
		||||
  class="{sizeClass} {colorClass} {interactiveClass} shadow-md overflow-hidden"
 | 
			
		||||
  class="{sizeClass} {colorClass} {interactiveClass} overflow-hidden shadow-md"
 | 
			
		||||
  class:rounded-full={rounded}
 | 
			
		||||
  title={showTitle ? title : undefined}
 | 
			
		||||
>
 | 
			
		||||
@ -57,7 +57,7 @@
 | 
			
		||||
    <img
 | 
			
		||||
      src={api.getProfileImageUrl(user.id)}
 | 
			
		||||
      alt="Profile image of {title}"
 | 
			
		||||
      class="object-cover w-full h-full"
 | 
			
		||||
      class="h-full w-full object-cover"
 | 
			
		||||
      class:hidden={showFallback}
 | 
			
		||||
      draggable="false"
 | 
			
		||||
      use:imageLoad
 | 
			
		||||
@ -66,7 +66,7 @@
 | 
			
		||||
  {/if}
 | 
			
		||||
  {#if showFallback}
 | 
			
		||||
    <span
 | 
			
		||||
      class="flex justify-center items-center w-full h-full select-none"
 | 
			
		||||
      class="flex h-full w-full select-none items-center justify-center"
 | 
			
		||||
      class:text-xs={size === 'sm'}
 | 
			
		||||
      class:text-lg={size === 'lg'}
 | 
			
		||||
      class:font-medium={!autoColor}
 | 
			
		||||
 | 
			
		||||
@ -43,15 +43,15 @@
 | 
			
		||||
{#if showModal}
 | 
			
		||||
  <FullScreenModal on:clickOutside={() => (showModal = false)}>
 | 
			
		||||
    <div
 | 
			
		||||
      class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray shadow-sm max-w-lg rounded-3xl py-10 px-8 dark:text-immich-dark-fg"
 | 
			
		||||
      class="max-w-lg rounded-3xl border bg-immich-bg px-8 py-10 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
 | 
			
		||||
    >
 | 
			
		||||
      <p class="text-2xl mb-4">🎉 NEW VERSION AVAILABLE 🎉</p>
 | 
			
		||||
      <p class="mb-4 text-2xl">🎉 NEW VERSION AVAILABLE 🎉</p>
 | 
			
		||||
 | 
			
		||||
      <div>
 | 
			
		||||
        Hi friend, there is a new release of
 | 
			
		||||
        <span class="font-immich-title text-immich-primary dark:text-immich-dark-primary font-bold">IMMICH</span>,
 | 
			
		||||
        <span class="font-immich-title font-bold text-immich-primary dark:text-immich-dark-primary">IMMICH</span>,
 | 
			
		||||
        please take your time to visit the
 | 
			
		||||
        <span class="underline font-medium"
 | 
			
		||||
        <span class="font-medium underline"
 | 
			
		||||
          ><a href="https://github.com/immich-app/immich/releases/latest" target="_blank" rel="noopener noreferrer"
 | 
			
		||||
            >release notes</a
 | 
			
		||||
          ></span
 | 
			
		||||
@ -60,7 +60,7 @@
 | 
			
		||||
        especially if you use WatchTower or any mechanism that handles updating your application automatically.
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="font-medium mt-4">Your friend, Alex</div>
 | 
			
		||||
      <div class="mt-4 font-medium">Your friend, Alex</div>
 | 
			
		||||
 | 
			
		||||
      <div class="font-sm mt-8">
 | 
			
		||||
        <code>Server Version: {serverVersionName}</code>
 | 
			
		||||
@ -68,7 +68,7 @@
 | 
			
		||||
        <code>Latest Version: {githubVersion}</code>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-right mt-8">
 | 
			
		||||
      <div class="mt-8 text-right">
 | 
			
		||||
        <Button fullwidth on:click={onAcknowledge}>Acknowledge</Button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
  class="w-full flex gap-4 dark:text-immich-gray transition-all border-b border-gray-200 dark:border-gray-600 hover:border-immich-primary dark:hover:border-immich-dark-primary py-4"
 | 
			
		||||
  class="flex w-full gap-4 border-b border-gray-200 py-4 transition-all hover:border-immich-primary dark:border-gray-600 dark:text-immich-gray dark:hover:border-immich-dark-primary"
 | 
			
		||||
>
 | 
			
		||||
  <div>
 | 
			
		||||
    {#await getAssetInfo()}
 | 
			
		||||
@ -69,7 +69,7 @@
 | 
			
		||||
        id={asset.id}
 | 
			
		||||
        src={api.getAssetThumbnailUrl(asset.id, ThumbnailFormat.Webp)}
 | 
			
		||||
        alt={asset.id}
 | 
			
		||||
        class="object-cover w-[100px] h-[100px] rounded-lg"
 | 
			
		||||
        class="h-[100px] w-[100px] rounded-lg object-cover"
 | 
			
		||||
        loading="lazy"
 | 
			
		||||
        draggable="false"
 | 
			
		||||
      />
 | 
			
		||||
@ -78,10 +78,10 @@
 | 
			
		||||
 | 
			
		||||
  <div class="flex flex-col justify-between">
 | 
			
		||||
    <div class="info-top">
 | 
			
		||||
      <div class="text-xs font-mono font-semibold text-gray-500 dark:text-gray-400">
 | 
			
		||||
      <div class="font-mono text-xs font-semibold text-gray-500 dark:text-gray-400">
 | 
			
		||||
        {#if link.expiresAt}
 | 
			
		||||
          {#if isExpired(link.expiresAt)}
 | 
			
		||||
            <p class="text-red-600 dark:text-red-400 font-bold">Expired</p>
 | 
			
		||||
            <p class="font-bold text-red-600 dark:text-red-400">Expired</p>
 | 
			
		||||
          {:else}
 | 
			
		||||
            <p>
 | 
			
		||||
              Expires {getCountDownExpirationDate()}
 | 
			
		||||
@ -93,7 +93,7 @@
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-sm">
 | 
			
		||||
        <div class="flex gap-2 place-items-center text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
        <div class="flex place-items-center gap-2 text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
          {#if link.type === SharedLinkType.Album}
 | 
			
		||||
            <p>
 | 
			
		||||
              {link.album?.albumName.toUpperCase()}
 | 
			
		||||
@ -122,7 +122,7 @@
 | 
			
		||||
    <div class="info-bottom flex gap-4">
 | 
			
		||||
      {#if link.allowUpload}
 | 
			
		||||
        <div
 | 
			
		||||
          class="text-xs px-2 py-1 bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-immich-dark-gray flex place-items-center place-content-center rounded-full w-[80px]"
 | 
			
		||||
          class="flex w-[80px] place-content-center place-items-center rounded-full bg-immich-primary px-2 py-1 text-xs text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray"
 | 
			
		||||
        >
 | 
			
		||||
          Upload
 | 
			
		||||
        </div>
 | 
			
		||||
@ -130,7 +130,7 @@
 | 
			
		||||
 | 
			
		||||
      {#if link.allowDownload}
 | 
			
		||||
        <div
 | 
			
		||||
          class="text-xs px-2 py-1 bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-immich-dark-gray flex place-items-center place-content-center rounded-full w-[100px]"
 | 
			
		||||
          class="flex w-[100px] place-content-center place-items-center rounded-full bg-immich-primary px-2 py-1 text-xs text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray"
 | 
			
		||||
        >
 | 
			
		||||
          Download
 | 
			
		||||
        </div>
 | 
			
		||||
@ -138,7 +138,7 @@
 | 
			
		||||
 | 
			
		||||
      {#if link.showExif}
 | 
			
		||||
        <div
 | 
			
		||||
          class="text-xs px-2 py-1 bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-immich-dark-gray flex place-items-center place-content-center rounded-full w-[60px]"
 | 
			
		||||
          class="flex w-[60px] place-content-center place-items-center rounded-full bg-immich-primary px-2 py-1 text-xs text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray"
 | 
			
		||||
        >
 | 
			
		||||
          EXIF
 | 
			
		||||
        </div>
 | 
			
		||||
@ -146,7 +146,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="flex-auto flex flex-col place-content-center place-items-end text-right">
 | 
			
		||||
  <div class="flex flex-auto flex-col place-content-center place-items-end text-right">
 | 
			
		||||
    <div class="flex">
 | 
			
		||||
      <CircleIconButton logo={Delete} on:click={() => dispatch('delete')} />
 | 
			
		||||
      <CircleIconButton logo={CircleEditOutline} on:click={() => dispatch('edit')} />
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@
 | 
			
		||||
<section class="my-4">
 | 
			
		||||
  <div in:fade={{ duration: 500 }}>
 | 
			
		||||
    <form autocomplete="off" on:submit|preventDefault>
 | 
			
		||||
      <div class="flex flex-col gap-4 ml-4 mt-4">
 | 
			
		||||
      <div class="ml-4 mt-4 flex flex-col gap-4">
 | 
			
		||||
        <SettingInputField
 | 
			
		||||
          inputType={SettingInputFieldType.PASSWORD}
 | 
			
		||||
          label="PASSWORD"
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,9 @@
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="flex flex-row w-full">
 | 
			
		||||
<div class="flex w-full flex-row">
 | 
			
		||||
  <!-- TODO: Device Image -->
 | 
			
		||||
  <div class="hidden sm:flex pr-2 justify-center items-center text-immich-primary dark:text-immich-dark-primary">
 | 
			
		||||
  <div class="hidden items-center justify-center pr-2 text-immich-primary dark:text-immich-dark-primary sm:flex">
 | 
			
		||||
    {#if device.deviceOS === 'Android'}
 | 
			
		||||
      <Android size="40" />
 | 
			
		||||
    {:else if device.deviceOS === 'iOS' || device.deviceOS === 'Mac OS'}
 | 
			
		||||
@ -41,8 +41,8 @@
 | 
			
		||||
      <Help size="40" />
 | 
			
		||||
    {/if}
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="pl-4 sm:pl-0 flex flex-row grow justify-between gap-1">
 | 
			
		||||
    <div class="flex flex-col gap-1 justify-center dark:text-white">
 | 
			
		||||
  <div class="flex grow flex-row justify-between gap-1 pl-4 sm:pl-0">
 | 
			
		||||
    <div class="flex flex-col justify-center gap-1 dark:text-white">
 | 
			
		||||
      <span class="text-sm">
 | 
			
		||||
        {#if device.deviceType || device.deviceOS}
 | 
			
		||||
          <span>{device.deviceOS || 'Unknown'} • {device.deviceType || 'Unknown'}</span>
 | 
			
		||||
@ -56,10 +56,10 @@
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    {#if !device.current}
 | 
			
		||||
      <div class="text-sm flex flex-col justify-center">
 | 
			
		||||
      <div class="flex flex-col justify-center text-sm">
 | 
			
		||||
        <button
 | 
			
		||||
          on:click={() => dispatcher('delete')}
 | 
			
		||||
          class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
 | 
			
		||||
          class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
			
		||||
          title="Log out"
 | 
			
		||||
        >
 | 
			
		||||
          <TrashCanOutline size="16" />
 | 
			
		||||
 | 
			
		||||
@ -71,13 +71,13 @@
 | 
			
		||||
<section class="my-4">
 | 
			
		||||
  {#if currentDevice}
 | 
			
		||||
    <div class="mb-6">
 | 
			
		||||
      <h3 class="font-medium text-xs mb-2 text-immich-primary dark:text-immich-dark-primary">CURRENT DEVICE</h3>
 | 
			
		||||
      <h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">CURRENT DEVICE</h3>
 | 
			
		||||
      <DeviceCard device={currentDevice} />
 | 
			
		||||
    </div>
 | 
			
		||||
  {/if}
 | 
			
		||||
  {#if otherDevices.length > 0}
 | 
			
		||||
    <div class="mb-6">
 | 
			
		||||
      <h3 class="font-medium text-xs mb-2 text-immich-primary dark:text-immich-dark-primary">OTHER DEVICES</h3>
 | 
			
		||||
      <h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">OTHER DEVICES</h3>
 | 
			
		||||
      {#each otherDevices as device, i}
 | 
			
		||||
        <DeviceCard {device} on:delete={() => (deleteDevice = device)} />
 | 
			
		||||
        {#if i !== otherDevices.length - 1}
 | 
			
		||||
@ -85,7 +85,7 @@
 | 
			
		||||
        {/if}
 | 
			
		||||
      {/each}
 | 
			
		||||
    </div>
 | 
			
		||||
    <h3 class="font-medium text-xs mb-2 text-immich-primary dark:text-immich-dark-primary">LOG OUT ALL DEVICES</h3>
 | 
			
		||||
    <h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">LOG OUT ALL DEVICES</h3>
 | 
			
		||||
    <div class="flex justify-end">
 | 
			
		||||
      <Button color="red" size="sm" on:click={() => (deleteAll = true)}>Log Out All Devices</Button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@
 | 
			
		||||
  <div in:fade={{ duration: 500 }}>
 | 
			
		||||
    <div class="flex justify-end">
 | 
			
		||||
      {#if loading}
 | 
			
		||||
        <div class="flex place-items-center place-content-center">
 | 
			
		||||
        <div class="flex place-content-center place-items-center">
 | 
			
		||||
          <LoadingSpinner />
 | 
			
		||||
        </div>
 | 
			
		||||
      {:else if config.enabled}
 | 
			
		||||
 | 
			
		||||
@ -37,22 +37,22 @@
 | 
			
		||||
 | 
			
		||||
<BaseModal on:close={() => dispatch('close')}>
 | 
			
		||||
  <svelte:fragment slot="title">
 | 
			
		||||
    <span class="flex gap-2 place-items-center">
 | 
			
		||||
    <span class="flex place-items-center gap-2">
 | 
			
		||||
      <ImmichLogo width={24} />
 | 
			
		||||
      <p class="font-medium">Add partner</p>
 | 
			
		||||
    </span>
 | 
			
		||||
  </svelte:fragment>
 | 
			
		||||
 | 
			
		||||
  <div class="max-h-[300px] overflow-y-auto immich-scrollbar">
 | 
			
		||||
  <div class="immich-scrollbar max-h-[300px] overflow-y-auto">
 | 
			
		||||
    {#if availableUsers.length > 0}
 | 
			
		||||
      {#each availableUsers as user}
 | 
			
		||||
        <button
 | 
			
		||||
          on:click={() => selectUser(user)}
 | 
			
		||||
          class="w-full flex place-items-center gap-4 py-4 px-5 hover:bg-gray-200 dark:hover:bg-gray-700 transition-all"
 | 
			
		||||
          class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
 | 
			
		||||
        >
 | 
			
		||||
          {#if selectedUsers.includes(user)}
 | 
			
		||||
            <span
 | 
			
		||||
              class="bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-immich-dark-bg rounded-full w-12 h-12 border flex place-items-center place-content-center text-3xl dark:border-immich-dark-gray"
 | 
			
		||||
              class="flex h-12 w-12 place-content-center place-items-center rounded-full border bg-immich-primary text-3xl text-white dark:border-immich-dark-gray dark:bg-immich-dark-primary dark:text-immich-dark-bg"
 | 
			
		||||
              >✓</span
 | 
			
		||||
            >
 | 
			
		||||
          {:else}
 | 
			
		||||
@ -71,7 +71,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
      {/each}
 | 
			
		||||
    {:else}
 | 
			
		||||
      <p class="text-sm p-5">
 | 
			
		||||
      <p class="p-5 text-sm">
 | 
			
		||||
        Looks like you shared your photos with all users or you don't have any user to share with.
 | 
			
		||||
      </p>
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@
 | 
			
		||||
  {#if partners.length > 0}
 | 
			
		||||
    <div class="flex flex-row gap-4">
 | 
			
		||||
      {#each partners as partner (partner.id)}
 | 
			
		||||
        <div class="flex rounded-lg gap-4 py-4 px-5 transition-all">
 | 
			
		||||
        <div class="flex gap-4 rounded-lg px-5 py-4 transition-all">
 | 
			
		||||
          <UserAvatar user={partner} size="md" autoColor />
 | 
			
		||||
          <div class="text-left">
 | 
			
		||||
            <p class="text-immich-fg dark:text-immich-dark-fg">
 | 
			
		||||
 | 
			
		||||
@ -116,43 +116,43 @@
 | 
			
		||||
 | 
			
		||||
<section class="my-4">
 | 
			
		||||
  <div class="flex flex-col gap-2" in:fade={{ duration: 500 }}>
 | 
			
		||||
    <div class="flex justify-end mb-2">
 | 
			
		||||
    <div class="mb-2 flex justify-end">
 | 
			
		||||
      <Button size="sm" on:click={() => (newKey = { name: 'API Key' })}>New API Key</Button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {#if keys.length > 0}
 | 
			
		||||
      <table class="text-left w-full">
 | 
			
		||||
      <table class="w-full text-left">
 | 
			
		||||
        <thead
 | 
			
		||||
          class="border rounded-md mb-4 bg-gray-50 flex text-immich-primary w-full h-12 dark:bg-immich-dark-gray dark:text-immich-dark-primary dark:border-immich-dark-gray"
 | 
			
		||||
          class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
 | 
			
		||||
        >
 | 
			
		||||
          <tr class="flex w-full place-items-center">
 | 
			
		||||
            <th class="text-center w-1/3 font-medium text-sm">Name</th>
 | 
			
		||||
            <th class="text-center w-1/3 font-medium text-sm">Created</th>
 | 
			
		||||
            <th class="text-center w-1/3 font-medium text-sm">Action</th>
 | 
			
		||||
            <th class="w-1/3 text-center text-sm font-medium">Name</th>
 | 
			
		||||
            <th class="w-1/3 text-center text-sm font-medium">Created</th>
 | 
			
		||||
            <th class="w-1/3 text-center text-sm font-medium">Action</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody class="overflow-y-auto rounded-md w-full block border dark:border-immich-dark-gray">
 | 
			
		||||
        <tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
 | 
			
		||||
          {#each keys as key, i}
 | 
			
		||||
            {#key key.id}
 | 
			
		||||
              <tr
 | 
			
		||||
                class={`text-center flex place-items-center w-full h-[80px] dark:text-immich-dark-fg ${
 | 
			
		||||
                class={`flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg ${
 | 
			
		||||
                  i % 2 == 0 ? 'bg-immich-gray dark:bg-immich-dark-gray/75' : 'bg-immich-bg dark:bg-immich-dark-gray/50'
 | 
			
		||||
                }`}
 | 
			
		||||
              >
 | 
			
		||||
                <td class="text-sm px-4 w-1/3 text-ellipsis">{key.name}</td>
 | 
			
		||||
                <td class="text-sm px-4 w-1/3 text-ellipsis"
 | 
			
		||||
                <td class="w-1/3 text-ellipsis px-4 text-sm">{key.name}</td>
 | 
			
		||||
                <td class="w-1/3 text-ellipsis px-4 text-sm"
 | 
			
		||||
                  >{new Date(key.createdAt).toLocaleDateString($locale, format)}
 | 
			
		||||
                </td>
 | 
			
		||||
                <td class="text-sm px-4 w-1/3 text-ellipsis">
 | 
			
		||||
                <td class="w-1/3 text-ellipsis px-4 text-sm">
 | 
			
		||||
                  <button
 | 
			
		||||
                    on:click={() => (editKey = key)}
 | 
			
		||||
                    class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
 | 
			
		||||
                    class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
			
		||||
                  >
 | 
			
		||||
                    <PencilOutline size="16" />
 | 
			
		||||
                  </button>
 | 
			
		||||
                  <button
 | 
			
		||||
                    on:click={() => (deleteKey = key)}
 | 
			
		||||
                    class="bg-immich-primary dark:bg-immich-dark-primary text-gray-100 dark:text-gray-700 rounded-full p-3 transition-all duration-150 hover:bg-immich-primary/75"
 | 
			
		||||
                    class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
			
		||||
                  >
 | 
			
		||||
                    <TrashCanOutline size="16" />
 | 
			
		||||
                  </button>
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user