Files
mnote/scripts/dev-hot.js
T
lix-2026 627213dc01 feat: add evidence search and stabilize pdf previews
- add document evidence parsing/search/open routes, Hermes tool wiring, local index settings/status, and the document-evidence skill plus design notes
- fix PDF resource tabs by rendering PDFs inline with pdf.js canvases instead of iframe preview pages, release PDF documents on close, and document the fourth-PDF stall bug
- keep PDF preview at 2x rendering while removing the previous lazy-load/placeholder direction, and make dev:hot bind loopback defaults externally reachable

Verification:
- node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
- node scripts/task-dev-hot-plan-test.js
- cargo test -p mnote-web --manifest-path rust/Cargo.toml pdf_preview_page_does_not_render_visible_toolbar
- cargo test -p mnote-web --manifest-path rust/Cargo.toml document_shell_returns_page_aggregate_snapshot
- cargo build -p mnote-web --manifest-path rust/Cargo.toml
- browser smoke: sequentially opened the four tea_seed_oil_cosmetic PDFs; fourth PDF rendered 15/15 canvases, iframeCount=0, browser errors=0
2026-06-04 18:51:16 +08:00

103 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
"use strict";
/**
* dev:hot 启动入口。
*
* 目标:
* - 不改动 desktop:hot 的默认行为。
* - 使用 cargo-watch 自动重编译并重启 mnote-web。
* - 通过 MNOTE_WEB_DEV_HOT_RELOAD 启用页面端轻量 reload 轮询。
*/
const { spawn } = require("node:child_process");
const fs = require("node:fs");
const path = require("node:path");
const rootDir = path.resolve(__dirname, "..");
const desktopHotPath = path.join(rootDir, "scripts", "desktop-hot.js");
function cargoWatchCommand(env = process.env) {
const extraArgs = String(env.MNOTE_WEB_DEV_HOT_CARGO_WATCH_ARGS || "").trim();
const watchArgs = [
"-w crates/mnote-web/src",
"-w crates/mnote-web/browser",
"-w crates/mnote-web/Cargo.toml",
"-w Cargo.toml",
"-x \"run -p mnote-web --bin mnote-web\"",
];
if (extraArgs) watchArgs.unshift(extraArgs);
return `cargo watch ${watchArgs.join(" ")}`;
}
function bindHost(bindAddr) {
const value = String(bindAddr || "").trim();
if (!value) return "";
const ipv6Match = value.match(/^\[([^\]]+)\]:(\d+)$/);
if (ipv6Match) return ipv6Match[1];
const lastColon = value.lastIndexOf(":");
if (lastColon <= 0) return "";
return value.slice(0, lastColon);
}
function bindPort(bindAddr, fallbackPort) {
const value = String(bindAddr || "").trim();
const match = value.match(/:(\d+)$/);
if (!match) return fallbackPort;
const port = Number(match[1]);
return Number.isFinite(port) && port > 0 ? Math.floor(port) : fallbackPort;
}
function devHotBindAddr(env = process.env) {
const frontendPort = bindPort(env.MNOTE_WEB_BIND, Number(env.FRONTEND_PORT || 3000));
const host = bindHost(env.MNOTE_WEB_BIND);
if (!host || host === "127.0.0.1" || host === "localhost" || host === "::1") {
return `0.0.0.0:${frontendPort}`;
}
return String(env.MNOTE_WEB_BIND).trim();
}
function buildDevHotEnv(baseEnv = process.env) {
return {
...baseEnv,
MNOTE_WEB_DEV_HOT_RELOAD: "1",
MNOTE_WEB_BIND: devHotBindAddr(baseEnv),
MNOTE_WEB_CMD: String(baseEnv.MNOTE_WEB_CMD || "").trim() || cargoWatchCommand(baseEnv),
};
}
function main() {
if (!fs.existsSync(desktopHotPath)) {
throw new Error(`缺少 desktop-hot 入口:${desktopHotPath}`);
}
const child = spawn(process.execPath, [desktopHotPath], {
cwd: rootDir,
env: buildDevHotEnv(process.env),
stdio: "inherit",
});
child.on("exit", (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
return;
}
process.exit(code ?? 0);
});
child.on("error", (error) => {
console.error(`[dev-hot] 启动失败:${error.message}`);
process.exit(1);
});
}
if (require.main === module) {
main();
}
module.exports = {
buildDevHotEnv,
cargoWatchCommand,
devHotBindAddr,
};