fix(编辑能力): 修复字号间距字体等提交丢失
build.yaml / build (push) Successful in 45s

This commit is contained in:
xiaoqidun committed 2026-10-28 20:00:15 +08:00
1 parent 62631472ea
commit 12aaada982
2 files changed
+56 -36

No files matched your search

+43 -34
View File
@@ -702,14 +702,14 @@ const fontManager = new FontManager({
const fontPicker = new FontPicker(el.textFont, el.textFontToggle, el.textFontList, changeTextFont, loadEditorFonts); const fontPicker = new FontPicker(el.textFont, el.textFontToggle, el.textFontList, changeTextFont, loadEditorFonts);
const textFormatButtons = [...document.querySelectorAll("[data-text-format]")]; const textFormatButtons = [...document.querySelectorAll("[data-text-format]")];
const textBackground = document.querySelector("#textBackground"); const textBackground = document.querySelector("#textBackground");
const editorColors = new Map(); const editorInputs = new Map();
const annotationFields = Object.fromEntries(["CreateForm", "CreateCancel", "Content", "Old", "Font", "Text", "TextRow", "Image", "ImageRow", "Author", "LinkKind", "Address", "AddressRow", "Target", "TargetRow", "X", "Y", "Width", "Height", "Angle", "Opacity", "Tile", "Placement", "PlacementLabel", "Pick", "Scope", "Pages"].map(key => [key, document.querySelector(`#annotation${key}`)])); const annotationFields = Object.fromEntries(["CreateForm", "CreateCancel", "Content", "Old", "Font", "Text", "TextRow", "Image", "ImageRow", "Author", "LinkKind", "Address", "AddressRow", "Target", "TargetRow", "X", "Y", "Width", "Height", "Angle", "Opacity", "Tile", "Placement", "PlacementLabel", "Pick", "Scope", "Pages"].map(key => [key, document.querySelector(`#annotation${key}`)]));
el.editorTools.addEventListener("scroll", () => fontPicker.position()); el.editorTools.addEventListener("scroll", () => fontPicker.position());
const canvasEditor = new CanvasEditor(el.viewerPanel, { const canvasEditor = new CanvasEditor(el.viewerPanel, {
textControls: [el.textAlign, el.textFont, el.textFontToggle, el.textFontAdd, el.textFontList, el.textSize, el.textColor, el.textLineHeight, el.textSpacing, el.textWrap, el.paragraphButton, el.paragraphPanel, textBackground, ...textFormatButtons, el.undoButton, el.redoButton, el.saveButton, el.saveAsButton, el.fontInput, document.querySelector("#textReflow")], textControls: [el.textAlign, el.textFont, el.textFontToggle, el.textFontAdd, el.textFontList, el.textSize, el.textColor, el.textLineHeight, el.textSpacing, el.textWrap, el.paragraphButton, el.paragraphPanel, textBackground, ...textFormatButtons, el.undoButton, el.redoButton, el.saveButton, el.saveAsButton, el.fontInput, document.querySelector("#textReflow")],
keepTextFocus: () => fontPicker.open || el.closePanel.open || el.paragraphPanel.open || canvasEditor.input?.fontDialog, keepTextFocus: () => fontPicker.open || el.closePanel.open || el.paragraphPanel.open || canvasEditor.input?.fontDialog,
prepareTextCommit: flushEditorColors, prepareTextCommit: flushEditorInputs,
onTextFormat: changeRangeFormat, onTextFormat: changeRangeFormat,
onRichInput: ensureRichInput, onRichInput: ensureRichInput,
textClipboardScope: () => `${documentTab().id}:${documentContentKey(state)}:${state.editorInfo?.revision ?? ""}`, textClipboardScope: () => `${documentTab().id}:${documentContentKey(state)}:${state.editorInfo?.revision ?? ""}`,
@@ -868,14 +868,14 @@ editorClick(el.editObjectButton, () => {
editCanvasObject(canvasEditor.selected); editCanvasObject(canvasEditor.selected);
} }
}); });
el.textSize.addEventListener("change", () => changeTextStyle(false)); bindEditorInput(el.textSize, () => changeTextStyle(false));
bindEditorColor(el.textColor, () => changeTextStyle(true)); bindEditorInput(el.textColor, () => changeTextStyle(true));
for (const button of textFormatButtons) button.addEventListener("click", () => changeRangeFormat(button.dataset.textFormat)); for (const button of textFormatButtons) button.addEventListener("click", () => changeRangeFormat(button.dataset.textFormat));
bindEditorColor(textBackground, value => changeRangeFormat("background", value)); bindEditorInput(textBackground, value => changeRangeFormat("background", value));
document.addEventListener("pointerdown", flushEditorColors, true); document.addEventListener("pointerdown", flushEditorInputs, true);
editorClick(el.textWrap, () => changeParagraph(!currentTextStyle().wrap), true); editorClick(el.textWrap, () => changeParagraph(!currentTextStyle().wrap), true);
el.textAlign.addEventListener("change", () => changeTextAlignment()); el.textAlign.addEventListener("change", () => changeTextAlignment());
el.textLineHeight.addEventListener("change", () => changeParagraph(undefined, "lineHeight")); bindEditorInput(el.textLineHeight, () => changeParagraph(undefined, "lineHeight"));
editorClick(el.paragraphButton, () => { editorClick(el.paragraphButton, () => {
const item = currentTextStyle(); const item = currentTextStyle();
for (const key of ["leftIndent", "rightIndent", "firstLineIndent"]) { for (const key of ["leftIndent", "rightIndent", "firstLineIndent"]) {
@@ -1210,7 +1210,7 @@ el.paragraphForm.addEventListener("submit", async event => {
if (isForegroundDocument(tab) && el.paragraphPanel.open) el.paragraphStatus.textContent = error.message; if (isForegroundDocument(tab) && el.paragraphPanel.open) el.paragraphStatus.textContent = error.message;
} }
}); });
el.textSpacing.addEventListener("change", () => changeParagraph(undefined, "letterSpacing")); bindEditorInput(el.textSpacing, () => changeParagraph(undefined, "letterSpacing"));
el.shapeWidth.addEventListener("focus", () => { el.shapeWidth.defaultValue = el.shapeWidth.value; }); el.shapeWidth.addEventListener("focus", () => { el.shapeWidth.defaultValue = el.shapeWidth.value; });
for (const input of [el.textSize, el.shapeWidth, el.textLineHeight, el.textSpacing]) { for (const input of [el.textSize, el.shapeWidth, el.textLineHeight, el.textSpacing]) {
input.addEventListener("keydown", (event) => { input.addEventListener("keydown", (event) => {
@@ -1220,6 +1220,9 @@ for (const input of [el.textSize, el.shapeWidth, el.textLineHeight, el.textSpaci
event.stopPropagation(); event.stopPropagation();
if (event.repeat) return; if (event.repeat) return;
if (event.key === "Escape") { if (event.key === "Escape") {
const control = editorInputs.get(input);
control.pending = null;
control.handled = true;
updateObjectControls(canvasEditor.selected, true); updateObjectControls(canvasEditor.selected, true);
if (input === el.shapeWidth && canvasEditor.selected?.type !== "PathObject") { if (input === el.shapeWidth && canvasEditor.selected?.type !== "PathObject") {
input.value = input.defaultValue; input.value = input.defaultValue;
@@ -1306,10 +1309,10 @@ editorClick(el.eraseButton, () => toggleDrawingTool(el.eraseMode.value));
el.eraseMode.addEventListener("change", () => { el.eraseMode.addEventListener("change", () => {
if (canvasEditor.tool.startsWith("erase-")) canvasEditor.setTool(el.eraseMode.value); if (canvasEditor.tool.startsWith("erase-")) canvasEditor.setTool(el.eraseMode.value);
}); });
for (const input of [el.shapeFill, el.shapeStroke, el.shapeWidth]) { for (const input of [el.shapeFill, el.shapeStroke]) {
input.addEventListener("change", changeShapeStyle); input.addEventListener("change", changeShapeStyle);
} }
for (const input of [el.shapeFillColor, el.shapeStrokeColor]) bindEditorColor(input, () => changeShapeStyle({ target: input })); for (const input of [el.shapeFillColor, el.shapeStrokeColor, el.shapeWidth]) bindEditorInput(input, () => changeShapeStyle({ target: input }));
el.addPageButton.addEventListener("change", async () => { el.addPageButton.addEventListener("change", async () => {
const action = el.addPageButton.value; const action = el.addPageButton.value;
el.addPageButton.value = ""; el.addPageButton.value = "";
@@ -2840,6 +2843,10 @@ function detachDocumentView() {
settings.sequence++; settings.sequence++;
fontBinding.sequence++; fontBinding.sequence++;
fontPicker.close(); fontPicker.close();
fontPicker.changing = null;
for (const [input, control] of editorInputs) {
editorInputs.set(input, { ...control, pending: null, task: null, handled: true });
}
canvasEditor.cancelNudge(); canvasEditor.cancelNudge();
canvasEditor.clear(); canvasEditor.clear();
canvasEditor.setTool(""); canvasEditor.setTool("");
@@ -3009,8 +3016,8 @@ async function releaseDocumentTab(tab) {
if (!prepared && workspace.objectClipboard === clipboard) workspace.objectClipboard = null; if (!prepared && workspace.objectClipboard === clipboard) workspace.objectClipboard = null;
} }
tab.closed = true; tab.closed = true;
for (const color of editorColors.values()) { for (const control of editorInputs.values()) {
if (color.pending?.tab === tab) { color.pending = null; color.handled = true; } if (control.pending?.tab === tab) { control.pending = null; control.handled = true; }
} }
tab.print?.destroy(); tab.print?.destroy();
tab.print = null; tab.print = null;
@@ -4215,43 +4222,45 @@ async function reflowCanvasText() {
updateObjectControls(canvasEditor.selected, true); updateObjectControls(canvasEditor.selected, true);
} }
function bindEditorColor(input, change) { function bindEditorInput(input, change) {
const color = { input, change, pending: null, task: null, handled: false }; editorInputs.set(input, { input, change, pending: null, task: null, handled: false });
editorColors.set(input, color);
const remember = () => { const remember = () => {
color.pending = { value: input.value, tab: documentTab(), content: documentContentKey(state), editing: canvasEditor.input, selected: canvasEditor.selected }; const control = editorInputs.get(input);
color.handled = false; control.pending = { value: input.value, tab: documentTab(), content: documentContentKey(state), editing: canvasEditor.input, selected: canvasEditor.selected };
control.handled = false;
}; };
input.addEventListener("input", remember); input.addEventListener("input", remember);
input.addEventListener("change", () => { input.addEventListener("change", () => {
const control = editorInputs.get(input);
let task; let task;
if (!color.handled) { if (!control.handled) {
if (!color.pending) remember(); if (!control.pending) remember();
task = flushEditorColor(color); task = flushEditorInput(control);
} }
color.handled = false; control.handled = false;
return task; return task;
}); });
} }
function flushEditorColor(color) { function flushEditorInput(control) {
if (color.task || !color.pending) return color.task; if (control.task || !control.pending) return control.task;
color.task = (async () => { control.task = (async () => {
while (color.pending) { while (control.pending) {
const pending = color.pending; const pending = control.pending;
color.pending = null; control.pending = null;
color.handled = true; control.handled = true;
if (!isForegroundDocument(pending.tab) || pending.content !== documentContentKey(state) if (!isForegroundDocument(pending.tab) || pending.content !== documentContentKey(state)
|| pending.editing !== canvasEditor.input || pending.selected !== canvasEditor.selected) continue; || pending.editing !== canvasEditor.input || pending.selected !== canvasEditor.selected) continue;
color.input.value = pending.value; control.input.value = pending.value;
await color.change(pending.value); await control.change(pending.value);
} }
})().finally(() => { color.task = null; }); })().finally(() => { control.task = null; });
return color.task; return control.task;
} }
function flushEditorColors() { function flushEditorInputs(event) {
const tasks = [...editorColors.values()].map(flushEditorColor).filter(Boolean); const tasks = [...editorInputs.values()].filter(control => !event || !control.input.contains(event.target))
.map(flushEditorInput).concat(fontPicker.prepareCommit(event)).filter(Boolean);
return tasks.length ? Promise.all(tasks) : null; return tasks.length ? Promise.all(tasks) : null;
} }
+13 -2
View File
@@ -131,7 +131,10 @@ export class FontPicker {
const changed = this.value !== String(index); const changed = this.value !== String(index);
this.value = String(index); this.value = String(index);
this.close(); this.close();
if (changed) return this.onChange(); if (changed) {
const task = this.changing = Promise.resolve(this.onChange());
return task.finally(() => { if (this.changing === task) this.changing = null; });
}
} }
commitExact() { commitExact() {
@@ -140,7 +143,15 @@ export class FontPicker {
&& [font.fullName, font.name, font.postscriptName, ...(font.names || [])].some(name => name?.toLocaleLowerCase() === value); && [font.fullName, font.name, font.postscriptName, ...(font.names || [])].some(name => name?.toLocaleLowerCase() === value);
const selected = Number(this.value); const selected = Number(this.value);
const index = matches(this.fonts[selected]) ? selected : this.fonts.findIndex(matches); const index = matches(this.fonts[selected]) ? selected : this.fonts.findIndex(matches);
if (index >= 0) this.choose(index); if (index >= 0) return this.choose(index);
}
prepareCommit(event) {
if (this.open && (!event || ![this.input, this.toggle, this.list].some(node => node.contains(event.target)))) {
this.commitExact();
this.close();
}
return this.changing;
} }
close() { close() {