优化分享页面

This commit is contained in:
eafonyang
2026-06-26 12:03:31 +08:00
parent 540ece7ec6
commit 522bb602b3
16 changed files with 135 additions and 40 deletions
+1
View File
@@ -230,6 +230,7 @@
"addPresetTitle": "Neue Voreinstellung", "addPresetTitle": "Neue Voreinstellung",
"addPresetNameEmpty": "Name darf nicht leer sein", "addPresetNameEmpty": "Name darf nicht leer sein",
"addPresetNameExists": "Dieser Name existiert bereits. Wählen Sie einen anderen.", "addPresetNameExists": "Dieser Name existiert bereits. Wählen Sie einen anderen.",
"addPresetNameTooLong": "Name darf maximal 80 Zeichen lang sein",
"overwritePresetTitle": "Voreinstellung existiert bereits", "overwritePresetTitle": "Voreinstellung existiert bereits",
"overwritePresetDesc": "Eine Voreinstellung mit dem Namen \"{{name}}\" existiert bereits. Überschreiben?", "overwritePresetDesc": "Eine Voreinstellung mit dem Namen \"{{name}}\" existiert bereits. Überschreiben?",
"overwritePresetConfirm": "Überschreiben", "overwritePresetConfirm": "Überschreiben",
+1
View File
@@ -230,6 +230,7 @@
"addPresetTitle": "New preset", "addPresetTitle": "New preset",
"addPresetNameEmpty": "Name cannot be empty", "addPresetNameEmpty": "Name cannot be empty",
"addPresetNameExists": "That name already exists. Choose another.", "addPresetNameExists": "That name already exists. Choose another.",
"addPresetNameTooLong": "Name cannot exceed 80 characters",
"overwritePresetTitle": "Preset name already exists", "overwritePresetTitle": "Preset name already exists",
"overwritePresetDesc": "A preset named \"{{name}}\" already exists. Overwrite it?", "overwritePresetDesc": "A preset named \"{{name}}\" already exists. Overwrite it?",
"overwritePresetConfirm": "Overwrite", "overwritePresetConfirm": "Overwrite",
+1
View File
@@ -230,6 +230,7 @@
"addPresetTitle": "Nuevo preset", "addPresetTitle": "Nuevo preset",
"addPresetNameEmpty": "El nombre no puede estar vacío", "addPresetNameEmpty": "El nombre no puede estar vacío",
"addPresetNameExists": "Ese nombre ya existe. Elija otro.", "addPresetNameExists": "Ese nombre ya existe. Elija otro.",
"addPresetNameTooLong": "El nombre no puede superar los 80 caracteres",
"overwritePresetTitle": "El nombre del preset ya existe", "overwritePresetTitle": "El nombre del preset ya existe",
"overwritePresetDesc": "Un preset llamado \"{{name}}\" ya existe. ¿Sobrescribirlo?", "overwritePresetDesc": "Un preset llamado \"{{name}}\" ya existe. ¿Sobrescribirlo?",
"overwritePresetConfirm": "Sobrescribir", "overwritePresetConfirm": "Sobrescribir",
+1
View File
@@ -230,6 +230,7 @@
"addPresetTitle": "Nouveau préréglage", "addPresetTitle": "Nouveau préréglage",
"addPresetNameEmpty": "Le nom ne peut pas être vide", "addPresetNameEmpty": "Le nom ne peut pas être vide",
"addPresetNameExists": "Ce nom existe déjà. Choisissez-en un autre.", "addPresetNameExists": "Ce nom existe déjà. Choisissez-en un autre.",
"addPresetNameTooLong": "Le nom ne peut pas dépasser 80 caractères",
"overwritePresetTitle": "Le nom du préréglage existe déjà", "overwritePresetTitle": "Le nom du préréglage existe déjà",
"overwritePresetDesc": "Un préréglage nommé \"{{name}}\" existe déjà. L'écraser ?", "overwritePresetDesc": "Un préréglage nommé \"{{name}}\" existe déjà. L'écraser ?",
"overwritePresetConfirm": "Écraser", "overwritePresetConfirm": "Écraser",
+1
View File
@@ -223,6 +223,7 @@
"addPresetTitle": "新增預設", "addPresetTitle": "新增預設",
"addPresetNameEmpty": "名稱不能為空", "addPresetNameEmpty": "名稱不能為空",
"addPresetNameExists": "名稱已存在,請修改後再儲存", "addPresetNameExists": "名稱已存在,請修改後再儲存",
"addPresetNameTooLong": "名稱不能超過 80 個字元",
"overwritePresetTitle": "預設名稱已存在", "overwritePresetTitle": "預設名稱已存在",
"overwritePresetDesc": "預設「{{name}}」已存在,確認覆蓋原有資料嗎?", "overwritePresetDesc": "預設「{{name}}」已存在,確認覆蓋原有資料嗎?",
"overwritePresetConfirm": "覆蓋", "overwritePresetConfirm": "覆蓋",
+1
View File
@@ -223,6 +223,7 @@
"addPresetTitle": "新增预设", "addPresetTitle": "新增预设",
"addPresetNameEmpty": "名称不能为空", "addPresetNameEmpty": "名称不能为空",
"addPresetNameExists": "名称已存在,请修改后再保存", "addPresetNameExists": "名称已存在,请修改后再保存",
"addPresetNameTooLong": "名称不能超过 80 个字符",
"overwritePresetTitle": "预设名称已存在", "overwritePresetTitle": "预设名称已存在",
"overwritePresetDesc": "预设「{{name}}」已存在,确认覆盖原有数据吗?", "overwritePresetDesc": "预设「{{name}}」已存在,确认覆盖原有数据吗?",
"overwritePresetConfirm": "覆盖", "overwritePresetConfirm": "覆盖",
+13
View File
@@ -84,6 +84,7 @@ import { BrandDrawer } from "./eq/components/BrandDrawer";
import { PeqOverwriteConfirmDialog } from "./eq/components/PeqOverwriteConfirmDialog"; import { PeqOverwriteConfirmDialog } from "./eq/components/PeqOverwriteConfirmDialog";
import { PeqPresetManageDialog } from "./eq/components/PeqPresetManageDialog"; import { PeqPresetManageDialog } from "./eq/components/PeqPresetManageDialog";
import { useRawCurve } from "./eq/hooks/useRawCurve"; import { useRawCurve } from "./eq/hooks/useRawCurve";
import { isPeqPresetNameTooLong } from "./eq/peqPresetName";
/* ── Default bands imported from eq/constants ── */ /* ── Default bands imported from eq/constants ── */
@@ -606,6 +607,10 @@ export default function EQPage() {
toast.error(eqUi.addPresetNameEmpty); toast.error(eqUi.addPresetNameEmpty);
return; return;
} }
if (isPeqPresetNameTooLong(nextName)) {
toast.error(eqUi.addPresetNameTooLong);
return;
}
if (headphoneModels.includes(nextName)) { if (headphoneModels.includes(nextName)) {
toast.error(eqUi.addPresetNameExists); toast.error(eqUi.addPresetNameExists);
return; return;
@@ -701,6 +706,10 @@ export default function EQPage() {
toast.error(eqUi.addPresetNameEmpty); toast.error(eqUi.addPresetNameEmpty);
return; return;
} }
if (isPeqPresetNameTooLong(nextName)) {
toast.error(eqUi.addPresetNameTooLong);
return;
}
if (headphoneModels.includes(nextName)) { if (headphoneModels.includes(nextName)) {
toast.error(eqUi.addPresetNameExists); toast.error(eqUi.addPresetNameExists);
return; return;
@@ -779,6 +788,10 @@ export default function EQPage() {
toast.error(eqUi.addPresetNameEmpty); toast.error(eqUi.addPresetNameEmpty);
return; return;
} }
if (isPeqPresetNameTooLong(nextName)) {
toast.error(eqUi.addPresetNameTooLong);
return;
}
if (!shareCode.trim()) { if (!shareCode.trim()) {
toast.error(eqUi.importCodeNotFound); toast.error(eqUi.importCodeNotFound);
return; return;
@@ -1,6 +1,9 @@
import { X } from "lucide-react"; import { X } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import type { PeqEqUi } from "../constants"; import type { PeqEqUi } from "../constants";
import { PEQ_PRESET_NAME_MAX_LENGTH } from "../constants";
import { clampPeqPresetName } from "../peqPresetName";
import { PeqPresetNameLengthHint } from "./PeqPresetNameLengthHint";
export function AddPresetDialog({ export function AddPresetDialog({
open, open,
@@ -63,12 +66,16 @@ export function AddPresetDialog({
onChange={() => onModeChange("copy")} onChange={() => onModeChange("copy")}
className="h-4 w-4 shrink-0 accent-[#00FFF6]" className="h-4 w-4 shrink-0 accent-[#00FFF6]"
/> />
<div className="min-w-0 flex-1">
<input <input
value={copyName} value={copyName}
onChange={(e) => onCopyNameChange(e.target.value)} onChange={(e) => onCopyNameChange(clampPeqPresetName(e.target.value))}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="h-10 min-w-0 flex-1 rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[15px] text-white/90 outline-none ring-0 ring-offset-0 placeholder:text-white/30 focus:border-white/15 focus:ring-0 focus-visible:ring-0" maxLength={PEQ_PRESET_NAME_MAX_LENGTH}
className="h-10 w-full rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[15px] text-white/90 outline-none ring-0 ring-offset-0 placeholder:text-white/30 focus:border-white/15 focus:ring-0 focus-visible:ring-0"
/> />
<PeqPresetNameLengthHint value={copyName} />
</div>
</label> </label>
<label <label
@@ -84,12 +91,16 @@ export function AddPresetDialog({
onChange={() => onModeChange("flat")} onChange={() => onModeChange("flat")}
className="h-4 w-4 shrink-0 accent-[#00FFF6]" className="h-4 w-4 shrink-0 accent-[#00FFF6]"
/> />
<div className="min-w-0 flex-1">
<input <input
value={flatName} value={flatName}
onChange={(e) => onFlatNameChange(e.target.value)} onChange={(e) => onFlatNameChange(clampPeqPresetName(e.target.value))}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="h-10 min-w-0 flex-1 rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[15px] text-white/90 outline-none ring-0 ring-offset-0 placeholder:text-white/30 focus:border-white/15 focus:ring-0 focus-visible:ring-0" maxLength={PEQ_PRESET_NAME_MAX_LENGTH}
className="h-10 w-full rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[15px] text-white/90 outline-none ring-0 ring-offset-0 placeholder:text-white/30 focus:border-white/15 focus:ring-0 focus-visible:ring-0"
/> />
<PeqPresetNameLengthHint value={flatName} />
</div>
</label> </label>
</div> </div>
@@ -3,6 +3,9 @@ import { X, Trash2, Pencil, Check, Loader2 } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { toast } from "sonner"; import { toast } from "sonner";
import type { PeqEqUi } from "../constants"; import type { PeqEqUi } from "../constants";
import { PEQ_PRESET_NAME_MAX_LENGTH } from "../constants";
import { clampPeqPresetName, isPeqPresetNameTooLong } from "../peqPresetName";
import { PeqPresetNameLengthHint } from "./PeqPresetNameLengthHint";
/* ── Custom checkbox ── */ /* ── Custom checkbox ── */
function Checkbox({ function Checkbox({
@@ -133,6 +136,10 @@ export function PeqPresetManageDialog({
toast.error(eqUi.renameEmptyName); toast.error(eqUi.renameEmptyName);
return; return;
} }
if (isPeqPresetNameTooLong(nextName)) {
toast.error(eqUi.addPresetNameTooLong);
return;
}
const oldItem = items[editingIdx]; const oldItem = items[editingIdx];
if (!oldItem) return; if (!oldItem) return;
if (nextName === oldItem.name) { if (nextName === oldItem.name) {
@@ -266,10 +273,12 @@ export function PeqPresetManageDialog({
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
{isEditing ? ( {isEditing ? (
<div className="space-y-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<input <input
value={editingName} value={editingName}
onChange={e => setEditingName(e.target.value)} onChange={e => setEditingName(clampPeqPresetName(e.target.value))}
maxLength={PEQ_PRESET_NAME_MAX_LENGTH}
className="h-9 min-w-0 flex-1 rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[14px] text-white/90 outline-none placeholder:text-white/30 focus:border-[#00FFF6]/40" className="h-9 min-w-0 flex-1 rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[14px] text-white/90 outline-none placeholder:text-white/30 focus:border-[#00FFF6]/40"
/> />
<button <button
@@ -301,6 +310,8 @@ export function PeqPresetManageDialog({
</button> </button>
</div> </div>
<PeqPresetNameLengthHint value={editingName} />
</div>
) : ( ) : (
<div <div
className="truncate text-[13px] text-white/85" className="truncate text-[13px] text-white/85"
@@ -0,0 +1,9 @@
import { peqPresetNameLengthText } from "../peqPresetName";
export function PeqPresetNameLengthHint({ value }: { value: string }) {
return (
<p className="mt-1 text-right text-[11px] text-white/35 tabular-nums">
{peqPresetNameLengthText(value)}
</p>
);
}
@@ -1,5 +1,8 @@
import { X } from "lucide-react"; import { X } from "lucide-react";
import type { PeqEqUi } from "../constants"; import type { PeqEqUi } from "../constants";
import { PEQ_PRESET_NAME_MAX_LENGTH } from "../constants";
import { clampPeqPresetName } from "../peqPresetName";
import { PeqPresetNameLengthHint } from "./PeqPresetNameLengthHint";
export function SaveBDialog({ export function SaveBDialog({
open, open,
@@ -40,12 +43,16 @@ export function SaveBDialog({
</button> </button>
</div> </div>
<div>
<input <input
value={presetName} value={presetName}
onChange={(e) => onNameChange(e.target.value)} onChange={(e) => onNameChange(clampPeqPresetName(e.target.value))}
maxLength={PEQ_PRESET_NAME_MAX_LENGTH}
className="h-11 w-full rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[15px] text-white/90 outline-none placeholder:text-white/30 focus:border-white/15" className="h-11 w-full rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[15px] text-white/90 outline-none placeholder:text-white/30 focus:border-white/15"
autoFocus autoFocus
/> />
<PeqPresetNameLengthHint value={presetName} />
</div>
<div className="mt-6 flex items-center justify-center gap-4 sm:gap-8"> <div className="mt-6 flex items-center justify-center gap-4 sm:gap-8">
<button <button
+25 -13
View File
@@ -7,6 +7,9 @@ import type { PeqEqUi } from "../constants";
import { resolveShareApiMessage } from "../shareMessages"; import { resolveShareApiMessage } from "../shareMessages";
import { eqInterp } from "../utils"; import { eqInterp } from "../utils";
import { PeqOverwriteConfirmDialog } from "./PeqOverwriteConfirmDialog"; import { PeqOverwriteConfirmDialog } from "./PeqOverwriteConfirmDialog";
import { PEQ_PRESET_NAME_MAX_LENGTH } from "../constants";
import { clampPeqPresetName, isPeqPresetNameTooLong } from "../peqPresetName";
import { PeqPresetNameLengthHint } from "./PeqPresetNameLengthHint";
type PeqItem = { type PeqItem = {
name: string; name: string;
@@ -100,6 +103,10 @@ export function ShareDialog({
if (importSaving || !importedEqData || !queriedShareCode) return; if (importSaving || !importedEqData || !queriedShareCode) return;
const nextName = importPresetName.trim(); const nextName = importPresetName.trim();
if (!nextName) return; if (!nextName) return;
if (isPeqPresetNameTooLong(nextName)) {
toast.error(eqUi.addPresetNameTooLong);
return;
}
setImportSaving(true); setImportSaving(true);
try { try {
@@ -144,7 +151,7 @@ export function ShareDialog({
return ( return (
<> <>
<div <div
className="fixed inset-0 z-[120] flex items-center justify-center bg-black/65 px-4" className="fixed inset-0 z-[120] flex items-start justify-center overflow-y-auto bg-black/65 px-4 pb-8 pt-[22vh]"
onClick={(e) => { onClick={(e) => {
if (e.target !== e.currentTarget) return; if (e.target !== e.currentTarget) return;
if (importSaving || deleteConfirmCode !== null || deletingShareCode) return; if (importSaving || deleteConfirmCode !== null || deletingShareCode) return;
@@ -152,7 +159,7 @@ export function ShareDialog({
}} }}
> >
<div <div
className="w-full max-w-[420px] rounded-[14px] p-5" className="flex w-full max-w-[420px] flex-col rounded-[14px] p-5"
style={{ style={{
background: "linear-gradient(180deg, rgba(34,36,40,0.98) 0%, rgba(24,26,30,0.98) 100%)", background: "linear-gradient(180deg, rgba(34,36,40,0.98) 0%, rgba(24,26,30,0.98) 100%)",
border: "1px solid rgba(255,255,255,0.12)", border: "1px solid rgba(255,255,255,0.12)",
@@ -161,13 +168,16 @@ export function ShareDialog({
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{/* header */} {/* header */}
<div className="flex items-center justify-between mb-4"> <div className="mb-4 flex shrink-0 items-center gap-2">
{/* tab bar */} {/* tab bar */}
<div className="flex gap-1 rounded-[10px] p-1" style={{ background: "rgba(44,44,46,0.8)" }}> <div
className="grid min-w-0 flex-1 grid-cols-3 gap-1 rounded-[10px] p-1"
style={{ background: "rgba(44,44,46,0.8)" }}
>
<button <button
type="button" type="button"
className={cn( className={cn(
"px-3 py-1.5 rounded-[8px] text-[13px] font-medium transition-colors", "h-[34px] truncate rounded-[8px] px-2 text-center text-[13px] font-medium transition-colors",
shareDialogTab === "share" shareDialogTab === "share"
? "bg-[#00FFF6] text-black font-semibold" ? "bg-[#00FFF6] text-black font-semibold"
: "text-white/60 hover:text-white/80", : "text-white/60 hover:text-white/80",
@@ -179,7 +189,7 @@ export function ShareDialog({
<button <button
type="button" type="button"
className={cn( className={cn(
"px-3 py-1.5 rounded-[8px] text-[13px] font-medium transition-colors", "h-[34px] truncate rounded-[8px] px-2 text-center text-[13px] font-medium transition-colors",
shareDialogTab === "import" shareDialogTab === "import"
? "bg-[#00FFF6] text-black font-semibold" ? "bg-[#00FFF6] text-black font-semibold"
: "text-white/60 hover:text-white/80", : "text-white/60 hover:text-white/80",
@@ -191,7 +201,7 @@ export function ShareDialog({
<button <button
type="button" type="button"
className={cn( className={cn(
"px-3 py-1.5 rounded-[8px] text-[13px] font-medium transition-colors", "h-[34px] truncate rounded-[8px] px-2 text-center text-[13px] font-medium transition-colors",
shareDialogTab === "myShares" shareDialogTab === "myShares"
? "bg-[#00FFF6] text-black font-semibold" ? "bg-[#00FFF6] text-black font-semibold"
: "text-white/60 hover:text-white/80", : "text-white/60 hover:text-white/80",
@@ -222,7 +232,7 @@ export function ShareDialog({
</div> </div>
<button <button
type="button" type="button"
className="rounded-full p-1.5 text-white/45 transition-colors hover:bg-white/10 hover:text-white/80 disabled:opacity-40 disabled:pointer-events-none" className="shrink-0 rounded-full p-1.5 text-white/45 transition-colors hover:bg-white/10 hover:text-white/80 disabled:pointer-events-none disabled:opacity-40"
disabled={importSaving} disabled={importSaving}
onClick={onClose} onClick={onClose}
> >
@@ -238,7 +248,7 @@ export function ShareDialog({
{/* EQ preset list */} {/* EQ preset list */}
<div <div
className="rounded-[10px] overflow-hidden max-h-48 overflow-y-auto mb-4" className="mb-4 max-h-48 overflow-hidden overflow-y-auto rounded-[10px]"
style={{ style={{
background: "rgba(10,12,16,0.98)", background: "rgba(10,12,16,0.98)",
border: "1px solid rgba(0,255,246,0.35)", border: "1px solid rgba(0,255,246,0.35)",
@@ -482,7 +492,7 @@ export function ShareDialog({
preamp: eq.preamp as number | undefined, preamp: eq.preamp as number | undefined,
autoPre: eq.autoPre as number | undefined, autoPre: eq.autoPre as number | undefined,
}); });
setImportPresetName(fetchedName); setImportPresetName(clampPeqPresetName(fetchedName));
setImportedSourceModel(res.model ?? null); setImportedSourceModel(res.model ?? null);
setQueriedShareCode(code); setQueriedShareCode(code);
} catch { } catch {
@@ -517,11 +527,13 @@ export function ShareDialog({
</p> </p>
<input <input
value={importPresetName} value={importPresetName}
onChange={(e) => setImportPresetName(e.target.value)} onChange={(e) => setImportPresetName(clampPeqPresetName(e.target.value))}
disabled={importSaving} disabled={importSaving}
maxLength={PEQ_PRESET_NAME_MAX_LENGTH}
className="h-10 w-full rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[14px] text-white/90 outline-none placeholder:text-white/30 focus:border-[#00FFF6]/40 disabled:opacity-60" className="h-10 w-full rounded-[10px] border border-white/12 bg-[#15171b] px-3 text-[14px] text-white/90 outline-none placeholder:text-white/30 focus:border-[#00FFF6]/40 disabled:opacity-60"
placeholder={eqUi.importEqName} placeholder={eqUi.importEqName}
/> />
<PeqPresetNameLengthHint value={importPresetName} />
</div> </div>
<button <button
type="button" type="button"
@@ -552,7 +564,7 @@ export function ShareDialog({
{shareDialogTab === "myShares" && ( {shareDialogTab === "myShares" && (
<> <>
{mySharesLoading && mySharesList.length === 0 ? ( {mySharesLoading && mySharesList.length === 0 ? (
<div className="flex items-center justify-center py-10 gap-2 text-white/50"> <div className="flex items-center justify-center gap-2 py-10 text-white/50">
<Loader2 size={16} className="animate-spin" /> <Loader2 size={16} className="animate-spin" />
<span className="text-[13px]">{eqUi.mySharesLoading}</span> <span className="text-[13px]">{eqUi.mySharesLoading}</span>
</div> </div>
@@ -560,7 +572,7 @@ export function ShareDialog({
<div className="py-10 text-center text-[13px] text-white/30">{eqUi.mySharesEmpty}</div> <div className="py-10 text-center text-[13px] text-white/30">{eqUi.mySharesEmpty}</div>
) : ( ) : (
<div <div
className="rounded-[10px] overflow-hidden max-h-64 overflow-y-auto" className="max-h-64 overflow-hidden overflow-y-auto rounded-[10px]"
style={{ style={{
background: "rgba(10,12,16,0.98)", background: "rgba(10,12,16,0.98)",
border: "1px solid rgba(0,255,246,0.35)", border: "1px solid rgba(0,255,246,0.35)",
+3
View File
@@ -25,6 +25,9 @@ export const BAND_Q_MAX = 10;
/** Maximum headphone PEQ presets stored on device. */ /** Maximum headphone PEQ presets stored on device. */
export const PEQ_PRESET_MAX = 50; export const PEQ_PRESET_MAX = 50;
/** Maximum characters for a custom PEQ preset name (letter, CJK, symbol, or space = 1). */
export const PEQ_PRESET_NAME_MAX_LENGTH = 80;
export type BandParamKind = "freq" | "gain" | "q"; export type BandParamKind = "freq" | "gain" | "q";
/* ── Band param value box style ── */ /* ── Band param value box style ── */
+13
View File
@@ -0,0 +1,13 @@
import { PEQ_PRESET_NAME_MAX_LENGTH } from "./constants";
export function clampPeqPresetName(value: string): string {
return value.slice(0, PEQ_PRESET_NAME_MAX_LENGTH);
}
export function isPeqPresetNameTooLong(value: string): boolean {
return value.trim().length > PEQ_PRESET_NAME_MAX_LENGTH;
}
export function peqPresetNameLengthText(value: string): string {
return `${value.length}/${PEQ_PRESET_NAME_MAX_LENGTH}`;
}
+5 -3
View File
@@ -11,6 +11,7 @@ import {
type BandParamKind, type BandParamKind,
type PeqCatalogItem, type PeqCatalogItem,
} from "./constants"; } from "./constants";
import { clampPeqPresetName } from "./peqPresetName";
/* ── Template interpolation ── */ /* ── Template interpolation ── */
export function eqInterp( export function eqInterp(
@@ -190,12 +191,13 @@ export function getUniquePresetName(
base: string, base: string,
existingNames: string[], existingNames: string[],
) { ) {
if (!existingNames.includes(base)) return base; const clippedBase = clampPeqPresetName(base);
if (!existingNames.includes(clippedBase)) return clippedBase;
let index = 1; let index = 1;
while (existingNames.includes(`${base}_${index}`)) { while (existingNames.includes(clampPeqPresetName(`${clippedBase}_${index}`))) {
index += 1; index += 1;
} }
return `${base}_${index}`; return clampPeqPresetName(`${clippedBase}_${index}`);
} }
/** Normalize shared/imported EQ filters into device PeqFilter[]. */ /** Normalize shared/imported EQ filters into device PeqFilter[]. */
+8
View File
@@ -79,6 +79,14 @@ echo "命令: ${CMD_ARGS[*]}"
echo "==========================================" echo "=========================================="
echo "" echo ""
read -r -p "确认执行部署? 输入 y 继续: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "已取消部署。"
exit 0
fi
echo ""
# 执行同步 # 执行同步
"${CMD_ARGS[@]}" "${CMD_ARGS[@]}"