Consolidate tabs, Quick Access links, mini player nav, link descriptions

- Merge Scripts/Callbacks/Links tabs into single Settings tab with collapsible sections
- Rename Actions tab to Quick Access showing both scripts and configured links
- Add prev/next buttons to mini (secondary) player
- Add optional description field to links (backend + frontend)
- Add CSS chevron indicators on collapsible settings sections
- Persist section collapse/expand state in localStorage
- Fix race condition in Quick Access rendering with generation counter
- Order settings sections: Scripts, Links, Callbacks

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 15:08:09 +03:00
parent 99dbbb1019
commit adf2d936da
7 changed files with 324 additions and 150 deletions

View File

@@ -18,11 +18,17 @@
</div>
</div>
<div class="mini-controls">
<button class="mini-control-btn mini-nav-btn" onclick="previousTrack()" data-i18n-title="player.previous" title="Previous">
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
</button>
<button class="mini-control-btn" onclick="togglePlayPause()" id="mini-btn-play-pause" title="Play/Pause">
<svg viewBox="0 0 24 24" id="mini-play-pause-icon">
<path d="M8 5v14l11-7z"/>
</svg>
</button>
<button class="mini-control-btn mini-nav-btn" onclick="nextTrack()" data-i18n-title="player.next" title="Next">
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
</button>
</div>
<div class="mini-progress-container">
<div class="mini-time-display">
@@ -112,19 +118,11 @@
</button>
<button class="tab-btn" data-tab="quick-actions" onclick="switchTab('quick-actions')" role="tab" aria-selected="false" aria-controls="panel-quick-actions" tabindex="-1">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M7 2v11h3v9l7-12h-4l4-8z"/></svg>
<span data-i18n="tab.quick_actions">Actions</span>
<span data-i18n="tab.quick_access">Quick Access</span>
</button>
<button class="tab-btn" data-tab="scripts" onclick="switchTab('scripts')" role="tab" aria-selected="false" aria-controls="panel-scripts" tabindex="-1">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
<span data-i18n="tab.scripts">Scripts</span>
</button>
<button class="tab-btn" data-tab="callbacks" onclick="switchTab('callbacks')" role="tab" aria-selected="false" aria-controls="panel-callbacks" tabindex="-1">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
<span data-i18n="tab.callbacks">Callbacks</span>
</button>
<button class="tab-btn" data-tab="links" onclick="switchTab('links')" role="tab" aria-selected="false" aria-controls="panel-links" tabindex="-1">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>
<span data-i18n="tab.links">Links</span>
<button class="tab-btn" data-tab="settings" onclick="switchTab('settings')" role="tab" aria-selected="false" aria-controls="panel-settings" tabindex="-1">
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.488.488 0 00-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 00-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
<span data-i18n="tab.settings">Settings</span>
</button>
</div>
@@ -268,100 +266,106 @@
<div class="scripts-grid" id="scripts-grid">
<div class="scripts-empty empty-state-illustration">
<svg viewBox="0 0 64 64"><path d="M20 8l-8 48"/><path d="M44 8l8 48"/><path d="M10 24h44"/><path d="M8 40h44"/></svg>
<p data-i18n="scripts.no_scripts">No scripts configured</p>
</div>
<div class="script-btn add-card-grid" onclick="showAddScriptDialog()">
<span class="add-card-icon">+</span>
<p data-i18n="quick_access.no_items">No quick actions or links configured</p>
</div>
</div>
</div>
<!-- Script Management Section -->
<div class="script-management" data-tab-content="scripts" role="tabpanel" id="panel-scripts">
<table class="scripts-table">
<thead>
<tr>
<th data-i18n="scripts.table.name">Name</th>
<th data-i18n="scripts.table.label">Label</th>
<th data-i18n="scripts.table.command">Command</th>
<th data-i18n="scripts.table.timeout">Timeout</th>
<th data-i18n="scripts.table.actions">Actions</th>
</tr>
</thead>
<tbody id="scriptsTableBody">
<tr>
<td colspan="5" class="empty-state">
<div class="empty-state-illustration">
<svg viewBox="0 0 64 64"><rect x="8" y="4" width="48" height="56" rx="4"/><path d="M20 20h24M20 32h16M20 44h20"/></svg>
<p data-i18n="scripts.empty">No scripts configured. Click "Add" to create one.</p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="add-card" onclick="showAddScriptDialog()">
<span class="add-card-icon">+</span>
</div>
</div>
<!-- Settings Section (Scripts, Callbacks, Links management) -->
<div class="settings-container" data-tab-content="settings" role="tabpanel" id="panel-settings">
<details class="settings-section" open>
<summary data-i18n="settings.section.scripts">Scripts</summary>
<div class="settings-section-content">
<table class="scripts-table">
<thead>
<tr>
<th data-i18n="scripts.table.name">Name</th>
<th data-i18n="scripts.table.label">Label</th>
<th data-i18n="scripts.table.command">Command</th>
<th data-i18n="scripts.table.timeout">Timeout</th>
<th data-i18n="scripts.table.actions">Actions</th>
</tr>
</thead>
<tbody id="scriptsTableBody">
<tr>
<td colspan="5" class="empty-state">
<div class="empty-state-illustration">
<svg viewBox="0 0 64 64"><rect x="8" y="4" width="48" height="56" rx="4"/><path d="M20 20h24M20 32h16M20 44h20"/></svg>
<p data-i18n="scripts.empty">No scripts configured. Click "Add" to create one.</p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="add-card" onclick="showAddScriptDialog()">
<span class="add-card-icon">+</span>
</div>
</div>
</details>
<!-- Callback Management Section -->
<div class="script-management" data-tab-content="callbacks" role="tabpanel" id="panel-callbacks">
<p style="color: var(--text-secondary); font-size: 0.875rem; margin-bottom: 1rem;" data-i18n="callbacks.description">
Callbacks are scripts triggered automatically by media control events (play, pause, stop, etc.)
</p>
<table class="scripts-table">
<thead>
<tr>
<th data-i18n="callbacks.table.event">Event</th>
<th data-i18n="callbacks.table.command">Command</th>
<th data-i18n="callbacks.table.timeout">Timeout</th>
<th data-i18n="callbacks.table.actions">Actions</th>
</tr>
</thead>
<tbody id="callbacksTableBody">
<tr>
<td colspan="4" class="empty-state">
<div class="empty-state-illustration">
<svg viewBox="0 0 64 64"><circle cx="32" cy="32" r="24"/><path d="M32 20v12l8 8"/></svg>
<p>No callbacks configured. Click "Add" to create one.</p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="add-card" onclick="showAddCallbackDialog()">
<span class="add-card-icon">+</span>
</div>
</div>
<details class="settings-section" open>
<summary data-i18n="settings.section.links">Links</summary>
<div class="settings-section-content">
<p class="settings-section-description" data-i18n="links.description">
Quick links displayed as icons in the header bar. Click an icon to open the URL in a new tab.
</p>
<table class="scripts-table">
<thead>
<tr>
<th data-i18n="links.table.name">Name</th>
<th data-i18n="links.table.url">URL</th>
<th data-i18n="links.table.label">Label</th>
<th data-i18n="links.table.actions">Actions</th>
</tr>
</thead>
<tbody id="linksTableBody">
<tr>
<td colspan="4" class="empty-state">
<div class="empty-state-illustration">
<svg viewBox="0 0 64 64"><path d="M26 20a10 10 0 010 14l-6 6a10 10 0 01-14-14l6-6a10 10 0 0114 0" fill="none" stroke="currentColor" stroke-width="2"/><path d="M38 44a10 10 0 010-14l6-6a10 10 0 0114 14l-6 6a10 10 0 01-14 0" fill="none" stroke="currentColor" stroke-width="2"/><path d="M24 40l16-16" stroke="currentColor" stroke-width="2"/></svg>
<p data-i18n="links.empty">No links configured. Click "Add" to create one.</p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="add-card" onclick="showAddLinkDialog()">
<span class="add-card-icon">+</span>
</div>
</div>
</details>
<!-- Links Management Section -->
<div class="script-management" data-tab-content="links" role="tabpanel" id="panel-links">
<p style="color: var(--text-secondary); font-size: 0.875rem; margin-bottom: 1rem;" data-i18n="links.description">
Quick links displayed as icons in the header bar. Click an icon to open the URL in a new tab.
</p>
<table class="scripts-table">
<thead>
<tr>
<th data-i18n="links.table.name">Name</th>
<th data-i18n="links.table.url">URL</th>
<th data-i18n="links.table.label">Label</th>
<th data-i18n="links.table.actions">Actions</th>
</tr>
</thead>
<tbody id="linksTableBody">
<tr>
<td colspan="4" class="empty-state">
<div class="empty-state-illustration">
<svg viewBox="0 0 64 64"><path d="M26 20a10 10 0 010 14l-6 6a10 10 0 01-14-14l6-6a10 10 0 0114 0" fill="none" stroke="currentColor" stroke-width="2"/><path d="M38 44a10 10 0 010-14l6-6a10 10 0 0114 14l-6 6a10 10 0 01-14 0" fill="none" stroke="currentColor" stroke-width="2"/><path d="M24 40l16-16" stroke="currentColor" stroke-width="2"/></svg>
<p data-i18n="links.empty">No links configured. Click "Add" to create one.</p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="add-card" onclick="showAddLinkDialog()">
<span class="add-card-icon">+</span>
</div>
<details class="settings-section" open>
<summary data-i18n="settings.section.callbacks">Callbacks</summary>
<div class="settings-section-content">
<p class="settings-section-description" data-i18n="callbacks.description">
Callbacks are scripts triggered automatically by media control events (play, pause, stop, etc.)
</p>
<table class="scripts-table">
<thead>
<tr>
<th data-i18n="callbacks.table.event">Event</th>
<th data-i18n="callbacks.table.command">Command</th>
<th data-i18n="callbacks.table.timeout">Timeout</th>
<th data-i18n="callbacks.table.actions">Actions</th>
</tr>
</thead>
<tbody id="callbacksTableBody">
<tr>
<td colspan="4" class="empty-state">
<div class="empty-state-illustration">
<svg viewBox="0 0 64 64"><circle cx="32" cy="32" r="24"/><path d="M32 20v12l8 8"/></svg>
<p>No callbacks configured. Click "Add" to create one.</p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="add-card" onclick="showAddCallbackDialog()">
<span class="add-card-icon">+</span>
</div>
</div>
</details>
</div>
<!-- Display Control Section -->
@@ -508,6 +512,11 @@
<span data-i18n="links.field.label">Label</span>
<input type="text" id="linkLabel" data-i18n-placeholder="links.placeholder.label" placeholder="Tooltip text">
</label>
<label>
<span data-i18n="links.field.description">Description</span>
<textarea id="linkDescription" data-i18n-placeholder="links.placeholder.description" placeholder="What does this link point to?"></textarea>
</label>
</div>
<div class="dialog-footer">
<button type="button" class="btn-secondary" onclick="closeLinkDialog()" data-i18n="links.button.cancel">Cancel</button>