From bc8fda5984e9eee928c18e3aeb44d51d7bf31967 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Thu, 19 Mar 2026 18:43:30 +0300 Subject: [PATCH] Add tooltip hints to form fields, fix navigation overlap bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create Hint component with fixed-position floating tooltip - Add hints to tracking configs (periodic/scheduled/memory modes, favorites, times, album mode, rating), template configs (section legends), targets (AI captions, media settings, config selectors), and trackers (scan interval) - Add 21 hint i18n keys in EN and RU - Fix transition:slide → in:slide on all pages to prevent content overlap when navigating away mid-animation - Merge Asset Display into Event Tracking fieldset; use consistent "Max Assets" label with hint in each section Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/lib/components/Hint.svelte | 44 +++++++++++++++++++ frontend/src/lib/i18n/en.json | 23 ++++++++++ frontend/src/lib/i18n/ru.json | 23 ++++++++++ frontend/src/routes/servers/+page.svelte | 2 +- frontend/src/routes/targets/+page.svelte | 19 ++++---- .../src/routes/template-configs/+page.svelte | 5 ++- frontend/src/routes/trackers/+page.svelte | 5 ++- .../src/routes/tracking-configs/+page.svelte | 44 ++++++++----------- 8 files changed, 126 insertions(+), 39 deletions(-) create mode 100644 frontend/src/lib/components/Hint.svelte diff --git a/frontend/src/lib/components/Hint.svelte b/frontend/src/lib/components/Hint.svelte new file mode 100644 index 0000000..d4593cf --- /dev/null +++ b/frontend/src/lib/components/Hint.svelte @@ -0,0 +1,44 @@ + + + + +{#if visible} + +{/if} diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json index b951af2..b1f5b62 100644 --- a/frontend/src/lib/i18n/en.json +++ b/frontend/src/lib/i18n/en.json @@ -267,6 +267,29 @@ "preview": "Preview", "confirmDelete": "Delete this template config?" }, + "hints": { + "periodicSummary": "Sends a scheduled summary of all tracked albums at specified times. Great for daily/weekly digests.", + "scheduledAssets": "Sends random or selected photos from tracked albums on a schedule. Like a daily photo pick.", + "memoryMode": "\"On This Day\" — sends photos taken on this date in previous years. Nostalgic flashbacks.", + "favoritesOnly": "Only include assets marked as favorites in Immich.", + "maxAssets": "Maximum number of asset details to include in a single notification message.", + "periodicStartDate": "The reference date for calculating periodic intervals. Summaries are sent every N days from this date.", + "times": "Time(s) of day to send notifications, in HH:MM format. Use commas for multiple times: 09:00,18:00", + "albumMode": "Per album: separate notification per album. Combined: one notification with all albums. Random: pick one album randomly.", + "minRating": "Only include assets with at least this star rating (0 = no filter).", + "eventMessages": "Templates for real-time event notifications. Use {variables} for dynamic content.", + "assetFormatting": "How individual assets are formatted within notification messages.", + "dateLocation": "Date and location formatting in notifications. Uses strftime syntax for dates.", + "scheduledMessages": "Templates for periodic summaries, scheduled photo picks, and On This Day memories.", + "aiCaptions": "Use Claude AI to generate a natural-language caption for notifications instead of the template.", + "maxMedia": "Maximum number of photos/videos to attach per notification (0 = text only).", + "groupSize": "Telegram media groups can contain 2-10 items. Larger batches are split into chunks.", + "chunkDelay": "Delay in milliseconds between sending media chunks. Prevents Telegram rate limiting.", + "maxAssetSize": "Skip assets larger than this size in MB. Telegram limits files to 50 MB.", + "trackingConfig": "Controls which events trigger notifications and how assets are filtered.", + "templateConfig": "Controls the message format. Uses default templates if not set.", + "scanInterval": "How often to poll the Immich server for changes, in seconds. Lower = faster detection but more API calls." + }, "common": { "loading": "Loading...", "save": "Save", diff --git a/frontend/src/lib/i18n/ru.json b/frontend/src/lib/i18n/ru.json index 1ab04af..a342654 100644 --- a/frontend/src/lib/i18n/ru.json +++ b/frontend/src/lib/i18n/ru.json @@ -267,6 +267,29 @@ "preview": "Предпросмотр", "confirmDelete": "Удалить эту конфигурацию шаблона?" }, + "hints": { + "periodicSummary": "Отправляет плановую сводку по всем отслеживаемым альбомам в указанное время. Подходит для ежедневных/еженедельных дайджестов.", + "scheduledAssets": "Отправляет случайные или выбранные фото из альбомов по расписанию. Как ежедневная подборка фото.", + "memoryMode": "\"В этот день\" — отправляет фото, сделанные в этот день в прошлые годы. Ностальгические воспоминания.", + "favoritesOnly": "Включать только ассеты, отмеченные как избранные в Immich.", + "maxAssets": "Максимальное количество ассетов в одном уведомлении.", + "periodicStartDate": "Опорная дата для расчёта интервалов. Сводки отправляются каждые N дней от этой даты.", + "times": "Время отправки уведомлений в формате ЧЧ:ММ. Для нескольких значений через запятую: 09:00,18:00", + "albumMode": "По альбому: отдельное уведомление для каждого. Объединённый: одно уведомление со всеми. Случайный: выбирается один альбом.", + "minRating": "Включать только ассеты с рейтингом не ниже указанного (0 = без фильтра).", + "eventMessages": "Шаблоны уведомлений о событиях в реальном времени. Используйте {переменные} для динамического контента.", + "assetFormatting": "Форматирование отдельных ассетов в сообщениях уведомлений.", + "dateLocation": "Форматирование даты и местоположения. Использует синтаксис strftime для дат.", + "scheduledMessages": "Шаблоны для периодических сводок, подборок фото и воспоминаний «В этот день».", + "aiCaptions": "Использовать Claude AI для генерации описания уведомления вместо шаблона.", + "maxMedia": "Максимальное количество фото/видео в одном уведомлении (0 = только текст).", + "groupSize": "Медиагруппы Telegram содержат 2-10 элементов. Большие пакеты разбиваются на части.", + "chunkDelay": "Задержка в миллисекундах между отправкой порций медиа. Предотвращает ограничение Telegram.", + "maxAssetSize": "Пропускать файлы больше указанного размера в МБ. Лимит Telegram — 50 МБ.", + "trackingConfig": "Управляет тем, какие события вызывают уведомления и как фильтруются ассеты.", + "templateConfig": "Управляет форматом сообщений. Используются шаблоны по умолчанию, если не задано.", + "scanInterval": "Как часто опрашивать сервер Immich на предмет изменений (в секундах). Меньше = быстрее обнаружение, но больше запросов к API." + }, "common": { "loading": "Загрузка...", "save": "Сохранить", diff --git a/frontend/src/routes/servers/+page.svelte b/frontend/src/routes/servers/+page.svelte index 32bda06..e8abdf3 100644 --- a/frontend/src/routes/servers/+page.svelte +++ b/frontend/src/routes/servers/+page.svelte @@ -91,7 +91,7 @@ {/if} {#if showForm} -
+
{#if error}
{error}
{/if}
diff --git a/frontend/src/routes/targets/+page.svelte b/frontend/src/routes/targets/+page.svelte index c9a3a69..af8487c 100644 --- a/frontend/src/routes/targets/+page.svelte +++ b/frontend/src/routes/targets/+page.svelte @@ -9,6 +9,7 @@ import IconPicker from '$lib/components/IconPicker.svelte'; import MdiIcon from '$lib/components/MdiIcon.svelte'; import ConfirmModal from '$lib/components/ConfirmModal.svelte'; + import Hint from '$lib/components/Hint.svelte'; let targets = $state([]); let trackingConfigs = $state([]); @@ -127,7 +128,7 @@ {/if} {#if showForm} -
+
{#if error}
{error}
{/if} @@ -190,21 +191,21 @@ {#if showTelegramSettings} -
+
- +
- +
- +
- +
@@ -227,14 +228,14 @@
- +
- + {t('targets.aiCaptions')} + diff --git a/frontend/src/routes/template-configs/+page.svelte b/frontend/src/routes/template-configs/+page.svelte index 2aae239..0adef8c 100644 --- a/frontend/src/routes/template-configs/+page.svelte +++ b/frontend/src/routes/template-configs/+page.svelte @@ -9,6 +9,7 @@ import IconPicker from '$lib/components/IconPicker.svelte'; import MdiIcon from '$lib/components/MdiIcon.svelte'; import ConfirmModal from '$lib/components/ConfirmModal.svelte'; + import Hint from '$lib/components/Hint.svelte'; let configs = $state([]); let loaded = $state(false); @@ -129,7 +130,7 @@ {#if !loaded}{:else} {#if showForm} -
+
{#if error}
{error}
{/if}
@@ -144,7 +145,7 @@ {#each templateSlots as group}
- {t(`templateConfig.${group.group}`)} + {t(`templateConfig.${group.group}`)}{#if group.group === 'eventMessages'}{:else if group.group === 'assetFormatting'}{:else if group.group === 'dateLocation'}{:else if group.group === 'scheduledMessages'}{/if}
{#each group.slots as slot}
diff --git a/frontend/src/routes/trackers/+page.svelte b/frontend/src/routes/trackers/+page.svelte index 0d1bc53..1a49394 100644 --- a/frontend/src/routes/trackers/+page.svelte +++ b/frontend/src/routes/trackers/+page.svelte @@ -9,6 +9,7 @@ import IconPicker from '$lib/components/IconPicker.svelte'; import MdiIcon from '$lib/components/MdiIcon.svelte'; import ConfirmModal from '$lib/components/ConfirmModal.svelte'; + import Hint from '$lib/components/Hint.svelte'; let loaded = $state(false); let loadError = $state(''); @@ -136,7 +137,7 @@ {:else if showForm} -
+
{#if error}
{error}
{/if} @@ -175,7 +176,7 @@
{/if}
- +
diff --git a/frontend/src/routes/tracking-configs/+page.svelte b/frontend/src/routes/tracking-configs/+page.svelte index 52feba6..c184dad 100644 --- a/frontend/src/routes/tracking-configs/+page.svelte +++ b/frontend/src/routes/tracking-configs/+page.svelte @@ -9,6 +9,7 @@ import IconPicker from '$lib/components/IconPicker.svelte'; import MdiIcon from '$lib/components/MdiIcon.svelte'; import ConfirmModal from '$lib/components/ConfirmModal.svelte'; + import Hint from '$lib/components/Hint.svelte'; let configs = $state([]); let loaded = $state(false); @@ -76,7 +77,7 @@ {#if !loaded}{:else} {#if showForm} -
+
{#if error}
{error}
{/if} @@ -99,20 +100,13 @@ - -
-
- - -
- {t('trackingConfig.assetDisplay')} -
+
- +
@@ -132,57 +126,57 @@
- {t('trackingConfig.periodicSummary')} + {t('trackingConfig.periodicSummary')} {#if form.periodic_enabled}
-
-
+
+
{/if}
- {t('trackingConfig.scheduledAssets')} + {t('trackingConfig.scheduledAssets')} {#if form.scheduled_enabled}
-
-
+
+
-
+
-
- +
+
{/if}
- {t('trackingConfig.memoryMode')} + {t('trackingConfig.memoryMode')} {#if form.memory_enabled}
-
-
+
+
-
+
-
- +
+
{/if}