修复 bug
This commit is contained in:
@@ -142,6 +142,7 @@ export interface PeqState {
|
|||||||
canDel?: number;
|
canDel?: number;
|
||||||
brand?: string;
|
brand?: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
|
form?: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,7 @@
|
|||||||
"addPresetNameExists": "That name already exists. Choose another.",
|
"addPresetNameExists": "That name already exists. Choose another.",
|
||||||
"toastAddPresetOk": "Preset added",
|
"toastAddPresetOk": "Preset added",
|
||||||
"toastAddPresetFail": "Failed to add preset",
|
"toastAddPresetFail": "Failed to add preset",
|
||||||
|
"toastPresetLimitReached": "Preset limit reached ({{max}}). Delete some presets before adding more.",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"tabBrands": "Brands",
|
"tabBrands": "Brands",
|
||||||
|
|||||||
@@ -201,6 +201,7 @@
|
|||||||
"addPresetNameExists": "名稱已存在,請修改後再儲存",
|
"addPresetNameExists": "名稱已存在,請修改後再儲存",
|
||||||
"toastAddPresetOk": "已新增預設",
|
"toastAddPresetOk": "已新增預設",
|
||||||
"toastAddPresetFail": "新增預設失敗",
|
"toastAddPresetFail": "新增預設失敗",
|
||||||
|
"toastPresetLimitReached": "預設已達上限({{max}} 個),請先清理後再操作",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"save": "儲存",
|
"save": "儲存",
|
||||||
"tabBrands": "品牌",
|
"tabBrands": "品牌",
|
||||||
|
|||||||
@@ -201,6 +201,7 @@
|
|||||||
"addPresetNameExists": "名称已存在,请修改后再保存",
|
"addPresetNameExists": "名称已存在,请修改后再保存",
|
||||||
"toastAddPresetOk": "已新增预设",
|
"toastAddPresetOk": "已新增预设",
|
||||||
"toastAddPresetFail": "新增预设失败",
|
"toastAddPresetFail": "新增预设失败",
|
||||||
|
"toastPresetLimitReached": "预设已达上限({{max}} 个),请先清理后再操作",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"tabBrands": "品牌",
|
"tabBrands": "品牌",
|
||||||
|
|||||||
+70
-19
@@ -52,6 +52,7 @@ import {
|
|||||||
DEFAULT_BANDS,
|
DEFAULT_BANDS,
|
||||||
FILTER_TYPES,
|
FILTER_TYPES,
|
||||||
FLAT_PRESET_FILTERS,
|
FLAT_PRESET_FILTERS,
|
||||||
|
PEQ_PRESET_MAX,
|
||||||
} from "./eq/eqConstants";
|
} from "./eq/eqConstants";
|
||||||
import {
|
import {
|
||||||
eqInterp,
|
eqInterp,
|
||||||
@@ -64,7 +65,13 @@ import {
|
|||||||
normalizeBandFreqHz,
|
normalizeBandFreqHz,
|
||||||
parseBandParamInput,
|
parseBandParamInput,
|
||||||
} from "./eq/eqFormatters";
|
} from "./eq/eqFormatters";
|
||||||
import { bandToPeqFilter, buildPeqCatalogSyncKey, cloneBands, getUniquePresetName } from "./eq/peqMappers";
|
import {
|
||||||
|
bandToPeqFilter,
|
||||||
|
buildPeqCatalogSyncKey,
|
||||||
|
cloneBands,
|
||||||
|
fetchEqSyncPeq,
|
||||||
|
getUniquePresetName,
|
||||||
|
} from "./eq/peqMappers";
|
||||||
import type { BandParamKind, PeqCatalogItem, PeqEqUi, PeqPresetLocalCache } from "./eq/types";
|
import type { BandParamKind, PeqCatalogItem, PeqEqUi, PeqPresetLocalCache } from "./eq/types";
|
||||||
|
|
||||||
/* ── Main Component ── */
|
/* ── Main Component ── */
|
||||||
@@ -137,6 +144,7 @@ export default function EQPage() {
|
|||||||
gain?: number;
|
gain?: number;
|
||||||
q?: number;
|
q?: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const bandSliderDragRef = useRef<BandParamKind | null>(null);
|
||||||
const [isBatchEditDialogOpen, setIsBatchEditDialogOpen] = useState(false);
|
const [isBatchEditDialogOpen, setIsBatchEditDialogOpen] = useState(false);
|
||||||
const [batchEditText, setBatchEditText] = useState("");
|
const [batchEditText, setBatchEditText] = useState("");
|
||||||
type BrandDrawerTab = "brands" | "models" | "target";
|
type BrandDrawerTab = "brands" | "models" | "target";
|
||||||
@@ -282,7 +290,17 @@ export default function EQPage() {
|
|||||||
}
|
}
|
||||||
}, [eqUi]);
|
}, [eqUi]);
|
||||||
|
|
||||||
|
const guardPeqPresetCapacity = () => {
|
||||||
|
if (peqItems.length < PEQ_PRESET_MAX) return true;
|
||||||
|
toast.error(
|
||||||
|
eqInterp(eqUi.toastPresetLimitReached, { max: PEQ_PRESET_MAX })
|
||||||
|
|| `Preset limit reached (${PEQ_PRESET_MAX}). Delete some presets first.`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
const openAddHeadsetCatalog = () => {
|
const openAddHeadsetCatalog = () => {
|
||||||
|
if (!guardPeqPresetCapacity()) return;
|
||||||
setIsBrandDrawerOpen(true);
|
setIsBrandDrawerOpen(true);
|
||||||
setBrandDrawerTab("brands");
|
setBrandDrawerTab("brands");
|
||||||
setBrandSearchQuery("");
|
setBrandSearchQuery("");
|
||||||
@@ -632,7 +650,7 @@ export default function EQPage() {
|
|||||||
|
|
||||||
delete peqPresetCacheRef.current[target.name];
|
delete peqPresetCacheRef.current[target.name];
|
||||||
await api.removePeq([target.name]);
|
await api.removePeq([target.name]);
|
||||||
const latest = await api.getPeqState();
|
const latest = await fetchEqSyncPeq(api, "deleteHeadphone");
|
||||||
applyPeqStateToUI(latest);
|
applyPeqStateToUI(latest);
|
||||||
toast.success(eqUi.toastDeleted);
|
toast.success(eqUi.toastDeleted);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -641,6 +659,7 @@ export default function EQPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openAddPresetDialog = () => {
|
const openAddPresetDialog = () => {
|
||||||
|
if (!guardPeqPresetCapacity()) return;
|
||||||
const existingNames = headphoneModels;
|
const existingNames = headphoneModels;
|
||||||
const currentName = headphoneModels[headphoneIdx] ?? "Preset";
|
const currentName = headphoneModels[headphoneIdx] ?? "Preset";
|
||||||
setCopyPresetName(getUniquePresetName(currentName, existingNames));
|
setCopyPresetName(getUniquePresetName(currentName, existingNames));
|
||||||
@@ -669,11 +688,13 @@ export default function EQPage() {
|
|||||||
|
|
||||||
const copyBrand = currentPeq?.brand?.trim() ?? "";
|
const copyBrand = currentPeq?.brand?.trim() ?? "";
|
||||||
const copyModel = currentPeq?.model?.trim() ?? "";
|
const copyModel = currentPeq?.model?.trim() ?? "";
|
||||||
|
const copyForm = currentPeq?.form?.trim() ?? "";
|
||||||
const copyPayload: PeqChangePayload = {
|
const copyPayload: PeqChangePayload = {
|
||||||
peqChange: {
|
peqChange: {
|
||||||
name: nextName,
|
name: nextName,
|
||||||
...(copyBrand ? { brand: copyBrand } : {}),
|
...(copyBrand ? { brand: copyBrand } : {}),
|
||||||
...(copyModel ? { model: copyModel } : {}),
|
...(copyModel ? { model: copyModel } : {}),
|
||||||
|
...(copyForm ? { form: copyForm } : {}),
|
||||||
filters: bands.map(bandToPeqFilter),
|
filters: bands.map(bandToPeqFilter),
|
||||||
autoPre: currentPeq?.autoPre ?? 0,
|
autoPre: currentPeq?.autoPre ?? 0,
|
||||||
preamp: currentPeq?.preamp ?? 0,
|
preamp: currentPeq?.preamp ?? 0,
|
||||||
@@ -694,7 +715,7 @@ export default function EQPage() {
|
|||||||
try {
|
try {
|
||||||
await upgradePeqChange(addPresetMode === "copy" ? copyPayload : flatPayload);
|
await upgradePeqChange(addPresetMode === "copy" ? copyPayload : flatPayload);
|
||||||
if (api && !isDemoMode) {
|
if (api && !isDemoMode) {
|
||||||
const latest = await api.getPeqState();
|
const latest = await fetchEqSyncPeq(api, "addPreset");
|
||||||
applyPeqStateToUI(latest as { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number });
|
applyPeqStateToUI(latest as { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number });
|
||||||
const createdIndex = latest.peq?.findIndex((item) => item.name === nextName) ?? -1;
|
const createdIndex = latest.peq?.findIndex((item) => item.name === nextName) ?? -1;
|
||||||
if (createdIndex >= 0) {
|
if (createdIndex >= 0) {
|
||||||
@@ -714,6 +735,7 @@ export default function EQPage() {
|
|||||||
name: nextName,
|
name: nextName,
|
||||||
...(addPresetMode === "copy" && copyBrand ? { brand: copyBrand } : {}),
|
...(addPresetMode === "copy" && copyBrand ? { brand: copyBrand } : {}),
|
||||||
...(addPresetMode === "copy" && copyModel ? { model: copyModel } : {}),
|
...(addPresetMode === "copy" && copyModel ? { model: copyModel } : {}),
|
||||||
|
...(addPresetMode === "copy" && copyForm ? { form: copyForm } : {}),
|
||||||
filters: localFilters,
|
filters: localFilters,
|
||||||
autoPre: localAutoPre,
|
autoPre: localAutoPre,
|
||||||
preamp: localPreamp,
|
preamp: localPreamp,
|
||||||
@@ -755,7 +777,7 @@ export default function EQPage() {
|
|||||||
try {
|
try {
|
||||||
await upgradePeqChange(payload);
|
await upgradePeqChange(payload);
|
||||||
if (api && !isDemoMode) {
|
if (api && !isDemoMode) {
|
||||||
const latest = await api.getPeqState();
|
const latest = await fetchEqSyncPeq(api, "saveBAsPreset");
|
||||||
applyPeqStateToUI(latest as { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number });
|
applyPeqStateToUI(latest as { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number });
|
||||||
const createdIndex = latest.peq?.findIndex((item) => item.name === nextName) ?? -1;
|
const createdIndex = latest.peq?.findIndex((item) => item.name === nextName) ?? -1;
|
||||||
if (createdIndex >= 0) {
|
if (createdIndex >= 0) {
|
||||||
@@ -902,7 +924,7 @@ export default function EQPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const loadedPeq = await api.getPeqState();
|
const loadedPeq = await fetchEqSyncPeq(api, "loadHeadphones");
|
||||||
if (loadedPeq.peq && loadedPeq.peq.length > 0) {
|
if (loadedPeq.peq && loadedPeq.peq.length > 0) {
|
||||||
syncPeqCatalog(loadedPeq);
|
syncPeqCatalog(loadedPeq);
|
||||||
setHeadphoneModels(loadedPeq.peq.map((h) => h.name));
|
setHeadphoneModels(loadedPeq.peq.map((h) => h.name));
|
||||||
@@ -1259,6 +1281,37 @@ export default function EQPage() {
|
|||||||
[setBands],
|
[setBands],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const clearBandSliderPreviewField = useCallback((kind: BandParamKind) => {
|
||||||
|
setBandSliderPreview((prev) => {
|
||||||
|
if (prev?.[kind] === undefined) return prev;
|
||||||
|
const next = { ...prev };
|
||||||
|
delete next[kind];
|
||||||
|
return next.freq !== undefined || next.gain !== undefined || next.q !== undefined ? next : null;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const beginBandSliderDrag = useCallback((kind: BandParamKind) => {
|
||||||
|
bandSliderDragRef.current = kind;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const endBandSliderDrag = useCallback(
|
||||||
|
(kind: BandParamKind) => {
|
||||||
|
if (bandSliderDragRef.current !== kind) return;
|
||||||
|
bandSliderDragRef.current = null;
|
||||||
|
clearBandSliderPreviewField(kind);
|
||||||
|
},
|
||||||
|
[clearBandSliderPreviewField],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBandSliderChange = useCallback(
|
||||||
|
(kind: BandParamKind, value: number) => {
|
||||||
|
if (bandSliderDragRef.current !== kind) return;
|
||||||
|
setBandSliderPreview((prev) => ({ ...prev, [kind]: value }));
|
||||||
|
updateBand(selectedBand, { [kind]: value });
|
||||||
|
},
|
||||||
|
[selectedBand, updateBand],
|
||||||
|
);
|
||||||
|
|
||||||
const bandParamDialogMeta = useMemo(() => {
|
const bandParamDialogMeta = useMemo(() => {
|
||||||
if (!bandParamDialog) return null;
|
if (!bandParamDialog) return null;
|
||||||
const rangeMessages = {
|
const rangeMessages = {
|
||||||
@@ -1328,9 +1381,11 @@ export default function EQPage() {
|
|||||||
toast.error(parsed.message);
|
toast.error(parsed.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
bandSliderDragRef.current = null;
|
||||||
if (bandParamDialog === "freq") updateBand(selectedBand, { freq: parsed.value });
|
if (bandParamDialog === "freq") updateBand(selectedBand, { freq: parsed.value });
|
||||||
else if (bandParamDialog === "gain") updateBand(selectedBand, { gain: parsed.value });
|
else if (bandParamDialog === "gain") updateBand(selectedBand, { gain: parsed.value });
|
||||||
else updateBand(selectedBand, { q: parsed.value });
|
else updateBand(selectedBand, { q: parsed.value });
|
||||||
|
setBandSliderPreview(null);
|
||||||
closeBandParamDialog();
|
closeBandParamDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1604,11 +1659,9 @@ export default function EQPage() {
|
|||||||
min={Math.log10(BAND_FREQ_MIN)}
|
min={Math.log10(BAND_FREQ_MIN)}
|
||||||
max={Math.log10(BAND_FREQ_MAX)}
|
max={Math.log10(BAND_FREQ_MAX)}
|
||||||
step={0.005}
|
step={0.005}
|
||||||
onChange={(v) => {
|
onInteractionStart={() => beginBandSliderDrag("freq")}
|
||||||
const freq = Math.round(Math.pow(10, v));
|
onInteractionEnd={() => endBandSliderDrag("freq")}
|
||||||
setBandSliderPreview((prev) => ({ ...prev, freq }));
|
onChange={(v) => handleBandSliderChange("freq", Math.round(Math.pow(10, v)))}
|
||||||
updateBand(selectedBand, { freq });
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1630,10 +1683,9 @@ export default function EQPage() {
|
|||||||
min={BAND_GAIN_MIN}
|
min={BAND_GAIN_MIN}
|
||||||
max={BAND_GAIN_MAX}
|
max={BAND_GAIN_MAX}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
onChange={(v) => {
|
onInteractionStart={() => beginBandSliderDrag("gain")}
|
||||||
setBandSliderPreview((prev) => ({ ...prev, gain: v }));
|
onInteractionEnd={() => endBandSliderDrag("gain")}
|
||||||
updateBand(selectedBand, { gain: v });
|
onChange={(v) => handleBandSliderChange("gain", v)}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1655,10 +1707,9 @@ export default function EQPage() {
|
|||||||
min={BAND_Q_MIN}
|
min={BAND_Q_MIN}
|
||||||
max={BAND_Q_MAX}
|
max={BAND_Q_MAX}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
onChange={(v) => {
|
onInteractionStart={() => beginBandSliderDrag("q")}
|
||||||
setBandSliderPreview((prev) => ({ ...prev, q: v }));
|
onInteractionEnd={() => endBandSliderDrag("q")}
|
||||||
updateBand(selectedBand, { q: v });
|
onChange={(v) => handleBandSliderChange("q", v)}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2189,7 +2240,7 @@ export default function EQPage() {
|
|||||||
await upgradePeqChange(postPeq);
|
await upgradePeqChange(postPeq);
|
||||||
const createdName = postPeq.peqChange.name;
|
const createdName = postPeq.peqChange.name;
|
||||||
if (api && !isDemoMode) {
|
if (api && !isDemoMode) {
|
||||||
const latest = await api.getPeqState();
|
const latest = await fetchEqSyncPeq(api, "addCatalogPreset");
|
||||||
applyPeqStateToUI(latest as { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number });
|
applyPeqStateToUI(latest as { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number });
|
||||||
const createdIndex = latest.peq?.findIndex((item) => item.name === createdName) ?? -1;
|
const createdIndex = latest.peq?.findIndex((item) => item.name === createdName) ?? -1;
|
||||||
if (createdIndex >= 0) {
|
if (createdIndex >= 0) {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export function CyanSlider({
|
|||||||
max,
|
max,
|
||||||
step = 1,
|
step = 1,
|
||||||
onChange,
|
onChange,
|
||||||
|
onInteractionStart,
|
||||||
|
onInteractionEnd,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: {
|
}: {
|
||||||
value: number;
|
value: number;
|
||||||
@@ -22,6 +24,8 @@ export function CyanSlider({
|
|||||||
max: number;
|
max: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
onChange: (v: number) => void;
|
onChange: (v: number) => void;
|
||||||
|
onInteractionStart?: () => void;
|
||||||
|
onInteractionEnd?: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const fillPct = Math.max(0, Math.min(100, ((value - min) / (max - min)) * 100));
|
const fillPct = Math.max(0, Math.min(100, ((value - min) / (max - min)) * 100));
|
||||||
@@ -43,8 +47,12 @@ export function CyanSlider({
|
|||||||
value={value}
|
value={value}
|
||||||
className="cyan-slider relative z-10"
|
className="cyan-slider relative z-10"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
onPointerDown={() => onInteractionStart?.()}
|
||||||
|
onFocus={() => onInteractionStart?.()}
|
||||||
|
onPointerUp={() => onInteractionEnd?.()}
|
||||||
|
onPointerCancel={() => onInteractionEnd?.()}
|
||||||
|
onBlur={() => onInteractionEnd?.()}
|
||||||
onChange={(e) => onChange(Number(e.target.value))}
|
onChange={(e) => onChange(Number(e.target.value))}
|
||||||
onInput={(e) => onChange(Number(e.currentTarget.value))}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ export const BAND_GAIN_MAX = 15;
|
|||||||
export const BAND_Q_MIN = 0.1;
|
export const BAND_Q_MIN = 0.1;
|
||||||
export const BAND_Q_MAX = 10;
|
export const BAND_Q_MAX = 10;
|
||||||
|
|
||||||
|
/** Maximum headphone PEQ presets stored on device. */
|
||||||
|
export const PEQ_PRESET_MAX = 50;
|
||||||
|
|
||||||
export const BAND_PARAM_VALUE_BOX_STYLE: CSSProperties = {
|
export const BAND_PARAM_VALUE_BOX_STYLE: CSSProperties = {
|
||||||
background: "rgba(44,44,46,0.9)",
|
background: "rgba(44,44,46,0.9)",
|
||||||
border: "1px solid rgba(255,255,255,0.08)",
|
border: "1px solid rgba(255,255,255,0.08)",
|
||||||
|
|||||||
@@ -6,6 +6,21 @@ export function cloneBands(source: EqBand[]) {
|
|||||||
return source.map((band) => ({ ...band }));
|
return source.map((band) => ({ ...band }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Dev: log decoded syncPeq JSON after EQ page fetches device PEQ state. */
|
||||||
|
export function logEqSyncPeqDev(source: string, data: PeqState): void {
|
||||||
|
if (!import.meta.env.DEV) return;
|
||||||
|
console.log(`[EQ syncPeq] ${source}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchEqSyncPeq(
|
||||||
|
api: { getPeqState: () => Promise<PeqState> },
|
||||||
|
source: string,
|
||||||
|
): Promise<PeqState> {
|
||||||
|
const data = await api.getPeqState();
|
||||||
|
logEqSyncPeqDev(source, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildPeqCatalogSyncKey(
|
export function buildPeqCatalogSyncKey(
|
||||||
remote: Pick<PeqState, "peq" | "peqSelect"> | null | undefined,
|
remote: Pick<PeqState, "peq" | "peqSelect"> | null | undefined,
|
||||||
devicePeqSelect?: number,
|
devicePeqSelect?: number,
|
||||||
|
|||||||
@@ -8,6 +8,23 @@ import { vitePluginManusRuntime } from "vite-plugin-manus-runtime";
|
|||||||
|
|
||||||
const PROJECT_ROOT = import.meta.dirname;
|
const PROJECT_ROOT = import.meta.dirname;
|
||||||
|
|
||||||
|
/** Dev: / → i.modern.html; /i.html → UA router entry */
|
||||||
|
function vitePluginModernDevEntry(): Plugin {
|
||||||
|
return {
|
||||||
|
name: "modern-dev-entry",
|
||||||
|
apply: "serve",
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use((req, _res, next) => {
|
||||||
|
const url = (req.url ?? "").split("?")[0];
|
||||||
|
if (url === "/" || url === "/index.html") {
|
||||||
|
req.url = "/i.modern.html";
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** 构建后将统一入口 i.html 复制到 dist/ 根目录 */
|
/** 构建后将统一入口 i.html 复制到 dist/ 根目录 */
|
||||||
function vitePluginCopyRouterEntry(): Plugin {
|
function vitePluginCopyRouterEntry(): Plugin {
|
||||||
const src = path.resolve(import.meta.dirname, "client", "i.html");
|
const src = path.resolve(import.meta.dirname, "client", "i.html");
|
||||||
@@ -24,6 +41,7 @@ function vitePluginCopyRouterEntry(): Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
|
vitePluginModernDevEntry(),
|
||||||
react(),
|
react(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
jsxLocPlugin(),
|
jsxLocPlugin(),
|
||||||
|
|||||||
@@ -79,8 +79,24 @@ export default defineConfig({
|
|||||||
input: {
|
input: {
|
||||||
i: path.resolve(PROJECT_ROOT, "client", "i.legacy.html"),
|
i: path.resolve(PROJECT_ROOT, "client", "i.legacy.html"),
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
manualChunks(id) {
|
||||||
|
if (!id.includes("node_modules")) return;
|
||||||
|
// Match modern build: echarts is lazy-loaded on EQ/Effects pages.
|
||||||
|
if (
|
||||||
|
id.includes("/echarts/") ||
|
||||||
|
id.includes("/zrender/") ||
|
||||||
|
id.includes("\\echarts\\") ||
|
||||||
|
id.includes("\\zrender\\")
|
||||||
|
) {
|
||||||
|
return "echarts-vendor";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
// streamdown pulls @shikijs/langs + mermaid; both lazy-loaded with AIPage.
|
||||||
|
chunkSizeWarningLimit: 15000,
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
strictPort: false,
|
strictPort: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user