From 1223876b9d0153796c75bbe3837d29662baa9ddf Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 13 Apr 2026 18:50:19 -0400 Subject: [PATCH] Refactor: load panel detection libs from vendor dir Update PanelDetector to load OpenCV, TensorFlow.js, and COCO-SSD from local vendor directory instead of importing npm packages. This ensures the libraries are available after build and avoids runtime dependency resolution issues. - Change imports to use ../vendor paths instead of npm packages - Add caching to prevent reloading libraries - Add error handling for failed library loads with console warnings - Update OpenCV loading to handle both default and named exports --- panel-detection/detector.js | 47 +++++++++++++++++++++++++------------ reader.js | 21 ++++++++++------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/panel-detection/detector.js b/panel-detection/detector.js index 6585e8c..b389fbb 100644 --- a/panel-detection/detector.js +++ b/panel-detection/detector.js @@ -97,23 +97,40 @@ export class PanelDetector { } async #loadOpenCV() { - const { default: cv } = await import("@techstark/opencv-js"); - await new Promise((resolve, reject) => { - const check = () => { - if (cv && cv.Mat) resolve(); - else if (!cv || cv.readyState === "complete") - reject(new Error("OpenCV failed to load")); - else setTimeout(check, 50); - }; - check(); - }); - return cv; - } + if (this.#opencv) return this.#opencv; + try { + const cv = await import("../vendor/opencv/opencv.js"); + + await new Promise((resolve, reject) => { + const check = () => { + if (cv && cv.Mat) resolve(); + else if (cv.readyState === "complete") + reject(new Error("OpenCV failed to load")); + else setTimeout(check, 50); + }; + check(); + }); + + this.#opencv = cv.default || cv; + return this.#opencv; + } catch (e) { + console.warn("Failed to load OpenCV:", e); + return null; + } + } async #loadModel() { - const tf = await import("@tensorflow/tfjs"); - const cocoSsd = await import("@tensorflow-models/coco-ssd"); - return await cocoSsd.load({ base: "lite_mobilenet_v2" }); + if (this.#model) return this.#model; + + try { + await import("../vendor/tfjs/tf.min.js"); + const cocoSsd = await import("../vendor/coco-ssd/coco-ssd.min.js"); + this.#model = await cocoSsd.load({ base: "lite_mobilenet_v2" }); + return this.#model; + } catch (e) { + console.warn("Failed to load ML model:", e); + return null; + } } clear() { diff --git a/reader.js b/reader.js index a439969..ba6410a 100644 --- a/reader.js +++ b/reader.js @@ -104,18 +104,23 @@ class Reader { { name: "panel-mode", label: "Panel Mode", - type: "checkbox", - checked: false, - onclick: () => { - // ← No 'value' parameter for checkbox + type: "radio", + items: [ + ["Off", "off"], + ["On", "on"], + ], + onclick: (value) => { const renderer = this.view?.renderer; if (!renderer?.togglePanelMode) return; - const isActive = renderer.hasAttribute("panel-mode"); - if (isActive) { - renderer.removeAttribute("panel-mode"); + if (value === "on") { + if (!renderer.hasAttribute("panel-mode")) { + renderer.togglePanelMode(); + } } else { - renderer.togglePanelMode(); + if (renderer.hasAttribute("panel-mode")) { + renderer.togglePanelMode(); + } } }, },