omnius 1.0.402 → 1.0.403
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/index.js +1100 -168
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3675,6 +3675,69 @@ function ensureCommand(command) {
|
|
|
3675
3675
|
_cache.set(command, result);
|
|
3676
3676
|
return result;
|
|
3677
3677
|
}
|
|
3678
|
+
function repairCommandPackage(command) {
|
|
3679
|
+
const dep = DESKTOP_DEPS.find((d2) => d2.command === command);
|
|
3680
|
+
if (!dep) {
|
|
3681
|
+
return {
|
|
3682
|
+
available: false,
|
|
3683
|
+
installed: false,
|
|
3684
|
+
error: `Unknown dependency: ${command}. Cannot repair package.`
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
const pm = detectPackageManager();
|
|
3688
|
+
if (!pm) {
|
|
3689
|
+
return {
|
|
3690
|
+
available: hasCommand(command),
|
|
3691
|
+
installed: false,
|
|
3692
|
+
error: `No supported package manager detected — cannot repair ${command}.`
|
|
3693
|
+
};
|
|
3694
|
+
}
|
|
3695
|
+
const pkg = dep.packages[pm];
|
|
3696
|
+
if (!pkg) {
|
|
3697
|
+
return {
|
|
3698
|
+
available: hasCommand(command),
|
|
3699
|
+
installed: false,
|
|
3700
|
+
error: `No package mapping for ${command} on ${pm} — cannot repair package.`
|
|
3701
|
+
};
|
|
3702
|
+
}
|
|
3703
|
+
let installCmd;
|
|
3704
|
+
switch (pm) {
|
|
3705
|
+
case "apt":
|
|
3706
|
+
installCmd = `apt-get install -y ${pkg}`;
|
|
3707
|
+
break;
|
|
3708
|
+
case "dnf":
|
|
3709
|
+
installCmd = `dnf install -y ${pkg}`;
|
|
3710
|
+
break;
|
|
3711
|
+
case "pacman":
|
|
3712
|
+
installCmd = `pacman -S --noconfirm ${pkg}`;
|
|
3713
|
+
break;
|
|
3714
|
+
case "brew":
|
|
3715
|
+
installCmd = `brew install ${pkg}`;
|
|
3716
|
+
break;
|
|
3717
|
+
case "choco":
|
|
3718
|
+
installCmd = `choco install -y ${pkg}`;
|
|
3719
|
+
break;
|
|
3720
|
+
}
|
|
3721
|
+
try {
|
|
3722
|
+
runInstall(installCmd);
|
|
3723
|
+
} catch (err) {
|
|
3724
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3725
|
+
const result = {
|
|
3726
|
+
available: hasCommand(command),
|
|
3727
|
+
installed: false,
|
|
3728
|
+
error: `Failed to repair ${pkg} via ${pm}: ${msg.slice(0, 200)}`
|
|
3729
|
+
};
|
|
3730
|
+
_cache.delete(command);
|
|
3731
|
+
return result;
|
|
3732
|
+
}
|
|
3733
|
+
_cache.delete(command);
|
|
3734
|
+
const available = hasCommand(command);
|
|
3735
|
+
return {
|
|
3736
|
+
available,
|
|
3737
|
+
installed: available,
|
|
3738
|
+
error: available ? void 0 : `Installed ${pkg} but ${command} still not found on PATH.`
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3678
3741
|
function ensureDepsForGroup(group) {
|
|
3679
3742
|
const deps = DESKTOP_DEPS.filter((d2) => d2.group === group);
|
|
3680
3743
|
const results = deps.map((d2) => ({ command: d2.command, result: ensureCommand(d2.command) }));
|
|
@@ -3781,6 +3844,18 @@ var init_system_deps = __esm({
|
|
|
3781
3844
|
packages: { apt: "ghostscript", dnf: "ghostscript", pacman: "ghostscript", brew: "ghostscript", choco: "ghostscript" },
|
|
3782
3845
|
description: "PostScript/PDF rendering (Ghostscript, required by ocrmypdf)",
|
|
3783
3846
|
group: "pdf"
|
|
3847
|
+
},
|
|
3848
|
+
{
|
|
3849
|
+
command: "ffmpeg",
|
|
3850
|
+
packages: { apt: "ffmpeg libavcodec-extra", dnf: "ffmpeg", pacman: "ffmpeg", brew: "ffmpeg", choco: "ffmpeg" },
|
|
3851
|
+
description: "Camera/media capture and MJPEG decode/encode support",
|
|
3852
|
+
group: "camera"
|
|
3853
|
+
},
|
|
3854
|
+
{
|
|
3855
|
+
command: "v4l2-ctl",
|
|
3856
|
+
packages: { apt: "v4l-utils", dnf: "v4l-utils", pacman: "v4l-utils", brew: "v4l-utils" },
|
|
3857
|
+
description: "Linux Video4Linux device capability probing",
|
|
3858
|
+
group: "camera"
|
|
3784
3859
|
}
|
|
3785
3860
|
];
|
|
3786
3861
|
_cache = /* @__PURE__ */ new Map();
|
|
@@ -542806,6 +542881,103 @@ function sortedVideoDevicePaths() {
|
|
|
542806
542881
|
return [];
|
|
542807
542882
|
}
|
|
542808
542883
|
}
|
|
542884
|
+
function ffmpegBin2() {
|
|
542885
|
+
return process.env["OMNIUS_FFMPEG"] || "ffmpeg";
|
|
542886
|
+
}
|
|
542887
|
+
function normalizePixelFormat(pixelFormat) {
|
|
542888
|
+
const fmt2 = pixelFormat.trim().toUpperCase();
|
|
542889
|
+
if (fmt2 === "MJPG" || fmt2 === "MJPEG")
|
|
542890
|
+
return "mjpeg";
|
|
542891
|
+
if (fmt2 === "YUYV" || fmt2 === "YUYV422")
|
|
542892
|
+
return "yuyv422";
|
|
542893
|
+
if (fmt2 === "UYVY" || fmt2 === "UYVY422")
|
|
542894
|
+
return "uyvy422";
|
|
542895
|
+
if (fmt2 === "NV12")
|
|
542896
|
+
return "nv12";
|
|
542897
|
+
if (fmt2 === "H264" || fmt2 === "H.264")
|
|
542898
|
+
return "h264";
|
|
542899
|
+
if (fmt2 === "HEVC" || fmt2 === "H265" || fmt2 === "H.265")
|
|
542900
|
+
return "hevc";
|
|
542901
|
+
return void 0;
|
|
542902
|
+
}
|
|
542903
|
+
function parseV4l2Formats(raw) {
|
|
542904
|
+
const formats = [];
|
|
542905
|
+
let current = null;
|
|
542906
|
+
for (const line of String(raw || "").split(/\r?\n/)) {
|
|
542907
|
+
const fmt2 = /(?:Pixel Format|^\s*\[\d+\])\s*:\s*'([^']+)'\s*(?:\(([^)]*)\))?/i.exec(line);
|
|
542908
|
+
if (fmt2) {
|
|
542909
|
+
current = {
|
|
542910
|
+
pixelFormat: fmt2[1].trim(),
|
|
542911
|
+
description: (fmt2[2] || "").trim(),
|
|
542912
|
+
ffmpegInputFormat: normalizePixelFormat(fmt2[1]),
|
|
542913
|
+
compressed: /compressed|mjpeg|h\.?264|hevc/i.test(fmt2[2] || fmt2[1] || ""),
|
|
542914
|
+
sizes: []
|
|
542915
|
+
};
|
|
542916
|
+
formats.push(current);
|
|
542917
|
+
continue;
|
|
542918
|
+
}
|
|
542919
|
+
const size = /Size:\s*Discrete\s+(\d+x\d+)/i.exec(line);
|
|
542920
|
+
if (size && current && !current.sizes.includes(size[1]))
|
|
542921
|
+
current.sizes.push(size[1]);
|
|
542922
|
+
}
|
|
542923
|
+
return formats;
|
|
542924
|
+
}
|
|
542925
|
+
function uniqueStrings(values) {
|
|
542926
|
+
const out = [];
|
|
542927
|
+
for (const value2 of values) {
|
|
542928
|
+
if (!value2 || out.includes(value2))
|
|
542929
|
+
continue;
|
|
542930
|
+
out.push(value2);
|
|
542931
|
+
}
|
|
542932
|
+
return out;
|
|
542933
|
+
}
|
|
542934
|
+
function buildV4l2CaptureAttempts(width, height, formats) {
|
|
542935
|
+
const requested = width > 0 && height > 0 ? `${width}x${height}` : void 0;
|
|
542936
|
+
const advertisedSizes = formats.flatMap((f2) => f2.sizes);
|
|
542937
|
+
const sizes = uniqueStrings([requested, ...advertisedSizes, ...DEFAULT_CAPTURE_SIZES]).slice(0, 10);
|
|
542938
|
+
const advertisedInputs = formats.map((f2) => f2.ffmpegInputFormat).filter((f2) => !!f2);
|
|
542939
|
+
const inputs = uniqueStrings([
|
|
542940
|
+
...advertisedInputs.filter((f2) => f2 === "mjpeg"),
|
|
542941
|
+
...advertisedInputs.filter((f2) => f2 !== "mjpeg"),
|
|
542942
|
+
"mjpeg",
|
|
542943
|
+
"yuyv422",
|
|
542944
|
+
void 0
|
|
542945
|
+
]);
|
|
542946
|
+
const attempts = [];
|
|
542947
|
+
for (const size of sizes) {
|
|
542948
|
+
for (const input of inputs) {
|
|
542949
|
+
if (input === "mjpeg") {
|
|
542950
|
+
attempts.push({ label: `mjpeg packet copy ${size}`, inputFormat: input, videoSize: size, copyPacket: true });
|
|
542951
|
+
}
|
|
542952
|
+
attempts.push({
|
|
542953
|
+
label: `${input || "auto"} ${size}`,
|
|
542954
|
+
inputFormat: input,
|
|
542955
|
+
videoSize: size,
|
|
542956
|
+
copyPacket: false
|
|
542957
|
+
});
|
|
542958
|
+
}
|
|
542959
|
+
}
|
|
542960
|
+
attempts.push({ label: "auto native", copyPacket: false });
|
|
542961
|
+
const seen = /* @__PURE__ */ new Set();
|
|
542962
|
+
return attempts.filter((attempt) => {
|
|
542963
|
+
const key = `${attempt.inputFormat || ""}|${attempt.videoSize || ""}|${attempt.copyPacket ? "copy" : "encode"}`;
|
|
542964
|
+
if (seen.has(key))
|
|
542965
|
+
return false;
|
|
542966
|
+
seen.add(key);
|
|
542967
|
+
return true;
|
|
542968
|
+
});
|
|
542969
|
+
}
|
|
542970
|
+
function cameraFfmpegErrorNeedsPackageRepair(message2) {
|
|
542971
|
+
return /mjpeg|codec|decoder|encoder|unknown input format|not support|not supported|invalid pixel format|video4linux2|v4l2/i.test(message2);
|
|
542972
|
+
}
|
|
542973
|
+
function processErrorDetail(err) {
|
|
542974
|
+
if (err instanceof AsyncProcessError) {
|
|
542975
|
+
const stderr = err.stderr.toString("utf8").trim();
|
|
542976
|
+
const stdout = err.stdout.toString("utf8").trim();
|
|
542977
|
+
return [err.message, stderr, stdout].filter(Boolean).join("\n").slice(0, 1200);
|
|
542978
|
+
}
|
|
542979
|
+
return (err instanceof Error ? err.message : String(err)).slice(0, 1200);
|
|
542980
|
+
}
|
|
542809
542981
|
function cameraProfileFor(rawProfile, expectedCameras) {
|
|
542810
542982
|
const key = String(rawProfile || "generic").trim().toLowerCase();
|
|
542811
542983
|
const base3 = CAMERA_PROFILES[key] ?? { ...DEFAULT_CAMERA_PROFILE, name: key || "generic" };
|
|
@@ -542857,15 +543029,17 @@ function formatRouterPlan(plan) {
|
|
|
542857
543029
|
}
|
|
542858
543030
|
return lines.join("\n");
|
|
542859
543031
|
}
|
|
542860
|
-
var KANDAO_USB_VENDORS, QOOCAM_WIFI_PASSWORD, QOOCAM_DEFAULT_IP, OSC_TIMEOUT, DEFAULT_CAMERA_PROFILE, CAMERA_PROFILES, CameraCaptureTool;
|
|
543032
|
+
var KANDAO_USB_VENDORS, QOOCAM_WIFI_PASSWORD, QOOCAM_DEFAULT_IP, OSC_TIMEOUT, DEFAULT_CAPTURE_SIZES, DEFAULT_CAMERA_PROFILE, CAMERA_PROFILES, CameraCaptureTool;
|
|
542861
543033
|
var init_camera_capture = __esm({
|
|
542862
543034
|
"packages/execution/dist/tools/camera-capture.js"() {
|
|
542863
543035
|
"use strict";
|
|
542864
543036
|
init_process_async();
|
|
543037
|
+
init_system_deps();
|
|
542865
543038
|
KANDAO_USB_VENDORS = ["12d1", "2aad"];
|
|
542866
543039
|
QOOCAM_WIFI_PASSWORD = "12345678";
|
|
542867
543040
|
QOOCAM_DEFAULT_IP = "192.168.173.1";
|
|
542868
543041
|
OSC_TIMEOUT = 15e3;
|
|
543042
|
+
DEFAULT_CAPTURE_SIZES = ["1280x720", "640x480", "1920x1080"];
|
|
542869
543043
|
DEFAULT_CAMERA_PROFILE = {
|
|
542870
543044
|
name: "generic",
|
|
542871
543045
|
streamBasePort: 9e3,
|
|
@@ -543078,17 +543252,22 @@ ${formatRouterPlan(plan)}`,
|
|
|
543078
543252
|
async discoverV4l2Devices() {
|
|
543079
543253
|
const devices = [];
|
|
543080
543254
|
const devs = sortedVideoDevicePaths();
|
|
543255
|
+
const canProbe = devs.length > 0 ? ensureCommand("v4l2-ctl").available : false;
|
|
543081
543256
|
for (const dev of devs) {
|
|
543082
543257
|
let name10 = "Unknown camera";
|
|
543083
543258
|
let driver = "unknown";
|
|
543259
|
+
let captureCapable;
|
|
543084
543260
|
try {
|
|
543085
|
-
|
|
543086
|
-
|
|
543087
|
-
|
|
543088
|
-
|
|
543089
|
-
|
|
543090
|
-
|
|
543091
|
-
|
|
543261
|
+
if (canProbe) {
|
|
543262
|
+
const info = await execFileText("v4l2-ctl", ["-d", dev, "--info"], { timeout: 3e3 });
|
|
543263
|
+
const nameMatch = info.match(/Card type\s*:\s*(.+)/);
|
|
543264
|
+
const driverMatch = info.match(/Driver name\s*:\s*(.+)/);
|
|
543265
|
+
if (nameMatch)
|
|
543266
|
+
name10 = nameMatch[1].trim();
|
|
543267
|
+
if (driverMatch)
|
|
543268
|
+
driver = driverMatch[1].trim();
|
|
543269
|
+
captureCapable = /(?:Device Caps|Capabilities)[\s\S]*Video Capture/i.test(info) && !/Metadata Capture/i.test(info);
|
|
543270
|
+
}
|
|
543092
543271
|
} catch {
|
|
543093
543272
|
try {
|
|
543094
543273
|
const idx = dev.match(/video(\d+)/)?.[1];
|
|
@@ -543107,6 +543286,7 @@ ${formatRouterPlan(plan)}`,
|
|
|
543107
543286
|
driver,
|
|
543108
543287
|
backend: "v4l2",
|
|
543109
543288
|
index,
|
|
543289
|
+
captureCapable,
|
|
543110
543290
|
realsense,
|
|
543111
543291
|
depthCapable: realsense || haystack.includes("depth")
|
|
543112
543292
|
});
|
|
@@ -543115,14 +543295,15 @@ ${formatRouterPlan(plan)}`,
|
|
|
543115
543295
|
}
|
|
543116
543296
|
async resolveV4l2Device(rawDevice) {
|
|
543117
543297
|
const devices = await this.discoverV4l2Devices();
|
|
543298
|
+
const captureDevices = devices.filter((device) => device.captureCapable !== false);
|
|
543118
543299
|
if (!rawDevice)
|
|
543119
|
-
return devices[0]?.device ?? null;
|
|
543300
|
+
return captureDevices[0]?.device ?? devices[0]?.device ?? null;
|
|
543120
543301
|
if (rawDevice.startsWith("/dev/video"))
|
|
543121
543302
|
return rawDevice;
|
|
543122
543303
|
const slotMatch = rawDevice.match(/^(?:slot:|camera)?(\d+)$/i);
|
|
543123
543304
|
if (slotMatch) {
|
|
543124
543305
|
const idx = Number(slotMatch[1]);
|
|
543125
|
-
return devices[idx]?.device ?? null;
|
|
543306
|
+
return captureDevices[idx]?.device ?? devices[idx]?.device ?? null;
|
|
543126
543307
|
}
|
|
543127
543308
|
return rawDevice;
|
|
543128
543309
|
}
|
|
@@ -543134,48 +543315,155 @@ ${formatRouterPlan(plan)}`,
|
|
|
543134
543315
|
if (!existsSync60(captureDir))
|
|
543135
543316
|
mkdirSync34(captureDir, { recursive: true });
|
|
543136
543317
|
const tempFile = outputPath3 || join75(captureDir, `capture-${Date.now()}.jpg`);
|
|
543318
|
+
const deps = await this.ensureV4l2CaptureStack();
|
|
543319
|
+
if (!deps.ok) {
|
|
543320
|
+
return { success: false, output: "", error: deps.error, durationMs: performance.now() - start2 };
|
|
543321
|
+
}
|
|
543322
|
+
const formats = await this.queryV4l2Formats(device);
|
|
543323
|
+
const attempts = buildV4l2CaptureAttempts(width, height, formats);
|
|
543324
|
+
const errors = [];
|
|
543325
|
+
let repaired = false;
|
|
543326
|
+
const captureState = {};
|
|
543327
|
+
const runAttempts = async () => {
|
|
543328
|
+
for (const attempt of attempts) {
|
|
543329
|
+
try {
|
|
543330
|
+
if (existsSync60(tempFile))
|
|
543331
|
+
unlinkSync11(tempFile);
|
|
543332
|
+
} catch {
|
|
543333
|
+
}
|
|
543334
|
+
const argv = this.ffmpegCaptureArgs(device, tempFile, attempt);
|
|
543335
|
+
try {
|
|
543336
|
+
await runProcessBuffer(ffmpegBin2(), argv, { timeout: 15e3 });
|
|
543337
|
+
if (existsSync60(tempFile) && readFileSync44(tempFile).length > 0) {
|
|
543338
|
+
captureState.successfulAttempt = attempt;
|
|
543339
|
+
return true;
|
|
543340
|
+
}
|
|
543341
|
+
errors.push(`${attempt.label}: ffmpeg produced no image bytes`);
|
|
543342
|
+
} catch (err) {
|
|
543343
|
+
const detail = processErrorDetail(err);
|
|
543344
|
+
errors.push(`${attempt.label}: ${detail}`);
|
|
543345
|
+
if (!repaired && cameraFfmpegErrorNeedsPackageRepair(detail)) {
|
|
543346
|
+
const repair = repairCommandPackage("ffmpeg");
|
|
543347
|
+
repaired = true;
|
|
543348
|
+
errors.push(repair.error ? `ffmpeg repair attempted: ${repair.error}` : "ffmpeg repair attempted: package install completed");
|
|
543349
|
+
if (repair.available)
|
|
543350
|
+
return runAttempts();
|
|
543351
|
+
}
|
|
543352
|
+
}
|
|
543353
|
+
}
|
|
543354
|
+
return false;
|
|
543355
|
+
};
|
|
543356
|
+
if (await runAttempts()) {
|
|
543357
|
+
const firstSuccess = errors.length > 0 ? `
|
|
543358
|
+
Recovered with capture profile: ${captureState.successfulAttempt?.label ?? "unknown"}. Last failed attempt: ${errors[errors.length - 1].slice(0, 260)}` : "";
|
|
543359
|
+
const result = this.returnCapturedFile(tempFile, captureState.successfulAttempt?.videoSize ?? `${width}x${height}`, device, outputPath3, start2);
|
|
543360
|
+
return result.success && firstSuccess ? { ...result, output: result.output + firstSuccess, llmContent: result.llmContent ? result.llmContent + firstSuccess : result.llmContent } : result;
|
|
543361
|
+
}
|
|
543362
|
+
const allErrors = errors.join("\n\n");
|
|
543363
|
+
if (/Device or resource busy/i.test(allErrors)) {
|
|
543364
|
+
return { success: false, output: "", error: `Camera ${device} is busy — close other camera apps.
|
|
543365
|
+
${allErrors.slice(0, 900)}`, durationMs: performance.now() - start2 };
|
|
543366
|
+
}
|
|
543367
|
+
if (/Permission denied/i.test(allErrors)) {
|
|
543368
|
+
return { success: false, output: "", error: `Permission denied opening ${device}. Add the user to the video group or run the daemon with camera permissions.
|
|
543369
|
+
${allErrors.slice(0, 900)}`, durationMs: performance.now() - start2 };
|
|
543370
|
+
}
|
|
543371
|
+
return {
|
|
543372
|
+
success: false,
|
|
543373
|
+
output: "",
|
|
543374
|
+
error: `Failed to capture from ${device} after ${attempts.length} ffmpeg profiles. Auto-install/repair was ${repaired ? "attempted" : "not triggered"}. On ARM Debian/Ubuntu, the camera stack package set is: sudo apt-get update && sudo apt-get install -y ffmpeg libavcodec-extra v4l-utils
|
|
543375
|
+
|
|
543376
|
+
` + allErrors.slice(0, 1800),
|
|
543377
|
+
durationMs: performance.now() - start2
|
|
543378
|
+
};
|
|
543379
|
+
}
|
|
543380
|
+
async ensureV4l2CaptureStack() {
|
|
543381
|
+
if (ffmpegBin2() === "ffmpeg") {
|
|
543382
|
+
const ffmpeg = ensureCommand("ffmpeg");
|
|
543383
|
+
if (!ffmpeg.available) {
|
|
543384
|
+
return {
|
|
543385
|
+
ok: false,
|
|
543386
|
+
error: `ffmpeg is required for camera_capture and auto-install failed: ${ffmpeg.error ?? "unknown error"}. On ARM Debian/Ubuntu install: sudo apt-get update && sudo apt-get install -y ffmpeg libavcodec-extra v4l-utils`
|
|
543387
|
+
};
|
|
543388
|
+
}
|
|
543389
|
+
}
|
|
543390
|
+
ensureCommand("v4l2-ctl");
|
|
543137
543391
|
try {
|
|
543138
|
-
await execFileText("
|
|
543139
|
-
|
|
543140
|
-
|
|
543141
|
-
|
|
543142
|
-
|
|
543143
|
-
|
|
543144
|
-
"
|
|
543145
|
-
|
|
543146
|
-
"-i",
|
|
543147
|
-
device,
|
|
543148
|
-
"-frames:v",
|
|
543149
|
-
"1",
|
|
543150
|
-
"-q:v",
|
|
543151
|
-
"2",
|
|
543152
|
-
"-y",
|
|
543153
|
-
tempFile
|
|
543154
|
-
], { timeout: 1e4 });
|
|
543392
|
+
const codecs2 = await execFileText(ffmpegBin2(), ["-hide_banner", "-codecs"], { timeout: 6e3 });
|
|
543393
|
+
const formats = await execFileText(ffmpegBin2(), ["-hide_banner", "-formats"], { timeout: 6e3 });
|
|
543394
|
+
const hasMjpegDecode = /^\s*D\S*\s+mjpeg\b/im.test(codecs2);
|
|
543395
|
+
const hasMjpegEncode = /^\s*\S*E\S*\s+mjpeg\b/im.test(codecs2);
|
|
543396
|
+
const hasV4l2 = /video4linux2|v4l2/i.test(formats);
|
|
543397
|
+
if ((!hasMjpegDecode || !hasMjpegEncode || !hasV4l2) && ffmpegBin2() === "ffmpeg") {
|
|
543398
|
+
repairCommandPackage("ffmpeg");
|
|
543399
|
+
}
|
|
543155
543400
|
} catch (err) {
|
|
543156
|
-
|
|
543157
|
-
|
|
543158
|
-
|
|
543401
|
+
if (ffmpegBin2() === "ffmpeg")
|
|
543402
|
+
repairCommandPackage("ffmpeg");
|
|
543403
|
+
}
|
|
543404
|
+
return { ok: true, error: "" };
|
|
543405
|
+
}
|
|
543406
|
+
async queryV4l2Formats(device) {
|
|
543407
|
+
const dep = ensureCommand("v4l2-ctl");
|
|
543408
|
+
if (dep.available) {
|
|
543409
|
+
try {
|
|
543410
|
+
return parseV4l2Formats(await execFileText("v4l2-ctl", ["-d", device, "--list-formats-ext"], { timeout: 5e3 }));
|
|
543411
|
+
} catch {
|
|
543159
543412
|
}
|
|
543160
|
-
return { success: false, output: "", error: `Failed to capture from ${device}: ${msg.slice(0, 300)}`, durationMs: performance.now() - start2 };
|
|
543161
543413
|
}
|
|
543162
|
-
|
|
543414
|
+
try {
|
|
543415
|
+
const probeResult = await runProcessBuffer(ffmpegBin2(), ["-hide_banner", "-f", "v4l2", "-list_formats", "all", "-i", device], { timeout: 5e3 }).catch((err) => {
|
|
543416
|
+
if (err instanceof AsyncProcessError)
|
|
543417
|
+
return err;
|
|
543418
|
+
throw err;
|
|
543419
|
+
});
|
|
543420
|
+
return parseV4l2Formats(Buffer.concat([probeResult.stdout, probeResult.stderr]).toString("utf8"));
|
|
543421
|
+
} catch {
|
|
543422
|
+
return [];
|
|
543423
|
+
}
|
|
543424
|
+
}
|
|
543425
|
+
ffmpegCaptureArgs(device, outputPath3, attempt) {
|
|
543426
|
+
const argv = ["-hide_banner", "-loglevel", "error", "-f", "v4l2", "-thread_queue_size", "4"];
|
|
543427
|
+
if (attempt.inputFormat)
|
|
543428
|
+
argv.push("-input_format", attempt.inputFormat);
|
|
543429
|
+
if (attempt.videoSize)
|
|
543430
|
+
argv.push("-video_size", attempt.videoSize);
|
|
543431
|
+
argv.push("-i", device, "-frames:v", "1");
|
|
543432
|
+
if (attempt.copyPacket) {
|
|
543433
|
+
argv.push("-c:v", "copy", "-f", "image2");
|
|
543434
|
+
} else {
|
|
543435
|
+
argv.push("-q:v", "2");
|
|
543436
|
+
}
|
|
543437
|
+
argv.push("-y", outputPath3);
|
|
543438
|
+
return argv;
|
|
543163
543439
|
}
|
|
543164
543440
|
async v4l2Info(device, start2) {
|
|
543165
543441
|
let info = `Camera: ${device} [v4l2]
|
|
543166
543442
|
`;
|
|
543443
|
+
const v4l2Ctl = ensureCommand("v4l2-ctl");
|
|
543444
|
+
const ffmpeg = ffmpegBin2() === "ffmpeg" ? ensureCommand("ffmpeg") : { available: true, installed: false };
|
|
543445
|
+
if (v4l2Ctl.installed || ffmpeg.installed) {
|
|
543446
|
+
info += `
|
|
543447
|
+
Auto-installed camera dependencies: ${[
|
|
543448
|
+
v4l2Ctl.installed ? "v4l2-ctl" : "",
|
|
543449
|
+
ffmpeg.installed ? "ffmpeg" : ""
|
|
543450
|
+
].filter(Boolean).join(", ")}
|
|
543451
|
+
`;
|
|
543452
|
+
}
|
|
543167
543453
|
try {
|
|
543454
|
+
if (!v4l2Ctl.available)
|
|
543455
|
+
throw new Error(v4l2Ctl.error || "v4l2-ctl unavailable");
|
|
543168
543456
|
const formats = await execFileText("v4l2-ctl", ["-d", device, "--list-formats-ext"], { timeout: 5e3 });
|
|
543169
543457
|
info += "\nSupported formats and resolutions:\n" + formats.trim();
|
|
543170
543458
|
} catch {
|
|
543171
543459
|
try {
|
|
543172
|
-
const probeResult = await runProcessBuffer(
|
|
543460
|
+
const probeResult = await runProcessBuffer(ffmpegBin2(), ["-hide_banner", "-f", "v4l2", "-list_formats", "all", "-i", device], { timeout: 5e3 }).catch((err) => {
|
|
543173
543461
|
if (err instanceof AsyncProcessError)
|
|
543174
543462
|
return err;
|
|
543175
543463
|
throw err;
|
|
543176
543464
|
});
|
|
543177
543465
|
const probe = Buffer.concat([probeResult.stdout, probeResult.stderr]).toString("utf8");
|
|
543178
|
-
const lines = probe.split("\n").filter((l2) => l2.includes("Raw") || l2.includes("Compressed") || l2.includes("video4linux2"));
|
|
543466
|
+
const lines = probe.split("\n").filter((l2) => l2.includes("Raw") || l2.includes("Compressed") || l2.includes("video4linux2") || l2.includes("MJPEG"));
|
|
543179
543467
|
info += "\nFormats (ffmpeg probe):\n" + (lines.length > 0 ? lines.join("\n") : "Could not query formats");
|
|
543180
543468
|
} catch {
|
|
543181
543469
|
info += "\nCould not query camera capabilities";
|
|
@@ -700102,58 +700390,69 @@ function getWebUI() {
|
|
|
700102
700390
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
700103
700391
|
<title>Omnius</title>
|
|
700104
700392
|
<style>
|
|
700105
|
-
/* ───
|
|
700106
|
-
*
|
|
700107
|
-
*
|
|
700108
|
-
* accent. Brand gold preserved as --color-brand for Omnius-only marks.
|
|
700109
|
-
* Typography flips body to Inter (UI) + JetBrains Mono (code only),
|
|
700110
|
-
* leaving the terminal aesthetic only where we explicitly opt in.
|
|
700393
|
+
/* ─── Cold Tactical HUD design tokens ──────────────────────────────
|
|
700394
|
+
* Source of truth:
|
|
700395
|
+
* /home/roko/Desktop/hud-ui-style-kit(6)/hud-ui-style-kit
|
|
700111
700396
|
*
|
|
700112
|
-
*
|
|
700113
|
-
*
|
|
700397
|
+
* The UI remains a single daemon-served HTML document, so these tokens
|
|
700398
|
+
* intentionally map onto the older --color-* variable names as well as
|
|
700399
|
+
* the HUD-specific --hud-* names.
|
|
700114
700400
|
*/
|
|
700115
|
-
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
700116
|
-
|
|
700117
700401
|
:root {
|
|
700118
|
-
|
|
700119
|
-
--
|
|
700120
|
-
--
|
|
700121
|
-
--
|
|
700122
|
-
--
|
|
700123
|
-
|
|
700124
|
-
|
|
700125
|
-
--
|
|
700126
|
-
--
|
|
700127
|
-
--
|
|
700128
|
-
--
|
|
700129
|
-
|
|
700130
|
-
|
|
700131
|
-
--
|
|
700132
|
-
--
|
|
700133
|
-
|
|
700134
|
-
|
|
700135
|
-
--
|
|
700136
|
-
--
|
|
700137
|
-
|
|
700138
|
-
|
|
700139
|
-
--color-
|
|
700140
|
-
--color-
|
|
700141
|
-
--color-
|
|
700142
|
-
|
|
700143
|
-
|
|
700144
|
-
|
|
700145
|
-
--color-
|
|
700146
|
-
--color-
|
|
700402
|
+
--hud-void: #060a0c;
|
|
700403
|
+
--hud-black: #0a0f12;
|
|
700404
|
+
--hud-panel-solid: #121b1f;
|
|
700405
|
+
--hud-steel: #353f47;
|
|
700406
|
+
--hud-steel-2: #48545a;
|
|
700407
|
+
--hud-dim: #687577;
|
|
700408
|
+
--hud-muted: #829094;
|
|
700409
|
+
--hud-ink-soft: #c3ccce;
|
|
700410
|
+
--hud-ink: #d9dfdf;
|
|
700411
|
+
--hud-accent: #d29a3a;
|
|
700412
|
+
--hud-accent-strong: #f1bd55;
|
|
700413
|
+
--hud-cyan: #b7d3d8;
|
|
700414
|
+
--hud-danger: #d06c5f;
|
|
700415
|
+
--hud-success: #9fb594;
|
|
700416
|
+
--hud-panel-clip: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 18px 100%, 0 calc(100% - 18px));
|
|
700417
|
+
--hud-button-clip: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
|
|
700418
|
+
--hud-panel-shadow: 0 24px 80px rgba(0,0,0,0.62), inset 0 1px 0 rgba(255,255,255,0.09);
|
|
700419
|
+
--hud-cyan-glow: 0 0 18px rgba(183,211,216,0.22);
|
|
700420
|
+
--hud-amber-glow: 0 0 22px rgba(210,154,58,0.28);
|
|
700421
|
+
|
|
700422
|
+
/* Legacy color variable bridge */
|
|
700423
|
+
--color-bg: var(--hud-void);
|
|
700424
|
+
--color-bg-elevated: rgba(18, 27, 31, 0.88);
|
|
700425
|
+
--color-bg-input: rgba(10, 15, 18, 0.82);
|
|
700426
|
+
--color-bg-hover: rgba(53, 63, 71, 0.56);
|
|
700427
|
+
|
|
700428
|
+
--color-fg: var(--hud-ink);
|
|
700429
|
+
--color-fg-muted: var(--hud-ink-soft);
|
|
700430
|
+
--color-fg-subtle: var(--hud-muted);
|
|
700431
|
+
--color-fg-faint: var(--hud-dim);
|
|
700432
|
+
|
|
700433
|
+
--color-border: var(--hud-steel);
|
|
700434
|
+
--color-border-strong: var(--hud-steel-2);
|
|
700435
|
+
|
|
700436
|
+
--color-accent: var(--hud-accent);
|
|
700437
|
+
--color-accent-hover: var(--hud-accent-strong);
|
|
700438
|
+
--color-accent-fg: #081014;
|
|
700439
|
+
--color-success: var(--hud-success);
|
|
700440
|
+
--color-warning: var(--hud-accent-strong);
|
|
700441
|
+
--color-error: var(--hud-danger);
|
|
700442
|
+
--color-info: var(--hud-cyan);
|
|
700443
|
+
--color-brand: var(--hud-accent);
|
|
700444
|
+
--color-brand-hover: var(--hud-accent-strong);
|
|
700147
700445
|
|
|
700148
700446
|
/* Typography */
|
|
700149
|
-
--font-
|
|
700150
|
-
--font-
|
|
700447
|
+
--font-display: 'DIN Condensed', 'Bahnschrift Condensed', 'Arial Narrow', 'Roboto Condensed', Impact, sans-serif;
|
|
700448
|
+
--font-ui: Inter, 'Segoe UI', Roboto, Arial, sans-serif;
|
|
700449
|
+
--font-mono: 'IBM Plex Mono', 'JetBrains Mono', 'Cascadia Mono', 'SF Mono', Consolas, ui-monospace, monospace;
|
|
700151
700450
|
|
|
700152
700451
|
/* Radii */
|
|
700153
|
-
--radius-sm:
|
|
700154
|
-
--radius-md:
|
|
700155
|
-
--radius-lg:
|
|
700156
|
-
--radius-pill:
|
|
700452
|
+
--radius-sm: 2px;
|
|
700453
|
+
--radius-md: 3px;
|
|
700454
|
+
--radius-lg: 4px;
|
|
700455
|
+
--radius-pill: 2px;
|
|
700157
700456
|
}
|
|
700158
700457
|
|
|
700159
700458
|
/* Light theme — opt in via [data-theme=light] on html.
|
|
@@ -701446,6 +701745,433 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
701446
701745
|
text-transform: uppercase;
|
|
701447
701746
|
letter-spacing: 0.05em;
|
|
701448
701747
|
}
|
|
701748
|
+
|
|
701749
|
+
/* ════════════════════════════════════════════════════════════
|
|
701750
|
+
HUD foundation ported from hud-ui-style-kit
|
|
701751
|
+
════════════════════════════════════════════════════════════ */
|
|
701752
|
+
body::before {
|
|
701753
|
+
content: "";
|
|
701754
|
+
position: fixed;
|
|
701755
|
+
inset: 0;
|
|
701756
|
+
pointer-events: none;
|
|
701757
|
+
z-index: -2;
|
|
701758
|
+
background:
|
|
701759
|
+
linear-gradient(rgba(183,211,216,0.045) 1px, transparent 1px),
|
|
701760
|
+
linear-gradient(90deg, rgba(183,211,216,0.045) 1px, transparent 1px),
|
|
701761
|
+
radial-gradient(circle at 22% 18%, rgba(183,211,216,0.09), transparent 30%),
|
|
701762
|
+
linear-gradient(135deg, #060a0c 0%, #0a1114 46%, #10191d 100%);
|
|
701763
|
+
background-size: 64px 64px, 64px 64px, auto, auto;
|
|
701764
|
+
}
|
|
701765
|
+
body::after {
|
|
701766
|
+
content: "";
|
|
701767
|
+
position: fixed;
|
|
701768
|
+
inset: 0;
|
|
701769
|
+
pointer-events: none;
|
|
701770
|
+
z-index: -1;
|
|
701771
|
+
background:
|
|
701772
|
+
repeating-linear-gradient(0deg, rgba(255,255,255,0.025) 0, rgba(255,255,255,0.025) 1px, transparent 1px, transparent 6px);
|
|
701773
|
+
mix-blend-mode: screen;
|
|
701774
|
+
opacity: 0.32;
|
|
701775
|
+
}
|
|
701776
|
+
|
|
701777
|
+
.hud-panel {
|
|
701778
|
+
position: relative;
|
|
701779
|
+
border: 1px solid var(--hud-steel);
|
|
701780
|
+
clip-path: var(--hud-panel-clip);
|
|
701781
|
+
background: rgba(10, 15, 18, 0.72);
|
|
701782
|
+
box-shadow: var(--hud-panel-shadow);
|
|
701783
|
+
overflow: hidden;
|
|
701784
|
+
}
|
|
701785
|
+
.hud-panel__chrome {
|
|
701786
|
+
position: absolute;
|
|
701787
|
+
inset: 0;
|
|
701788
|
+
pointer-events: none;
|
|
701789
|
+
z-index: 0;
|
|
701790
|
+
}
|
|
701791
|
+
.hud-panel__chrome::before {
|
|
701792
|
+
content: "";
|
|
701793
|
+
position: absolute;
|
|
701794
|
+
inset: 0;
|
|
701795
|
+
background:
|
|
701796
|
+
linear-gradient(rgba(183,211,216,0.055) 1px, transparent 1px),
|
|
701797
|
+
linear-gradient(90deg, rgba(183,211,216,0.045) 1px, transparent 1px);
|
|
701798
|
+
background-size: 18px 18px;
|
|
701799
|
+
opacity: 0.38;
|
|
701800
|
+
}
|
|
701801
|
+
.hud-panel__chrome::after {
|
|
701802
|
+
content: "";
|
|
701803
|
+
position: absolute;
|
|
701804
|
+
inset: 1px;
|
|
701805
|
+
border: 1px solid rgba(217,223,223,0.08);
|
|
701806
|
+
clip-path: var(--hud-panel-clip);
|
|
701807
|
+
}
|
|
701808
|
+
.hud-panel > :not(.hud-panel__chrome) {
|
|
701809
|
+
position: relative;
|
|
701810
|
+
z-index: 1;
|
|
701811
|
+
}
|
|
701812
|
+
.hud-panel--cut::before,
|
|
701813
|
+
.hud-panel--slant::before {
|
|
701814
|
+
content: "";
|
|
701815
|
+
position: absolute;
|
|
701816
|
+
top: 0;
|
|
701817
|
+
right: 0;
|
|
701818
|
+
width: 31px;
|
|
701819
|
+
border-top: 1px solid var(--hud-steel-2);
|
|
701820
|
+
transform: rotate(45deg);
|
|
701821
|
+
transform-origin: top right;
|
|
701822
|
+
z-index: 2;
|
|
701823
|
+
}
|
|
701824
|
+
.hud-panel--cut::after,
|
|
701825
|
+
.hud-panel--slant::after {
|
|
701826
|
+
content: "";
|
|
701827
|
+
position: absolute;
|
|
701828
|
+
left: 0;
|
|
701829
|
+
bottom: 0;
|
|
701830
|
+
width: 26px;
|
|
701831
|
+
border-top: 1px solid var(--hud-steel-2);
|
|
701832
|
+
transform: rotate(45deg);
|
|
701833
|
+
transform-origin: bottom left;
|
|
701834
|
+
z-index: 2;
|
|
701835
|
+
}
|
|
701836
|
+
.hud-panel__header {
|
|
701837
|
+
display: flex;
|
|
701838
|
+
align-items: center;
|
|
701839
|
+
justify-content: space-between;
|
|
701840
|
+
gap: 12px;
|
|
701841
|
+
padding: 12px 14px 8px;
|
|
701842
|
+
border-bottom: 1px solid rgba(72,84,90,0.64);
|
|
701843
|
+
}
|
|
701844
|
+
.hud-panel__body {
|
|
701845
|
+
padding: 12px 14px 14px;
|
|
701846
|
+
}
|
|
701847
|
+
.kicker,
|
|
701848
|
+
.hud-kicker {
|
|
701849
|
+
color: var(--hud-muted);
|
|
701850
|
+
font-family: var(--font-mono);
|
|
701851
|
+
font-size: 0.56rem;
|
|
701852
|
+
letter-spacing: 0.17em;
|
|
701853
|
+
text-transform: uppercase;
|
|
701854
|
+
}
|
|
701855
|
+
.hud-title {
|
|
701856
|
+
color: var(--hud-ink);
|
|
701857
|
+
font-family: var(--font-display);
|
|
701858
|
+
font-size: clamp(1.15rem, 2.2vw, 2rem);
|
|
701859
|
+
line-height: 0.95;
|
|
701860
|
+
letter-spacing: 0.08em;
|
|
701861
|
+
text-transform: uppercase;
|
|
701862
|
+
}
|
|
701863
|
+
.hud-subtitle {
|
|
701864
|
+
color: var(--hud-muted);
|
|
701865
|
+
font-family: var(--font-mono);
|
|
701866
|
+
font-size: 0.68rem;
|
|
701867
|
+
line-height: 1.45;
|
|
701868
|
+
}
|
|
701869
|
+
.hud-btn,
|
|
701870
|
+
.hud-tab {
|
|
701871
|
+
--hud-btn-border: var(--hud-steel);
|
|
701872
|
+
position: relative;
|
|
701873
|
+
display: inline-flex;
|
|
701874
|
+
align-items: center;
|
|
701875
|
+
justify-content: center;
|
|
701876
|
+
gap: 8px;
|
|
701877
|
+
min-height: 30px;
|
|
701878
|
+
border: 1px solid var(--hud-btn-border);
|
|
701879
|
+
clip-path: var(--hud-button-clip);
|
|
701880
|
+
background: rgba(18,27,31,0.74);
|
|
701881
|
+
color: var(--hud-ink-soft);
|
|
701882
|
+
font-family: var(--font-mono);
|
|
701883
|
+
font-size: 0.64rem;
|
|
701884
|
+
letter-spacing: 0.08em;
|
|
701885
|
+
text-transform: uppercase;
|
|
701886
|
+
cursor: pointer;
|
|
701887
|
+
overflow: hidden;
|
|
701888
|
+
}
|
|
701889
|
+
.hud-btn__chrome,
|
|
701890
|
+
.hud-tab__chrome {
|
|
701891
|
+
position: absolute;
|
|
701892
|
+
inset: 0;
|
|
701893
|
+
pointer-events: none;
|
|
701894
|
+
}
|
|
701895
|
+
.hud-btn__chrome::before,
|
|
701896
|
+
.hud-tab__chrome::before {
|
|
701897
|
+
content: "";
|
|
701898
|
+
position: absolute;
|
|
701899
|
+
top: 0;
|
|
701900
|
+
right: 0;
|
|
701901
|
+
width: 17px;
|
|
701902
|
+
border-top: 1px solid var(--hud-btn-border);
|
|
701903
|
+
transform: rotate(45deg);
|
|
701904
|
+
transform-origin: top right;
|
|
701905
|
+
}
|
|
701906
|
+
.hud-btn__chrome::after,
|
|
701907
|
+
.hud-tab__chrome::after {
|
|
701908
|
+
content: "";
|
|
701909
|
+
position: absolute;
|
|
701910
|
+
left: 0;
|
|
701911
|
+
bottom: 0;
|
|
701912
|
+
width: 17px;
|
|
701913
|
+
border-top: 1px solid var(--hud-btn-border);
|
|
701914
|
+
transform: rotate(45deg);
|
|
701915
|
+
transform-origin: bottom left;
|
|
701916
|
+
}
|
|
701917
|
+
.hud-btn__label,
|
|
701918
|
+
.hud-tab__label {
|
|
701919
|
+
position: relative;
|
|
701920
|
+
z-index: 1;
|
|
701921
|
+
white-space: nowrap;
|
|
701922
|
+
}
|
|
701923
|
+
.hud-btn:hover,
|
|
701924
|
+
.hud-tab:hover {
|
|
701925
|
+
--hud-btn-border: var(--hud-cyan);
|
|
701926
|
+
color: var(--hud-ink);
|
|
701927
|
+
box-shadow: var(--hud-cyan-glow);
|
|
701928
|
+
}
|
|
701929
|
+
.hud-btn--primary,
|
|
701930
|
+
.hud-tab[aria-selected="true"],
|
|
701931
|
+
.hud-tab.active {
|
|
701932
|
+
--hud-btn-border: var(--hud-accent);
|
|
701933
|
+
color: #081014;
|
|
701934
|
+
background: linear-gradient(180deg, var(--hud-accent-strong), var(--hud-accent));
|
|
701935
|
+
box-shadow: var(--hud-amber-glow);
|
|
701936
|
+
}
|
|
701937
|
+
.hud-btn--ghost {
|
|
701938
|
+
background: rgba(10,15,18,0.58);
|
|
701939
|
+
}
|
|
701940
|
+
.hud-btn--danger {
|
|
701941
|
+
--hud-btn-border: var(--hud-danger);
|
|
701942
|
+
color: var(--hud-danger);
|
|
701943
|
+
}
|
|
701944
|
+
|
|
701945
|
+
#omnius-shell {
|
|
701946
|
+
background: transparent !important;
|
|
701947
|
+
}
|
|
701948
|
+
#omnius-sidebar {
|
|
701949
|
+
background: rgba(8,13,16,0.88) !important;
|
|
701950
|
+
border-right: 1px solid var(--hud-steel-2) !important;
|
|
701951
|
+
box-shadow: 18px 0 48px rgba(0,0,0,0.38);
|
|
701952
|
+
}
|
|
701953
|
+
#omnius-sidebar::before {
|
|
701954
|
+
content: "";
|
|
701955
|
+
position: absolute;
|
|
701956
|
+
inset: 0;
|
|
701957
|
+
pointer-events: none;
|
|
701958
|
+
background:
|
|
701959
|
+
linear-gradient(rgba(183,211,216,0.04) 1px, transparent 1px),
|
|
701960
|
+
linear-gradient(90deg, rgba(183,211,216,0.035) 1px, transparent 1px);
|
|
701961
|
+
background-size: 18px 18px;
|
|
701962
|
+
opacity: 0.48;
|
|
701963
|
+
}
|
|
701964
|
+
#omnius-main {
|
|
701965
|
+
background: rgba(6,10,12,0.62) !important;
|
|
701966
|
+
}
|
|
701967
|
+
#header {
|
|
701968
|
+
min-height: 46px;
|
|
701969
|
+
background: rgba(10,15,18,0.84) !important;
|
|
701970
|
+
border-bottom: 1px solid var(--hud-steel-2) !important;
|
|
701971
|
+
box-shadow: inset 0 -1px 0 rgba(183,211,216,0.08);
|
|
701972
|
+
}
|
|
701973
|
+
#header .accent,
|
|
701974
|
+
#sidebar-brand {
|
|
701975
|
+
font-family: var(--font-display) !important;
|
|
701976
|
+
letter-spacing: 0.08em !important;
|
|
701977
|
+
text-transform: uppercase;
|
|
701978
|
+
}
|
|
701979
|
+
.sb-nav {
|
|
701980
|
+
border-radius: 0 !important;
|
|
701981
|
+
clip-path: var(--hud-button-clip);
|
|
701982
|
+
font-family: var(--font-mono) !important;
|
|
701983
|
+
letter-spacing: 0.08em;
|
|
701984
|
+
text-transform: uppercase;
|
|
701985
|
+
}
|
|
701986
|
+
.sb-nav.active {
|
|
701987
|
+
background: rgba(210,154,58,0.18) !important;
|
|
701988
|
+
border-color: var(--hud-accent) !important;
|
|
701989
|
+
color: var(--hud-accent-strong) !important;
|
|
701990
|
+
box-shadow: inset 2px 0 0 var(--hud-accent);
|
|
701991
|
+
}
|
|
701992
|
+
.sb-chat,
|
|
701993
|
+
.sb-folder-row,
|
|
701994
|
+
#sidebar-search,
|
|
701995
|
+
#input-area,
|
|
701996
|
+
textarea,
|
|
701997
|
+
input,
|
|
701998
|
+
select {
|
|
701999
|
+
border-radius: 0 !important;
|
|
702000
|
+
}
|
|
702001
|
+
input,
|
|
702002
|
+
textarea,
|
|
702003
|
+
select {
|
|
702004
|
+
background: rgba(6,10,12,0.72) !important;
|
|
702005
|
+
border-color: var(--hud-steel) !important;
|
|
702006
|
+
color: var(--hud-ink) !important;
|
|
702007
|
+
font-family: var(--font-mono);
|
|
702008
|
+
}
|
|
702009
|
+
input:focus,
|
|
702010
|
+
textarea:focus,
|
|
702011
|
+
select:focus {
|
|
702012
|
+
border-color: var(--hud-cyan) !important;
|
|
702013
|
+
box-shadow: var(--hud-cyan-glow);
|
|
702014
|
+
}
|
|
702015
|
+
button {
|
|
702016
|
+
border-radius: 0 !important;
|
|
702017
|
+
}
|
|
702018
|
+
#chat-container,
|
|
702019
|
+
#agent-panel,
|
|
702020
|
+
#jobs-panel,
|
|
702021
|
+
#config-panel,
|
|
702022
|
+
#activity-panel,
|
|
702023
|
+
#projects-panel,
|
|
702024
|
+
#voice-panel,
|
|
702025
|
+
#generate-panel {
|
|
702026
|
+
background:
|
|
702027
|
+
linear-gradient(rgba(183,211,216,0.032) 1px, transparent 1px),
|
|
702028
|
+
linear-gradient(90deg, rgba(183,211,216,0.028) 1px, transparent 1px) !important;
|
|
702029
|
+
background-size: 32px 32px !important;
|
|
702030
|
+
}
|
|
702031
|
+
.session-topbar,
|
|
702032
|
+
#footer,
|
|
702033
|
+
#input-toolbar,
|
|
702034
|
+
#input-row,
|
|
702035
|
+
#processes-row,
|
|
702036
|
+
#tasks-row {
|
|
702037
|
+
background: rgba(8,13,16,0.88) !important;
|
|
702038
|
+
border-color: var(--hud-steel) !important;
|
|
702039
|
+
}
|
|
702040
|
+
.msg.assistant::before {
|
|
702041
|
+
border-radius: 0 !important;
|
|
702042
|
+
clip-path: var(--hud-button-clip);
|
|
702043
|
+
background: rgba(210,154,58,0.16) !important;
|
|
702044
|
+
border: 1px solid var(--hud-accent);
|
|
702045
|
+
color: var(--hud-accent-strong) !important;
|
|
702046
|
+
}
|
|
702047
|
+
.msg.user > *,
|
|
702048
|
+
.msg.system > *,
|
|
702049
|
+
.msg .tool-card,
|
|
702050
|
+
.msg pre,
|
|
702051
|
+
.omnius-modal-window,
|
|
702052
|
+
.settings-provider-module,
|
|
702053
|
+
.settings-provider-setup {
|
|
702054
|
+
border-radius: 0 !important;
|
|
702055
|
+
border-color: var(--hud-steel) !important;
|
|
702056
|
+
background: rgba(10,15,18,0.72) !important;
|
|
702057
|
+
}
|
|
702058
|
+
|
|
702059
|
+
.hud-workbench {
|
|
702060
|
+
display: grid;
|
|
702061
|
+
grid-template-columns: minmax(190px, 240px) minmax(0, 1fr) minmax(260px, 360px);
|
|
702062
|
+
gap: 12px;
|
|
702063
|
+
min-height: 100%;
|
|
702064
|
+
}
|
|
702065
|
+
.hud-rail {
|
|
702066
|
+
display: flex;
|
|
702067
|
+
flex-direction: column;
|
|
702068
|
+
gap: 8px;
|
|
702069
|
+
}
|
|
702070
|
+
.hud-rail-group {
|
|
702071
|
+
display: grid;
|
|
702072
|
+
gap: 6px;
|
|
702073
|
+
}
|
|
702074
|
+
.hud-rail-label {
|
|
702075
|
+
padding: 0 2px;
|
|
702076
|
+
color: var(--hud-dim);
|
|
702077
|
+
font-family: var(--font-mono);
|
|
702078
|
+
font-size: 0.55rem;
|
|
702079
|
+
letter-spacing: 0.17em;
|
|
702080
|
+
text-transform: uppercase;
|
|
702081
|
+
}
|
|
702082
|
+
.hud-field {
|
|
702083
|
+
display: grid;
|
|
702084
|
+
gap: 4px;
|
|
702085
|
+
margin-bottom: 10px;
|
|
702086
|
+
}
|
|
702087
|
+
.hud-field label {
|
|
702088
|
+
color: var(--hud-muted);
|
|
702089
|
+
font-family: var(--font-mono);
|
|
702090
|
+
font-size: 0.58rem;
|
|
702091
|
+
letter-spacing: 0.12em;
|
|
702092
|
+
text-transform: uppercase;
|
|
702093
|
+
}
|
|
702094
|
+
.hud-form-grid {
|
|
702095
|
+
display: grid;
|
|
702096
|
+
grid-template-columns: repeat(3, minmax(90px, 1fr));
|
|
702097
|
+
gap: 8px;
|
|
702098
|
+
}
|
|
702099
|
+
.hud-status-line {
|
|
702100
|
+
min-height: 1.2em;
|
|
702101
|
+
color: var(--hud-muted);
|
|
702102
|
+
font-family: var(--font-mono);
|
|
702103
|
+
font-size: 0.64rem;
|
|
702104
|
+
line-height: 1.45;
|
|
702105
|
+
}
|
|
702106
|
+
.hud-meter {
|
|
702107
|
+
height: 8px;
|
|
702108
|
+
background: rgba(6,10,12,0.9);
|
|
702109
|
+
border: 1px solid var(--hud-steel);
|
|
702110
|
+
overflow: hidden;
|
|
702111
|
+
}
|
|
702112
|
+
.hud-meter > div {
|
|
702113
|
+
height: 100%;
|
|
702114
|
+
width: 0%;
|
|
702115
|
+
background: linear-gradient(90deg, var(--hud-accent), var(--hud-cyan));
|
|
702116
|
+
transition: width 0.2s;
|
|
702117
|
+
}
|
|
702118
|
+
.hud-result {
|
|
702119
|
+
display: grid;
|
|
702120
|
+
gap: 8px;
|
|
702121
|
+
color: var(--hud-ink-soft);
|
|
702122
|
+
font-family: var(--font-mono);
|
|
702123
|
+
font-size: 0.64rem;
|
|
702124
|
+
line-height: 1.45;
|
|
702125
|
+
}
|
|
702126
|
+
.hud-result-card {
|
|
702127
|
+
border: 1px solid rgba(72,84,90,0.78);
|
|
702128
|
+
background: rgba(6,10,12,0.68);
|
|
702129
|
+
padding: 8px;
|
|
702130
|
+
}
|
|
702131
|
+
.hud-result-card strong {
|
|
702132
|
+
display: block;
|
|
702133
|
+
margin-bottom: 4px;
|
|
702134
|
+
color: var(--hud-accent-strong);
|
|
702135
|
+
font-family: var(--font-display);
|
|
702136
|
+
font-size: 0.9rem;
|
|
702137
|
+
letter-spacing: 0.08em;
|
|
702138
|
+
text-transform: uppercase;
|
|
702139
|
+
}
|
|
702140
|
+
.hud-chip-row {
|
|
702141
|
+
display: flex;
|
|
702142
|
+
flex-wrap: wrap;
|
|
702143
|
+
gap: 5px;
|
|
702144
|
+
}
|
|
702145
|
+
.hud-chip {
|
|
702146
|
+
border: 1px solid var(--hud-steel);
|
|
702147
|
+
background: rgba(6,10,12,0.62);
|
|
702148
|
+
color: var(--hud-ink-soft);
|
|
702149
|
+
padding: 3px 6px;
|
|
702150
|
+
font-family: var(--font-mono);
|
|
702151
|
+
font-size: 0.56rem;
|
|
702152
|
+
letter-spacing: 0.08em;
|
|
702153
|
+
text-transform: uppercase;
|
|
702154
|
+
}
|
|
702155
|
+
.hud-chip.live { border-color: var(--hud-success); color: var(--hud-success); }
|
|
702156
|
+
.hud-chip.mock { border-color: var(--hud-danger); color: var(--hud-danger); }
|
|
702157
|
+
.hud-gallery-grid {
|
|
702158
|
+
display: grid;
|
|
702159
|
+
grid-template-columns: repeat(auto-fill,minmax(128px,1fr));
|
|
702160
|
+
gap: 8px;
|
|
702161
|
+
}
|
|
702162
|
+
.hud-scroll {
|
|
702163
|
+
max-height: 100%;
|
|
702164
|
+
overflow: auto;
|
|
702165
|
+
}
|
|
702166
|
+
|
|
702167
|
+
@media (max-width: 1180px) {
|
|
702168
|
+
.hud-workbench { grid-template-columns: 190px minmax(0, 1fr); }
|
|
702169
|
+
.hud-workbench > .hud-side-output { grid-column: 1 / -1; }
|
|
702170
|
+
}
|
|
702171
|
+
@media (max-width: 820px) {
|
|
702172
|
+
.hud-workbench { grid-template-columns: 1fr; }
|
|
702173
|
+
.hud-form-grid { grid-template-columns: 1fr; }
|
|
702174
|
+
}
|
|
701449
702175
|
</style>
|
|
701450
702176
|
</head>
|
|
701451
702177
|
<body>
|
|
@@ -701771,94 +702497,162 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
701771
702497
|
the unified ~/.omnius model store. Loads models from /v1/media/models,
|
|
701772
702498
|
generates via /v1/media/<kind>, and browses the global gallery. -->
|
|
701773
702499
|
<div id="generate-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
|
|
701774
|
-
<div
|
|
701775
|
-
<
|
|
701776
|
-
|
|
701777
|
-
|
|
701778
|
-
|
|
701779
|
-
|
|
701780
|
-
|
|
701781
|
-
|
|
701782
|
-
|
|
701783
|
-
|
|
701784
|
-
|
|
701785
|
-
|
|
701786
|
-
|
|
701787
|
-
|
|
701788
|
-
|
|
701789
|
-
|
|
701790
|
-
|
|
701791
|
-
<
|
|
701792
|
-
|
|
701793
|
-
|
|
702500
|
+
<div class="hud-workbench">
|
|
702501
|
+
<aside class="hud-panel hud-panel--slant hud-rail" id="gen-mode-rail">
|
|
702502
|
+
<div class="hud-panel__chrome" aria-hidden="true"></div>
|
|
702503
|
+
<header class="hud-panel__header">
|
|
702504
|
+
<div>
|
|
702505
|
+
<div class="kicker">Media Operations</div>
|
|
702506
|
+
<h2 class="hud-title">Generate</h2>
|
|
702507
|
+
</div>
|
|
702508
|
+
</header>
|
|
702509
|
+
<div class="hud-panel__body hud-rail">
|
|
702510
|
+
<div class="hud-rail-group" id="gen-kind-row">
|
|
702511
|
+
<div class="hud-rail-label">Synthesis</div>
|
|
702512
|
+
<button class="hud-tab gen-kind gen-mode-tab active" aria-selected="true" data-kind="image" data-gen-mode="image" onclick="genSelectMode('image')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Image</span></button>
|
|
702513
|
+
<button class="hud-tab gen-kind gen-mode-tab" aria-selected="false" data-kind="video" data-gen-mode="video" onclick="genSelectMode('video')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Video</span></button>
|
|
702514
|
+
<button class="hud-tab gen-kind gen-mode-tab" aria-selected="false" data-kind="audio" data-gen-mode="audio" onclick="genSelectMode('audio')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Audio</span></button>
|
|
702515
|
+
<button class="hud-tab gen-kind gen-mode-tab" aria-selected="false" data-kind="music" data-gen-mode="music" onclick="genSelectMode('music')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Music</span></button>
|
|
702516
|
+
</div>
|
|
702517
|
+
<div class="hud-rail-group">
|
|
702518
|
+
<div class="hud-rail-label">Analysis</div>
|
|
702519
|
+
<button class="hud-tab gen-mode-tab" aria-selected="false" data-gen-mode="audio-analysis" onclick="genSelectMode('audio-analysis')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Audio Analysis</span></button>
|
|
702520
|
+
<button class="hud-tab gen-mode-tab" aria-selected="false" data-gen-mode="video-analysis" onclick="genSelectMode('video-analysis')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Video Analysis</span></button>
|
|
702521
|
+
<button class="hud-tab gen-mode-tab" aria-selected="false" data-gen-mode="live-analysis" onclick="genSelectMode('live-analysis')"><span class="hud-tab__chrome" aria-hidden="true"></span><span class="hud-tab__label">Live AV Analysis</span></button>
|
|
702522
|
+
</div>
|
|
702523
|
+
<div class="hud-result-card">
|
|
702524
|
+
<strong>Store</strong>
|
|
702525
|
+
<div class="hud-status-line"><code id="gen-store-path" style="color:var(--hud-cyan);word-break:break-all">~/.omnius</code></div>
|
|
702526
|
+
<div class="hud-status-line" id="gen-store-disk"></div>
|
|
702527
|
+
<div class="hud-status-line" id="gen-store-info"></div>
|
|
702528
|
+
<button class="hud-btn hud-btn--ghost" onclick="toggleMediaLocationPanel()" style="width:100%;margin-top:8px"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Change Location</span></button>
|
|
702529
|
+
<div id="gen-relocate-panel" style="display:none;margin-top:10px;border-top:1px solid var(--hud-steel);padding-top:10px">
|
|
702530
|
+
<div class="hud-status-line">Move model weights, venvs, and gallery. DBs/sessions stay in ~/.omnius.</div>
|
|
702531
|
+
<div id="gen-folder-browser" style="max-height:180px;overflow:auto;border:1px solid var(--hud-steel);padding:5px;font-size:0.64rem;margin:6px 0;font-family:var(--font-mono)"></div>
|
|
702532
|
+
<div class="hud-field">
|
|
702533
|
+
<input id="gen-relocate-target" type="text" placeholder="/absolute/folder" style="width:100%;padding:6px 8px;font-size:0.64rem">
|
|
702534
|
+
</div>
|
|
702535
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
|
702536
|
+
<button class="hud-btn hud-btn--ghost" onclick="previewMediaRelocate()"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Preview</span></button>
|
|
702537
|
+
<button class="hud-btn hud-btn--primary" onclick="startMediaRelocate()"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Migrate</span></button>
|
|
702538
|
+
</div>
|
|
702539
|
+
<div id="gen-relocate-status" class="hud-status-line" style="margin-top:6px"></div>
|
|
702540
|
+
<div id="gen-relocate-bar-wrap" class="hud-meter" style="display:none;margin-top:6px"><div id="gen-relocate-bar"></div></div>
|
|
702541
|
+
</div>
|
|
702542
|
+
</div>
|
|
701794
702543
|
</div>
|
|
701795
|
-
|
|
701796
|
-
|
|
701797
|
-
|
|
702544
|
+
</aside>
|
|
702545
|
+
|
|
702546
|
+
<main class="hud-rail">
|
|
702547
|
+
<section id="gen-create-panel" class="hud-panel hud-panel--cut">
|
|
702548
|
+
<div class="hud-panel__chrome" aria-hidden="true"></div>
|
|
702549
|
+
<header class="hud-panel__header">
|
|
702550
|
+
<div>
|
|
702551
|
+
<div class="kicker">Synthesis Console</div>
|
|
702552
|
+
<h2 class="hud-title" id="gen-mode-title">Image Generator</h2>
|
|
702553
|
+
</div>
|
|
702554
|
+
<button id="gen-run-btn" class="hud-btn hud-btn--primary" onclick="generateMedia()"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Generate</span></button>
|
|
702555
|
+
</header>
|
|
702556
|
+
<div class="hud-panel__body">
|
|
702557
|
+
<div class="hud-field">
|
|
702558
|
+
<label for="gen-model">Model</label>
|
|
702559
|
+
<select id="gen-model" style="width:100%;padding:7px 9px;font-size:0.68rem">
|
|
702560
|
+
<option value="auto">auto (recommended)</option>
|
|
702561
|
+
</select>
|
|
702562
|
+
</div>
|
|
702563
|
+
<div class="hud-field">
|
|
702564
|
+
<label for="gen-prompt">Prompt</label>
|
|
702565
|
+
<textarea id="gen-prompt" rows="5" placeholder="Describe what to generate..." style="width:100%;padding:8px 10px;font-size:0.74rem;resize:vertical"></textarea>
|
|
702566
|
+
</div>
|
|
702567
|
+
<div class="hud-form-grid">
|
|
702568
|
+
<div class="hud-field"><label for="gen-seed">Seed</label><input id="gen-seed" type="number" placeholder="random" style="width:100%;padding:6px 8px;font-size:0.66rem"></div>
|
|
702569
|
+
<div id="gen-steps-wrap" class="hud-field"><label for="gen-steps">Steps</label><input id="gen-steps" type="number" placeholder="model default" style="width:100%;padding:6px 8px;font-size:0.66rem"></div>
|
|
702570
|
+
<div id="gen-duration-wrap" class="hud-field" style="display:none"><label for="gen-duration">Duration s</label><input id="gen-duration" type="number" placeholder="model default" style="width:100%;padding:6px 8px;font-size:0.66rem"></div>
|
|
702571
|
+
</div>
|
|
702572
|
+
<div id="gen-status" class="hud-status-line" style="margin-top:4px"></div>
|
|
702573
|
+
<div id="gen-progress-wrap" style="display:none;margin-top:8px">
|
|
702574
|
+
<div class="hud-status-line"><span id="gen-progress-stage">starting...</span> <span id="gen-progress-msg"></span></div>
|
|
702575
|
+
<div class="hud-meter" style="margin-top:4px"><div id="gen-progress-bar"></div></div>
|
|
702576
|
+
</div>
|
|
702577
|
+
</div>
|
|
702578
|
+
</section>
|
|
702579
|
+
|
|
702580
|
+
<section id="gen-analysis-panel" class="hud-panel hud-panel--cut" style="display:none">
|
|
702581
|
+
<div class="hud-panel__chrome" aria-hidden="true"></div>
|
|
702582
|
+
<header class="hud-panel__header">
|
|
702583
|
+
<div>
|
|
702584
|
+
<div class="kicker">Perception Console</div>
|
|
702585
|
+
<h2 class="hud-title" id="av-mode-title">Audio Analysis</h2>
|
|
702586
|
+
</div>
|
|
702587
|
+
<button id="av-run-btn" class="hud-btn hud-btn--primary" onclick="analyzeAvMedia()"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Analyze</span></button>
|
|
702588
|
+
</header>
|
|
702589
|
+
<div class="hud-panel__body">
|
|
702590
|
+
<div class="hud-field">
|
|
702591
|
+
<label for="av-source">Source path or file URI</label>
|
|
702592
|
+
<input id="av-source" type="text" placeholder="/path/to/audio-or-video.mp4 or file:///path/to/media.wav" style="width:100%;padding:7px 9px;font-size:0.68rem">
|
|
702593
|
+
</div>
|
|
702594
|
+
<div class="hud-field">
|
|
702595
|
+
<label for="av-question">Question</label>
|
|
702596
|
+
<textarea id="av-question" rows="4" placeholder="What is happening? Who or what appears? What audio events matter?" style="width:100%;padding:8px 10px;font-size:0.72rem;resize:vertical"></textarea>
|
|
702597
|
+
</div>
|
|
702598
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
702599
|
+
<button class="hud-btn hud-btn--ghost" onclick="analyzeAvMedia('audio-analysis')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Audio Pass</span></button>
|
|
702600
|
+
<button class="hud-btn hud-btn--ghost" onclick="analyzeAvMedia('video-analysis')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Video Pass</span></button>
|
|
702601
|
+
<button class="hud-btn hud-btn--ghost" onclick="analyzeAvMedia('live-analysis')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Full AV Pass</span></button>
|
|
702602
|
+
</div>
|
|
702603
|
+
<div id="av-status" class="hud-status-line" style="margin-top:8px"></div>
|
|
702604
|
+
<div id="av-result" class="hud-result" style="margin-top:10px"></div>
|
|
702605
|
+
</div>
|
|
702606
|
+
</section>
|
|
702607
|
+
|
|
702608
|
+
<section class="hud-panel hud-panel--cut">
|
|
702609
|
+
<div class="hud-panel__chrome" aria-hidden="true"></div>
|
|
702610
|
+
<header class="hud-panel__header">
|
|
702611
|
+
<div>
|
|
702612
|
+
<div class="kicker">Media Archive</div>
|
|
702613
|
+
<h2 class="hud-title">Gallery</h2>
|
|
702614
|
+
</div>
|
|
702615
|
+
<button class="hud-btn hud-btn--ghost" onclick="loadGenerateGallery()"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Refresh</span></button>
|
|
702616
|
+
</header>
|
|
702617
|
+
<div class="hud-panel__body">
|
|
702618
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:8px;font-size:0.6rem">
|
|
702619
|
+
<div id="gen-filter-kinds" style="display:flex;gap:4px;flex-wrap:wrap">
|
|
702620
|
+
<button class="hud-btn hud-btn--primary gen-filter-kind active" data-fkind="" onclick="setGalleryKindFilter('')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">All</span></button>
|
|
702621
|
+
<button class="hud-btn hud-btn--ghost gen-filter-kind" data-fkind="image" onclick="setGalleryKindFilter('image')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Image</span></button>
|
|
702622
|
+
<button class="hud-btn hud-btn--ghost gen-filter-kind" data-fkind="video" onclick="setGalleryKindFilter('video')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Video</span></button>
|
|
702623
|
+
<button class="hud-btn hud-btn--ghost gen-filter-kind" data-fkind="audio" onclick="setGalleryKindFilter('audio')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Audio</span></button>
|
|
702624
|
+
<button class="hud-btn hud-btn--ghost gen-filter-kind" data-fkind="music" onclick="setGalleryKindFilter('music')"><span class="hud-btn__chrome" aria-hidden="true"></span><span class="hud-btn__label">Music</span></button>
|
|
702625
|
+
</div>
|
|
702626
|
+
<input id="gen-filter-q" type="text" placeholder="search prompt..." oninput="scheduleGalleryReload()" style="flex:1;min-width:140px;padding:5px 8px;font-size:0.6rem">
|
|
702627
|
+
<input id="gen-filter-model" type="text" placeholder="model..." oninput="scheduleGalleryReload()" style="width:110px;padding:5px 8px;font-size:0.6rem">
|
|
702628
|
+
<label class="hud-subtitle">from <input id="gen-filter-from" type="date" onchange="scheduleGalleryReload()" style="padding:4px 6px;font-size:0.6rem"></label>
|
|
702629
|
+
<label class="hud-subtitle">to <input id="gen-filter-to" type="date" onchange="scheduleGalleryReload()" style="padding:4px 6px;font-size:0.6rem"></label>
|
|
702630
|
+
<select id="gen-filter-sort" onchange="scheduleGalleryReload()" style="padding:4px 6px;font-size:0.6rem">
|
|
702631
|
+
<option value="new">Newest</option><option value="old">Oldest</option><option value="big">Largest</option><option value="small">Smallest</option>
|
|
702632
|
+
</select>
|
|
702633
|
+
<span id="gen-gallery-count" class="hud-status-line"></span>
|
|
702634
|
+
</div>
|
|
702635
|
+
<div id="gen-gallery" class="hud-gallery-grid" style="font-size:0.62rem"></div>
|
|
702636
|
+
<div id="gen-gallery-more" style="text-align:center;margin-top:10px"></div>
|
|
702637
|
+
</div>
|
|
702638
|
+
</section>
|
|
702639
|
+
</main>
|
|
702640
|
+
|
|
702641
|
+
<aside class="hud-panel hud-panel--slant hud-side-output">
|
|
702642
|
+
<div class="hud-panel__chrome" aria-hidden="true"></div>
|
|
702643
|
+
<header class="hud-panel__header">
|
|
702644
|
+
<div>
|
|
702645
|
+
<div class="kicker">Evidence Output</div>
|
|
702646
|
+
<h2 class="hud-title">Signal</h2>
|
|
702647
|
+
</div>
|
|
702648
|
+
</header>
|
|
702649
|
+
<div class="hud-panel__body">
|
|
702650
|
+
<div id="gen-analysis-summary" class="hud-result">
|
|
702651
|
+
<div class="hud-result-card"><strong>Ready</strong><div>Generate new media or select Audio Analysis / Video Analysis from the left rail.</div></div>
|
|
702652
|
+
</div>
|
|
701798
702653
|
</div>
|
|
701799
|
-
</
|
|
701800
|
-
</div>
|
|
701801
|
-
|
|
701802
|
-
<!-- Kind selector -->
|
|
701803
|
-
<div id="gen-kind-row" style="display:flex;gap:6px;margin-bottom:10px">
|
|
701804
|
-
<button class="gen-kind active" data-kind="image" onclick="genSelectKind('image')" style="flex:1;background:var(--color-brand);border:1px solid var(--color-border);color:var(--color-accent-fg);padding:6px;border-radius:4px;font-size:0.66rem;cursor:pointer;font-family:inherit">Image</button>
|
|
701805
|
-
<button class="gen-kind" data-kind="video" onclick="genSelectKind('video')" style="flex:1;background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:6px;border-radius:4px;font-size:0.66rem;cursor:pointer;font-family:inherit">Video</button>
|
|
701806
|
-
<button class="gen-kind" data-kind="audio" onclick="genSelectKind('audio')" style="flex:1;background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:6px;border-radius:4px;font-size:0.66rem;cursor:pointer;font-family:inherit">Audio</button>
|
|
701807
|
-
<button class="gen-kind" data-kind="music" onclick="genSelectKind('music')" style="flex:1;background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:6px;border-radius:4px;font-size:0.66rem;cursor:pointer;font-family:inherit">Music</button>
|
|
701808
|
-
</div>
|
|
701809
|
-
|
|
701810
|
-
<!-- Model selector -->
|
|
701811
|
-
<label style="font-size:0.62rem;color:var(--color-fg-subtle);display:block;margin-bottom:3px">Model</label>
|
|
701812
|
-
<select id="gen-model" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:4px;font-size:0.66rem;width:100%;margin-bottom:10px;font-family:inherit">
|
|
701813
|
-
<option value="auto">auto (recommended)</option>
|
|
701814
|
-
</select>
|
|
701815
|
-
|
|
701816
|
-
<!-- Prompt -->
|
|
701817
|
-
<label style="font-size:0.62rem;color:var(--color-fg-subtle);display:block;margin-bottom:3px">Prompt</label>
|
|
701818
|
-
<textarea id="gen-prompt" rows="3" placeholder="Describe what to generate…" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:6px 8px;border-radius:4px;font-size:0.7rem;width:100%;margin-bottom:10px;font-family:inherit;resize:vertical"></textarea>
|
|
701819
|
-
|
|
701820
|
-
<!-- Optional params -->
|
|
701821
|
-
<div style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap">
|
|
701822
|
-
<div style="flex:1;min-width:90px"><label style="font-size:0.6rem;color:var(--color-fg-subtle);display:block;margin-bottom:2px">Seed (optional)</label><input id="gen-seed" type="number" placeholder="random" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:4px 8px;border-radius:4px;font-size:0.64rem;width:100%;font-family:inherit"></div>
|
|
701823
|
-
<div id="gen-steps-wrap" style="flex:1;min-width:90px"><label style="font-size:0.6rem;color:var(--color-fg-subtle);display:block;margin-bottom:2px">Steps (optional)</label><input id="gen-steps" type="number" placeholder="model default" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:4px 8px;border-radius:4px;font-size:0.64rem;width:100%;font-family:inherit"></div>
|
|
701824
|
-
<div id="gen-duration-wrap" style="flex:1;min-width:90px;display:none"><label style="font-size:0.6rem;color:var(--color-fg-subtle);display:block;margin-bottom:2px">Duration s (optional)</label><input id="gen-duration" type="number" placeholder="model default" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:4px 8px;border-radius:4px;font-size:0.64rem;width:100%;font-family:inherit"></div>
|
|
702654
|
+
</aside>
|
|
701825
702655
|
</div>
|
|
701826
|
-
|
|
701827
|
-
<button id="gen-run-btn" onclick="generateMedia()" style="background:var(--color-brand);border:none;color:var(--color-accent-fg);padding:8px 16px;border-radius:4px;font-size:0.72rem;font-weight:600;cursor:pointer;font-family:inherit;width:100%">Generate</button>
|
|
701828
|
-
<div id="gen-status" style="font-size:0.66rem;color:var(--color-fg-subtle);margin-top:8px;min-height:1em"></div>
|
|
701829
|
-
<!-- Live generation progress (download / load / generate) via SSE -->
|
|
701830
|
-
<div id="gen-progress-wrap" style="display:none;margin-top:8px">
|
|
701831
|
-
<div style="font-size:0.62rem;color:var(--color-fg-muted)"><span id="gen-progress-stage">starting…</span> <span id="gen-progress-msg" style="color:var(--color-fg-faint)"></span></div>
|
|
701832
|
-
<div style="margin-top:4px;height:8px;background:var(--color-bg);border:1px solid var(--color-border);border-radius:4px;overflow:hidden">
|
|
701833
|
-
<div id="gen-progress-bar" style="height:100%;width:0%;background:var(--color-brand);transition:width 0.2s"></div>
|
|
701834
|
-
</div>
|
|
701835
|
-
</div>
|
|
701836
|
-
|
|
701837
|
-
<!-- Gallery -->
|
|
701838
|
-
<div style="display:flex;justify-content:space-between;align-items:center;margin:18px 0 8px">
|
|
701839
|
-
<h3 style="color:var(--color-brand);font-size:0.7rem;margin:0">Gallery</h3>
|
|
701840
|
-
<button onclick="loadGenerateGallery()" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg-subtle);padding:3px 10px;border-radius:3px;font-size:0.62rem;cursor:pointer;font-family:inherit">↻ Refresh</button>
|
|
701841
|
-
</div>
|
|
701842
|
-
<!-- Gallery filter bar -->
|
|
701843
|
-
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:8px;font-size:0.6rem">
|
|
701844
|
-
<div id="gen-filter-kinds" style="display:flex;gap:4px">
|
|
701845
|
-
<button class="gen-filter-kind active" data-fkind="" onclick="setGalleryKindFilter('')" style="background:var(--color-brand);border:1px solid var(--color-border);color:var(--color-accent-fg);padding:3px 8px;border-radius:3px;cursor:pointer;font-family:inherit">All</button>
|
|
701846
|
-
<button class="gen-filter-kind" data-fkind="image" onclick="setGalleryKindFilter('image')" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;cursor:pointer;font-family:inherit">Image</button>
|
|
701847
|
-
<button class="gen-filter-kind" data-fkind="video" onclick="setGalleryKindFilter('video')" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;cursor:pointer;font-family:inherit">Video</button>
|
|
701848
|
-
<button class="gen-filter-kind" data-fkind="audio" onclick="setGalleryKindFilter('audio')" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;cursor:pointer;font-family:inherit">Audio</button>
|
|
701849
|
-
<button class="gen-filter-kind" data-fkind="music" onclick="setGalleryKindFilter('music')" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;cursor:pointer;font-family:inherit">Music</button>
|
|
701850
|
-
</div>
|
|
701851
|
-
<input id="gen-filter-q" type="text" placeholder="search prompt…" oninput="scheduleGalleryReload()" style="flex:1;min-width:120px;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;font-size:0.6rem;font-family:inherit">
|
|
701852
|
-
<input id="gen-filter-model" type="text" placeholder="model…" oninput="scheduleGalleryReload()" style="width:100px;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;font-size:0.6rem;font-family:inherit">
|
|
701853
|
-
<label style="color:var(--color-fg-faint)">from <input id="gen-filter-from" type="date" onchange="scheduleGalleryReload()" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:2px 4px;border-radius:3px;font-size:0.6rem;font-family:inherit"></label>
|
|
701854
|
-
<label style="color:var(--color-fg-faint)">to <input id="gen-filter-to" type="date" onchange="scheduleGalleryReload()" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:2px 4px;border-radius:3px;font-size:0.6rem;font-family:inherit"></label>
|
|
701855
|
-
<select id="gen-filter-sort" onchange="scheduleGalleryReload()" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:2px 6px;border-radius:3px;font-size:0.6rem;font-family:inherit">
|
|
701856
|
-
<option value="new">Newest</option><option value="old">Oldest</option><option value="big">Largest</option><option value="small">Smallest</option>
|
|
701857
|
-
</select>
|
|
701858
|
-
<span id="gen-gallery-count" style="color:var(--color-fg-faint)"></span>
|
|
701859
|
-
</div>
|
|
701860
|
-
<div id="gen-gallery" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:8px;font-size:0.62rem"></div>
|
|
701861
|
-
<div id="gen-gallery-more" style="text-align:center;margin-top:10px"></div>
|
|
701862
702656
|
</div>
|
|
701863
702657
|
<!-- Media detail modal -->
|
|
701864
702658
|
<div id="gen-detail-modal" class="omnius-modal" style="display:none" onclick="if(event.target===this)closeGenDetail()">
|
|
@@ -704667,6 +705461,7 @@ function routePathForTab(tab) {
|
|
|
704667
705461
|
chat: '/chat',
|
|
704668
705462
|
agent: '/agent',
|
|
704669
705463
|
voice: '/voice',
|
|
705464
|
+
generate: '/generate',
|
|
704670
705465
|
projects: '/projects',
|
|
704671
705466
|
jobs: '/dashboard',
|
|
704672
705467
|
activity: '/activity',
|
|
@@ -704681,6 +705476,7 @@ function tabForRoutePath(pathname) {
|
|
|
704681
705476
|
'/chat': 'chat',
|
|
704682
705477
|
'/agent': 'agent',
|
|
704683
705478
|
'/voice': 'voice',
|
|
705479
|
+
'/generate': 'generate',
|
|
704684
705480
|
'/projects': 'projects',
|
|
704685
705481
|
'/dashboard': 'jobs',
|
|
704686
705482
|
'/jobs': 'jobs',
|
|
@@ -704756,13 +705552,55 @@ window.switchTab = switchTab;
|
|
|
704756
705552
|
// /v1/media/file as authenticated blobs.
|
|
704757
705553
|
// ════════════════════════════════════════════════════════════
|
|
704758
705554
|
let genKind = 'image';
|
|
705555
|
+
let genMode = 'image';
|
|
704759
705556
|
let genModelsCache = null;
|
|
704760
705557
|
|
|
705558
|
+
function setHudControlLabel(el, text) {
|
|
705559
|
+
if (!el) return;
|
|
705560
|
+
const label = el.querySelector && el.querySelector('.hud-btn__label,.hud-tab__label');
|
|
705561
|
+
if (label) label.textContent = text;
|
|
705562
|
+
else el.textContent = text;
|
|
705563
|
+
}
|
|
705564
|
+
|
|
705565
|
+
function genSelectMode(mode) {
|
|
705566
|
+
genMode = mode || 'image';
|
|
705567
|
+
const analysis = genMode === 'audio-analysis' || genMode === 'video-analysis' || genMode === 'live-analysis';
|
|
705568
|
+
document.querySelectorAll('#gen-mode-rail .gen-mode-tab').forEach(b => {
|
|
705569
|
+
const active = b.getAttribute('data-gen-mode') === genMode;
|
|
705570
|
+
b.classList.toggle('active', active);
|
|
705571
|
+
b.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
705572
|
+
});
|
|
705573
|
+
const createPanel = document.getElementById('gen-create-panel');
|
|
705574
|
+
const analysisPanel = document.getElementById('gen-analysis-panel');
|
|
705575
|
+
if (createPanel) createPanel.style.display = analysis ? 'none' : 'block';
|
|
705576
|
+
if (analysisPanel) analysisPanel.style.display = analysis ? 'block' : 'none';
|
|
705577
|
+
const modeTitle = document.getElementById('gen-mode-title');
|
|
705578
|
+
const avTitle = document.getElementById('av-mode-title');
|
|
705579
|
+
const avBtn = document.getElementById('av-run-btn');
|
|
705580
|
+
if (!analysis) {
|
|
705581
|
+
genSelectKind(genMode);
|
|
705582
|
+
if (modeTitle) modeTitle.textContent = genMode.charAt(0).toUpperCase() + genMode.slice(1) + ' Generator';
|
|
705583
|
+
return;
|
|
705584
|
+
}
|
|
705585
|
+
const label = genMode === 'audio-analysis'
|
|
705586
|
+
? 'Audio Analysis'
|
|
705587
|
+
: genMode === 'video-analysis'
|
|
705588
|
+
? 'Video Analysis'
|
|
705589
|
+
: 'Live AV Analysis';
|
|
705590
|
+
if (avTitle) avTitle.textContent = label;
|
|
705591
|
+
setHudControlLabel(avBtn, genMode === 'live-analysis' ? 'Analyze AV' : 'Analyze');
|
|
705592
|
+
const summary = document.getElementById('gen-analysis-summary');
|
|
705593
|
+
if (summary && !summary.getAttribute('data-has-result')) {
|
|
705594
|
+
summary.innerHTML = '<div class="hud-result-card"><strong>' + escapeHtml(label) + '</strong><div>Provide a local media path or file URI, then run analysis. Results will show adapter status, source metadata, answer, entities, events, relations, and shots.</div></div>';
|
|
705595
|
+
}
|
|
705596
|
+
}
|
|
705597
|
+
|
|
704761
705598
|
function genSelectKind(kind) {
|
|
704762
705599
|
genKind = kind;
|
|
704763
705600
|
document.querySelectorAll('#gen-kind-row .gen-kind').forEach(b => {
|
|
704764
705601
|
const active = b.getAttribute('data-kind') === kind;
|
|
704765
705602
|
b.classList.toggle('active', active);
|
|
705603
|
+
b.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
704766
705604
|
b.style.background = active ? 'var(--color-brand)' : 'var(--color-bg)';
|
|
704767
705605
|
b.style.color = active ? 'var(--color-accent-fg)' : 'var(--color-fg)';
|
|
704768
705606
|
});
|
|
@@ -704938,7 +705776,7 @@ async function generateMedia() {
|
|
|
704938
705776
|
if (seed !== '') body.seed = Number(seed);
|
|
704939
705777
|
if (steps !== '' && (genKind === 'image' || genKind === 'video')) body.steps = Number(steps);
|
|
704940
705778
|
if (duration !== '' && (genKind === 'audio' || genKind === 'music')) body.duration = Number(duration);
|
|
704941
|
-
if (btn) { btn.disabled = true; btn
|
|
705779
|
+
if (btn) { btn.disabled = true; setHudControlLabel(btn, 'Generating'); }
|
|
704942
705780
|
if (statusEl) statusEl.textContent = 'Generating ' + genKind + ' — this can take a while on first run (model download + warmup).';
|
|
704943
705781
|
// Reveal the live progress bar; SSE media.generation_progress fills it in.
|
|
704944
705782
|
const pw = document.getElementById('gen-progress-wrap'); if (pw) pw.style.display = 'block';
|
|
@@ -704956,11 +705794,103 @@ async function generateMedia() {
|
|
|
704956
705794
|
} catch (e) {
|
|
704957
705795
|
if (statusEl) statusEl.textContent = 'Error: ' + (e && e.message || e);
|
|
704958
705796
|
} finally {
|
|
704959
|
-
if (btn) { btn.disabled = false; btn
|
|
705797
|
+
if (btn) { btn.disabled = false; setHudControlLabel(btn, 'Generate'); }
|
|
704960
705798
|
if (pw) setTimeout(() => { pw.style.display = 'none'; if (pb) pb.style.width = '0%'; }, 1000);
|
|
704961
705799
|
}
|
|
704962
705800
|
}
|
|
704963
705801
|
|
|
705802
|
+
function avArraySummary(items, label, limit) {
|
|
705803
|
+
const arr = Array.isArray(items) ? items.slice(0, limit || 8) : [];
|
|
705804
|
+
if (!arr.length) return '<div class="hud-result-card"><strong>' + label + '</strong><div>none reported</div></div>';
|
|
705805
|
+
return '<div class="hud-result-card"><strong>' + label + '</strong><pre style="white-space:pre-wrap;margin:0;background:transparent;border:0;padding:0;color:var(--hud-ink-soft)">' +
|
|
705806
|
+
escapeHtml(JSON.stringify(arr, null, 2)) +
|
|
705807
|
+
'</pre></div>';
|
|
705808
|
+
}
|
|
705809
|
+
|
|
705810
|
+
function renderAvAnalysis(data, mode) {
|
|
705811
|
+
const result = document.getElementById('av-result');
|
|
705812
|
+
const summary = document.getElementById('gen-analysis-summary');
|
|
705813
|
+
if (!result) return;
|
|
705814
|
+
const d = data || {};
|
|
705815
|
+
const source = d.source || {};
|
|
705816
|
+
const adapters = d.adapters || {};
|
|
705817
|
+
const adapterHtml = Object.keys(adapters).length
|
|
705818
|
+
? '<div class="hud-chip-row">' + Object.keys(adapters).map(k => {
|
|
705819
|
+
const v = adapters[k];
|
|
705820
|
+
const cls = v === 'live' ? 'live' : 'mock';
|
|
705821
|
+
return '<span class="hud-chip ' + cls + '">' + escapeHtml(k + ': ' + v) + '</span>';
|
|
705822
|
+
}).join('') + '</div>'
|
|
705823
|
+
: '<div class="hud-status-line">adapter status unavailable</div>';
|
|
705824
|
+
const answerRaw = d.answer;
|
|
705825
|
+
const answer = typeof answerRaw === 'string'
|
|
705826
|
+
? answerRaw
|
|
705827
|
+
: answerRaw && typeof answerRaw.answer === 'string'
|
|
705828
|
+
? answerRaw.answer
|
|
705829
|
+
: answerRaw
|
|
705830
|
+
? JSON.stringify(answerRaw, null, 2)
|
|
705831
|
+
: 'No composed answer returned.';
|
|
705832
|
+
const sourceBits = [
|
|
705833
|
+
source.path || source.uri || source.mediaUri || '',
|
|
705834
|
+
source.durationSec != null ? String(Math.round(source.durationSec * 10) / 10) + 's' : '',
|
|
705835
|
+
source.audio ? 'audio' : '',
|
|
705836
|
+
source.video ? 'video' : '',
|
|
705837
|
+
d.stack ? 'stack ' + d.stack : '',
|
|
705838
|
+
d.modelsLive ? 'live models' : 'mock/partial models',
|
|
705839
|
+
].filter(Boolean).join(' | ');
|
|
705840
|
+
const cards =
|
|
705841
|
+
'<div class="hud-result-card"><strong>Answer</strong><div style="white-space:pre-wrap">' + escapeHtml(answer) + '</div></div>' +
|
|
705842
|
+
'<div class="hud-result-card"><strong>Source</strong><div>' + escapeHtml(sourceBits || 'unknown source') + '</div></div>' +
|
|
705843
|
+
'<div class="hud-result-card"><strong>Adapters</strong>' + adapterHtml + '</div>' +
|
|
705844
|
+
(d.note ? '<div class="hud-result-card"><strong>Quality Note</strong><div>' + escapeHtml(d.note) + '</div></div>' : '') +
|
|
705845
|
+
avArraySummary(d.entities, 'Entities', 10) +
|
|
705846
|
+
avArraySummary(d.events, 'Events', 10) +
|
|
705847
|
+
avArraySummary(d.relations, 'Relations', 10) +
|
|
705848
|
+
avArraySummary(d.shots, 'Shots', 8);
|
|
705849
|
+
result.innerHTML = cards;
|
|
705850
|
+
if (summary) {
|
|
705851
|
+
summary.setAttribute('data-has-result', '1');
|
|
705852
|
+
summary.innerHTML =
|
|
705853
|
+
'<div class="hud-result-card"><strong>' + escapeHtml((mode || genMode).replace(/-/g, ' ')) + '</strong>' +
|
|
705854
|
+
'<div>' + escapeHtml(sourceBits || 'analysis complete') + '</div></div>' +
|
|
705855
|
+
'<div class="hud-result-card"><strong>Answer</strong><div style="white-space:pre-wrap">' + escapeHtml(String(answer).slice(0, 700)) + '</div></div>' +
|
|
705856
|
+
'<div class="hud-result-card"><strong>Adapters</strong>' + adapterHtml + '</div>';
|
|
705857
|
+
}
|
|
705858
|
+
}
|
|
705859
|
+
|
|
705860
|
+
async function analyzeAvMedia(modeOverride) {
|
|
705861
|
+
if (modeOverride) genSelectMode(modeOverride);
|
|
705862
|
+
const sourceEl = document.getElementById('av-source');
|
|
705863
|
+
const questionEl = document.getElementById('av-question');
|
|
705864
|
+
const statusEl = document.getElementById('av-status');
|
|
705865
|
+
const btn = document.getElementById('av-run-btn');
|
|
705866
|
+
const source = (sourceEl && sourceEl.value || '').trim();
|
|
705867
|
+
const question = (questionEl && questionEl.value || '').trim();
|
|
705868
|
+
if (!source) {
|
|
705869
|
+
if (statusEl) statusEl.textContent = 'Provide a local media path or file URI first.';
|
|
705870
|
+
return;
|
|
705871
|
+
}
|
|
705872
|
+
if (statusEl) statusEl.textContent = 'Analyzing media through /v1/media/av/analyze...';
|
|
705873
|
+
if (btn) { btn.disabled = true; setHudControlLabel(btn, 'Analyzing'); }
|
|
705874
|
+
const body = source.indexOf('file://') === 0
|
|
705875
|
+
? { mediaUri: source, question }
|
|
705876
|
+
: { path: source, question };
|
|
705877
|
+
try {
|
|
705878
|
+
const r = await fetch('/v1/media/av/analyze', { method: 'POST', headers: headers(), body: JSON.stringify(body) });
|
|
705879
|
+
const j = await r.json().catch(() => ({}));
|
|
705880
|
+
if (!r.ok) {
|
|
705881
|
+
if (statusEl) statusEl.textContent = 'Analysis failed: ' + (j.detail || j.title || ('HTTP ' + r.status));
|
|
705882
|
+
return;
|
|
705883
|
+
}
|
|
705884
|
+
if (statusEl) statusEl.textContent = 'Analysis complete: ' + ((j.data && j.data.episodeId) || 'episode captured');
|
|
705885
|
+
renderAvAnalysis(j.data || {}, genMode);
|
|
705886
|
+
} catch (e) {
|
|
705887
|
+
if (statusEl) statusEl.textContent = 'Analysis error: ' + (e && e.message || e);
|
|
705888
|
+
} finally {
|
|
705889
|
+
if (btn) { btn.disabled = false; setHudControlLabel(btn, genMode === 'live-analysis' ? 'Analyze AV' : 'Analyze'); }
|
|
705890
|
+
}
|
|
705891
|
+
}
|
|
705892
|
+
window.analyzeAvMedia = analyzeAvMedia;
|
|
705893
|
+
|
|
704964
705894
|
// ─── Gallery: filters, metadata cards, pagination, detail modal ───────────────
|
|
704965
705895
|
let genGallery = { kind: '', q: '', model: '', from: '', to: '', sort: 'new', offset: 0, items: [] };
|
|
704966
705896
|
let _genGalleryTimer = null;
|
|
@@ -705083,7 +706013,7 @@ function copyGenPrompt(idx) { const it = _genGalleryDetail[idx]; if (it && it.pr
|
|
|
705083
706013
|
function reuseGenPrompt(idx) {
|
|
705084
706014
|
const it = _genGalleryDetail[idx]; if (!it) return;
|
|
705085
706015
|
if (it.prompt) { const p = document.getElementById('gen-prompt'); if (p) p.value = it.prompt; }
|
|
705086
|
-
if (it.kind)
|
|
706016
|
+
if (it.kind) genSelectMode(it.kind);
|
|
705087
706017
|
closeGenDetail();
|
|
705088
706018
|
document.getElementById('gen-prompt')?.scrollIntoView({ behavior: 'smooth' });
|
|
705089
706019
|
}
|
|
@@ -705142,13 +706072,14 @@ function loadGenerateTab() {
|
|
|
705142
706072
|
}
|
|
705143
706073
|
}
|
|
705144
706074
|
} catch {}
|
|
705145
|
-
|
|
706075
|
+
genSelectMode(genKind || 'image');
|
|
705146
706076
|
if (!genModelsCache) loadGenerateModels();
|
|
705147
706077
|
genStoreInfo();
|
|
705148
706078
|
loadGenerateGallery(true);
|
|
705149
706079
|
_genTabLoaded = true;
|
|
705150
706080
|
}
|
|
705151
706081
|
window.genSelectKind = genSelectKind;
|
|
706082
|
+
window.genSelectMode = genSelectMode;
|
|
705152
706083
|
window.generateMedia = generateMedia;
|
|
705153
706084
|
window.loadGenerateGallery = loadGenerateGallery;
|
|
705154
706085
|
window.addEventListener('popstate', () => {
|
|
@@ -717198,6 +718129,7 @@ async function handleRequest(req3, res, ollamaUrl, verbose, runtimeDefaults = {}
|
|
|
717198
718129
|
"/chat",
|
|
717199
718130
|
"/agent",
|
|
717200
718131
|
"/voice",
|
|
718132
|
+
"/generate",
|
|
717201
718133
|
"/projects",
|
|
717202
718134
|
"/dashboard",
|
|
717203
718135
|
"/jobs",
|