Skip to content

feat: add x-markdown component for rendering markdown content with st…#2190

Open
PupilTong wants to merge 6 commits intolynx-family:mainfrom
PupilTong:p/hw/support-x-markdown
Open

feat: add x-markdown component for rendering markdown content with st…#2190
PupilTong wants to merge 6 commits intolynx-family:mainfrom
PupilTong:p/hw/support-x-markdown

Conversation

@PupilTong
Copy link
Copy Markdown
Collaborator

@PupilTong PupilTong commented Feb 3, 2026

…yling support

Summary by CodeRabbit

  • New Features

    • Adds an x-markdown component: incremental append rendering, presentational styles, markdown-style attribute overrides, and link/image interaction events with content context.
  • Tests

    • Adds comprehensive Playwright tests and multiple interactive fixtures covering rendering, styling, events, images, and incremental updates.
  • Documentation

    • Updated test guidance: use dedicated spec files and install Playwright browsers before running tests.
  • Packaging

    • Exposes XMarkdown as an opt-in import path.

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@PupilTong PupilTong requested a review from Sherry-hue as a code owner February 3, 2026 12:38
@PupilTong PupilTong self-assigned this Feb 3, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 3, 2026

🦋 Changeset detected

Latest commit: d9fb103

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@lynx-js/web-elements Patch
@lynx-js/web-core-wasm Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new XMarkdown web component (parser, incremental renderer, markdown-style CSS injection, link/image events), exposes it via package exports, updates templates and CSS imports, and adds Playwright tests and HTML fixtures plus test-run guidance.

Changes

Cohort / File(s) Summary
Docs & package surface
\.github/lynx-stack.instructions.md, packages/web-platform/web-elements/package.json, packages/web-platform/web-elements/index.css, packages/web-platform/web-elements/tests/fixtures/shell-project.ts
Updated Playwright test guidance and browser install instructions; added ./XMarkdown export; imported x-markdown.css; added dompurify, markdown-it, and @types/markdown-it; added shell import for XMarkdown.
XMarkdown implementation
packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdown.ts, packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts, packages/web-platform/web-elements/src/elements/XMarkdown/index.ts, packages/web-platform/web-elements/src/elements/XMarkdown/x-markdown.css
New x-markdown component and attributes class implementing MarkdownIt parsing, DOMPurify sanitization, markdown-style JSON→CSS injection, debounced/incremental rendering, anchor/image click event dispatch (bindlink, bindimageTap), lifecycle and cleanup, and base styles.
Templates
packages/web-platform/web-elements/src/elements/htmlTemplates.ts
Added templateXMarkdown; wrapped many template exports with /#PURE/ wrappers and adjusted some image/XSS-related template exports.
Tests & fixtures
packages/web-platform/web-elements/tests/x-markdown.spec.ts, packages/web-platform/web-elements/tests/fixtures/x-markdown/*.html, packages/web-platform/web-elements/tests/fixtures/*
Added Playwright test suite for x-markdown and fixtures (basic, events, image, style, incremental); tests cover rendering, styling overrides, images, events, incremental append behavior.
Changeset
.changeset/nasty-lizards-refuse.md
Added changeset documenting opt-in import pattern and example import for XMarkdown.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • Sherry-hue
  • HuJean

Poem

🐇 I nibble at markers, turn stars into trees,
I tuck style notes into shadowed leaves.
I catch a clicked link, I blink at an image,
I stitch tiny CSS to each little hinge.
Hopping through markup, I render with ease.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main addition: a new x-markdown component for rendering markdown content with styling support, which aligns with the substantial changeset across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/web-platform/web-elements/src/elements/htmlTemplates.ts (1)

89-98: ⚠️ Potential issue | 🟠 Major

Regex with global flag causes inconsistent behavior in .test() calls.

The XSSDetector regex uses the global flag (/g), which maintains lastIndex state between .test() calls. This causes alternating true/false results for consecutive matching inputs on the same regex instance.

🐛 Proposed fix: remove the global flag
-const XSSDetector = /*#__PURE__*/ /<\s*script/g;
+const XSSDetector = /*#__PURE__*/ /<\s*script/i;

The i flag (case-insensitive) is more useful here for catching <SCRIPT variants, while removing g eliminates the stateful behavior issue.

🤖 Fix all issues with AI agents
In `@packages/web-platform/web-elements/package.json`:
- Around line 61-65: Add the missing public API import for the XMarkdown element
by editing src/elements/all.ts and following the existing pattern: add an import
statement for './XMarkdown/index.js' alongside the other element imports so
XMarkdown is bundled and exported; ensure the import path matches the
package.json export entry and that it is placed with the other element imports
in src/elements/all.ts.

In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`:
- Around line 192-208: The markdown HTML returned by
markdownParser.render(this.#content) is injected directly into the DOM in
`#render`(), risking XSS if `#content` is user-controlled; update `#render`() to
sanitize the rendered HTML before assigning to root.innerHTML (e.g., pass the
output through a sanitizer like DOMPurify or use the markdown parser's
safe/render-to-fragment APIs), or render into a DocumentFragment and sanitize
links/attributes, and ensure all uses of markdownParser.render and assignments
to root.innerHTML are replaced with the sanitized/safer output path.

In `@packages/web-platform/web-elements/src/types/markdown-it.d.ts`:
- Line 1: Remove the empty ambient module declaration "declare module
'markdown-it';" which overrides the installed `@types/markdown-it` and causes all
imports to be typed as any; either delete this declaration file entirely or
replace it with a proper re-export of the upstream types (e.g., export * from
'markdown-it' and export {default} from 'markdown-it') so imports pick up the
correct typings from `@types/markdown-it`.
🧹 Nitpick comments (2)
packages/web-platform/web-elements/src/elements/htmlTemplates.ts (1)

409-411: Inconsistent template pattern: templateXSvg is now a function returning a string.

Other templates are either string constants or functions with parameters (like templateXImage). This parameterless function wrapper seems unnecessary and inconsistent with the pattern used elsewhere.

♻️ Suggested simplification
-export const templateXSvg = /*#__PURE__*/ () => {
-  return `<img part="img" alt="" loading="lazy" id="img" /> `;
-};
+export const templateXSvg = /*#__PURE__*/ `<img part="img" alt="" loading="lazy" id="img" /> `;
packages/web-platform/web-elements/src/elements/XMarkdown/x-markdown.css (1)

6-11: Consider wiring Lynx linear-layout CSS variables.

If x-markdown should participate in Lynx linear layout, expose the custom properties so the engine can override layout without hard-coded flex.

♻️ Suggested tweak
x-markdown {
+  --lynx-display: linear;
+  --lynx-linear-orientation: column;
+  --lynx-linear-weight: 0;
+  --lynx-linear-weight-sum: 0;
+  --lynx-linear-weight-basis: auto;
   display: flex;
   flex-direction: column;
   align-items: stretch;
   color: inherit;
}

As per coding guidelines, Use custom CSS properties to control linear layout behavior: --lynx-display: linear, --lynx-linear-orientation, --lynx-linear-weight, --lynx-linear-weight-sum, and --lynx-linear-weight-basis.

Comment thread packages/web-platform/web-elements/package.json
Comment thread packages/web-platform/web-elements/src/types/markdown-it.d.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`:
- Around line 160-165: The dispose() method currently removes click listeners
but doesn't cancel any pending append flush timers, which can still run and
modify the shadow DOM after disconnect; update dispose() to clear any scheduled
append timers (e.g., clearTimeout/clearInterval on the append flush handle(s)
used elsewhere in this class — look for symbols like appendFlushTimer,
`#appendFlushTimer`, scheduleAppendFlush, flushAppends or similar), null out or
reset those timer fields, and ensure no further flush is attempted (set any
pending flags to false) before setting `#eventsAttached` = false and returning.

Comment on lines +160 to +165
dispose() {
if (!this.#eventsAttached) return;
const root = this.#root();
root.removeEventListener('click', this.#handleClick);
this.#eventsAttached = false;
}
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.

⚠️ Potential issue | 🟡 Minor

Clear pending append timers on dispose to avoid post-disconnect DOM writes.
A scheduled append flush can still fire after dispose() and touch the shadow DOM.

🧹 Proposed fix
  dispose() {
    if (!this.#eventsAttached) return;
+    this.#clearAppendFlushTimer();
+    this.#appendRemainder = '';
+    this.#pendingRender = false;
     const root = this.#root();
     root.removeEventListener('click', this.#handleClick);
     this.#eventsAttached = false;
  }
🤖 Prompt for AI Agents
In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`
around lines 160 - 165, The dispose() method currently removes click listeners
but doesn't cancel any pending append flush timers, which can still run and
modify the shadow DOM after disconnect; update dispose() to clear any scheduled
append timers (e.g., clearTimeout/clearInterval on the append flush handle(s)
used elsewhere in this class — look for symbols like appendFlushTimer,
`#appendFlushTimer`, scheduleAppendFlush, flushAppends or similar), null out or
reset those timer fields, and ensure no further flush is attempted (set any
pending flags to false) before setting `#eventsAttached` = false and returning.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`:
- Line 6: CI fails because dompurify lacks bundled TypeScript declarations; add
the dev type package and update lockfile by installing `@types/dompurify` for the
`@aspect/web-elements` package (this will satisfy the import createDOMPurify in
XMarkdownAttributes.ts). Run: pnpm add -D `@types/dompurify` --filter
`@aspect/web-elements` and commit the updated package.json and pnpm-lock.yaml.
- Around line 48-52: The sanitizeHtml helper currently returns the raw value
when getHtmlSanitizer() is null (fail-open); change it to fail-closed by
returning a safe fallback (e.g., an empty string or an escaped/plain-text
representation) when getHtmlSanitizer() returns null. Update the sanitizeHtml
function (referenced by name) to check the sanitizer result and, instead of
returning the unsanitized value, return the chosen safe fallback so no raw HTML
is ever passed through if sanitizer initialization fails.

// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
*/
import createDOMPurify from 'dompurify';
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.

⚠️ Potential issue | 🟡 Minor

Install type declarations for dompurify to fix CI failure.

The CI check is failing because dompurify doesn't bundle TypeScript declarations. Install @types/dompurify as a dev dependency.

pnpm add -D `@types/dompurify` --filter `@aspect/web-elements`
🧰 Tools
🪛 GitHub Check: code-style-check

[failure] 6-6:
Could not find a declaration file for module 'dompurify'. '/home/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/dompurify@3.0.8/node_modules/dompurify/dist/purify.cjs.js' implicitly has an 'any' type.

🤖 Prompt for AI Agents
In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`
at line 6, CI fails because dompurify lacks bundled TypeScript declarations; add
the dev type package and update lockfile by installing `@types/dompurify` for the
`@aspect/web-elements` package (this will satisfy the import createDOMPurify in
XMarkdownAttributes.ts). Run: pnpm add -D `@types/dompurify` --filter
`@aspect/web-elements` and commit the updated package.json and pnpm-lock.yaml.

Comment on lines +48 to +52
const sanitizeHtml = (value: string) => {
const sanitizer = getHtmlSanitizer();
if (!sanitizer) return value;
return sanitizer.sanitize(value, { USE_PROFILES: { html: true } }) as string;
};
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.

⚠️ Potential issue | 🟠 Major

Fail-closed on sanitizer initialization failure to prevent XSS.

If getHtmlSanitizer() returns null (due to initialization error), the function currently returns the raw unsanitized value. This fail-open behavior is a security risk—if DOMPurify fails to load, user content would be rendered without sanitization.

🛡️ Proposed fix: fail-closed behavior
 const sanitizeHtml = (value: string) => {
   const sanitizer = getHtmlSanitizer();
-  if (!sanitizer) return value;
+  if (!sanitizer) return ''; // Fail-closed: render nothing if sanitizer unavailable
   return sanitizer.sanitize(value, { USE_PROFILES: { html: true } }) as string;
 };

Alternatively, you could escape the content as plain text, but returning an empty string is safer and more predictable.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sanitizeHtml = (value: string) => {
const sanitizer = getHtmlSanitizer();
if (!sanitizer) return value;
return sanitizer.sanitize(value, { USE_PROFILES: { html: true } }) as string;
};
const sanitizeHtml = (value: string) => {
const sanitizer = getHtmlSanitizer();
if (!sanitizer) return ''; // Fail-closed: render nothing if sanitizer unavailable
return sanitizer.sanitize(value, { USE_PROFILES: { html: true } }) as string;
};
🤖 Prompt for AI Agents
In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`
around lines 48 - 52, The sanitizeHtml helper currently returns the raw value
when getHtmlSanitizer() is null (fail-open); change it to fail-closed by
returning a safe fallback (e.g., an empty string or an escaped/plain-text
representation) when getHtmlSanitizer() returns null. Update the sanitizeHtml
function (referenced by name) to check the sanitizer result and, instead of
returning the unsanitized value, return the chosen safe fallback so no raw HTML
is ever passed through if sanitizer initialization fails.

@PupilTong PupilTong force-pushed the p/hw/support-x-markdown branch from ada5d42 to 543fff4 Compare February 5, 2026 06:11
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Feb 5, 2026

Merging this PR will degrade performance by 69.33%

❌ 1 regressed benchmark
✅ 71 untouched benchmarks
⏩ 3 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
basic-performance-large-css 16.1 ms 52.4 ms -69.33%

Comparing PupilTong:p/hw/support-x-markdown (d9fb103) with main (d32c4c6)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci bot commented Feb 5, 2026

Web Explorer

#7919 Bundle Size — 383.64KiB (+0.03%).

d9fb103(current) vs d32c4c6 main#7915(baseline)

Bundle metrics  Change 3 changes Regression 1 regression
                 Current
#7919
     Baseline
#7915
No change  Initial JS 154.63KiB 154.63KiB
Regression  Initial CSS 35.21KiB(+0.32%) 35.1KiB
Change  Cache Invalidation 9.15% 49.49%
No change  Chunks 8 8
No change  Assets 8 8
No change  Modules 239 239
No change  Duplicate Modules 16 16
Change  Duplicate Code 2.98%(-0.33%) 2.99%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  Change 1 change Regression 1 regression
                 Current
#7919
     Baseline
#7915
No change  JS 252.58KiB 252.58KiB
No change  Other 95.85KiB 95.85KiB
Regression  CSS 35.21KiB (+0.32%) 35.1KiB

Bundle analysis reportBranch PupilTong:p/hw/support-x-markdow...Project dashboard


Generated by RelativeCIDocumentationReport issue

@PupilTong PupilTong force-pushed the p/hw/support-x-markdown branch from 7a95b9a to fab3080 Compare February 5, 2026 08:32
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.changeset/nasty-lizards-refuse.md:
- Line 7: Fix the typo in the changeset text by replacing the string
"x-markdonw" with "x-markdown" in the .changeset/nasty-lizards-refuse.md file
(search for the exact token "x-markdonw" and update it to "x-markdown" so the
description reads correctly).


feat: add x-markdown support

The x-markdonw is under opt-in importing pattern.
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.

⚠️ Potential issue | 🟡 Minor

Fix typo: “x-markdonw” → “x-markdown”.

Minor spelling issue in the changeset description.

✍️ Proposed fix
-The x-markdonw is under opt-in importing pattern.
+The x-markdown is under opt-in importing pattern.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The x-markdonw is under opt-in importing pattern.
The x-markdown is under opt-in importing pattern.
🧰 Tools
🪛 LanguageTool

[grammar] ~7-~7: Ensure spelling is correct
Context: ...-- feat: add x-markdown support The x-markdonw is under opt-in importing pattern. ```...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
In @.changeset/nasty-lizards-refuse.md at line 7, Fix the typo in the changeset
text by replacing the string "x-markdonw" with "x-markdown" in the
.changeset/nasty-lizards-refuse.md file (search for the exact token "x-markdonw"
and update it to "x-markdown" so the description reads correctly).

@PupilTong PupilTong force-pushed the p/hw/support-x-markdown branch from 62d3190 to d9fb103 Compare March 3, 2026 08:38
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
.changeset/nasty-lizards-refuse.md (1)

7-7: ⚠️ Potential issue | 🟡 Minor

Fix typo in changeset description.

x-markdonw should be x-markdown.

✍️ Proposed fix
-The x-markdonw is under opt-in importing pattern.
+The x-markdown is under opt-in importing pattern.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/nasty-lizards-refuse.md at line 7, Typo in the changeset
description: replace the misspelled token "x-markdonw" with the correct
"x-markdown" in the .changeset/nasty-lizards-refuse.md file so the opt-in import
pattern description reads correctly; locate the phrase "x-markdonw" in the file
and update it to "x-markdown".
packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts (2)

177-182: ⚠️ Potential issue | 🟡 Minor

Clear pending append work during dispose.

dispose() should also cancel pending append timers/state to avoid post-disconnect DOM writes.

🧹 Proposed fix
   dispose() {
     if (!this.#eventsAttached) return;
+    this.#clearAppendFlushTimer();
+    this.#appendRemainder = '';
+    this.#pendingRender = false;
     const root = this.#root();
     root.removeEventListener('click', this.#handleClick);
     this.#eventsAttached = false;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`
around lines 177 - 182, The dispose() method currently only removes click
listeners and flips `#eventsAttached`; extend it to also cancel any pending append
work by clearing any append timer and queued state: if you use an internal timer
field (e.g. this.#appendTimer) call clearTimeout(this.#appendTimer) and set it
to undefined, clear any append queue (e.g. this.#appendQueue = []) and reset any
scheduled flag (e.g. this.#appendScheduled = false), and if you have a helper
like `#cancelPendingAppend`(), invoke it from dispose(); ensure you still call
`#root`().removeEventListener('click', this.#handleClick) and set `#eventsAttached`
= false after cleaning up.

43-46: ⚠️ Potential issue | 🟠 Major

Do not fail open when sanitizer is unavailable.

If sanitizer resolution fails, returning raw HTML reintroduces XSS risk. Use a safe fallback instead of raw passthrough.

🛡️ Proposed fix
 const sanitizeHtml = (value: string) => {
   const sanitizer = getHtmlSanitizer();
-  if (!sanitizer) return value;
+  if (!sanitizer) return '';
   return sanitizer.sanitize(value, { USE_PROFILES: { html: true } }) as string;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`
around lines 43 - 46, The current sanitizeHtml function returns raw HTML when
getHtmlSanitizer() yields null, reintroducing XSS risk; change sanitizeHtml so
it never returns raw input: if sanitizer is available call
sanitizer.sanitize(value, { USE_PROFILES: { html: true } }), otherwise apply a
safe fallback such as an HTML-escaping helper (e.g. escapeHtml(value)) or a
sanitizer-less safe-strip (remove all tags / return an empty string) and
optionally log a warning; update or add the escapeHtml helper and ensure
sanitizeHtml and any callers use it instead of returning value when sanitizer
=== null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web-platform/web-elements/index.css`:
- Line 16: The CSS import uses url(...) notation which violates the stylelint
rule; replace the `@import` url("./src/elements/XMarkdown/x-markdown.css");
statement with the direct string form `@import`
"./src/elements/XMarkdown/x-markdown.css"; so the import uses a plain string
instead of url(...) (look for the `@import` line referencing x-markdown.css to
update).

---

Duplicate comments:
In @.changeset/nasty-lizards-refuse.md:
- Line 7: Typo in the changeset description: replace the misspelled token
"x-markdonw" with the correct "x-markdown" in the
.changeset/nasty-lizards-refuse.md file so the opt-in import pattern description
reads correctly; locate the phrase "x-markdonw" in the file and update it to
"x-markdown".

In
`@packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts`:
- Around line 177-182: The dispose() method currently only removes click
listeners and flips `#eventsAttached`; extend it to also cancel any pending append
work by clearing any append timer and queued state: if you use an internal timer
field (e.g. this.#appendTimer) call clearTimeout(this.#appendTimer) and set it
to undefined, clear any append queue (e.g. this.#appendQueue = []) and reset any
scheduled flag (e.g. this.#appendScheduled = false), and if you have a helper
like `#cancelPendingAppend`(), invoke it from dispose(); ensure you still call
`#root`().removeEventListener('click', this.#handleClick) and set `#eventsAttached`
= false after cleaning up.
- Around line 43-46: The current sanitizeHtml function returns raw HTML when
getHtmlSanitizer() yields null, reintroducing XSS risk; change sanitizeHtml so
it never returns raw input: if sanitizer is available call
sanitizer.sanitize(value, { USE_PROFILES: { html: true } }), otherwise apply a
safe fallback such as an HTML-escaping helper (e.g. escapeHtml(value)) or a
sanitizer-less safe-strip (remove all tags / return an empty string) and
optionally log a warning; update or add the escapeHtml helper and ensure
sanitizeHtml and any callers use it instead of returning value when sanitizer
=== null.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fab3080 and d9fb103.

📒 Files selected for processing (15)
  • .changeset/nasty-lizards-refuse.md
  • .github/lynx-stack.instructions.md
  • packages/web-platform/web-elements/index.css
  • packages/web-platform/web-elements/package.json
  • packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdown.ts
  • packages/web-platform/web-elements/src/elements/XMarkdown/XMarkdownAttributes.ts
  • packages/web-platform/web-elements/src/elements/XMarkdown/index.ts
  • packages/web-platform/web-elements/src/elements/XMarkdown/x-markdown.css
  • packages/web-platform/web-elements/src/elements/htmlTemplates.ts
  • packages/web-platform/web-elements/tests/fixtures/shell-project.ts
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/basic.html
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/events.html
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/image.html
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/incremental.html
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/style.html
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/web-platform/web-elements/package.json
  • packages/web-platform/web-elements/src/elements/XMarkdown/index.ts
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/image.html
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/style.html
  • packages/web-platform/web-elements/tests/fixtures/x-markdown/basic.html
  • packages/web-platform/web-elements/src/elements/XMarkdown/x-markdown.css
  • .github/lynx-stack.instructions.md

@import url("./src/elements/XSvg/x-svg.css");
@import url("./src/elements/XImage/x-image.css");
@import url("./src/elements/XInput/x-input.css");
@import url("./src/elements/XMarkdown/x-markdown.css");
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.

⚠️ Potential issue | 🟡 Minor

Fix CSS import notation to satisfy stylelint.

Line 16 uses url(...) import notation, but the configured rule expects direct string notation.

🔧 Proposed fix
-@import url("./src/elements/XMarkdown/x-markdown.css");
+@import "./src/elements/XMarkdown/x-markdown.css";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@import url("./src/elements/XMarkdown/x-markdown.css");
`@import` "./src/elements/XMarkdown/x-markdown.css";
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 16-16: Expected "url("./src/elements/XMarkdown/x-markdown.css")" to be ""./src/elements/XMarkdown/x-markdown.css"" (import-notation)

(import-notation)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web-platform/web-elements/index.css` at line 16, The CSS import uses
url(...) notation which violates the stylelint rule; replace the `@import`
url("./src/elements/XMarkdown/x-markdown.css"); statement with the direct string
form `@import` "./src/elements/XMarkdown/x-markdown.css"; so the import uses a
plain string instead of url(...) (look for the `@import` line referencing
x-markdown.css to update).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant