feat(sources): add the shared source row and credential-free formatting

One app-source-row (regular for the Sources page, compact for the
playlist switcher) with an app-source-tile, built on pure helpers:

- resolvePlaylistSourceKind: one provider rule for icons, filters and
  counts (the list filter and the panel counts used to disagree).
- sourceDisplayName / sourceDisplayUrl: a name or URL never shows a
  password, token or MAC. URL imports that stored
  "get.php?username=…&password=…" as the title now show the host.
- stripUrlCredentials (shared/logging): drops what the log redactor
  would mask, plus short user/pass keys.
- formatSourceDate / sourceMetaParts / sourceStatusTag: one meta line
  order and Intl dates and durations in the UI language.
- SourceStatusService + SourceHealthHistoryService: one status per
  source (desktop health check, browser Xtream portal status) and the
  last conclusive result kept across restarts, so "not responding for
  3 days" survives a relaunch. No URL or credential is stored.

Also fixes playlistNameHasCredentials answering differently on repeated
calls (global regex lastIndex), and adds the UI-kit button, menu and
segmented-control mixins (libs/ui/styles/_app-controls.scss); menus opt
in with class="app-menu", styled by the components that use them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-12 22:34:22 +02:00
1 parent 6fe684af51
commit 028a3db880
51 files changed
+3455 -25

No files matched your search

+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "قوائم Stalker",
"ALL_PLAYLISTS": "جميع قوائم التشغيل",
"PLAYLIST_COUNT_ONE": "قائمة تشغيل واحدة",
"PLAYLIST_COUNT_OTHER": "{{count}} قائمة تشغيل"
"PLAYLIST_COUNT_OTHER": "{{count}} قائمة تشغيل",
"ROW": {
"TYPE_M3U_FILE": "M3U · ملف",
"ADDED": "أُضيف {{date}}",
"UPDATED": "حُدِّث {{date}}",
"UNREACHABLE": "لا يستجيب",
"UNREACHABLE_FOR": "لا يستجيب · {{duration}}",
"EXPIRED": "منتهي",
"EDIT": "تعديل…",
"COPY_URL": "نسخ الرابط",
"OPEN": "فتح"
}
},
"CONTEXT": {
"CATEGORIES": "الفئات",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "قوائم تشغيل Stalker",
"ALL_PLAYLISTS": "جميع قوائم التشغيل",
"PLAYLIST_COUNT_ONE": "قائمة تشغيل وحدة",
"PLAYLIST_COUNT_OTHER": "{{count}} ديال قوائم التشغيل"
"PLAYLIST_COUNT_OTHER": "{{count}} ديال قوائم التشغيل",
"ROW": {
"TYPE_M3U_FILE": "M3U · فيشي",
"ADDED": "تزاد {{date}}",
"UPDATED": "تحدّث {{date}}",
"UNREACHABLE": "ما كيجاوبش",
"UNREACHABLE_FOR": "ما كيجاوبش · {{duration}}",
"EXPIRED": "سالات",
"EDIT": "بدّل…",
"COPY_URL": "نسخ الرابط",
"OPEN": "حلّ"
}
},
"CONTEXT": {
"CATEGORIES": "الفئات",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker плэйлісты",
"ALL_PLAYLISTS": "Усе плэйлісты",
"PLAYLIST_COUNT_ONE": "1 плэйліст",
"PLAYLIST_COUNT_OTHER": "{{count}} плэйлістаў"
"PLAYLIST_COUNT_OTHER": "{{count}} плэйлістаў",
"ROW": {
"TYPE_M3U_FILE": "M3U · файл",
"ADDED": "дададзены {{date}}",
"UPDATED": "абноўлены {{date}}",
"UNREACHABLE": "Не адказвае",
"UNREACHABLE_FOR": "Не адказвае · {{duration}}",
"EXPIRED": "Скончыўся",
"EDIT": "Змяніць…",
"COPY_URL": "Скапіяваць URL",
"OPEN": "Адкрыць"
}
},
"CONTEXT": {
"CATEGORIES": "Катэгорыі",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker-Playlists",
"ALL_PLAYLISTS": "Alle Playlists",
"PLAYLIST_COUNT_ONE": "1 Playlist",
"PLAYLIST_COUNT_OTHER": "{{count}} Playlists"
"PLAYLIST_COUNT_OTHER": "{{count}} Playlists",
"ROW": {
"TYPE_M3U_FILE": "M3U · Datei",
"ADDED": "hinzugefügt am {{date}}",
"UPDATED": "aktualisiert am {{date}}",
"UNREACHABLE": "Antwortet nicht",
"UNREACHABLE_FOR": "Antwortet nicht · {{duration}}",
"EXPIRED": "Abgelaufen",
"EDIT": "Bearbeiten…",
"COPY_URL": "URL kopieren",
"OPEN": "Öffnen"
}
},
"CONTEXT": {
"CATEGORIES": "Kategorien",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Λίστες αναπαραγωγής Stalker",
"ALL_PLAYLISTS": "Όλες οι λίστες αναπαραγωγής",
"PLAYLIST_COUNT_ONE": "1 λίστα αναπαραγωγής",
"PLAYLIST_COUNT_OTHER": "{{count}} λίστες αναπαραγωγής"
"PLAYLIST_COUNT_OTHER": "{{count}} λίστες αναπαραγωγής",
"ROW": {
"TYPE_M3U_FILE": "M3U · αρχείο",
"ADDED": "προστέθηκε {{date}}",
"UPDATED": "ενημερώθηκε {{date}}",
"UNREACHABLE": "Δεν αποκρίνεται",
"UNREACHABLE_FOR": "Δεν αποκρίνεται · {{duration}}",
"EXPIRED": "Έληξε",
"EDIT": "Επεξεργασία…",
"COPY_URL": "Αντιγραφή URL",
"OPEN": "Άνοιγμα"
}
},
"CONTEXT": {
"CATEGORIES": "Κατηγορίες",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker Playlists",
"ALL_PLAYLISTS": "All Playlists",
"PLAYLIST_COUNT_ONE": "1 playlist",
"PLAYLIST_COUNT_OTHER": "{{count}} playlists"
"PLAYLIST_COUNT_OTHER": "{{count}} playlists",
"ROW": {
"TYPE_M3U_FILE": "M3U · file",
"ADDED": "added {{date}}",
"UPDATED": "updated {{date}}",
"UNREACHABLE": "Not responding",
"UNREACHABLE_FOR": "Not responding · {{duration}}",
"EXPIRED": "Expired",
"EDIT": "Edit…",
"COPY_URL": "Copy URL",
"OPEN": "Open"
}
},
"CONTEXT": {
"CATEGORIES": "Categories",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Listas Stalker",
"ALL_PLAYLISTS": "Todas las listas",
"PLAYLIST_COUNT_ONE": "1 lista de reproducción",
"PLAYLIST_COUNT_OTHER": "{{count}} listas de reproducción"
"PLAYLIST_COUNT_OTHER": "{{count}} listas de reproducción",
"ROW": {
"TYPE_M3U_FILE": "M3U · archivo",
"ADDED": "añadido el {{date}}",
"UPDATED": "actualizado el {{date}}",
"UNREACHABLE": "No responde",
"UNREACHABLE_FOR": "No responde · {{duration}}",
"EXPIRED": "Caducada",
"EDIT": "Editar…",
"COPY_URL": "Copiar URL",
"OPEN": "Abrir"
}
},
"CONTEXT": {
"CATEGORIES": "Categorías",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Listes de lecture Stalker",
"ALL_PLAYLISTS": "Toutes les listes de lecture",
"PLAYLIST_COUNT_ONE": "1 liste de lecture",
"PLAYLIST_COUNT_OTHER": "{{count}} listes de lecture"
"PLAYLIST_COUNT_OTHER": "{{count}} listes de lecture",
"ROW": {
"TYPE_M3U_FILE": "M3U · fichier",
"ADDED": "ajouté le {{date}}",
"UPDATED": "mis à jour le {{date}}",
"UNREACHABLE": "Ne répond pas",
"UNREACHABLE_FOR": "Ne répond pas · {{duration}}",
"EXPIRED": "Expiré",
"EDIT": "Modifier…",
"COPY_URL": "Copier l’URL",
"OPEN": "Ouvrir"
}
},
"CONTEXT": {
"CATEGORIES": "Catégories",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker-lejátszási listák",
"ALL_PLAYLISTS": "Összes lejátszási lista",
"PLAYLIST_COUNT_ONE": "1 lejátszási lista",
"PLAYLIST_COUNT_OTHER": "{{count}} lejátszási lista"
"PLAYLIST_COUNT_OTHER": "{{count}} lejátszási lista",
"ROW": {
"TYPE_M3U_FILE": "M3U · fájl",
"ADDED": "hozzáadva: {{date}}",
"UPDATED": "frissítve: {{date}}",
"UNREACHABLE": "Nem válaszol",
"UNREACHABLE_FOR": "Nem válaszol · {{duration}}",
"EXPIRED": "Lejárt",
"EDIT": "Szerkesztés…",
"COPY_URL": "URL másolása",
"OPEN": "Megnyitás"
}
},
"CONTEXT": {
"CATEGORIES": "Kategóriák",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Playlist Stalker",
"ALL_PLAYLISTS": "Tutte le playlist",
"PLAYLIST_COUNT_ONE": "1 playlist",
"PLAYLIST_COUNT_OTHER": "{{count}} playlist"
"PLAYLIST_COUNT_OTHER": "{{count}} playlist",
"ROW": {
"TYPE_M3U_FILE": "M3U · file locale",
"ADDED": "aggiunto il {{date}}",
"UPDATED": "aggiornato il {{date}}",
"UNREACHABLE": "Non risponde",
"UNREACHABLE_FOR": "Non risponde · {{duration}}",
"EXPIRED": "Scaduto",
"EDIT": "Modifica…",
"COPY_URL": "Copia URL",
"OPEN": "Apri"
}
},
"CONTEXT": {
"CATEGORIES": "Categorie",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalkerプレイリスト",
"ALL_PLAYLISTS": "すべてのプレイリスト",
"PLAYLIST_COUNT_ONE": "1 件のプレイリスト",
"PLAYLIST_COUNT_OTHER": "{{count}} 件のプレイリスト"
"PLAYLIST_COUNT_OTHER": "{{count}} 件のプレイリスト",
"ROW": {
"TYPE_M3U_FILE": "M3U · ファイル",
"ADDED": "{{date}}に追加",
"UPDATED": "{{date}}に更新",
"UNREACHABLE": "応答なし",
"UNREACHABLE_FOR": "応答なし · {{duration}}",
"EXPIRED": "期限切れ",
"EDIT": "編集…",
"COPY_URL": "URLをコピー",
"OPEN": "開く"
}
},
"CONTEXT": {
"CATEGORIES": "カテゴリー",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker 재생목록",
"ALL_PLAYLISTS": "모든 재생목록",
"PLAYLIST_COUNT_ONE": "1개 재생목록",
"PLAYLIST_COUNT_OTHER": "{{count}}개 재생목록"
"PLAYLIST_COUNT_OTHER": "{{count}}개 재생목록",
"ROW": {
"TYPE_M3U_FILE": "M3U · 파일",
"ADDED": "{{date}} 추가됨",
"UPDATED": "{{date}} 업데이트됨",
"UNREACHABLE": "응답 없음",
"UNREACHABLE_FOR": "응답 없음 · {{duration}}",
"EXPIRED": "만료됨",
"EDIT": "편집…",
"COPY_URL": "URL 복사",
"OPEN": "열기"
}
},
"CONTEXT": {
"CATEGORIES": "카테고리",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker-afspeellijsten",
"ALL_PLAYLISTS": "Alle afspeellijsten",
"PLAYLIST_COUNT_ONE": "1 afspeellijst",
"PLAYLIST_COUNT_OTHER": "{{count}} afspeellijsten"
"PLAYLIST_COUNT_OTHER": "{{count}} afspeellijsten",
"ROW": {
"TYPE_M3U_FILE": "M3U · bestand",
"ADDED": "toegevoegd {{date}}",
"UPDATED": "bijgewerkt {{date}}",
"UNREACHABLE": "Reageert niet",
"UNREACHABLE_FOR": "Reageert niet · {{duration}}",
"EXPIRED": "Verlopen",
"EDIT": "Bewerken…",
"COPY_URL": "URL kopiëren",
"OPEN": "Openen"
}
},
"CONTEXT": {
"CATEGORIES": "Categorieën",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Listy odtwarzania Stalker",
"ALL_PLAYLISTS": "Wszystkie listy odtwarzania",
"PLAYLIST_COUNT_ONE": "1 lista odtwarzania",
"PLAYLIST_COUNT_OTHER": "{{count}} list odtwarzania"
"PLAYLIST_COUNT_OTHER": "{{count}} list odtwarzania",
"ROW": {
"TYPE_M3U_FILE": "M3U · plik",
"ADDED": "dodano {{date}}",
"UPDATED": "zaktualizowano {{date}}",
"UNREACHABLE": "Nie odpowiada",
"UNREACHABLE_FOR": "Nie odpowiada · {{duration}}",
"EXPIRED": "Wygasło",
"EDIT": "Edytuj…",
"COPY_URL": "Kopiuj URL",
"OPEN": "Otwórz"
}
},
"CONTEXT": {
"CATEGORIES": "Kategorie",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Playlists Stalker",
"ALL_PLAYLISTS": "Todas as playlists",
"PLAYLIST_COUNT_ONE": "1 playlist",
"PLAYLIST_COUNT_OTHER": "{{count}} playlists"
"PLAYLIST_COUNT_OTHER": "{{count}} playlists",
"ROW": {
"TYPE_M3U_FILE": "M3U · arquivo",
"ADDED": "adicionado em {{date}}",
"UPDATED": "atualizado em {{date}}",
"UNREACHABLE": "Não responde",
"UNREACHABLE_FOR": "Não responde · {{duration}}",
"EXPIRED": "Expirada",
"EDIT": "Editar…",
"COPY_URL": "Copiar URL",
"OPEN": "Abrir"
}
},
"CONTEXT": {
"CATEGORIES": "Categorias",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker плейлисты",
"ALL_PLAYLISTS": "Все плейлисты",
"PLAYLIST_COUNT_ONE": "1 плейлист",
"PLAYLIST_COUNT_OTHER": "{{count}} плейлистов"
"PLAYLIST_COUNT_OTHER": "{{count}} плейлистов",
"ROW": {
"TYPE_M3U_FILE": "M3U · файл",
"ADDED": "добавлен {{date}}",
"UPDATED": "обновлён {{date}}",
"UNREACHABLE": "Не отвечает",
"UNREACHABLE_FOR": "Не отвечает · {{duration}}",
"EXPIRED": "Истёк",
"EDIT": "Изменить…",
"COPY_URL": "Копировать URL",
"OPEN": "Открыть"
}
},
"CONTEXT": {
"CATEGORIES": "Категории",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker Oynatma Listeleri",
"ALL_PLAYLISTS": "Tüm Oynatma Listeleri",
"PLAYLIST_COUNT_ONE": "1 oynatma listesi",
"PLAYLIST_COUNT_OTHER": "{{count}} oynatma listesi"
"PLAYLIST_COUNT_OTHER": "{{count}} oynatma listesi",
"ROW": {
"TYPE_M3U_FILE": "M3U · dosya",
"ADDED": "{{date}} eklendi",
"UPDATED": "{{date}} güncellendi",
"UNREACHABLE": "Yanıt vermiyor",
"UNREACHABLE_FOR": "Yanıt vermiyor · {{duration}}",
"EXPIRED": "Süresi doldu",
"EDIT": "Düzenle…",
"COPY_URL": "URL'yi kopyala",
"OPEN": "Aç"
}
},
"CONTEXT": {
"CATEGORIES": "Kategoriler",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker 播放列表",
"ALL_PLAYLISTS": "所有播放列表",
"PLAYLIST_COUNT_ONE": "1 个播放列表",
"PLAYLIST_COUNT_OTHER": "{{count}} 个播放列表"
"PLAYLIST_COUNT_OTHER": "{{count}} 个播放列表",
"ROW": {
"TYPE_M3U_FILE": "M3U · 文件",
"ADDED": "{{date}} 添加",
"UPDATED": "{{date}} 更新",
"UNREACHABLE": "无响应",
"UNREACHABLE_FOR": "无响应 · {{duration}}",
"EXPIRED": "已过期",
"EDIT": "编辑…",
"COPY_URL": "复制 URL",
"OPEN": "打开"
}
},
"CONTEXT": {
"CATEGORIES": "分类",
+12 -1
View File
@@ -1904,7 +1904,18 @@
"STALKER_PLAYLISTS": "Stalker 播放清單",
"ALL_PLAYLISTS": "所有播放清單",
"PLAYLIST_COUNT_ONE": "1 個播放清單",
"PLAYLIST_COUNT_OTHER": "{{count}} 個播放清單"
"PLAYLIST_COUNT_OTHER": "{{count}} 個播放清單",
"ROW": {
"TYPE_M3U_FILE": "M3U · 檔案",
"ADDED": "{{date}} 新增",
"UPDATED": "{{date}} 更新",
"UNREACHABLE": "無回應",
"UNREACHABLE_FOR": "無回應 · {{duration}}",
"EXPIRED": "已過期",
"EDIT": "編輯…",
"COPY_URL": "複製 URL",
"OPEN": "開啟"
}
},
"CONTEXT": {
"CATEGORIES": "類別",
+30
View File
@@ -169,6 +169,23 @@ html {
// 4.5:1 over hero artwork under the light scrim (#a16207 measured 3.4:1).
--app-rating-color: #7a4a00;
// Source lists (Sources page, playlist switcher) and their popovers.
--app-line-strong: rgba(0, 0, 0, 0.12);
--app-font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
// A failing source's tag and destructive menu items.
--app-danger-text: #b3261e;
--app-danger-surface: color-mix(in srgb, #d12c2c 12%, transparent);
--app-neutral-tag-surface: rgba(0, 0, 0, 0.06);
// One selected-row look for side lists and the switcher's current row:
// the Settings navigation's soft tint, plus an outline in light theme
// (selection reads as outline + tint here, not a solid slab). Light
// text stays the heading colour: the blue on its own tint is 3.3:1.
--app-list-selected-bg: var(--app-selection-surface);
--app-list-selected-fg: var(--app-heading-color);
--app-list-selected-outline: var(--app-selection-border);
--app-popover-shadow: 0 24px 70px rgba(15, 20, 28, 0.18);
--app-popover-backdrop: rgba(20, 24, 32, 0.28);
.dark-theme {
@include mat.all-component-colors($dark-theme);
// Only the color roles differ per theme: the elevation shadows are
@@ -274,6 +291,19 @@ html {
--app-cta-hover-bg: #ffffff;
--app-cta-meta-fg: #5a6274;
--app-rating-color: #ffcf5c;
// Source lists and popovers. The selected-row tokens are
// re-declared so they resolve against this context's selection
// colour, not the light values computed on `html`.
--app-line-strong: rgba(255, 255, 255, 0.1);
--app-danger-text: #ff8d9b;
--app-danger-surface: rgba(255, 91, 110, 0.14);
--app-neutral-tag-surface: rgba(255, 255, 255, 0.08);
--app-list-selected-bg: var(--app-selection-surface);
--app-list-selected-fg: var(--app-selection-color);
--app-list-selected-outline: transparent;
--app-popover-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
--app-popover-backdrop: rgba(4, 6, 10, 0.5);
}
}