This commit is contained in:
2026-05-31 23:30:35 -07:00
parent 431ffdff06
commit 3e50191530
20 changed files with 539 additions and 78 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
window.__EVANESCERE_FRONTEND_CONFIG__ = {
apiBaseUrl: "http://localhost:8000"
apiBaseUrl: "http://192.168.1.44:8080"
};
+34 -5
View File
@@ -1,6 +1,7 @@
import {
Check,
Clapperboard,
FolderSync,
Play,
RefreshCw,
Save,
@@ -19,6 +20,7 @@ import {
patchSettings,
renderClip,
runVideo,
scanWebDav,
uploadClip,
} from "./api";
import type { ClipSuggestion, PipelineSettings, TranscriptSegment, Video } from "./types";
@@ -47,7 +49,7 @@ function formatTimeRange(start: number, end: number) {
}
function statusTone(value: string) {
if (["done", "stable", "approved", "auto_approved"].includes(value)) return "good";
if (["done", "stable", "existing_done", "approved", "auto_approved"].includes(value)) return "good";
if (["failed", "error"].includes(value)) return "bad";
if (["running", "queued", "observing", "pending"].includes(value)) return "busy";
return "neutral";
@@ -61,6 +63,7 @@ export function App() {
const [clips, setClips] = useState<ClipSuggestion[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [notice, setNotice] = useState<string | null>(null);
const selectedVideo = useMemo(
() => videos.find((video) => video.id === selectedVideoId) ?? null,
@@ -124,6 +127,26 @@ export function App() {
}
}
async function syncWebDav() {
setLoading(true);
setError(null);
setNotice(null);
try {
const result = await scanWebDav();
if (result.baseline_initialized) {
setNotice(
`Imported ${result.baseline_inserted + result.baseline_marked_existing} existing files as manual-only baseline entries.`,
);
} else {
setNotice(`Observed ${result.observed} files; ${result.newly_stable} newly stable.`);
}
await refresh();
} catch (caught) {
setError(caught instanceof Error ? caught.message : "Unknown error");
setLoading(false);
}
}
async function saveSettings() {
if (!settings) return;
await withRefresh(() => patchSettings(settings));
@@ -149,6 +172,7 @@ export function App() {
</header>
{error && <div className="error-strip">{error}</div>}
{notice && <div className="notice-strip">{notice}</div>}
<section className="band controls-band">
<div className="section-title">
@@ -176,9 +200,15 @@ export function App() {
<section className="workbench">
<aside className="video-list">
<div className="section-title">
<Clapperboard size={18} />
<h2>Videos</h2>
<div className="list-header">
<div className="section-title">
<Clapperboard size={18} />
<h2>Videos</h2>
</div>
<button type="button" className="icon-button" onClick={() => void syncWebDav()} disabled={loading}>
<FolderSync size={16} />
Sync WebDAV
</button>
</div>
<div className="table-scroll">
<table>
@@ -301,4 +331,3 @@ function Metric({ label, value }: { label: string; value: string }) {
</div>
);
}
+13 -2
View File
@@ -1,8 +1,15 @@
import type { Artifact, ClipSuggestion, PipelineSettings, TranscriptSegment, Video } from "./types";
import type {
Artifact,
ClipSuggestion,
PipelineSettings,
TranscriptSegment,
Video,
WebDavScanResult,
} from "./types";
const runtimeApiBase = window.__EVANESCERE_FRONTEND_CONFIG__?.apiBaseUrl;
export const apiBaseUrl = (runtimeApiBase || "http://localhost:8000").replace(
export const apiBaseUrl = (runtimeApiBase || "http://localhost:8080").replace(
/\/$/,
"",
);
@@ -37,6 +44,10 @@ export function getVideos(): Promise<Video[]> {
return request<Video[]>("/videos");
}
export function scanWebDav(): Promise<WebDavScanResult> {
return request<WebDavScanResult>("/webdav/scan", { method: "POST" });
}
export function runVideo(videoId: number) {
return request(`/videos/${videoId}/run`, { method: "POST" });
}
+17 -1
View File
@@ -120,6 +120,16 @@ button {
overflow-wrap: anywhere;
}
.notice-strip {
margin-bottom: 14px;
border-left: 4px solid #21756b;
background: #e9f3f1;
color: #155c54;
padding: 10px 12px;
border-radius: 6px;
font-size: 14px;
}
.band {
border-top: 1px solid #d8dee6;
padding: 16px 0;
@@ -190,6 +200,13 @@ button {
padding-top: 16px;
}
.list-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.table-scroll {
margin-top: 12px;
overflow: auto;
@@ -431,4 +448,3 @@ tr.selected td {
grid-template-columns: 1fr;
}
}
+8
View File
@@ -59,3 +59,11 @@ export interface Artifact {
artifact_metadata: Record<string, unknown>;
created_at: string;
}
export interface WebDavScanResult {
observed: number;
newly_stable: number;
baseline_initialized: boolean;
baseline_inserted: number;
baseline_marked_existing: number;
}