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
8 changes: 3 additions & 5 deletions packages/website/src/components/playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ <h2>JavaScript </h2>
</li>
</ul>
</div>
<div class="border-[1px] border-slate rounded flex-grow p-2 bg-gray-100 h-[400px]">
<div class="border-[1px] border-slate rounded flex-grow p-2 bg-gray-100 h-[400px] overflow-scroll">
<div id="errors" class="block">
HERE
</div>
<pre id="fixed" class="hidden text-sm">
HERE
</pre>
<pre id="fixed" class="hidden text-sm">
</pre>
</div>
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions packages/website/src/scripts/playground/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class App {
"changeLanguage",
() => this.handleLanguageChange()
);
this.model.on(
"autofixed",
() => this.view.codeEditor.setValue(this.model.getCode())
);
this.view.codeEditor.on(
"change",
(editor) => this.handleCodeChange(editor)
Expand All @@ -37,6 +41,19 @@ class App {
this.model.setLanguage($tab.dataset.language);
}
);
this.view.$errors.addEventListener('click', (event) => {
const $button = event.target.closest("button");
if ($button) {
const $li = $button.closest("li");
const index = Array.from($li.parentNode.children).indexOf($li);
Copy link

Copilot AI Aug 6, 2025

Choose a reason for hiding this comment

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

This approach to find the message index is fragile and could break if the DOM structure changes. Consider storing the message index as a data attribute on the list item instead.

Suggested change
const index = Array.from($li.parentNode.children).indexOf($li);
const index = $li.dataset.index ? parseInt($li.dataset.index, 10) : -1;

Copilot uses AI. Check for mistakes.
const message = this.model.messages[index];
if ($button.textContent.includes("fix")) {
this.model.applyFix(message);
} else if ($button.textContent.includes("suggestion")) {
Comment on lines +47 to +52
Copy link

Copilot AI Aug 6, 2025

Choose a reason for hiding this comment

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

Using textContent to determine button action is brittle and could break with internationalization or text changes. Consider using data attributes or CSS classes to identify button types.

Suggested change
const $li = $button.closest("li");
const index = Array.from($li.parentNode.children).indexOf($li);
const message = this.model.messages[index];
if ($button.textContent.includes("fix")) {
this.model.applyFix(message);
} else if ($button.textContent.includes("suggestion")) {
// NOTE: Buttons must have a data-action attribute set to "fix" or "suggestion"
const $li = $button.closest("li");
const index = Array.from($li.parentNode.children).indexOf($li);
const message = this.model.messages[index];
if ($button.dataset.action === "fix") {
this.model.applyFix(message);
} else if ($button.dataset.action === "suggestion") {

Copilot uses AI. Check for mistakes.
this.model.applySuggestion(message);
}
}
});
}

start() {
Expand Down
43 changes: 41 additions & 2 deletions packages/website/src/scripts/playground/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @import eslint from "eslint";
* @import {Language} from "./language";
* @import {RulesRecord, ParserOptions} from "./linter";
* @typedef {"lint" | "changeLanguage"} EventType
* @typedef {"lint" | "changeLanguage" | "autofixed"} EventType
*/

import {
Expand Down Expand Up @@ -74,7 +74,8 @@ export class Model {
*/
this.observers = {
lint: new Set(),
changeLanguage: new Set()
changeLanguage: new Set(),
autofixed: new Set()
};
}

Expand Down Expand Up @@ -157,6 +158,44 @@ export class Model {
this.observers[type].forEach((observer) => observer());
}

/**
*
* @param {LintMessage} message
*/
applyFix(message) {
const {
fix
} = message;
if (fix) {
const code = this.getCode();
const [start, end] = fix.range;
const fixed = code.slice(0, start) +
fix.text +
code.slice(end);
this.setCode(fixed);
this.lint();
this.notify('autofixed');
}
}

applySuggestion(message) {
const {
suggestions
} = message;
if (suggestions && suggestions.length > 0) {
const suggestion = suggestions[0];
if (suggestion.fix) {
const code = this.getCode();
const [start, end] = suggestion.fix.range;
const fixed = code.slice(0, start) +
suggestion.fix.text +
code.slice(end);
this.setCode(fixed);
this.lint();
this.notify('autofixed');
}
}
}
/**
* @returns {string}
*/
Expand Down
30 changes: 18 additions & 12 deletions packages/website/src/scripts/playground/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class View {
);

this.$languageTabs = document.getElementById("language-tabs");
this.$errors = document.getElementById("errors");
}

/**
Expand Down Expand Up @@ -72,12 +73,10 @@ export class View {
* @param {LintMessage[]} messages
*/
renderErrors(messages) {
const $errors = document.getElementById("errors");

const children = messages.
map((message) => this.lintMessageHTML(message));

$errors.innerHTML = html`<ul class="text-sm">
this.$errors.innerHTML = html`<ul class="text-sm flex flex-col gap-2">
${children}
</ul>`;

Expand Down Expand Up @@ -106,22 +105,29 @@ export class View {
* @param {LintMessage} message
*/
lintMessageHTML({
line, column, message, ruleId, fatal
line, column, message, ruleId, fatal, fix, suggestions
}) {
if (fatal) {
console.error(message);
return html`<li class="bg-red-100 text-red-800 px-2 py-1 my-1 rounded">
${line}:${column} - ${message}
</li>`;
}

return html`<li class="bg-red-100 text-red-800 px-2 py-1 my-1 rounded">
${line}:${column} - ${escapeHTML(message)}(
<a href="/docs/rules/${ruleId.replace(
"@html-eslint/",
""
)}">${ruleId}</a>
)
const rule = ruleId.replace(
"@html-eslint/",
""
)
return html`<li class="bg-red-100 text-red-800 px-2 rounded flex items-center">
<span class="my-4">
<span>${line}:${column} - ${escapeHTML(message)}</span>
<a href="/docs/rules/${rule}" class="ml-1 hover:underline">(${rule})</a>
</span>
<div class="ml-auto mr-0 my-2">
${fix ? html`<button class="bg-accent text-white px-4 py-1 rounded hover:opacity-80 w-max">apply fix</button>` : ""}
${suggestions && suggestions.length > 0 ? html`
<button class="bg-accent text-white px-4 py-1 rounded hover:opacity-80 w-max">apply suggestion</button>
` : ""}
</div>
</li>`;
}
}