Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ The release run heads these entries with the version and opens a fresh
being edited, so a reader who tapped a cell twice lost it. A cell now holds
its pin there; a row or column header still clears on that click.

- Editing scope `paragraph` takes `odr.editing.format` inside one paragraph and
refuses it over two, rather than refusing every call. The `formatBold` chords
stay behind the gate.

## v7.1.0 - 2026-09-19

- The pdf view reports the pending annotation count on
Expand Down
11 changes: 9 additions & 2 deletions src/odr/internal/html/frontend/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,8 @@
/// States @p style on @p at, for a host's `format` and for a chord. A
/// collapsed caret marks the word it sits in; at a word boundary, or in a
/// paragraph holding no run, the mark waits for the next typed text.
/// Formatting sits behind the scope gate whole.
/// Scope `paragraph` holds the range, not the style: a mark inside one
/// paragraph opens none.
function format(style, at) {
gesture += 1;
if (!odr.editing.isEnabled()) {
Expand All @@ -767,7 +768,7 @@
refuse(null, "range", at);
return false;
}
if (odr.editing.scope() === "paragraph") {
if (outOfScope(at)) {
refuse(null, "outOfScope", at);
return false;
}
Expand Down Expand Up @@ -1499,6 +1500,12 @@
if (!odr.takesKeys("shortcuts")) {
return;
}
// The host draws no button for a key, so it cannot offer less here.
// The chord keeps the gate `format` no longer carries.
if (odr.editing.scope() === "paragraph") {
refuse(event, "outOfScope", at);
return;
}
toggle(property, at);
return;
}
Expand Down
21 changes: 14 additions & 7 deletions test/browser/text/tests.html
Original file line number Diff line number Diff line change
Expand Up @@ -799,15 +799,17 @@
document.body.setAttribute("data-odr-editing-scope", "paragraph");
check("until the host narrows it", odr.editing.scope() === "paragraph");

// Formatting sits behind the gate whole, so these land in runs no other
// refusal of this group names.
// The gate holds the range, not the style. The chord keeps it: the
// host draws no button for a key, so it cannot offer less.
select(run(12).firstChild, 2);
check("a chord is refused", input("formatBold") === "refused");
check("a chord is still refused", input("formatBold") === "refused");
check("as out of scope", refusals.pop() === "outOfScope 1010");
selectRuns(51, 0, 51, 4);
check("and so is the host's format", odr.editing.format({ bold: true }) === false);
check("as out of scope too", refusals.pop() === "outOfScope 1010");
check("with nothing marked", run(51).getAttribute("style") === null);
check("while the host's own format is taken", odr.editing.format({ bold: true }) === true);
check("with the run marked", run(51).getAttribute("style") !== null);
selectRuns(11, 2, 31, 2);
check("a format over two paragraphs is refused", odr.editing.format({ bold: true }) === false);
check("as out of scope", refusals.pop().indexOf("outOfScope ") === 0);

select(run(11).firstChild, 5);
check("typing inside a run is taken", input("insertText", "X") === "taken");
Expand Down Expand Up @@ -864,7 +866,12 @@
check(
"the log names no paragraph operation",
ops().every(function (op) {
return op.op === "setText" || op.op === "removeElement" || op.op === "insertText";
return (
op.op === "setText" ||
op.op === "setTextStyle" ||
op.op === "removeElement" ||
op.op === "insertText"
);
})
);

Expand Down
Loading