nextcloud-libraries / nextcloud-libraries/nextcloud-vue

[RFC] Support ||spoiler|| syntax in NcRichText

Open
#8,784 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. to triage design enhancement feature: richtext
Dominant language
Vue
Stars
246
Forks
99
Avg merge
1d 20h
Merged PRs (30d)
103

Description

Summary

NcRichText renders user-generated markdown across Nextcloud apps (e.g. Talk). Spoilers are a nice addition in chat/comment contexts.
RFC to add support for inline spoilers in NcRichText using the Discord-style and Telegram-style syntax. Spoiler content renders hidden behind a bar and is revealed on click - before that .

Discord (p.s. images are out of scope of this RFC)
Image

Telegram
Image

Syntax

Markdown has no standard spoiler syntax (see commonmark/commonmark-spec/issues/570)
Suggested convention: ||…|| (double pipes, inline).

This is a ||hidden|| word.
Behavior
  • Gating: extended markdown only (useExtendedMarkdown) — spoilers are an extension, alongside GFM.
  • Reveal: click to reveal. State is per-instance and one-way (once revealed, stays revealed).
  • Accessibility: to be discussed - if revealed on click, maybe pass to readers as-is, or wrap with "spoiler alert" - keyboard a11y should be respected + aria-expanded + descriptive aria-label.
Proposed implementation

Follows the existing custom-plugin pattern in emarkAutolink, remarkPlaceholder,remarkUnescape, remarkStripCode).

  1. remarkSpoiler.ts — remark transformer that visits text nodes, splits on ||…|| (handles multiple matches per node), and emits a dedicated spoiler element node carrying the inner text.
  2. Pipeline registration in NcRichText.vue → renderMarkdown(), gated on useExtendedMarkdown, placed after remarkGfm (verified: GFM leaves ||…|| untouched in normal text, so ordering is safe — see Limitations for the table exception).
  3. NcTextSpoiler.vue — small <script setup> component (sibling of NcRichTextExternalLink.vue) wrapping a with internal revealed ref; hidden state styled via CSS module. (animated or not?)
  4. createElement mapping in NcRichText.vue — branch rendering the spoiler node to NcRichTextSpoiler.
  5. unit tests in remarkSpoiler.spec.js, mirroring autoLink.spec.js, playwrigth tests with kbd a11y and clicks

To discuss - if we make it client-side only, or with server involvement (pass spoiler text with richObjectParameters)

Limitations (v1)
  • No nested inline formatting inside a spoiler: ||bold|| renders the ** literally. Formatting around a spoiler (||x||) works. This matches the library's behavior too; full nesting would require a micromark syntax extension. Should be documented.
  • No spoilers inside GFM table cells: GFM's table tokenizer consumes | as cell delimiters before any transformer runs. Matches Discord/GFM behavior — escape as | in tables. Should be documented.

Alternative: existing library

remark-inline-spoiler implements exactly this syntax via a proper micromark tokenizer (micromark-extension-inline-spoiler + mdast-util-inline-spoiler), but:

  • it is new and has small amount of downloads (although maintained)
  • it also does not support nested markdown
  • it adds a 3-package dependency tree, where we can do it with a small helper as we already have

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing custom remark plugins and renderMarkdown() in NcRichText.vue, then review the proposed remarkSpoiler.ts and NcTextSpoiler.vue boundaries. Use remarkSpoiler.spec.js and the planned Playwright keyboard-accessibility and click tests to define done, including extended-markdown gating, reveal behavior, and documented limitations.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.