@emgeebee/music_downcoder 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAUrB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAgBjF,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,WAAW,GAAI,aAAa,MAAM,KAAG,SAOjD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,SAAS,UAAU,KAAG,OAAO,CAAC,SAAS,CAiDzE,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,YAAY,MAAM,EAClB,SAAQ,SAAuB,KAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAoD1C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EAAE,EACjB,SAAQ,SAAuB,KAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAU1C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAQ,SAAuB,KAAG,IAS/D,CAAC;AAEF,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAUrB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAgBjF,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,WAAW,GAAI,aAAa,MAAM,KAAG,SAOjD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,SAAS,UAAU,KAAG,OAAO,CAAC,SAAS,CAiDzE,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,YAAY,MAAM,EAClB,SAAQ,SAAuB,KAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA+C1C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EAAE,EACjB,SAAQ,SAAuB,KAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAU1C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAQ,SAAuB,KAAG,IAS/D,CAAC;AAEF,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC"}
package/dist/run.js CHANGED
@@ -4,7 +4,7 @@ import path from "path";
4
4
  import { buildConfigMap, getConfig, loadConfig, } from "./config.js";
5
5
  import { MetaGetter } from "./getMeta.js";
6
6
  import { listCommandScripts, prepareCommandFolders, processEncoder, } from "./processEncoder.js";
7
- import { normalizeDir, normalizeTerminalOutput } from "./paths.js";
7
+ import { normalizeDir } from "./paths.js";
8
8
  import { captureConsole, clearJobLogs, appendJobLog } from "./jobLog.js";
9
9
  import { clearJobProgress, setJobProgress } from "./progress.js";
10
10
  import { createAutoPrompts, createCliPrompts } from "./prompts.js";
@@ -83,20 +83,8 @@ export const executeScript = (scriptPath, config = getConfig()) => {
83
83
  const child = spawn("bash", [queuePath], {
84
84
  cwd: config.workDir,
85
85
  env: process.env,
86
+ stdio: ["ignore", "ignore", "ignore"],
86
87
  });
87
- let output = "";
88
- const logStream = (chunk) => {
89
- const text = chunk.toString();
90
- output += text;
91
- for (const line of text.split("\n")) {
92
- const trimmed = line.trim();
93
- if (trimmed) {
94
- appendJobLog(trimmed);
95
- }
96
- }
97
- };
98
- child.stdout.on("data", logStream);
99
- child.stderr.on("data", logStream);
100
88
  child.on("error", (error) => {
101
89
  restoreConsole();
102
90
  try {
@@ -115,7 +103,13 @@ export const executeScript = (scriptPath, config = getConfig()) => {
115
103
  catch {
116
104
  // ignore cleanup errors
117
105
  }
118
- resolve({ code: code ?? 1, output: normalizeTerminalOutput(output) });
106
+ const exitCode = code ?? 1;
107
+ const label = filename.replace(/-commands\.sh$/, "");
108
+ const status = exitCode === 0
109
+ ? `✓ ${label} completed`
110
+ : `✗ ${label} failed (exit ${exitCode})`;
111
+ appendJobLog(status);
112
+ resolve({ code: exitCode, output: status });
119
113
  });
120
114
  });
121
115
  };
@@ -123,7 +117,7 @@ export const executeAllScripts = async (scripts, config = getConfig()) => {
123
117
  let combined = "";
124
118
  for (const script of scripts) {
125
119
  const result = await executeScript(script, config);
126
- combined += result.output;
120
+ combined += (combined ? "\n" : "") + result.output;
127
121
  if (result.code !== 0) {
128
122
  return { code: result.code, output: combined };
129
123
  }
package/dist/run.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
@@ -7,96 +7,391 @@
7
7
  <style>
8
8
  :root {
9
9
  color-scheme: light dark;
10
- font-family: system-ui, sans-serif;
11
- line-height: 1.4;
10
+ --accent: #6d5ef8;
11
+ --accent-hover: #5b4de6;
12
+ --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
13
+ --surface: color-mix(in srgb, Canvas 92%, var(--accent) 8%);
14
+ --surface-raised: color-mix(in srgb, Canvas 88%, var(--accent) 12%);
15
+ --border: color-mix(in srgb, CanvasText 14%, transparent);
16
+ --border-strong: color-mix(in srgb, CanvasText 22%, transparent);
17
+ --text-muted: color-mix(in srgb, CanvasText 55%, transparent);
18
+ --danger: #e5484d;
19
+ --danger-soft: color-mix(in srgb, var(--danger) 14%, transparent);
20
+ --radius: 12px;
21
+ --radius-sm: 8px;
22
+ --shadow: 0 1px 2px #0001, 0 8px 24px #0000000f;
23
+ font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
24
+ line-height: 1.45;
25
+ }
26
+ * {
27
+ box-sizing: border-box;
12
28
  }
13
29
  body {
14
- margin: 0 auto;
15
- max-width: 900px;
16
- padding: 1.5rem;
30
+ margin: 0;
31
+ }
32
+ .app-layout {
33
+ display: flex;
34
+ height: 100vh;
35
+ overflow: hidden;
17
36
  }
18
- h1, h2 {
19
- margin-top: 0;
37
+ .main-column {
38
+ background:
39
+ radial-gradient(ellipse 80% 50% at 0% 0%, var(--accent-soft), transparent 55%),
40
+ Canvas;
41
+ flex: 1;
42
+ min-width: 0;
43
+ overflow-y: auto;
44
+ padding: 1.75rem 2rem 2.5rem;
20
45
  }
21
- fieldset {
22
- border: 1px solid #8884;
23
- border-radius: 8px;
24
- margin-bottom: 1rem;
25
- padding: 1rem;
46
+ .page-header {
47
+ margin-bottom: 1.75rem;
48
+ }
49
+ .page-header h1 {
50
+ font-size: 1.85rem;
51
+ font-weight: 700;
52
+ letter-spacing: -0.02em;
53
+ margin: 0 0 0.35rem;
54
+ }
55
+ .tagline {
56
+ color: var(--text-muted);
57
+ margin: 0;
58
+ max-width: 36rem;
59
+ }
60
+ .panel {
61
+ background: var(--surface);
62
+ border: 1px solid var(--border);
63
+ border-radius: var(--radius);
64
+ box-shadow: var(--shadow);
65
+ margin-bottom: 1.25rem;
66
+ padding: 1.25rem 1.35rem 1.35rem;
67
+ }
68
+ #scripts-section {
69
+ margin-top: 1.75rem;
70
+ }
71
+ .panel-header {
72
+ align-items: center;
73
+ display: flex;
74
+ gap: 0.65rem;
75
+ margin-bottom: 1.1rem;
76
+ }
77
+ .panel-header h2 {
78
+ font-size: 1.1rem;
79
+ font-weight: 650;
80
+ margin: 0;
81
+ }
82
+ .step-badge {
83
+ align-items: center;
84
+ background: linear-gradient(135deg, var(--accent), #8b7cf8);
85
+ border-radius: 999px;
86
+ color: #fff;
87
+ display: inline-flex;
88
+ flex-shrink: 0;
89
+ font-size: 0.78rem;
90
+ font-weight: 700;
91
+ height: 1.65rem;
92
+ justify-content: center;
93
+ width: 1.65rem;
94
+ }
95
+ .form-group {
96
+ margin-bottom: 1.1rem;
97
+ }
98
+ .form-group:last-of-type {
99
+ margin-bottom: 0;
100
+ }
101
+ .form-label {
102
+ color: var(--text-muted);
103
+ font-size: 0.78rem;
104
+ font-weight: 650;
105
+ letter-spacing: 0.04em;
106
+ margin: 0 0 0.55rem;
107
+ text-transform: uppercase;
108
+ }
109
+ .option-grid {
110
+ display: grid;
111
+ gap: 0.55rem;
112
+ }
113
+ .option-card {
114
+ align-items: flex-start;
115
+ background: var(--surface-raised);
116
+ border: 1px solid var(--border);
117
+ border-radius: var(--radius-sm);
118
+ cursor: pointer;
119
+ display: flex;
120
+ gap: 0.65rem;
121
+ margin: 0;
122
+ padding: 0.7rem 0.8rem;
123
+ transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
124
+ }
125
+ .option-card:hover {
126
+ border-color: var(--border-strong);
127
+ box-shadow: 0 2px 8px #0000000a;
26
128
  }
27
- label {
129
+ .option-card:has(input:checked) {
130
+ background: var(--accent-soft);
131
+ border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
132
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent);
133
+ }
134
+ .option-card input {
135
+ accent-color: var(--accent);
136
+ flex-shrink: 0;
137
+ margin-top: 0.2rem;
138
+ }
139
+ .option-body {
140
+ display: flex;
141
+ flex-direction: column;
142
+ gap: 0.15rem;
143
+ min-width: 0;
144
+ }
145
+ .option-title {
146
+ font-weight: 600;
147
+ line-height: 1.3;
148
+ }
149
+ .option-meta {
150
+ color: var(--text-muted);
151
+ font-size: 0.82rem;
152
+ overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ white-space: nowrap;
155
+ }
156
+ .filter-field {
157
+ margin-top: 1.1rem;
158
+ }
159
+ .filter-field > span {
160
+ color: var(--text-muted);
28
161
  display: block;
29
- margin: 0.35rem 0;
162
+ font-size: 0.78rem;
163
+ font-weight: 650;
164
+ letter-spacing: 0.04em;
165
+ margin-bottom: 0.45rem;
166
+ text-transform: uppercase;
167
+ }
168
+ .filter-wrap input {
169
+ background: var(--surface-raised);
170
+ border: 1px solid var(--border);
171
+ border-radius: var(--radius-sm);
172
+ padding: 0.55rem 0.7rem;
173
+ transition: border-color 0.15s, box-shadow 0.15s;
174
+ width: 100%;
175
+ }
176
+ .filter-wrap input:focus {
177
+ border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
178
+ box-shadow: 0 0 0 3px var(--accent-soft);
179
+ outline: none;
180
+ }
181
+ .btn-row {
182
+ display: flex;
183
+ flex-wrap: wrap;
184
+ gap: 0.55rem;
185
+ margin-top: 1.15rem;
30
186
  }
31
187
  button {
32
- margin-right: 0.5rem;
33
- margin-top: 0.5rem;
34
- padding: 0.5rem 0.9rem;
35
- }
36
- pre {
37
- background: #0001;
38
- border-radius: 8px;
39
- min-height: 8rem;
40
- overflow: auto;
188
+ border: 1px solid transparent;
189
+ border-radius: var(--radius-sm);
190
+ cursor: pointer;
191
+ font: inherit;
192
+ font-weight: 600;
193
+ padding: 0.55rem 1rem;
194
+ transition: background 0.15s, border-color 0.15s, transform 0.1s, opacity 0.15s;
195
+ }
196
+ button:active:not(:disabled) {
197
+ transform: translateY(1px);
198
+ }
199
+ button:disabled {
200
+ cursor: not-allowed;
201
+ opacity: 0.55;
202
+ }
203
+ .btn-primary {
204
+ background: linear-gradient(135deg, var(--accent), #7c6df7);
205
+ color: #fff;
206
+ }
207
+ .btn-primary:hover:not(:disabled) {
208
+ background: linear-gradient(135deg, var(--accent-hover), #6d5ef8);
209
+ }
210
+ .btn-secondary {
211
+ background: var(--surface-raised);
212
+ border-color: var(--border);
213
+ color: CanvasText;
214
+ }
215
+ .btn-secondary:hover:not(:disabled) {
216
+ border-color: var(--border-strong);
217
+ }
218
+ .btn-danger {
219
+ background: var(--danger-soft);
220
+ border-color: color-mix(in srgb, var(--danger) 35%, transparent);
221
+ color: var(--danger);
222
+ }
223
+ .btn-danger:hover:not(:disabled) {
224
+ background: color-mix(in srgb, var(--danger) 22%, transparent);
225
+ }
226
+ .btn-sm {
227
+ font-size: 0.88rem;
228
+ padding: 0.35rem 0.75rem;
229
+ }
230
+ .log-panel {
231
+ background: color-mix(in srgb, Canvas 96%, CanvasText 4%);
232
+ border-left: 1px solid var(--border);
233
+ box-sizing: border-box;
234
+ display: flex;
235
+ flex-direction: column;
236
+ flex-shrink: 0;
237
+ height: 100vh;
238
+ padding: 1.5rem 1.25rem;
239
+ width: min(28rem, 42vw);
240
+ }
241
+ .log-panel h2 {
242
+ flex-shrink: 0;
243
+ font-size: 1rem;
244
+ margin: 0 0 0.75rem;
245
+ }
246
+ #log {
247
+ background: #00000014;
248
+ border: 1px solid var(--border);
249
+ border-radius: var(--radius-sm);
250
+ flex: 1;
251
+ font-family: ui-monospace, "Cascadia Code", "SF Mono", monospace;
252
+ font-size: 0.82rem;
253
+ margin: 0;
254
+ min-height: 0;
255
+ overflow-y: auto;
41
256
  padding: 1rem;
42
257
  white-space: pre-wrap;
43
258
  }
259
+ @media (max-width: 768px) {
260
+ .app-layout {
261
+ flex-direction: column;
262
+ height: auto;
263
+ min-height: 100vh;
264
+ overflow: visible;
265
+ }
266
+ .main-column {
267
+ overflow: visible;
268
+ padding: 1.25rem;
269
+ }
270
+ .log-panel {
271
+ border-left: none;
272
+ border-top: 1px solid var(--border);
273
+ height: 50vh;
274
+ width: auto;
275
+ }
276
+ }
44
277
  .hidden {
45
278
  display: none;
46
279
  }
280
+ .scripts-list {
281
+ display: flex;
282
+ flex-direction: column;
283
+ gap: 0.45rem;
284
+ margin-bottom: 0.25rem;
285
+ }
286
+ .empty-state {
287
+ color: var(--text-muted);
288
+ font-size: 0.92rem;
289
+ padding: 0.5rem 0;
290
+ }
47
291
  .script-row {
48
292
  align-items: center;
293
+ background: var(--surface-raised);
294
+ border: 1px solid var(--border);
295
+ border-radius: var(--radius-sm);
49
296
  display: flex;
50
297
  gap: 0.75rem;
51
298
  justify-content: space-between;
52
- margin: 0.4rem 0;
299
+ padding: 0.55rem 0.65rem 0.55rem 0.8rem;
53
300
  }
54
301
  .script-row code {
55
302
  flex: 1;
303
+ font-family: ui-monospace, "Cascadia Code", "SF Mono", monospace;
304
+ font-size: 0.84rem;
56
305
  overflow: hidden;
57
306
  text-overflow: ellipsis;
58
307
  white-space: nowrap;
59
308
  }
60
309
  .app-version {
61
- color: #888;
62
- font-size: 0.9rem;
63
- font-weight: normal;
310
+ color: var(--text-muted);
311
+ font-size: 0.82rem;
312
+ font-weight: 500;
313
+ }
314
+ .filter-wrap {
315
+ position: relative;
316
+ }
317
+ #filter-dropdown {
318
+ background: Canvas;
319
+ border: 1px solid var(--border-strong);
320
+ border-radius: var(--radius-sm);
321
+ box-shadow: var(--shadow);
322
+ list-style: none;
323
+ margin: 0.25rem 0 0;
324
+ max-height: 12rem;
325
+ overflow: auto;
326
+ padding: 0.25rem 0;
327
+ position: absolute;
328
+ width: 100%;
329
+ z-index: 10;
330
+ }
331
+ #filter-dropdown li {
332
+ cursor: pointer;
333
+ padding: 0.4rem 0.7rem;
334
+ }
335
+ #filter-dropdown li:hover,
336
+ #filter-dropdown li:focus {
337
+ background: var(--accent-soft);
64
338
  }
65
339
  </style>
66
340
  </head>
67
341
  <body>
68
- <h1>Music Downcoder <span id="app-version" class="app-version"></span></h1>
69
- <p>Generate ffmpeg conversion scripts, then run them from the browser.</p>
70
-
71
- <section id="setup-section">
72
- <h2>1. Scan library</h2>
73
- <form id="run-form">
74
- <fieldset>
75
- <legend>Source folder</legend>
76
- <div id="start-folders"></div>
77
- </fieldset>
78
- <fieldset>
79
- <legend>Encoders</legend>
80
- <div id="encoders"></div>
81
- </fieldset>
82
- <label>
83
- Artist filter (prefix)
84
- <input id="filter" type="text" value="" />
85
- </label>
86
- <button type="submit" id="run-btn">Generate scripts</button>
87
- </form>
88
- </section>
89
-
90
- <section id="scripts-section" class="hidden">
91
- <h2>2. Run scripts</h2>
92
- <div id="scripts-list"></div>
93
- <button type="button" id="run-all-btn">Run all scripts</button>
94
- <button type="button" id="refresh-btn">Refresh list</button>
95
- <button type="button" id="clear-btn">Clear scripts</button>
96
- </section>
97
-
98
- <h2>Output</h2>
99
- <pre id="log"></pre>
342
+ <div class="app-layout">
343
+ <main class="main-column">
344
+ <header class="page-header">
345
+ <h1>Music Downcoder <span id="app-version" class="app-version"></span></h1>
346
+ <p class="tagline">Generate ffmpeg conversion scripts, then run them from the browser.</p>
347
+ </header>
348
+
349
+ <section class="panel" id="setup-section">
350
+ <div class="panel-header">
351
+ <span class="step-badge">1</span>
352
+ <h2>Scan library</h2>
353
+ </div>
354
+ <form id="run-form">
355
+ <div class="form-group">
356
+ <h3 class="form-label">Source folder</h3>
357
+ <div class="option-grid" id="start-folders"></div>
358
+ </div>
359
+ <div class="form-group">
360
+ <h3 class="form-label">Encoders</h3>
361
+ <div class="option-grid" id="encoders"></div>
362
+ </div>
363
+ <label class="filter-field">
364
+ <span>Artist filter (prefix)</span>
365
+ <div class="filter-wrap">
366
+ <input id="filter" type="text" value="" autocomplete="off" placeholder="e.g. Keri Hilson" />
367
+ <ul id="filter-dropdown" class="hidden"></ul>
368
+ </div>
369
+ </label>
370
+ <div class="btn-row">
371
+ <button type="submit" class="btn-primary" id="run-btn">Generate scripts</button>
372
+ </div>
373
+ </form>
374
+ </section>
375
+
376
+ <section class="panel hidden" id="scripts-section">
377
+ <div class="panel-header">
378
+ <span class="step-badge">2</span>
379
+ <h2>Run scripts</h2>
380
+ </div>
381
+ <div class="scripts-list" id="scripts-list"></div>
382
+ <div class="btn-row">
383
+ <button type="button" class="btn-primary" id="run-all-btn">Run all scripts</button>
384
+ <button type="button" class="btn-secondary" id="refresh-btn">Refresh list</button>
385
+ <button type="button" class="btn-danger" id="clear-btn">Clear scripts</button>
386
+ </div>
387
+ </section>
388
+ </main>
389
+
390
+ <aside class="log-panel">
391
+ <h2>Output</h2>
392
+ <pre id="log"></pre>
393
+ </aside>
394
+ </div>
100
395
 
101
396
  <script>
102
397
  const logEl = document.getElementById("log");
@@ -104,6 +399,69 @@
104
399
  const scriptsList = document.getElementById("scripts-list");
105
400
  const startFoldersEl = document.getElementById("start-folders");
106
401
  const encodersEl = document.getElementById("encoders");
402
+ const filterInput = document.getElementById("filter");
403
+ const filterDropdown = document.getElementById("filter-dropdown");
404
+
405
+ const FILTER_HISTORY_KEY = "music_downcoder_filter_history";
406
+ const MAX_FILTER_HISTORY = 15;
407
+
408
+ const getFilterHistory = () => {
409
+ try {
410
+ const raw = localStorage.getItem(FILTER_HISTORY_KEY);
411
+ return raw ? JSON.parse(raw) : [];
412
+ } catch {
413
+ return [];
414
+ }
415
+ };
416
+
417
+ const saveFilterValue = (value) => {
418
+ const trimmed = value.trim();
419
+ if (!trimmed) {
420
+ return;
421
+ }
422
+ const history = getFilterHistory().filter((entry) => entry !== trimmed);
423
+ history.unshift(trimmed);
424
+ localStorage.setItem(
425
+ FILTER_HISTORY_KEY,
426
+ JSON.stringify(history.slice(0, MAX_FILTER_HISTORY))
427
+ );
428
+ };
429
+
430
+ const hideFilterDropdown = () => {
431
+ filterDropdown.classList.add("hidden");
432
+ filterDropdown.innerHTML = "";
433
+ };
434
+
435
+ const renderFilterDropdown = () => {
436
+ const query = filterInput.value.trim().toLowerCase();
437
+ const matches = getFilterHistory().filter(
438
+ (entry) => !query || entry.toLowerCase().startsWith(query)
439
+ );
440
+
441
+ filterDropdown.innerHTML = "";
442
+ if (!matches.length) {
443
+ hideFilterDropdown();
444
+ return;
445
+ }
446
+
447
+ for (const entry of matches) {
448
+ const item = document.createElement("li");
449
+ item.textContent = entry;
450
+ item.addEventListener("mousedown", (event) => {
451
+ event.preventDefault();
452
+ filterInput.value = entry;
453
+ hideFilterDropdown();
454
+ });
455
+ filterDropdown.append(item);
456
+ }
457
+ filterDropdown.classList.remove("hidden");
458
+ };
459
+
460
+ filterInput.addEventListener("focus", renderFilterDropdown);
461
+ filterInput.addEventListener("input", renderFilterDropdown);
462
+ filterInput.addEventListener("blur", () => {
463
+ setTimeout(hideFilterDropdown, 150);
464
+ });
107
465
 
108
466
  const appendLog = (text) => {
109
467
  logEl.textContent += text + "\n";
@@ -156,7 +514,10 @@
156
514
  const renderScripts = (scripts) => {
157
515
  scriptsList.innerHTML = "";
158
516
  if (!scripts.length) {
159
- scriptsList.textContent = "No scripts found in cmd folder.";
517
+ const empty = document.createElement("p");
518
+ empty.className = "empty-state";
519
+ empty.textContent = "No scripts found in cmd folder.";
520
+ scriptsList.append(empty);
160
521
  return;
161
522
  }
162
523
  for (const script of scripts) {
@@ -167,6 +528,7 @@
167
528
  code.title = script.name;
168
529
  const btn = document.createElement("button");
169
530
  btn.type = "button";
531
+ btn.className = "btn-primary btn-sm";
170
532
  btn.textContent = "Run";
171
533
  btn.addEventListener("click", async () => {
172
534
  setBusy(true);
@@ -181,7 +543,7 @@
181
543
  if (data.error) {
182
544
  appendLog(`Error: ${data.error}`);
183
545
  } else if (data.output) {
184
- appendLog(data.output);
546
+ appendLog(data.output.trim());
185
547
  }
186
548
  if (data.scripts) {
187
549
  renderScripts(data.scripts);
@@ -209,14 +571,19 @@
209
571
  data.startFolders.forEach((folder, index) => {
210
572
  const id = `start-${index}`;
211
573
  const label = document.createElement("label");
212
- label.innerHTML = `<input type="radio" name="startFolder" id="${id}" value="${folder.path}" ${index === 0 ? "checked" : ""} /> ${folder.name} <small>(${folder.path})</small>`;
574
+ label.className = "option-card";
575
+ label.htmlFor = id;
576
+ label.innerHTML = `<input type="radio" name="startFolder" id="${id}" value="${folder.path}" ${index === 0 ? "checked" : ""} /><span class="option-body"><span class="option-title">${folder.name}</span><span class="option-meta">${folder.path}</span></span>`;
213
577
  startFoldersEl.append(label);
214
578
  });
215
579
 
216
580
  data.encoders.forEach((encoder, index) => {
217
581
  const id = `encoder-${index}`;
218
582
  const label = document.createElement("label");
219
- label.innerHTML = `<input type="checkbox" name="encoder" id="${id}" value="${encoder.id}" ${encoder.default ? "checked" : ""} /> ${encoder.label}${encoder.description ? ` — ${encoder.description}` : ""}`;
583
+ label.className = "option-card";
584
+ label.htmlFor = id;
585
+ const description = encoder.description ? `<span class="option-meta">${encoder.description}</span>` : "";
586
+ label.innerHTML = `<input type="checkbox" name="encoder" id="${id}" value="${encoder.id}" ${encoder.default ? "checked" : ""} /><span class="option-body"><span class="option-title">${encoder.label}</span>${description}</span>`;
220
587
  encodersEl.append(label);
221
588
  });
222
589
 
@@ -229,13 +596,16 @@
229
596
  event.preventDefault();
230
597
  const startFolder = document.querySelector('input[name="startFolder"]:checked')?.value;
231
598
  const encoderIds = [...document.querySelectorAll('input[name="encoder"]:checked')].map((el) => el.value);
232
- const filter = document.getElementById("filter").value;
599
+ const filter = filterInput.value;
233
600
 
234
601
  if (!startFolder || !encoderIds.length) {
235
602
  appendLog("Select a source folder and at least one encoder.");
236
603
  return;
237
604
  }
238
605
 
606
+ saveFilterValue(filter);
607
+ hideFilterDropdown();
608
+
239
609
  setBusy(true);
240
610
  appendLog("Scanning library and generating scripts...");
241
611
  let data = {};
@@ -272,7 +642,7 @@
272
642
  if (data.error) {
273
643
  appendLog(`Error: ${data.error}`);
274
644
  } else if (data.output) {
275
- appendLog(data.output);
645
+ appendLog(data.output.trim());
276
646
  }
277
647
  if (data.scripts) {
278
648
  renderScripts(data.scripts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emgeebee/music_downcoder",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Scan music libraries and generate ffmpeg conversion scripts with a CLI or web UI",
5
5
  "type": "module",
6
6
  "bin": {