summaryrefslogtreecommitdiff
path: root/web/index.html
blob: 7ea78b8bff1f8146e3d96223b480f67ab22f6863 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Player</title>
<link rel="manifest" href="/manifest.json">
<link rel="stylesheet" href="/css/theme.css">
<link rel="stylesheet" href="/css/layout.css">
<link rel="stylesheet" href="/css/components.css">
<link rel="stylesheet" href="/css/player.css">
<script type="module" src="/js/app.js"></script>
</head>
<body>
<header class="site-header" role="banner">
  <a href="/" class="logo">Player</a>
  <div class="spacer"></div>
  <div id="scan-indicator" class="scan-indicator hidden" role="status" aria-live="polite">
    <span class="scan-dot" aria-hidden="true"></span>
    <span id="scan-indicator-text">Scanning</span>
  </div>
  <button id="help-toggle" class="icon-btn" title="Help (?)">?</button>
  <button id="admin-toggle" class="icon-btn hidden" title="Admin panel">⚙</button>
  <button id="logout-btn" class="btn btn-ghost btn-sm">Log out</button>
</header>

<!-- Sidebar: hidden by default, toggled via keyboard or menu -->
<nav id="sidebar" class="sidebar" aria-label="Sets">
  <div class="sidebar-header">
    <h2>Sets</h2>
    <button id="menu-close" class="icon-btn btn-sm" title="Close">✕</button>
  </div>
  <div id="set-list"></div>
</nav>

<!-- Main page area -->
<div class="page" role="main">
  <div class="main-content">
    <!-- Toolbar: hidden by default, shown on 't' key or when searching -->
    <div id="toolbar" class="toolbar hidden">
      <select id="filter-type" aria-label="Filter by type">
        <option value="">All</option>
        <option value="video">Video</option>
        <option value="audio">Audio</option>
      </select>
      <button id="filter-favorites" class="btn btn-ghost btn-sm">♥ Favorites</button>
      <div class="spacer"></div>
      <button id="filter-toggle" class="btn btn-ghost btn-sm" title="More filters">Filters ▾</button>
      <span id="result-count" class="text-muted text-xs"></span>
    </div>
    <div id="filter-advanced" class="toolbar hidden">
      <input id="filter-tags" type="text" placeholder="Tags (comma)" aria-label="Filter by tags" class="input-sm">
      <input id="filter-min-duration" type="number" min="0" placeholder="Min min" aria-label="Min duration minutes" class="input-sm">
      <input id="filter-max-duration" type="number" min="0" placeholder="Max min" aria-label="Max duration minutes" class="input-sm">
    </div>

    <!-- Search bar: hidden by default, shown on '/' key -->
    <div id="search-bar" class="search-bar hidden" role="search">
      <span aria-hidden="true">🔎</span>
      <input id="search-input" type="search" placeholder="Search… (press /)" autocomplete="off" aria-label="Search media">
      <button id="search-clear" class="icon-btn btn-sm" title="Clear">✕</button>
    </div>

    <!-- Empty state hint: shown when no set is selected -->
    <div id="empty-hint" class="empty-hint">
      <p>Welcome to Player</p>
      <p class="text-muted text-sm">Press <kbd>s</kbd> to browse sets, <kbd>/</kbd> to search, or <kbd>?</kbd> for help.</p>
    </div>

    <!-- Breadcrumb bar for folder navigation -->
    <div id="breadcrumb-bar" class="breadcrumb-bar hidden"></div>

    <!-- Media grid: hidden until a set is loaded or search is active -->
    <div id="media-grid" class="media-grid hidden" tabindex="0" aria-label="Media grid"></div>
  </div>
</div>

<!-- Player: hidden until something plays -->
<aside id="player" class="player" aria-label="Player">
  <div class="stage">
    <video id="media-video" playsinline controlslist="nodownload"></video>
    <audio id="media-audio" class="hidden"></audio>
    <img id="cover-art" class="cover-art hidden" alt="Cover" loading="lazy">
    <div id="big-play" class="big-play" role="button" aria-label="Play" tabindex="0">
      <div class="play-icon">▶</div>
    </div>
  </div>
  <div class="controls">
    <button id="btn-toggle-stage" class="icon-btn" aria-label="Toggle stage (c)">⛶</button>
    <button id="btn-play" class="icon-btn" aria-label="Play/Pause (Space/p)">▶</button>
    <div class="time" id="time-elapsed">0:00</div>
    <div id="progress-track" class="progress-track" tabindex="0" aria-label="Seek">
      <div id="progress-buffered" class="progress-buffered"></div>
      <div id="progress-fill" class="progress-fill"></div>
      <div id="progress-thumb" class="progress-thumb" aria-hidden="true"></div>
    </div>
    <div class="time" id="time-total">0:00</div>
    <button id="btn-prev" class="icon-btn" aria-label="Previous">⏮</button>
    <button id="btn-next" class="icon-btn" aria-label="Next">⏭</button>
    <div class="volume-control">
      <button id="btn-mute" class="icon-btn" aria-label="Mute">🔊</button>
      <input id="volume-slider" type="range" min="0" max="1" step="0.05" value="1" class="volume-slider" aria-label="Volume">
    </div>
    <button id="btn-fullscreen" class="icon-btn" aria-label="Fullscreen (f)">⛶</button>
  </div>
</aside>

<!-- Detached player placeholder bar -->
<div id="detached-bar" class="detached-bar hidden" role="status" aria-label="Player detached">
  <span>Player detached — press <kbd>d</kbd> to reattach</span>
</div>

<footer class="toast" id="toast" role="status" aria-live="polite"></footer>

<!-- Help Modal -->
<div id="help-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="help-title">
  <div class="modal">
    <div class="modal-header">
      <h3 id="help-title">Keyboard Shortcuts</h3>
      <div class="spacer"></div>
      <button id="help-close" class="icon-btn" aria-label="Close">✕</button>
    </div>
    <div class="help-content">
      <table class="help-table">
        <tr><td><kbd>↑</kbd> <kbd>↓</kbd></td><td>Navigate media list</td></tr>
        <tr><td><kbd>k</kbd> <kbd>j</kbd></td><td>Navigate media list (vim-style)</td></tr>
        <tr><td><kbd>←</kbd> <kbd>→</kbd></td><td>Switch sets / pages</td></tr>
        <tr><td><kbd>h</kbd> <kbd>l</kbd></td><td>Switch sets / pages (vim-style)</td></tr>
        <tr><td><kbd>Enter</kbd></td><td>Open selected media / enter folder</td></tr>
        <tr><td><kbd>Space</kbd> <kbd>p</kbd></td><td>Play / pause</td></tr>
        <tr><td><kbd>f</kbd></td><td>Toggle fullscreen</td></tr>
        <tr><td><kbd>c</kbd></td><td>Collapse / expand player stage</td></tr>
        <tr><td><kbd>d</kbd></td><td>Detach / attach player popup</td></tr>
        <tr><td><kbd>D</kbd></td><td>Download selected media</td></tr>
        <tr><td><kbd>Esc</kbd></td><td>Exit fullscreen, deselect item</td></tr>
        <tr><td><kbd>Backspace</kbd></td><td>Go up one folder</td></tr>
        <tr><td><kbd>r</kbd></td><td>Toggle shuffle</td></tr>
        <tr><td><kbd>s</kbd></td><td>Toggle sets sidebar</td></tr>
        <tr><td><kbd>S</kbd></td><td>Share selected media</td></tr>
        <tr><td><kbd>L</kbd></td><td>My Shares (opens list)</td></tr>
      </table>
      <p class="text-muted text-xs mt-2">Inside the <strong>My Shares</strong> list: <kbd>↑</kbd> / <kbd>↓</kbd> or <kbd>k</kbd> / <kbd>j</kbd> to navigate; <kbd>Enter</kbd> to copy the selected link; <kbd>Delete</kbd> to revoke it; <kbd>Esc</kbd> to close.</p>
    </div>
  </div>
</div>

<!-- Notes Modal -->
<div id="notes-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="notes-title">
  <div class="modal">
    <div class="modal-header">
      <h3 id="notes-title">Notes</h3>
      <div class="spacer"></div>
      <button id="notes-close" class="icon-btn" aria-label="Close">✕</button>
    </div>
    <textarea id="notes-textarea" rows="6" placeholder="Add your notes…"></textarea>
    <div class="flex justify-end gap-2 mt-1">
      <button id="notes-delete" class="btn btn-danger btn-sm">Delete</button>
      <button id="notes-save" class="btn btn-primary btn-sm">Save</button>
    </div>
  </div>
</div>

<!-- Tags Modal -->
<div id="tags-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="tags-title">
  <div class="modal">
    <div class="modal-header">
      <h3 id="tags-title">Tags</h3>
      <div class="spacer"></div>
      <button id="tags-close" class="icon-btn" aria-label="Close">✕</button>
    </div>
    <div id="tags-list" class="flex flex-wrap gap-2 mb-2"></div>
    <div class="flex gap-2">
      <input id="tags-new" type="text" placeholder="New tag…" autocomplete="off">
      <button id="tags-add" class="btn btn-primary btn-sm">Add</button>
    </div>
  </div>
</div>

<!-- Upload Modal -->
<div id="upload-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="upload-title">
  <div class="modal">
    <div class="modal-header">
      <h3 id="upload-title">Upload</h3>
      <div class="spacer"></div>
      <button id="upload-close" class="icon-btn" aria-label="Close">✕</button>
    </div>
    <form id="upload-form" class="flex flex-col gap-2">
      <input id="upload-file" type="file" required>
      <button type="submit" class="btn btn-primary btn-sm">Upload to current set</button>
    </form>
  </div>
</div>

<!-- Admin Modal -->
<div id="admin-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="admin-title">
    <div class="modal modal-wide">
    <div class="modal-header">
      <h3 id="admin-title">Admin</h3>
      <div class="spacer"></div>
      <button id="admin-close" class="icon-btn" aria-label="Close">✕</button>
    </div>
    <div class="flex gap-4 flex-wrap">
      <section class="flex-1-18">
        <h4 class="my-1 text-90">Users</h4>
        <div id="admin-users"></div>
        <form id="admin-user-form" class="mt-2">
          <input name="username" placeholder="Username" required autocomplete="off">
          <input type="password" name="password" placeholder="Password" required autocomplete="new-password">
          <label class="flex align-center gap-1 text-xs">
            <input type="checkbox" name="is_admin"> Admin
          </label>
          <button type="submit" class="btn btn-primary btn-sm mt-1">Add user</button>
        </form>
      </section>
      <section class="flex-1-18">
        <h4 class="my-1 text-90">Permissions</h4>
        <div id="admin-permissions"></div>
      </section>
    </div>
    <div class="flex gap-2 mt-3">
      <button id="admin-rescan" class="btn btn-primary btn-sm">Trigger rescan</button>
      <button id="admin-trash" class="btn btn-danger btn-sm">View trash</button>
    </div>
  </div>
</div>
<!-- Shares Modal -->
<div id="shares-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="shares-title">
  <div class="modal modal-wide">
    <div class="modal-header">
      <h3 id="shares-title">My Shares</h3>
      <div class="spacer"></div>
      <button id="shares-close" class="icon-btn" aria-label="Close">&#10005;</button>
    </div>
    <div id="shares-list"></div>
    <div class="shares-hint">
      <kbd>&#8593;</kbd> <kbd>&#8595;</kbd> <kbd>k</kbd> <kbd>j</kbd> navigate &middot;
      <kbd>Enter</kbd> copy &middot;
        <kbd>Delete</kbd> revoke &middot;
        <kbd>Esc</kbd> close
    </div>
  </div>
</div>

</body>
</html>