From 54bbb2ca048bcec2bde350d4c7883ba3bf3e1e5e Mon Sep 17 00:00:00 2001 From: Raphix Date: Thu, 8 Oct 2026 10:06:45 +0200 Subject: [PATCH] Version 2.1.0 - Update UI --- index.html | 12 +- package.json | 2 +- src/App.vue | 8 +- src/assets/Global.scss | 315 ++- src/components/Admin/AdminBanner.vue | 52 + src/components/Admin/AdminCard.vue | 222 ++- src/components/Admin/AdminConfig.vue | 553 ++---- src/components/Admin/AdminConfirm.vue | 61 +- src/components/Admin/AdminDashboard.vue | 569 +++++- src/components/Admin/AdminField.vue | 145 ++ src/components/Admin/AdminGrid.vue | 73 + src/components/Admin/AdminKv.vue | 38 +- src/components/Admin/AdminList.vue | 34 + src/components/Admin/AdminLogs.vue | 602 +++--- src/components/Admin/AdminMaintenance.vue | 223 ++- src/components/Admin/AdminMeter.vue | 67 + src/components/Admin/AdminMetrics.vue | 311 +-- src/components/Admin/AdminOverview.vue | 401 ++-- src/components/Admin/AdminPills.vue | 62 +- src/components/Admin/AdminPlayerCard.vue | 453 ++--- src/components/Admin/AdminPlayers.vue | 103 +- src/components/Admin/AdminPlaylists.vue | 501 +++-- src/components/Admin/AdminRow.vue | 85 + src/components/Admin/AdminSection.vue | 89 +- src/components/Admin/AdminServerDetail.vue | 490 ++--- src/components/Admin/AdminServers.vue | 219 +-- src/components/Admin/AdminStat.vue | 137 +- src/components/Admin/AdminStatRow.vue | 34 + src/components/Admin/AdminState.vue | 68 +- src/components/Admin/AdminStep.vue | 46 + src/components/Admin/AdminSteps.vue | 20 + src/components/Admin/AdminToasts.vue | 108 +- src/components/Admin/AdminToolbar.vue | 70 + src/components/Admin/AdminUserDetail.vue | 461 ++--- src/components/Admin/AdminUserLine.vue | 114 +- src/components/Admin/AdminUsers.vue | 224 +-- src/components/Admin/AdminWarp.vue | 328 ++-- src/components/Admin/AdminYoutube.vue | 1229 ++++++------ src/components/Admin/admin.scss | 1745 +++++++++++++++++ src/components/Admin/adminUi.js | 85 + src/components/Layout/Account.vue | 17 +- src/components/Layout/GuildHeader.vue | 341 ++-- src/components/Layout/Player.vue | 161 +- src/components/Layout/Playlist.vue | 707 +++++-- src/components/Layout/Queue.vue | 142 +- src/components/Layout/Search.vue | 416 +++- src/components/Layout/View.vue | 210 +- src/components/UI/Announcement.vue | 13 +- src/components/UI/Avatar.vue | 94 +- src/components/UI/Box.vue | 24 +- src/components/UI/Button.vue | 311 ++- src/components/UI/Card.vue | 206 ++ src/components/UI/Carousel.vue | 76 +- src/components/UI/Circle.vue | 18 +- src/components/UI/ContextMenu.vue | 432 +++- src/components/UI/CustomIcon.vue | 106 +- src/components/UI/DefaultSplash.vue | 4 + src/components/UI/DurationBar.vue | 181 +- src/components/UI/EmptyState.vue | 96 + src/components/UI/Error.vue | 46 +- src/components/UI/Field.vue | 125 ++ src/components/UI/Glider.vue | 82 +- src/components/UI/IconAction.vue | 129 +- src/components/UI/Info.vue | 52 +- src/components/UI/ListenBox.vue | 49 +- src/components/UI/Logo.vue | 7 +- src/components/UI/Marquee.vue | 171 +- src/components/UI/Metric.vue | 6 +- src/components/UI/Modal.vue | 480 ++++- src/components/UI/ModalTree.vue | 41 +- src/components/UI/MusicAnimation.vue | 5 +- src/components/UI/PlaylistItem.vue | 259 ++- src/components/UI/Role.vue | 13 +- src/components/UI/SegmentedControl.vue | 224 +++ src/components/UI/Selector.vue | 544 +++-- src/components/UI/Server.vue | 51 +- src/components/UI/ServerAvatar.vue | 78 +- src/components/UI/Spinner.vue | 72 + src/components/UI/Splash.vue | 11 +- src/components/UI/SubsonicsLogo.vue | 9 +- src/components/UI/Success.vue | 46 +- src/components/UI/Switch.vue | 176 ++ src/components/UI/Tag.vue | 66 +- src/components/UI/User.vue | 4 + src/components/UI/Video.vue | 22 +- src/components/UI/VideoList.vue | 15 +- src/components/UI/VideoQueue.vue | 15 +- src/components/Widget/Dispatcher.vue | 2 +- .../Widget/Guild/GuildHeaderUsers.vue | 134 +- src/components/Widget/Guild/GuildSettings.vue | 18 +- .../Widget/Guild/Settings/GuildPlaylists.vue | 190 ++ .../Widget/Guild/Settings/GuildSecurity.vue | 12 +- src/components/Widget/Playlist/ActionMenu.vue | 376 ++++ .../Widget/Playlist/PlaylistCreateModal.vue | 707 +++++++ .../Widget/Playlist/PlaylistHost.vue | 25 + .../Widget/Playlist/PlaylistSongList.vue | 451 +++++ .../Widget/Playlist/PlaylistSongRow.vue | 267 +++ .../Widget/Playlist/PlaylistSortMenu.vue | 437 +++++ .../Widget/Playlist/PlaylistToasts.vue | 203 ++ .../Widget/Playlist/SaveToPlaylistModal.vue | 452 +++++ .../Widget/Playlist/playlistMeta.js | 233 +++ src/components/Widget/Queue/NextList.vue | 528 +++-- src/components/Widget/Queue/PreviousList.vue | 221 ++- src/components/Widget/ReturnHomeButton.vue | 27 +- .../Widget/Server/ServerListItem.vue | 220 ++- .../Widget/Server/ServerOnlinePicture.vue | 79 +- src/components/Widget/User/GoogleCallback.vue | 30 +- .../Widget/User/Settings/BugReport.vue | 218 +- .../User/Settings/InformationSettings.vue | 20 +- .../Widget/User/Settings/PrivacySettings.vue | 53 +- .../Widget/User/Settings/ThemeSelector.vue | 190 +- src/components/Widget/User/UserSettings.vue | 4 +- src/components/Widget/User/YoutubeAccount.vue | 27 +- src/components/Widget/VideoComposable.vue | 441 +++-- src/components/Widget/View/Default.vue | 128 +- .../Widget/View/Home/AccountMobile.vue | 51 +- src/components/Widget/View/Home/Advice.vue | 154 +- .../Widget/View/Home/AdviceItem.vue | 62 +- src/components/Widget/View/Home/Changelog.vue | 287 --- .../Widget/View/Home/ChangelogModal.vue | 643 ++++++ src/components/Widget/View/Home/History.vue | 221 ++- src/components/Widget/View/Home/HomeHero.vue | 517 +++++ .../Widget/View/Home/HomeNowPlaying.vue | 347 ++++ .../Widget/View/Home/HomeQuickActions.vue | 133 ++ .../Widget/View/Home/HomeRecent.vue | 446 +++++ src/components/Widget/View/Home/HomeStats.vue | 251 +++ src/components/Widget/View/Home/HomeTip.vue | 98 + src/components/Widget/View/Home/Welcome.vue | 72 - src/components/Widget/View/Home/changelog.js | 168 ++ src/components/Widget/View/Home/tips.js | 77 + src/components/Widget/View/Home/useHistory.js | 61 + .../Widget/View/Home/userSettingsOpener.js | 61 + src/components/Widget/View/LoadingView.vue | 67 +- .../Widget/View/Lyrics/LyricsSearchModal.vue | 432 ++++ .../Widget/View/Lyrics/LyricsSynced.vue | 471 +++++ .../Widget/View/Lyrics/lyricsUtils.js | 165 ++ src/components/Widget/View/LyricsView.vue | 906 +++++++++ .../Widget/View/Playlist/PlaylistView.vue | 112 +- .../Widget/View/Search/PlaylistHeader.vue | 1029 ++++++---- .../Widget/View/Search/SearchSuggestions.vue | 178 +- src/components/Widget/View/SearchResults.vue | 143 +- src/components/Widget/View/UploadFiles.vue | 574 ++++-- src/main.js | 5 +- src/routes.js | 1 - src/stores/globalStore.js | 3 + src/stores/playerStore.js | 26 +- src/stores/playlistStore.js | 938 +++++++++ src/stores/userStore.js | 19 +- src/utils/Loader.vue | 17 +- src/utils/Logic.js | 195 +- src/utils/ModalContext.js | 33 + src/utils/SocketEnvironment.vue | 40 +- src/views/Interface.vue | 13 +- src/views/Login.vue | 142 +- src/views/PageError.vue | 57 +- src/views/Privacy.vue | 30 +- src/views/Servers.vue | 220 ++- src/views/Terms.vue | 31 +- 158 files changed, 24215 insertions(+), 7686 deletions(-) create mode 100644 src/components/Admin/AdminBanner.vue create mode 100644 src/components/Admin/AdminField.vue create mode 100644 src/components/Admin/AdminGrid.vue create mode 100644 src/components/Admin/AdminList.vue create mode 100644 src/components/Admin/AdminMeter.vue create mode 100644 src/components/Admin/AdminRow.vue create mode 100644 src/components/Admin/AdminStatRow.vue create mode 100644 src/components/Admin/AdminStep.vue create mode 100644 src/components/Admin/AdminSteps.vue create mode 100644 src/components/Admin/AdminToolbar.vue create mode 100644 src/components/Admin/admin.scss create mode 100644 src/components/Admin/adminUi.js create mode 100644 src/components/UI/Card.vue create mode 100644 src/components/UI/EmptyState.vue create mode 100644 src/components/UI/Field.vue create mode 100644 src/components/UI/SegmentedControl.vue create mode 100644 src/components/UI/Spinner.vue create mode 100644 src/components/UI/Switch.vue create mode 100644 src/components/Widget/Guild/Settings/GuildPlaylists.vue create mode 100644 src/components/Widget/Playlist/ActionMenu.vue create mode 100644 src/components/Widget/Playlist/PlaylistCreateModal.vue create mode 100644 src/components/Widget/Playlist/PlaylistHost.vue create mode 100644 src/components/Widget/Playlist/PlaylistSongList.vue create mode 100644 src/components/Widget/Playlist/PlaylistSongRow.vue create mode 100644 src/components/Widget/Playlist/PlaylistSortMenu.vue create mode 100644 src/components/Widget/Playlist/PlaylistToasts.vue create mode 100644 src/components/Widget/Playlist/SaveToPlaylistModal.vue create mode 100644 src/components/Widget/Playlist/playlistMeta.js delete mode 100644 src/components/Widget/View/Home/Changelog.vue create mode 100644 src/components/Widget/View/Home/ChangelogModal.vue create mode 100644 src/components/Widget/View/Home/HomeHero.vue create mode 100644 src/components/Widget/View/Home/HomeNowPlaying.vue create mode 100644 src/components/Widget/View/Home/HomeQuickActions.vue create mode 100644 src/components/Widget/View/Home/HomeRecent.vue create mode 100644 src/components/Widget/View/Home/HomeStats.vue create mode 100644 src/components/Widget/View/Home/HomeTip.vue delete mode 100644 src/components/Widget/View/Home/Welcome.vue create mode 100644 src/components/Widget/View/Home/changelog.js create mode 100644 src/components/Widget/View/Home/tips.js create mode 100644 src/components/Widget/View/Home/useHistory.js create mode 100644 src/components/Widget/View/Home/userSettingsOpener.js create mode 100644 src/components/Widget/View/Lyrics/LyricsSearchModal.vue create mode 100644 src/components/Widget/View/Lyrics/LyricsSynced.vue create mode 100644 src/components/Widget/View/Lyrics/lyricsUtils.js create mode 100644 src/components/Widget/View/LyricsView.vue create mode 100644 src/stores/playlistStore.js create mode 100644 src/utils/ModalContext.js diff --git a/index.html b/index.html index 6ec2cdb..52bad1d 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,18 @@ - + + + + + + Subsonics + + +
diff --git a/package.json b/package.json index 24076cf..14067c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chopin-frontend", - "version": "2.0.0", + "version": "2.1.0", "private": true, "scripts": { "dev": "vite --host --port 8080", diff --git a/src/App.vue b/src/App.vue index 4ce31f3..d481d66 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,10 +1,14 @@ \ No newline at end of file + diff --git a/src/assets/Global.scss b/src/assets/Global.scss index b7cef2e..51c545b 100644 --- a/src/assets/Global.scss +++ b/src/assets/Global.scss @@ -1,10 +1,13 @@ @font-face { font-family: 'Gunship'; src: url("/gunship.ttf"); + font-display: swap; } -[data-theme='dark'] { - +/* Thème sombre : appliqué aussi tant que data-theme n'est pas encore posé (évite un flash blanc) */ +[data-theme='dark'], +:root:not([data-theme]) { + color-scheme: dark; --primary: #111210; --primary-inverse: #FFFFFF; @@ -19,11 +22,14 @@ --text-error: #ff2b2b; --text-success: #00ff00; --text-warning: #FFA500; - - + --text-info: #4db4ff; + + --scrollbar-thumb: color-mix(in srgb, var(--text) 30%, transparent); + --scrollbar-thumb-hover: color-mix(in srgb, var(--text) 55%, transparent); } [data-theme='light'] { + color-scheme: light; --primary: #FFFFFF; --primary-inverse: #111210; @@ -34,25 +40,84 @@ --text: #111210; --text-inverse: #FFFFFF; --text-secondary: #404040; - --text-tertiary: #C5c3c3; - --text-error: #CD034F; - --text-success: #00a900; - --text-warning: #FFA500; + /* Contraste suffisant sur fond clair (l'ancien #C5c3c3 était quasi invisible) */ + --text-tertiary: #6b6b6b; + --text-error: #CD034F; + --text-success: #007a1f; + --text-warning: #a35f00; + --text-info: #0063b1; + + --scrollbar-thumb: color-mix(in srgb, var(--text) 30%, transparent); + --scrollbar-thumb-hover: color-mix(in srgb, var(--text) 55%, transparent); } :root { --main: #CD034F; --main-hover: color-mix(in srgb, var(--main) 90%, black); --main-active: color-mix(in srgb, var(--main) 70%, black); + --main-soft: color-mix(in srgb, var(--main) 16%, transparent); + --main-contrast: #fff; --admin-color: #209AFE; --owner-color: #FFAA32; --mod-color: #0BFF89; + + /* Variables utilisées par des composants mais jamais définies jusqu'ici */ + --text-primary: var(--text); + --text-color: var(--text); + --text-hover: var(--text-secondary); + --border: color-mix(in srgb, var(--quaternary) 45%, transparent); + --background: transparent; + + /* Surfaces */ + --surface-1: var(--secondary); + --surface-2: var(--tertiary); + --surface-3: var(--quaternary); + + /* Rayons */ + --radius-sm: 6px; + --radius: 10px; + --radius-lg: 14px; + --radius-pill: 999px; + + /* Espacements */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 24px; + --space-6: 32px; + + /* Élévation */ + --shadow-sm: 0 1px 3px rgba(0, 0, 0, .25); + --shadow: 0 0 10px rgba(0, 0, 0, .35); + --shadow-lg: 0 10px 30px rgba(0, 0, 0, .45); + + /* Focus clavier : anneau doublé pour rester visible sur fond rose comme sur fond sombre */ + --focus-ring: 0 0 0 2px var(--primary), 0 0 0 4px var(--main); + /* Variante intérieure, pour un élément coupé par un conteneur overflow: hidden */ + --focus-ring-inset: inset 0 0 0 2px var(--main); + + /* Survol / pression d'une ligne de liste (teinte neutre qui suit le thème) */ + --surface-hover: color-mix(in srgb, var(--text) 8%, transparent); + --surface-active: color-mix(in srgb, var(--text) 13%, transparent); + + /* Voile des modales et texte posé sur une image (miniatures) */ + --overlay: rgba(0, 0, 0, .55); + --on-media: #fff; + + /* Mouvement */ + --ease: cubic-bezier(.2, .7, .2, 1); + --dur-fast: .15s; + --dur: .25s; + + --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; + --font-mono: ui-monospace, 'Cascadia Mono', 'SFMono-Regular', Menlo, Consolas, monospace; } html, body { margin: 0 !important; - font-family: 'Inter', sans-serif; + font-family: var(--font); min-height: 100vh; min-width: 100vw; margin: 0; @@ -60,10 +125,11 @@ html, body { height: 100%; overflow-x: hidden; background-color: var(--primary); - transition: all 0.2s ease-in-out; + /* Seules les couleurs s'animent au changement de thème (avant : transition: all) */ + transition: background-color var(--dur) ease-in-out, color var(--dur) ease-in-out; color: var(--text); - - + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } #app { @@ -71,64 +137,198 @@ html, body { display: flex; } +/* Les boutons natifs n'héritent pas de la police par défaut */ +button, input, select, textarea { + font-family: inherit; +} + .no-decoration { text-decoration: none !important; -} +} .underline { text-decoration: underline !important; -} +} -h1 { +h1 { font-family: 'Gunship'; font-size: 30px; color: var(--text-color); -} +} a { color: var(--text-secondary) } +a:hover { + color: var(--text); +} + +/* Focus clavier visible partout (la souris ne déclenche pas :focus-visible) */ +:focus-visible { + outline: 2px solid var(--main); + outline-offset: 2px; +} + +::selection { + background-color: color-mix(in srgb, var(--main) 38%, transparent); +} + /*Scrollbar */ ::-webkit-scrollbar { width: 5px; + height: 5px; margin-right: 20px; margin-left: 20px; - } +} + ::-webkit-scrollbar-track { border-radius: 12px; - } +} + ::-webkit-scrollbar-thumb { - background: #ffffff56; + background: var(--scrollbar-thumb); border-radius: 5px; transition: 0.2s; - } +} ::-webkit-scrollbar-thumb:hover { - background: #ffffffa8; - } - + background: var(--scrollbar-thumb-hover); +} + +/* Firefox ne connaît pas ::-webkit-scrollbar */ +@supports not selector(::-webkit-scrollbar) { + * { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb) transparent; + } +} + .full { width: 100%; } +/* Champs de formulaire */ +input:is([type="text"], [type="search"], [type="url"], [type="password"], [type="number"], [type="email"]), +select, +textarea { + background-color: var(--tertiary); + color: var(--text); + border: none; + border-radius: var(--radius-sm); + padding: 8px 10px; + font-family: var(--font); + font-size: inherit; + /* Contour transparent : invisible normalement, mais rendu en mode contraste élevé */ + outline: 2px solid transparent; + outline-offset: 0; + transition: box-shadow var(--dur-fast) ease, background-color var(--dur-fast) ease; +} + +input:is([type="text"], [type="search"], [type="url"], [type="password"], [type="number"], [type="email"]):focus, +select:focus, +textarea:focus { + box-shadow: var(--focus-ring); +} + +input:is([type="text"], [type="search"], [type="url"], [type="password"], [type="number"], [type="email"]):disabled, +select:disabled, +textarea:disabled { + opacity: .6; + cursor: not-allowed; +} + +input::placeholder, +textarea::placeholder { + color: var(--text-secondary); + opacity: .7; +} + +select { + cursor: pointer; +} + textarea { resize: vertical; - background-color: var(--tertiary); - border: none; - border-radius: 5px; - padding: 5px; - color: var(--text); - font-family: 'Inter', sans-serif; - outline: none; min-height: 4vh; max-height: 15vh; } -textarea:focus { - box-shadow: 0 0 5px var(--main); +/* Champ posé sur une carte --tertiary : on redescend d'un niveau pour garder le contraste */ +.input-inset { + background-color: var(--secondary) !important; } +input[type="checkbox"], +input[type="radio"], +input[type="range"], +progress { + accent-color: var(--main); +} + +input[type="checkbox"], +input[type="radio"] { + cursor: pointer; +} + +/* Utilitaires */ + +/* Bouton sans apparence (ligne de liste, carte cliquable…) : garde le focus visible du navigateur */ +.u-plain-button { + margin: 0; + padding: 0; + border: none; + background: none; + color: inherit; + font: inherit; + text-align: inherit; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.u-plain-button:disabled { + cursor: not-allowed; +} + +/* Titre de vue (résultats, fichiers…) : même échelle partout */ +.u-view-title { + display: flex; + align-items: center; + gap: 10px; + margin: 0; + font-size: 1.25rem; + font-weight: 700; + line-height: 1.25; + color: var(--text); +} + +.u-view-title > svg { + color: var(--main); +} + +.sr-only { + position: absolute !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; +} + +.u-mono { + font-family: var(--font-mono); + font-variant-numeric: tabular-nums; +} + +.u-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} @keyframes appear { from { @@ -150,30 +350,6 @@ textarea:focus { } } - -input[type="checkbox"] { - background-color: #FFFFFF !important; -} - -input[type="checkbox"]:checked { - background-color: var(--main-hover) !important; -} - -input[type="checkbox"]:hover { - background-color: var(--main-active) !important; -} - -input[type="text"] { - background-color: var(--tertiary); - border: none; - border-radius: 5px; - padding: 7px; - color: var(--text); - font-family: 'Inter', sans-serif; - outline: none; -} - - @keyframes scroll { 0% { transform: translateX(0); @@ -181,4 +357,29 @@ input[type="text"] { 100% { transform: translateX(var(--scroll-distance)); } -} \ No newline at end of file +} + +@keyframes u-fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes u-spin { + to { transform: rotate(360deg); } +} + +/* Mouvement réduit : on coupe les animations et transitions (notes du splash, marquee, glissements…) */ +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto !important; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/src/components/Admin/AdminBanner.vue b/src/components/Admin/AdminBanner.vue new file mode 100644 index 0000000..d7cdaaf --- /dev/null +++ b/src/components/Admin/AdminBanner.vue @@ -0,0 +1,52 @@ + + diff --git a/src/components/Admin/AdminCard.vue b/src/components/Admin/AdminCard.vue index f78976e..efced37 100644 --- a/src/components/Admin/AdminCard.vue +++ b/src/components/Admin/AdminCard.vue @@ -1,73 +1,187 @@ - diff --git a/src/components/Admin/AdminConfig.vue b/src/components/Admin/AdminConfig.vue index a1da7fd..f211ca9 100644 --- a/src/components/Admin/AdminConfig.vue +++ b/src/components/Admin/AdminConfig.vue @@ -1,154 +1,111 @@