Skip to content

Bring back underline, strikethrough and align support, and remove RTE link support - #6151

Merged
rtibbles merged 6 commits into
learningequality:unstablefrom
AlexVelezLl:qti-style-attr
Sep 29, 2026
Merged

rtibbles merged 6 commits into
learningequality:unstablefrom
AlexVelezLl:qti-style-attr

Conversation

@AlexVelezLl

@AlexVelezLl AlexVelezLl commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Brings back the underline, strikethrough, and align buttons.
    • Tweaks the backend assessment item viewset's convert, markdown-to-HTML (for already existing items), and HTML-to-markdown (for Perseus file generation from QTI items) flows to support it.
    • Now QTI exercise generator supports the style attribute and filters allowed style rules.
  • Disconnects link support from TipTapEditor
    • LinkBubbleMenu, LinkEditor, useLinkHandling, the CustomLink extension and its dependency all stay in the codebase in case we want to reuse them in the future — only the wiring is removed.

Behaviour changes worth knowing about

  • __x__ now publishes as an underline instead of <strong>, which is what the RTE and Perseus already show for it. Published packages only change when their channel is republished.
    • Two shapes stay as literal underscores in QTI, because CommonMark does not read them as emphasis while the RTE does: foo__bar__baz (intraword) and a __ spaced __ one (space inside the delimiters).
  • Editing an item strips color. The conversion keeps it, but the RTE schema has no colour mark, so it is dropped on load and not written back on save.

References

Closes #6138.

Reviewer guidance

  • Check that a non-qti item on the backend with ~~ of __ markdown styles render correctly in the RTE.
  • Check that a non-QTI item on the backend with ~~ or __ markdown styles can be correctly published as a QTI package.
  • Check that new items having strikethrough and underline styles can be correctly published as both QTI and Perseus packages.
  • Check that the space between two adjacent styled runs (~~a~~ __b__) survives conversion rather than the words running together.
  • Check that copy-pasting links does not bring the link in. And LinkEditor should not appear.
  • Check that copy-pasting content from Word or Google Docs keeps working correctly.
  • Check that copy-pasting content with styles gets their styles removed if they are not part of the allowed styles list.

AI usage

Used Claude to research and implement what was needed to enable these buttons again, and to disconnect (but not remove) the link support. I reviewed all its outputs and iteratively polished its results.

@radinamatic radinamatic added bug P1 - important Priority: High impact on UX DEV: frontend labels Sep 21, 2026
@AlexVelezLl
AlexVelezLl force-pushed the qti-style-attr branch 2 times, most recently from 1bdada4 to 7a149c2 Compare September 23, 2026 15:04
@AlexVelezLl
AlexVelezLl marked this pull request as ready for review September 23, 2026 16:29
@AlexVelezLl
AlexVelezLl requested review from a team, marcellamaki and rtibblesbot September 23, 2026 16:29
@rtibblesbot

rtibblesbot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

🟡 Waiting for changes

Last updated: 2026-09-29 18:50 UTC

@rtibbles rtibbles self-assigned this Sep 23, 2026

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: legacy conversion merges words that sit between two decorated runs. Otherwise, strikethrough and underline now publish as valid QTI.

CI passing. Manual QA covered desktop and mobile editor, paste, legacy conversion, and publish functions run locally.

  • Decorations and alignment save and survive reload.

  • The sync payload carries only <span style="text-decoration: …">.

  • Server-side apply wasn't observed; the QA stack had no Celery worker.

  • axe found nothing new.

  • blocking: adjacent decorated runs lose the space between them (convert.py)

  • suggestion: link removal is outside #6138 and leaves orphaned code; move it to its own PR

  • suggestion: __x__ now publishes as underline instead of bold; state this in the PR body

  • suggestion: the editor drops color on first save

  • suggestion: the align test can't fail

  • suggestion: the Docs paste tests skip decoration

  • suggestion: the <s> reverse test was replaced rather than kept

  • nitpick: clashing decorations override each other


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran a phased review pipeline over the pull request diff:

  • Classified the diff to select review passes (core, frontend, backend) and whether manual QA was required
  • Core review pass checked correctness, design, architecture, testing, completeness, and DRY/SRP/Rule-of-Three principles
  • Specialized frontend/backend review passes applied framework-specific lenses where those files changed
  • For UI changes: manual QA and an accessibility audit against a live dev server, when available
  • Checked CI status and linked issue acceptance criteria
  • Synthesized one review from those passes and chose the verdict from the findings, CI status, and QA evidence

Comment thread contentcuration/contentcuration/utils/assessment/qti/convert.py
Comment thread contentcuration/contentcuration/utils/assessment/markdown.py
Comment thread contentcuration/contentcuration/utils/assessment/qti/constants.py
Comment thread contentcuration/contentcuration/utils/assessment/qti/convert.py Outdated

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: a space before inline math is still dropped (blocking, inline on base.py).

  • 8 of 9 prior findings are resolved.
  • Link removal (useEditor.js) is contested. It is still out of scope for #6138. Move it to its own PR.

CI pending. Manual QA did not run.

Prior-finding status

RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py:168 — Adjacent decorated runs lose the whitespace between them (math siblings still affected; see new blocking)
CONTESTED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js:31 — Link removal is outside #6138 and only half done
RESOLVED — contentcuration/contentcuration/utils/assessment/markdown.py:221 — __x__ now publishes as underline
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/constants.py:81 — Editor drops color on first save
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useToolbarActions.spec.js — Align test can't fail
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useEditor.spec.js — Docs paste tests skip decoration
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_html_to_markdown.py:62 — Restore <s> assertion
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py — Clashing decorations override each other
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/TextDecoration.js:17 — praise: renderHTML override


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

Comment thread contentcuration/contentcuration/utils/assessment/qti/base.py Outdated
Comment thread contentcuration/contentcuration/tests/utils/qti/test_convert.py

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: no new findings. All 10 prior findings are resolved or acknowledged. CI is pending. Manual QA did not run.

Prior-finding status

RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py:168 — Adjacent decorated runs lose the whitespace between them
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/base.py — Space before inline math still dropped
RESOLVED — contentcuration/contentcuration/utils/assessment/markdown.py:221 — Every stored __x__ publishes as an underline
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/constants.py:81 — Editor drops color on first save
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useToolbarActions.spec.js — Align test passes even if the button is hidden
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useEditor.spec.js — Tests don't cover anything this branch changes
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_html_to_markdown.py:62 — s/del/strike → ~~ no longer tested
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py — Clashing decorations override instead of combining
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/TextDecoration.js:17 — Overriding only renderHTML (praise)
ACKNOWLEDGED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js:31 — Link removal outside #6138 (kept on purpose per maintainer)


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

options: OptionsDict,
env: EnvType,
) -> str:
return f'<span style="text-decoration: {STRIKETHROUGH_DECORATION}">'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: the editor writes style="text-decoration: underline;" but the backend writes it without the trailing semicolon, so an item saved from the RTE and one converted here differ for the same content. Could the backend add the ; too? Same for the underline span at L232 and the style joins in convert.py (_add_decoration, _filter_style).

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: all 11 prior findings resolved; no new findings.

Javascript Tests failed on 18671f6. No frontend file changed since it passed on 10a82ab. Please rerun it.

Prior-finding status

RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py:168 — Adjacent decorated runs lose the whitespace between them
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js:31 — Link removal is outside #6138 and only half done
RESOLVED — contentcuration/contentcuration/utils/assessment/markdown.py:221 — Every stored __x__ now publishes as an underline
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/constants.py:81 — Editor drops color on first save
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useToolbarActions.spec.js — Test passes even if align button hidden
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useEditor.spec.js — Tests don't cover anything this branch changes
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_html_to_markdown.py:62 — s/del/strike → ~~ no longer tested
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py — Clashing decorations override instead of combining
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/TextDecoration.js:17 — (praise) Overriding only renderHTML
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/base.py — Space before inline math dropped
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_convert.py:443 — (praise) Whitespace fix tested from both sides


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: all 11 prior findings are resolved. No new findings. CI is pending. Manual QA did not run.

Prior-finding status

RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py:168 — Adjacent decorated runs lose the whitespace between them
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js:31 — Link removal is outside #6138 and only half done
RESOLVED — contentcuration/contentcuration/utils/assessment/markdown.py:221 — Every stored __x__ now publishes as an underline
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/constants.py:81 — Editor drops color on first save
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useToolbarActions.spec.js — Test passes even if align button hidden
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useEditor.spec.js — Tests don't cover anything this branch changes
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_html_to_markdown.py:62 — s/del/strike → ~~ no longer tested
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py — Clashing decorations override instead of combining
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/TextDecoration.js:17 — (praise) Overriding only renderHTML
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/base.py — Space before inline math still dropped
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_convert.py:443 — (praise) Whitespace fix tested from both sides


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

AlexVelezLl and others added 5 commits September 29, 2026 09:36
QTI 3.0 declares no style attribute, and the HTML profile it admits has no
element for a text decoration and no attribute for an alignment. The item XSD
does accept one: every element definition carries a lax attribute wildcard
(extensionAnyTypeLax.Attr, namespace ##any), and Studio does not run the
Schematron that would tighten it. The alternative — shipping a qti-stylesheet
and a vocabulary of classes with every item — asks every renderer to agree on
that vocabulary before anything displays.

The models forbid extra fields, so until now an item carrying a style was
refused before it ever reached the XSD. That was not only a limit on what the
editor could write: converting a legacy question whose markdown held a raw
<p style="text-align: right"> raised a pydantic ValidationError inside
ExerciseArchiveGenerator, which nothing there guards, so the channel publish
failed on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A legacy question could say ~~not four~~ or __underlined__, and the conversion
threw both away, because the QTI 3.0 HTML profile has no <s> and no <u> to put
them in. Now that a span may carry a style, each decoration survives the trip as
a text-decoration on a <span>, which the profile does have.

Underline needs the parser changed as well as the renderer. CommonMark spells
strong two ways, so markdown-it reads __x__ as <strong>; Perseus simple-markdown
-- which renders every legacy exercise in Kolibri, and which the editor's
serializer was written to match -- reads it as <u>. Leaving it as strong turned
an author's underline into bold, both in the published package and in the editor,
which converts a legacy item to QTI on read. The two spellings are told apart by
the token's markup, so ** keeps its <strong>.

An author's raw <u> or <s> is neither case: markdown-it passes raw HTML through
as an opaque chunk with no token to hang a render rule on, so those are rewritten
a layer later, where _strip_unsupported_markup used to unwrap them. It now adapts
rather than strips, and only <a> and <mark> still lose their tag -- an anchor has
nothing to navigate to on a device with no internet access, and no single
declaration says what a <mark> means.

An author's own style attribute is filtered rather than refused, down to the
properties Kolibri's SafeHTML will render, so a declaration it would drop is
dropped here instead of travelling as far as the learner. The property name is
lowercased on the way out: the reverse conversion and Kolibri's allowlist both
match a property by name, so an authored TEXT-DECORATION leaves in the one
spelling everything downstream looks for. The value is passed through untouched,
since nothing reads it by name and normalizing it would edit content.

html_to_markdown reads both decorations back off the span, whatever their casing,
so the Perseus archive derived from a QTI item keeps them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All three were withdrawn for the same reason: the QTI 3.0 HTML profile has no
<u> and no <s>, and TextAlign expresses an alignment as a style, which the models
refused. With the style attribute allowed, the two decorations have somewhere to
live -- a <span> carrying a text-decoration, which the profile does have -- and
the alignment button needs nothing but unhiding.

Only the output changes. Upstream's parse rules already read text-decoration off
a style attribute, so a span written here comes back as the mark that wrote it,
and a pasted <u> or <s> still arrives as one.

The two marks stay switched off in StarterKit and are re-registered from
extensions/TextDecoration.js, which overrides renderHTML alone. Nothing can reach
the <u> and <s> renderers that way: restoring the buttons without restoring the
marks themselves would have left the keyboard shortcuts and the paste path
producing content that could not be saved, which is why they were dropped from
the schema in the first place.

alignActionHidden goes with them, and with it the group-level hide the desktop
toolbar carried for its sake; the action-level flag stays, since the link tool
still uses it.

The paste path grows tests of its own. The schema is what decides which pasted
declarations survive -- it turns font-weight into <strong>, font-style into <em>,
vertical-align into <sup> and text-decoration into a decorated span, then drops
every declaration it has no mark for -- so the transform must not filter those
away before it can read them. Google Docs is the case that exercises all of it:
it wraps its clipboard HTML in a <b style="font-weight:normal">, the exact
declaration tiptap's Bold rule disqualifies the tag by.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
QTI's <a> takes a LocalHrefPath: validate_local_href_path rejects any scheme,
netloc or leading slash, so a link to anywhere off the device was never
publishable, and the legacy conversion unwraps every anchor it finds, since one
has nothing to navigate to on a device with no internet access. The editor should
not offer a tool whose output an item refuses or the conversion discards.

The toolbar button was already hidden behind the action's `hide` flag, but hiding
it was not enough: the mark stayed in the schema, so the keyboard shortcut and
the paste path could still produce an anchor the item could not hold. Unregistering
CustomLink is what actually closes it, and the editor no longer mounts the bubble
menu or the link editor that drove it.

Nothing is deleted. LinkBubbleMenu, LinkEditor, useLinkHandling, the CustomLink
extension, the two icons, the strings, the mark's markdown serialization and the
@tiptap/extension-link dependency all stay where they are, so another site can
mount them; the editor simply no longer wires them up. Restoring them here is
registering the extension again and putting back the two blocks in TipTapEditor.vue.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The editor serializes a decorated span as `text-decoration: underline;`,
so an item converted on the backend should carry the same style string
for the same content.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: all 11 prior findings are resolved. One new suggestion is inline.

CI is pending. Manual QA did not run, so the UI is unverified.

Prior-finding status

RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py:168 — adjacent decorated runs lose whitespace
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js:57 — link removal outside #6138, half done
RESOLVED — contentcuration/contentcuration/utils/assessment/markdown.py:221 — stored __x__ publishes as underline
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/constants.py:81 — editor drops color on first save
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useToolbarActions.spec.js — align test passes if button hidden
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useEditor.spec.js — tests cover nothing this branch changes
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_html_to_markdown.py:62 — s/del/strike → ~~ untested
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py — clashing decorations override instead of combining
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/TextDecoration.js:17 — renderHTML-only override (praise)
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/base.py — space before inline math dropped
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_convert.py:443 — whitespace fix tested from both sides (praise)


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

Disconnecting link support dropped `editor` from TipTapEditor's setup
return, and the minimize test reached into the instance for it. Emit
`ready` with the editor once it is usable, as learningequality#6192 does, and have the
test take it from there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6151: all 11 prior findings are resolved. No new findings. Not approving yet, because CI is pending. Manual QA also did not run.

Prior-finding status

RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py:168 — adjacent decorated runs lose whitespace
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js:57 — link removal outside #6138, half done
RESOLVED — contentcuration/contentcuration/utils/assessment/markdown.py:221 — stored __x__ publishes as underline
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/constants.py:81 — editor drops color on first save
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useToolbarActions.spec.js — align test passes when hidden
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/tests/useEditor.spec.js — tests don't cover branch changes
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_html_to_markdown.py:62 — s/del/strike → ~~ untested
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/convert.py — clashing decorations override instead of combining
RESOLVED — contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/TextDecoration.js:17 — overriding only renderHTML (praise)
RESOLVED — contentcuration/contentcuration/utils/assessment/qti/base.py — space before inline math dropped
RESOLVED — contentcuration/contentcuration/tests/utils/qti/test_convert.py:443 — whitespace fix tested from both sides (praise)


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

@rtibbles rtibbles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small nitpick addressed, this is good to go.

@rtibbles
rtibbles merged commit 1beec6e into learningequality:unstable Sep 29, 2026
13 checks passed
@AlexVelezLl
AlexVelezLl deleted the qti-style-attr branch September 30, 2026 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug DEV: frontend P1 - important Priority: High impact on UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[QTI] Cannot publish a channel because of an exercise containing strikethrough formatted text

4 participants