diff --git a/docs/docs/developer/setup.md b/docs/docs/developer/setup.md index 76106803e8..5e821e9b9b 100644 --- a/docs/docs/developer/setup.md +++ b/docs/docs/developer/setup.md @@ -75,11 +75,12 @@ npm run dev To see local changes to `@immich/ui` in Immich, do the following: 1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui` -1. Build the `@immich/ui` project via `npm run build` -1. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`) -1. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`) -1. Start up the stack via `make dev` -1. After making changes in `@immich/ui`, rebuild it (`npm run build`) +2. Build the `@immich/ui` project via `npm run build` +3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`) +4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`) +5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';` +6. Start up the stack via `make dev` +7. After making changes in `@immich/ui`, rebuild it (`npm run build`) ### Mobile app diff --git a/web/package-lock.json b/web/package-lock.json index cc29dd6856..bde201c176 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.22.0", + "@immich/ui": "^0.22.1", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -1341,15 +1341,15 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.22.0.tgz", - "integrity": "sha512-bBx9hPy7/VECZPcEiBGty6Lu9jmD4vJf6VL2ud+LHLQcpZebv4FVFZzzVFf7ctBwooYJWTEfWZTPNgAo0rbQtQ==", + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.22.1.tgz", + "integrity": "sha512-/QdqctBit+eX8QZgTL4PlgS7l6/NCGXeDjR6kQNLOVBPhbjkmtwqsvZ+RymYClcHAEhutXOKRhnlQU9mNLC/SA==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", "bits-ui": "^1.0.0-next.46", "tailwind-merge": "^2.5.4", - "tailwind-variants": "^0.3.0" + "tailwind-variants": "^1.0.0" }, "peerDependencies": { "svelte": "^5.0.0" @@ -9479,12 +9479,12 @@ } }, "node_modules/tailwind-variants": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.1.tgz", - "integrity": "sha512-krn67M3FpPwElg4FsZrOQd0U26o7UDH/QOkK8RNaiCCrr052f6YJPBUfNKnPo/s/xRzNPtv1Mldlxsg8Tb46BQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-1.0.0.tgz", + "integrity": "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==", "license": "MIT", "dependencies": { - "tailwind-merge": "2.5.4" + "tailwind-merge": "3.0.2" }, "engines": { "node": ">=16.x", @@ -9495,9 +9495,9 @@ } }, "node_modules/tailwind-variants/node_modules/tailwind-merge": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz", - "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", + "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", "license": "MIT", "funding": { "type": "github", diff --git a/web/package.json b/web/package.json index d352afe45b..99df56b7f0 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.22.0", + "@immich/ui": "^0.22.1", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", diff --git a/web/src/app.css b/web/src/app.css index 6160af1b8e..96639845b5 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -1,5 +1,6 @@ @import 'tailwindcss'; @import '@immich/ui/theme/default.css'; +/* @import '/usr/ui/dist/theme/default.css'; */ @config '../tailwind.config.js'; diff --git a/web/src/lib/components/admin-page/server-stats/stats-card.svelte b/web/src/lib/components/admin-page/server-stats/stats-card.svelte index aa18c6f3ae..6250a8ef7f 100644 --- a/web/src/lib/components/admin-page/server-stats/stats-card.svelte +++ b/web/src/lib/components/admin-page/server-stats/stats-card.svelte @@ -30,7 +30,7 @@
{zeros()}{value} {#if unit} - {unit} + {unit} {/if}
diff --git a/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte b/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte index a294eb1768..8e85664750 100644 --- a/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte +++ b/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte @@ -1,13 +1,12 @@ {#if !selectedRemoveUser} - -
-
-

{$t('settings').toUpperCase()}

-
- {#if order} - + +
+
+

{$t('settings').toUpperCase()}

+
+ {#if order} + + {/if} + - {/if} - -
-
-
-
{$t('people').toUpperCase()}
-
- - -
-
- -
-
{user.name}
-
{$t('owner')}
+
+
+
{$t('people').toUpperCase()}
+
+ - {#each album.albumUsers as { user, role } (user.id)} -
+
{user.name}
- {#if role === AlbumUserRole.Viewer} - {$t('role_viewer')} - {:else} - {$t('role_editor')} - {/if} - {#if user.id !== album.ownerId} - - {#if role === AlbumUserRole.Viewer} - handleUpdateSharedUserRole(user, AlbumUserRole.Editor)} - text={$t('allow_edits')} - /> - {:else} - handleUpdateSharedUserRole(user, AlbumUserRole.Viewer)} - text={$t('disallow_edits')} - /> - {/if} - - handleMenuRemove(user)} text={$t('remove')} /> - - {/if} +
{$t('owner')}
- {/each} + + {#each album.albumUsers as { user, role } (user.id)} +
+
+ +
+
{user.name}
+ {#if role === AlbumUserRole.Viewer} + {$t('role_viewer')} + {:else} + {$t('role_editor')} + {/if} + {#if user.id !== album.ownerId} + + {#if role === AlbumUserRole.Viewer} + handleUpdateSharedUserRole(user, AlbumUserRole.Editor)} + text={$t('allow_edits')} + /> + {:else} + handleUpdateSharedUserRole(user, AlbumUserRole.Viewer)} + text={$t('disallow_edits')} + /> + {/if} + + handleMenuRemove(user)} text={$t('remove')} /> + + {/if} +
+ {/each} +
-
- + + {/if} {#if selectedRemoveUser} diff --git a/web/src/lib/components/album-page/album-title.svelte b/web/src/lib/components/album-page/album-title.svelte index f5b1a4fa1e..f2559c86e5 100644 --- a/web/src/lib/components/album-page/album-title.svelte +++ b/web/src/lib/components/album-page/album-title.svelte @@ -40,7 +40,7 @@ onblur={handleUpdateName} class="w-[99%] mb-2 border-b-2 border-transparent text-2xl md:text-4xl lg:text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned ? 'hover:border-gray-400' - : 'hover:border-transparent'} focus:border-b-2 focus:border-immich-primary focus:outline-none bg-light dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray" + : 'hover:border-transparent'} focus:border-b-2 focus:border-immich-primary focus:outline-none bg-light dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray placeholder:text-primary/90" type="text" bind:value={newAlbumName} disabled={!isOwned} diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index eee7a7c0b6..5da6324528 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -50,7 +50,7 @@ {#each tags as tag (tag.id)}

diff --git a/web/src/lib/components/forms/edit-album-form.svelte b/web/src/lib/components/forms/edit-album-form.svelte index e269f0d8c0..32e1e422e7 100644 --- a/web/src/lib/components/forms/edit-album-form.svelte +++ b/web/src/lib/components/forms/edit-album-form.svelte @@ -1,9 +1,8 @@ - -

-
- - -
-
- - + + + +
+ -
- - +
+
+ + +
+ +
+ + +
+ + + + +
+ +
- - - {#snippet stickyBottom()} - - - {/snippet} - +
+ diff --git a/web/src/lib/components/forms/library-exclusion-pattern-form.svelte b/web/src/lib/components/forms/library-exclusion-pattern-form.svelte index d6ce4bcda0..e069e5c7a2 100644 --- a/web/src/lib/components/forms/library-exclusion-pattern-form.svelte +++ b/web/src/lib/components/forms/library-exclusion-pattern-form.svelte @@ -1,9 +1,8 @@ - -
-

- {$t('admin.exclusion_pattern_description')} -

- {$t('admin.add_exclusion_pattern_description')} -

-
- - -
-
- {#if isDuplicate} -

{$t('errors.exclusion_pattern_already_exists')}

+ + + +

+ {$t('admin.exclusion_pattern_description')} +

+ {$t('admin.add_exclusion_pattern_description')} +

+
+ + +
+
+ {#if isDuplicate} +

{$t('errors.exclusion_pattern_already_exists')}

+ {/if} +
+ +
+ +
+ + {#if isEditing} + {/if} +
- - - {#snippet stickyBottom()} - - {#if isEditing} - - {/if} - - {/snippet} - +
+
diff --git a/web/src/lib/components/forms/library-import-path-form.svelte b/web/src/lib/components/forms/library-import-path-form.svelte index 512b8919e3..ee2a273708 100644 --- a/web/src/lib/components/forms/library-import-path-form.svelte +++ b/web/src/lib/components/forms/library-import-path-form.svelte @@ -1,6 +1,5 @@ - -
-

{$t('admin.library_import_path_description')}

+ + + +

{$t('admin.library_import_path_description')}

-
- - -
+
+ + +
-
- {#if isDuplicate} -

{$t('errors.import_path_already_exists')}

+
+ {#if isDuplicate} +

{$t('errors.import_path_already_exists')}

+ {/if} +
+ + + + +
+ + {#if isEditing} + {/if} +
- - - {#snippet stickyBottom()} - - {#if isEditing} - - {/if} - - {/snippet} - +
+ diff --git a/web/src/lib/components/forms/library-rename-form.svelte b/web/src/lib/components/forms/library-rename-form.svelte index af60bdc2be..0c04c77da1 100644 --- a/web/src/lib/components/forms/library-rename-form.svelte +++ b/web/src/lib/components/forms/library-rename-form.svelte @@ -1,7 +1,6 @@ -
- - - - + + + + + + + + - {#snippet stickyBottom()} + +
- - {/snippet} - - + +
+
+
diff --git a/web/src/lib/components/forms/library-user-picker-form.svelte b/web/src/lib/components/forms/library-user-picker-form.svelte index 673fe7d5e9..43b3eb69f1 100644 --- a/web/src/lib/components/forms/library-user-picker-form.svelte +++ b/web/src/lib/components/forms/library-user-picker-form.svelte @@ -2,11 +2,10 @@ import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import { user } from '$lib/stores/user.store'; import { searchUsersAdmin } from '@immich/sdk'; - import { Button } from '@immich/ui'; + import { Button, Modal, ModalBody, ModalFooter } from '@immich/ui'; import { mdiFolderSync } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; - import FullScreenModal from '../shared-components/full-screen-modal.svelte'; interface Props { onCancel: () => void; @@ -30,15 +29,19 @@ }; - -
-

{$t('admin.note_cannot_be_changed_later')}

+ + + +

{$t('admin.note_cannot_be_changed_later')}

- - + + +
- {#snippet stickyBottom()} - - - {/snippet} -
+ +
+ + +
+
+ diff --git a/web/src/lib/components/forms/tag-asset-form.svelte b/web/src/lib/components/forms/tag-asset-form.svelte index c757ff5335..a6e0928ece 100644 --- a/web/src/lib/components/forms/tag-asset-form.svelte +++ b/web/src/lib/components/forms/tag-asset-form.svelte @@ -1,13 +1,12 @@ - -
-
- ({ id: tag.id, label: tag.value, value: tag.id }))} - placeholder={$t('search_tags')} - /> + + + +
+ ({ id: tag.id, label: tag.value, value: tag.id }))} + placeholder={$t('search_tags')} + /> +
+ + +
+ {#each selectedIds as tagId (tagId)} + {@const tag = tagMap[tagId]} + {#if tag} +
+ +

+ {tag.value} +

+
+ + +
+ {/if} + {/each} +
+
+ + +
+ +
- - -
- {#each selectedIds as tagId (tagId)} - {@const tag = tagMap[tagId]} - {#if tag} -
- -

- {tag.value} -

-
- - -
- {/if} - {/each} -
- - {#snippet stickyBottom()} - - - {/snippet} - +
+
diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index cace59e9d6..2dd25491a8 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -314,8 +314,9 @@ /> {#if assetInteraction.selectionActive} -
+
cancelMultiselect(assetInteraction)} > @@ -605,6 +606,7 @@ {/if} + {#if current}
diff --git a/web/src/lib/components/photos-page/asset-select-control-bar.svelte b/web/src/lib/components/photos-page/asset-select-control-bar.svelte index 17b06c0e7e..b21693bc51 100644 --- a/web/src/lib/components/photos-page/asset-select-control-bar.svelte +++ b/web/src/lib/components/photos-page/asset-select-control-bar.svelte @@ -24,9 +24,10 @@ clearSelect: () => void; ownerId?: string | undefined; children?: Snippet; + forceDark?: boolean; } - let { assets, clearSelect, ownerId = undefined, children }: Props = $props(); + let { assets, clearSelect, ownerId = undefined, children, forceDark }: Props = $props(); setContext({ getAssets: () => assets, @@ -35,9 +36,11 @@ }); - + {#snippet leading()} -
+

{assets.length}

diff --git a/web/src/lib/components/shared-components/album-selection/album-selection-modal.svelte b/web/src/lib/components/shared-components/album-selection/album-selection-modal.svelte index 3bc0161236..ab763546af 100644 --- a/web/src/lib/components/shared-components/album-selection/album-selection-modal.svelte +++ b/web/src/lib/components/shared-components/album-selection/album-selection-modal.svelte @@ -5,9 +5,9 @@ AlbumModalRowType, isSelectableRowType, } from '$lib/components/shared-components/album-selection/album-selection-utils'; - import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; import { albumViewSettings } from '$lib/stores/preferences.store'; import { type AlbumResponseDto, getAllAlbums } from '@immich/sdk'; + import { Modal, ModalBody } from '@immich/ui'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; import AlbumListItem from '../../asset-viewer/album-list-item.svelte'; @@ -80,49 +80,51 @@ const handleAlbumClick = (album: AlbumResponseDto) => () => onAlbumClick(album); - -
- {#if loading} - - {#each { length: 3 } as _} -
-
-
- -
- - + + +
+ {#if loading} + + {#each { length: 3 } as _} +
+
+
+ +
+ + +
-
- {/each} - {:else} - -
- - {#each albumModalRows as row} - {#if row.type === AlbumModalRowType.NEW_ALBUM} - - {:else if row.type === AlbumModalRowType.SECTION} -

{row.text}

- {:else if row.type === AlbumModalRowType.MESSAGE} -

{row.text}

- {:else if row.type === AlbumModalRowType.ALBUM_ITEM && row.album} - - {/if} {/each} -
- {/if} -
- + {:else} + +
+ + {#each albumModalRows as row} + {#if row.type === AlbumModalRowType.NEW_ALBUM} + + {:else if row.type === AlbumModalRowType.SECTION} +

{row.text}

+ {:else if row.type === AlbumModalRowType.MESSAGE} +

{row.text}

+ {:else if row.type === AlbumModalRowType.ALBUM_ITEM && row.album} + + {/if} + {/each} +
+ {/if} +
+ + diff --git a/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte b/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte index 593baafc7c..9e5d5d2391 100644 --- a/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte +++ b/web/src/lib/components/shared-components/context-menu/button-context-menu.svelte @@ -38,6 +38,10 @@ buttonClass?: string | undefined; hideContent?: boolean; children?: Snippet; + offset?: { + x: number; + y: number; + }; } & HTMLAttributes; let { @@ -51,6 +55,7 @@ buttonClass = undefined, hideContent = false, children, + offset, ...restProps }: Props = $props(); @@ -186,13 +191,14 @@ ]} > {@render children?.()} diff --git a/web/src/lib/components/shared-components/control-app-bar.svelte b/web/src/lib/components/shared-components/control-app-bar.svelte index 0476ba6bfd..6830dfefa0 100644 --- a/web/src/lib/components/shared-components/control-app-bar.svelte +++ b/web/src/lib/components/shared-components/control-app-bar.svelte @@ -66,7 +66,7 @@ let buttonClass = $derived(forceDark ? 'hover:text-immich-dark-gray' : undefined); -
+