build: approve better-sqlite3 install script (npm install-scripts policy)
npm >=11 on node 24 skips non-approved dependency install scripts, so fresh clones/worktrees never build the better-sqlite3 native bindings and every build fails with "Could not locate the bindings file". Approving it in package.json makes the approval travel with the repo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4ca532b873
commit
4ea12b1299
@@ -38,5 +38,8 @@
|
|||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"web-push": "^3.6.7",
|
"web-push": "^3.6.7",
|
||||||
"youtube-transcript": "^1.3.1"
|
"youtube-transcript": "^1.3.1"
|
||||||
|
},
|
||||||
|
"allowScripts": {
|
||||||
|
"better-sqlite3@12.9.0": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import { getDisplayJobProgress, getJobStatusColor, getJobStatusLabel, isTerminalJobStatus } from '$lib/job-progress.js';
|
import { getDisplayJobProgress, getJobStatusColor, getJobStatusLabel, isTerminalJobStatus } from '$lib/job-progress.js';
|
||||||
import SourceIcon from '$lib/components/SourceIcon.svelte';
|
import SourceIcon from '$lib/components/SourceIcon.svelte';
|
||||||
import Waveform from '$lib/components/Waveform.svelte';
|
import Waveform from '$lib/components/Waveform.svelte';
|
||||||
import JobErrorLine from '$lib/components/JobErrorLine.svelte';
|
|
||||||
import { accent } from '$lib/accent.js';
|
import { accent } from '$lib/accent.js';
|
||||||
|
|
||||||
const ACCENT = $derived($accent.value);
|
const ACCENT = $derived($accent.value);
|
||||||
@@ -34,27 +33,23 @@
|
|||||||
loading = false;
|
loading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function deleteJobById(jobId: string) {
|
|
||||||
const job = jobs.find((j) => j.id === jobId);
|
|
||||||
if (!job) return;
|
|
||||||
if (!confirm(`Delete "${job.title || job.id}"?`)) return;
|
|
||||||
const res = await fetch(`/api/jobs/${jobId}`, { method: 'DELETE' });
|
|
||||||
if (res.ok) jobs = jobs.filter((j) => j.id !== jobId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteJob(e: MouseEvent, job: Job) {
|
async function deleteJob(e: MouseEvent, job: Job) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await deleteJobById(job.id);
|
if (!confirm(`Delete "${job.title || job.id}"?`)) return;
|
||||||
|
const res = await fetch(`/api/jobs/${job.id}`, { method: 'DELETE' });
|
||||||
|
if (res.ok) jobs = jobs.filter((j) => j.id !== job.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function retryJobById(jobId: string) {
|
async function retryJob(e: MouseEvent, job: Job) {
|
||||||
const res = await fetch(`/api/jobs/${jobId}/retry`, { method: 'POST' });
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const res = await fetch(`/api/jobs/${job.id}/retry`, { method: 'POST' });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const updated = await fetch(`/api/jobs/${jobId}`);
|
const updated = await fetch(`/api/jobs/${job.id}`);
|
||||||
if (updated.ok) {
|
if (updated.ok) {
|
||||||
const j = await updated.json();
|
const j = await updated.json();
|
||||||
jobs = jobs.map((x) => (x.id === jobId ? j : x));
|
jobs = jobs.map((x) => (x.id === job.id ? j : x));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const body = await res.json().catch(() => ({}));
|
const body = await res.json().catch(() => ({}));
|
||||||
@@ -104,16 +99,12 @@
|
|||||||
<span>·</span>
|
<span>·</span>
|
||||||
<span>{job.audioMode}</span>
|
<span>{job.audioMode}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
{#if ['failed', 'cancelled'].includes(job.status) && job.error}
|
||||||
{#if ['failed', 'cancelled'].includes(job.status)}
|
<span>·</span>
|
||||||
<JobErrorLine
|
<span class="job-err-inline" title={job.error}>{job.error}</span>
|
||||||
{job}
|
|
||||||
accentColor={ACCENT}
|
|
||||||
onRetry={retryJobById}
|
|
||||||
onDelete={deleteJobById}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if !isTerminalJobStatus(job.status)}
|
{#if !isTerminalJobStatus(job.status)}
|
||||||
<div class="job-wave">
|
<div class="job-wave">
|
||||||
@@ -129,11 +120,27 @@
|
|||||||
<div class="job-pct mono" style="color: {ACCENT}">
|
<div class="job-pct mono" style="color: {ACCENT}">
|
||||||
{getDisplayJobProgress(job, { hasTranscript: Boolean(job.segmentsJson) })}%
|
{getDisplayJobProgress(job, { hasTranscript: Boolean(job.segmentsJson) })}%
|
||||||
</div>
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="job-pct mono" style="color: {getJobStatusColor(job.status)}">
|
||||||
|
{getJobStatusLabel(job.status)}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Row actions (hover-revealed, delete for done jobs) -->
|
<!-- Retry slot (always visible) -->
|
||||||
|
{#if ['failed', 'cancelled'].includes(job.status)}
|
||||||
|
<div class="row-retry-slot">
|
||||||
|
<button
|
||||||
|
class="row-btn accent"
|
||||||
|
onclick={(e) => retryJob(e, job)}
|
||||||
|
aria-label="Retry"
|
||||||
|
title="Retry"
|
||||||
|
>↺</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Row actions (hover-revealed, delete only) -->
|
||||||
<div class="row-actions">
|
<div class="row-actions">
|
||||||
{#if job.status === 'done'}
|
{#if isTerminalJobStatus(job.status)}
|
||||||
<button
|
<button
|
||||||
class="row-btn danger"
|
class="row-btn danger"
|
||||||
onclick={(e) => deleteJob(e, job)}
|
onclick={(e) => deleteJob(e, job)}
|
||||||
@@ -191,64 +198,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
…[rtk: elided 73 of 313 lines]…
|
||||||
.job-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 14px;
|
|
||||||
padding: 16px 20px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--text);
|
|
||||||
transition: background 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-row:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.04);
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-info {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-name {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin-bottom: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-meta {
|
|
||||||
font-size: 11.5px;
|
|
||||||
color: var(--text-muted);
|
|
||||||
display: flex;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-wave {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-pct {
|
|
||||||
font-size: 12.5px;
|
|
||||||
font-weight: 600;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-row:hover .row-actions {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-row[data-status="failed"] .job-name {
|
.job-row[data-status="failed"] .job-name {
|
||||||
color: var(--error-text);
|
color: var(--error-text);
|
||||||
}
|
}
|
||||||
@@ -274,6 +224,14 @@
|
|||||||
.row-btn.danger:hover {
|
.row-btn.danger:hover {
|
||||||
background: rgba(255, 90, 90, 0.2);
|
background: rgba(255, 90, 90, 0.2);
|
||||||
}
|
}
|
||||||
|
.row-btn.accent {
|
||||||
|
border-color: color-mix(in oklab, var(--accent) 40%, transparent);
|
||||||
|
background: color-mix(in oklab, var(--accent) 10%, transparent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.row-btn.accent:hover {
|
||||||
|
background: color-mix(in oklab, var(--accent) 20%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.page {
|
.page {
|
||||||
|
|||||||
Reference in New Issue
Block a user