Conversation
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Use Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "22" # Using latest Node 22 for 2026 compatibility | ||
|
|
||
| - name: Install dependencies | ||
| working-directory: Build | ||
| run: npm i | ||
|
|
||
| - name: Build | ||
| working-directory: Build | ||
| run: npm run build:single | ||
|
|
||
| - name: Rename for distribution | ||
| working-directory: Build/dist | ||
| run: mv index.html HTMLRunner.html | ||
|
|
||
| - name: Upload Build Artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: HTMLRunner-Standalone | ||
| path: Build/dist/HTMLRunner.html | ||
| if-no-files-found: error |
📝 WalkthroughWalkthroughThis PR modernizes HTMLRunner's build toolchain from Webpack to Vite, replaces localStorage-based state management with a signal-driven reactive architecture, removes the offline-first service-worker strategy, reorganizes the application entry point to use TypeScript modules, and updates GitHub Actions workflows to support both standard and single-file artifact builds. ChangesBuild System & Architecture Modernization
Sequence DiagramsequenceDiagram
participant User
participant Browser
participant index.html
participant main.ts
participant appState
participant localStorage
participant editor.ts
participant ui.ts
User->>Browser: Load HTMLRunner
Browser->>index.html: Parse & render
index.html->>main.ts: Load ESM module
activate main.ts
main.ts->>appState: loadState()
activate appState
appState->>localStorage: Read persisted state
localStorage-->>appState: JSON snapshot (or null)
appState->>appState: Validate & apply snapshot to signals
appState-->>main.ts: State hydrated, signals updated
deactivate appState
main.ts->>editor.ts: Initialize editors with htmlState/cssState/jsState
editor.ts->>editor.ts: Seed each editor from signal values
main.ts->>ui.ts: Apply darkModeState & autoRunState to UI
ui.ts->>ui.ts: Update theme class, toggle icons, configure listeners
main.ts->>main.ts: runCode() on initial load
deactivate main.ts
User->>editor.ts: Edit HTML/CSS/JS
editor.ts->>appState: Update contentState signal
activate appState
appState->>appState: Trigger persistence effect
appState->>localStorage: Persist updated state
deactivate appState
alt autoRunState enabled
editor.ts->>main.ts: Debounced runCode()
main.ts->>Browser: Execute user code in iframe
end
User->>ui.ts: Toggle dark mode
ui.ts->>appState: Update darkModeState signal
activate appState
appState->>localStorage: Persist setting
deactivate appState
ui.ts->>Browser: Apply theme class
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
♻️ Duplicate comments (1)
.github/workflows/single-file.yml (1)
13-41:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd explicit permissions block to the workflow.
The workflow does not specify permissions for
GITHUB_TOKEN. As a security best practice, explicitly declare the minimum required permissions.🔒 Proposed fix to add permissions
jobs: deploy: runs-on: ubuntu-latest + permissions: + contents: read steps:As per coding guidelines, the CodeQL static analysis tool recommends setting an explicit permissions block with
contents: readas a minimal starting point.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/single-file.yml around lines 13 - 41, The workflow lacks an explicit permissions block for GITHUB_TOKEN; add a minimal permissions declaration (e.g., permissions: contents: read) either at the workflow root or scoped to the deploy job to limit token access; update the top-level or the deploy job (job name "deploy") to include this permissions entry so the GITHUB_TOKEN has only the required read access.
🧹 Nitpick comments (1)
.github/workflows/single-file.yml (1)
22-22: 💤 Low valueInconsistent Node.js version across workflows.
This workflow uses Node.js 22 while
.github/workflows/static.ymluses Node.js 20. Unless there's a specific reason for this difference, consider standardizing on a single Node.js version for consistency and easier maintenance.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/single-file.yml at line 22, The workflows are using different Node.js versions (node-version: "22" in this workflow vs node-version: "20" in the other workflow); pick one Node.js version to standardize on (e.g., "20" or "22") and update the node-version key in this workflow to match the version used in the other workflow (change node-version: "22" to the chosen version), and then run CI locally or via GitHub Actions to confirm no compatibility issues; also search for any other occurrences of node-version in workflow files and align them to the same value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/single-file.yml:
- Around line 8-10: The workflow's concurrency stanza currently uses group:
"pages", which collides with the static.yml workflow; update the concurrency
group value (the concurrency block and its group key) in the single-file
workflow to a unique name such as "single-file-build" (keep cancel-in-progress
as-is) so this workflow no longer blocks or is blocked by the Pages workflow.
In `@Build/pwa-assets.config.js`:
- Around line 1-27: The dependency constraint for `@vite-pwa/assets-generator` is
incorrect (1.0.1 does not exist); update the package version in your dependency
manifest so installs resolve deterministically — change the version specifier
from ^1.0.1 to ^1.0.2 (or to ^1.0.0 if you intend compatibility with the initial
1.0.0 release). Ensure the updated version is saved in package.json and any
lockfile is refreshed so the build uses the published release compatible with
the existing usage of defineConfig and minimal2023Preset.
In `@Build/src/appState.ts`:
- Around line 82-88: The code currently only type-checks
snapshot.activeTab/activeOutput before calling
activeTabState.set/activeOutputState.set, which lets invalid persisted strings
propagate; update this to whitelist values: define or import the canonical
allowed sets (e.g., TAB_KEYS or an enum used by the UI/editor and OUTPUT_KEYS)
and verify snapshot.activeTab and snapshot.activeOutput are members (e.g.,
allowedTabs.includes(snapshot.activeTab)) before calling
activeTabState.set/activeOutputState.set; if a value is invalid, skip setting
(or set a safe default) and optionally log a warning so downstream lookups
remain stable.
- Around line 48-54: The effect currently calls
localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot()))
on every content mutation, causing sync hot-path writes; wrap this persistence
in a debounce/throttle so writes occur at a limited frequency (e.g., debounce
250–1000ms) or use requestIdleCallback to perform the write when idle, and
optionally skip writes if the new snapshot equals the last persisted snapshot.
Update the effect that references stateHydrated.get(), createStateSnapshot(),
and the localStorage.setItem call to schedule the serialized write via the
chosen debounce/throttle/idle mechanism and cancel any pending timer on cleanup.
In `@Build/src/defaultContent.ts`:
- Line 8: The default HTML in Build/src/defaultContent.ts references <script
src="main.js"></script> but the build/export emits "script.js", so update the
script tag in the default export to <script src="script.js"></script> (i.e.,
replace "main.js" with "script.js" in the default content string) so the
exported project loads the correct JS file out of the box.
In `@Build/src/runner.ts`:
- Around line 17-33: The cached promise prettierBundlePromise currently holds a
rejected promise if the dynamic imports fail, causing all future format attempts
to fail; modify the initialization in the import block so the
Promise.all(...).then(...) chain also has a .catch(err => {
prettierBundlePromise = undefined; throw err; }) (or similar reset) so that on
load failure the cached prettierBundlePromise is cleared and subsequent calls
retry the dynamic imports; reference the prettierBundlePromise variable and the
Promise.all([... import(...) ...]).then(...) chain in runner.ts when applying
this change.
In `@Build/vite.config.mjs`:
- Around line 7-25: The plugin inlineSvgFaviconPlugin is treating the file at
options.svg as UTF-8 SVG (transformIndexHtml reads it as text and runs
XML/whitespace regex) but the project supplies a PNG, corrupting the favicon;
fix by either (A) replacing the PNG with a real SVG and updating the favicon
reference to public/favicon.svg so inlineSvgFaviconPlugin can continue to read
UTF-8 SVG, or (B) update inlineSvgFaviconPlugin/transformIndexHtml to detect the
file type from options.svg (e.g., by extension or mime sniff), read binary for
non-SVG files (fs.readFileSync(options.svg) without "utf8"), skip the
XML/whitespace regex for binary images, base64-encode the raw buffer, and set
the correct type in faviconTag (image/png vs image/svg+xml) before inserting
into the head.
In `@TODO.md`:
- Line 12: Update the Auto Completion checklist item text to hyphenate
“built-in” in the phrase inside the markdown list item (the line containing "- [
] **Auto Completion** (using the built in CodeMirror tools)"); change "built in"
to "built-in" so the item reads "(using the built-in CodeMirror tools)".
---
Duplicate comments:
In @.github/workflows/single-file.yml:
- Around line 13-41: The workflow lacks an explicit permissions block for
GITHUB_TOKEN; add a minimal permissions declaration (e.g., permissions:
contents: read) either at the workflow root or scoped to the deploy job to limit
token access; update the top-level or the deploy job (job name "deploy") to
include this permissions entry so the GITHUB_TOKEN has only the required read
access.
---
Nitpick comments:
In @.github/workflows/single-file.yml:
- Line 22: The workflows are using different Node.js versions (node-version:
"22" in this workflow vs node-version: "20" in the other workflow); pick one
Node.js version to standardize on (e.g., "20" or "22") and update the
node-version key in this workflow to match the version used in the other
workflow (change node-version: "22" to the chosen version), and then run CI
locally or via GitHub Actions to confirm no compatibility issues; also search
for any other occurrences of node-version in workflow files and align them to
the same value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d116896b-483e-43ba-b136-24f76242b42e
⛔ Files ignored due to path filters (2)
Build/package-lock.jsonis excluded by!**/package-lock.jsonBuild/public/favicon.pngis excluded by!**/*.png
📒 Files selected for processing (28)
.github/workflows/single-file.yml.github/workflows/static.ymlBuild/Buildscripts/build-all.shBuild/Buildscripts/build-inline.shBuild/Buildscripts/build.shBuild/index.htmlBuild/package.jsonBuild/pwa-assets.config.jsBuild/src/appState.tsBuild/src/defaultContent.tsBuild/src/editor.tsBuild/src/global.d.tsBuild/src/index.htmlBuild/src/main.tsBuild/src/runner.tsBuild/src/state.tsBuild/src/types.tsBuild/src/ui.tsBuild/styles/styles.cssBuild/tsconfig.jsonBuild/vite.config.mjsBuild/webpack.config.jsREADME.mdTODO.mdmain.jsmanifest.jsonservice-worker.jsstyles.css
💤 Files with no reviewable changes (10)
- Build/webpack.config.js
- service-worker.js
- Build/Buildscripts/build-all.sh
- manifest.json
- Build/src/index.html
- Build/Buildscripts/build.sh
- styles.css
- Build/src/state.ts
- Build/Buildscripts/build-inline.sh
- Build/tsconfig.json
| effect(() => { | ||
| if (!stateHydrated.get()) { | ||
| return; | ||
| } | ||
|
|
||
| localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot())); | ||
| }); |
There was a problem hiding this comment.
Throttle persistence writes to localStorage.
This effect writes full serialized state on every content mutation; with editor updates, that can become a synchronous hot path and hurt typing responsiveness.
💡 Proposed fix
+let persistTimer: number | undefined;
+
effect(() => {
if (!stateHydrated.get()) {
return;
}
-
- localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot()));
+ if (persistTimer) window.clearTimeout(persistTimer);
+ persistTimer = window.setTimeout(() => {
+ localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot()));
+ }, 150);
});📝 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.
| effect(() => { | |
| if (!stateHydrated.get()) { | |
| return; | |
| } | |
| localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot())); | |
| }); | |
| let persistTimer: number | undefined; | |
| effect(() => { | |
| if (!stateHydrated.get()) { | |
| return; | |
| } | |
| if (persistTimer) window.clearTimeout(persistTimer); | |
| persistTimer = window.setTimeout(() => { | |
| localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot())); | |
| }, 150); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Build/src/appState.ts` around lines 48 - 54, The effect currently calls
localStorage.setItem("htmlRunnerState", JSON.stringify(createStateSnapshot()))
on every content mutation, causing sync hot-path writes; wrap this persistence
in a debounce/throttle so writes occur at a limited frequency (e.g., debounce
250–1000ms) or use requestIdleCallback to perform the write when idle, and
optionally skip writes if the new snapshot equals the last persisted snapshot.
Update the effect that references stateHydrated.get(), createStateSnapshot(),
and the localStorage.setItem call to schedule the serialized write via the
chosen debounce/throttle/idle mechanism and cancel any pending timer on cleanup.
| if (typeof snapshot.activeTab === "string") { | ||
| activeTabState.set(snapshot.activeTab); | ||
| } | ||
|
|
||
| if (typeof snapshot.activeOutput === "string") { | ||
| activeOutputState.set(snapshot.activeOutput); | ||
| } |
There was a problem hiding this comment.
Validate activeTab/activeOutput against allowed values before setting.
Current checks only validate type, so corrupted persisted data can store invalid keys and destabilize downstream UI/editor lookups.
💡 Proposed fix
- if (typeof snapshot.activeTab === "string") {
+ if (snapshot.activeTab === "html" || snapshot.activeTab === "css" || snapshot.activeTab === "js") {
activeTabState.set(snapshot.activeTab);
}
- if (typeof snapshot.activeOutput === "string") {
+ if (snapshot.activeOutput === "preview" || snapshot.activeOutput === "console") {
activeOutputState.set(snapshot.activeOutput);
}📝 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.
| if (typeof snapshot.activeTab === "string") { | |
| activeTabState.set(snapshot.activeTab); | |
| } | |
| if (typeof snapshot.activeOutput === "string") { | |
| activeOutputState.set(snapshot.activeOutput); | |
| } | |
| if (snapshot.activeTab === "html" || snapshot.activeTab === "css" || snapshot.activeTab === "js") { | |
| activeTabState.set(snapshot.activeTab); | |
| } | |
| if (snapshot.activeOutput === "preview" || snapshot.activeOutput === "console") { | |
| activeOutputState.set(snapshot.activeOutput); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Build/src/appState.ts` around lines 82 - 88, The code currently only
type-checks snapshot.activeTab/activeOutput before calling
activeTabState.set/activeOutputState.set, which lets invalid persisted strings
propagate; update this to whitelist values: define or import the canonical
allowed sets (e.g., TAB_KEYS or an enum used by the UI/editor and OUTPUT_KEYS)
and verify snapshot.activeTab and snapshot.activeOutput are members (e.g.,
allowedTabs.includes(snapshot.activeTab)) before calling
activeTabState.set/activeOutputState.set; if a value is invalid, skip setting
(or set a safe default) and optionally log a warning so downstream lookups
remain stable.
| <link rel="stylesheet" href="styles.css"> | ||
| </head> | ||
| <body> | ||
| <script src="main.js"><\/script> |
There was a problem hiding this comment.
Align default script filename with export output.
Line 8 points to main.js, but export currently writes script.js, so the default exported project loses JS execution out of the box.
💡 Proposed fix
-<script src="main.js"><\/script>
+<script src="script.js"><\/script>📝 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.
| <script src="main.js"><\/script> | |
| <script src="script.js"><\/script> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Build/src/defaultContent.ts` at line 8, The default HTML in
Build/src/defaultContent.ts references <script src="main.js"></script> but the
build/export emits "script.js", so update the script tag in the default export
to <script src="script.js"></script> (i.e., replace "main.js" with "script.js"
in the default content string) so the exported project loads the correct JS file
out of the box.
| if (!prettierBundlePromise) { | ||
| prettierBundlePromise = Promise.all([ | ||
| import("prettier/standalone"), | ||
| import("prettier/plugins/html"), | ||
| import("prettier/plugins/postcss"), | ||
| import("prettier/plugins/babel"), | ||
| import("prettier/plugins/estree"), | ||
| ]).then( | ||
| ([prettier, parserHtml, parserCss, parserBabel, prettierPluginEstree]) => ({ | ||
| prettier, | ||
| parserHtml, | ||
| parserCss, | ||
| parserBabel, | ||
| prettierPluginEstree, | ||
| }) | ||
| ); | ||
| } |
There was a problem hiding this comment.
Reset cached Prettier promise on load failure.
If dynamic import fails once, the rejected promise is retained and all future format attempts fail until reload.
💡 Proposed fix
if (!prettierBundlePromise) {
prettierBundlePromise = Promise.all([
import("prettier/standalone"),
import("prettier/plugins/html"),
import("prettier/plugins/postcss"),
import("prettier/plugins/babel"),
import("prettier/plugins/estree"),
]).then(
([prettier, parserHtml, parserCss, parserBabel, prettierPluginEstree]) => ({
prettier,
parserHtml,
parserCss,
parserBabel,
prettierPluginEstree,
})
- );
+ ).catch((error) => {
+ prettierBundlePromise = undefined;
+ throw error;
+ });
}📝 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.
| if (!prettierBundlePromise) { | |
| prettierBundlePromise = Promise.all([ | |
| import("prettier/standalone"), | |
| import("prettier/plugins/html"), | |
| import("prettier/plugins/postcss"), | |
| import("prettier/plugins/babel"), | |
| import("prettier/plugins/estree"), | |
| ]).then( | |
| ([prettier, parserHtml, parserCss, parserBabel, prettierPluginEstree]) => ({ | |
| prettier, | |
| parserHtml, | |
| parserCss, | |
| parserBabel, | |
| prettierPluginEstree, | |
| }) | |
| ); | |
| } | |
| if (!prettierBundlePromise) { | |
| prettierBundlePromise = Promise.all([ | |
| import("prettier/standalone"), | |
| import("prettier/plugins/html"), | |
| import("prettier/plugins/postcss"), | |
| import("prettier/plugins/babel"), | |
| import("prettier/plugins/estree"), | |
| ]).then( | |
| ([prettier, parserHtml, parserCss, parserBabel, prettierPluginEstree]) => ({ | |
| prettier, | |
| parserHtml, | |
| parserCss, | |
| parserBabel, | |
| prettierPluginEstree, | |
| }) | |
| ).catch((error) => { | |
| prettierBundlePromise = undefined; | |
| throw error; | |
| }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Build/src/runner.ts` around lines 17 - 33, The cached promise
prettierBundlePromise currently holds a rejected promise if the dynamic imports
fail, causing all future format attempts to fail; modify the initialization in
the import block so the Promise.all(...).then(...) chain also has a .catch(err
=> { prettierBundlePromise = undefined; throw err; }) (or similar reset) so that
on load failure the cached prettierBundlePromise is cleared and subsequent calls
retry the dynamic imports; reference the prettierBundlePromise variable and the
Promise.all([... import(...) ...]).then(...) chain in runner.ts when applying
this change.
| function inlineSvgFaviconPlugin(options) { | ||
| return { | ||
| name: "inline-svg-favicon", | ||
| enforce: "post", | ||
| transformIndexHtml(html) { | ||
| if (!fs.existsSync(options.svg)) return html; | ||
| let svgContent = fs.readFileSync(options.svg, "utf8"); | ||
| // Remove XML header if present, minify spaces | ||
| svgContent = svgContent | ||
| .replace(/<\?xml[^>]*>\s*/g, "") | ||
| .replace(/\s+/g, " "); | ||
| // Base64 encode the SVG | ||
| const base64 = Buffer.from(svgContent).toString("base64"); | ||
| const faviconTag = `<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,${base64}"/>\n`; | ||
| // Insert favicon into <head> | ||
| return html.replace(/<head>(.*?)/, `<head>$1\n ${faviconTag}`); | ||
| }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Critical: Plugin designed for SVG but receives PNG file.
The inlineSvgFaviconPlugin expects an SVG file but is given "public/favicon.png" at line 69. This causes multiple issues:
- Line 13: Reads PNG binary data as UTF-8 text, corrupting the data
- Lines 15-17: Attempts to remove XML headers and minify whitespace from PNG binary
- Line 19: Base64-encodes corrupted data
- Result: Broken favicon in single-file builds
Solutions:
Option 1 (Recommended): Convert favicon to SVG format
- Save
public/favicon.pngaspublic/favicon.svg - Update line 69 to reference the SVG file
Option 2: Fix the plugin to handle PNG correctly
🐛 Proposed fix for Option 2 (handle PNG correctly)
-function inlineSvgFaviconPlugin(options) {
+function inlineFaviconPlugin(options) {
return {
- name: "inline-svg-favicon",
+ name: "inline-favicon",
enforce: "post",
transformIndexHtml(html) {
- if (!fs.existsSync(options.svg)) return html;
- let svgContent = fs.readFileSync(options.svg, "utf8");
- // Remove XML header if present, minify spaces
- svgContent = svgContent
- .replace(/<\?xml[^>]*>\s*/g, "")
- .replace(/\s+/g, " ");
- // Base64 encode the SVG
- const base64 = Buffer.from(svgContent).toString("base64");
- const faviconTag = `<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,${base64}"/>\n`;
+ if (!fs.existsSync(options.image)) return html;
+ const imageBuffer = fs.readFileSync(options.image);
+ const ext = options.image.split('.').pop().toLowerCase();
+ const mimeType = ext === 'svg' ? 'image/svg+xml' : `image/${ext}`;
+ const base64 = imageBuffer.toString("base64");
+ const faviconTag = `<link rel="icon" type="${mimeType}" href="data:${mimeType};base64,${base64}"/>\n`;
// Insert favicon into <head>
return html.replace(/<head>(.*?)/, `<head>$1\n ${faviconTag}`);
},
};
}And update the call site:
- isSingleFile && inlineSvgFaviconPlugin({ svg: "public/favicon.png" }),
+ isSingleFile && inlineFaviconPlugin({ image: "public/favicon.png" }),📝 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.
| function inlineSvgFaviconPlugin(options) { | |
| return { | |
| name: "inline-svg-favicon", | |
| enforce: "post", | |
| transformIndexHtml(html) { | |
| if (!fs.existsSync(options.svg)) return html; | |
| let svgContent = fs.readFileSync(options.svg, "utf8"); | |
| // Remove XML header if present, minify spaces | |
| svgContent = svgContent | |
| .replace(/<\?xml[^>]*>\s*/g, "") | |
| .replace(/\s+/g, " "); | |
| // Base64 encode the SVG | |
| const base64 = Buffer.from(svgContent).toString("base64"); | |
| const faviconTag = `<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,${base64}"/>\n`; | |
| // Insert favicon into <head> | |
| return html.replace(/<head>(.*?)/, `<head>$1\n ${faviconTag}`); | |
| }, | |
| }; | |
| } | |
| function inlineFaviconPlugin(options) { | |
| return { | |
| name: "inline-favicon", | |
| enforce: "post", | |
| transformIndexHtml(html) { | |
| if (!fs.existsSync(options.image)) return html; | |
| const imageBuffer = fs.readFileSync(options.image); | |
| const ext = options.image.split('.').pop().toLowerCase(); | |
| const mimeType = ext === 'svg' ? 'image/svg+xml' : `image/${ext}`; | |
| const base64 = imageBuffer.toString("base64"); | |
| const faviconTag = `<link rel="icon" type="${mimeType}" href="data:${mimeType};base64,${base64}"/>\n`; | |
| // Insert favicon into <head> | |
| return html.replace(/<head>(.*?)/, `<head>$1\n ${faviconTag}`); | |
| }, | |
| }; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Build/vite.config.mjs` around lines 7 - 25, The plugin inlineSvgFaviconPlugin
is treating the file at options.svg as UTF-8 SVG (transformIndexHtml reads it as
text and runs XML/whitespace regex) but the project supplies a PNG, corrupting
the favicon; fix by either (A) replacing the PNG with a real SVG and updating
the favicon reference to public/favicon.svg so inlineSvgFaviconPlugin can
continue to read UTF-8 SVG, or (B) update
inlineSvgFaviconPlugin/transformIndexHtml to detect the file type from
options.svg (e.g., by extension or mime sniff), read binary for non-SVG files
(fs.readFileSync(options.svg) without "utf8"), skip the XML/whitespace regex for
binary images, base64-encode the raw buffer, and set the correct type in
faviconTag (image/png vs image/svg+xml) before inserting into the head.
| - [ ] **Version Control** - Built-in Git integration using [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) | ||
| - [ ] **Template Library** - Pre-built code snippets | ||
| - [ ] **Performance Profiler** - Analyze code performance | ||
| - [ ] **Auto Completion** (using the built in CodeMirror tools) |
There was a problem hiding this comment.
Hyphenate “built-in” in the Auto Completion item.
Line 12 has a small grammar issue: “built in” should be “built-in” as a compound adjective.
✏️ Suggested edit
-- [ ] **Auto Completion** (using the built in CodeMirror tools)
+- [ ] **Auto Completion** (using the built-in CodeMirror tools)📝 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.
| - [ ] **Auto Completion** (using the built in CodeMirror tools) | |
| - [ ] **Auto Completion** (using the built-in CodeMirror tools) |
🧰 Tools
🪛 LanguageTool
[grammar] ~12-~12: Use a hyphen to join words.
Context: ...[ ] Auto Completion (using the built in CodeMirror tools) - [ ] **Code Foldin...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@TODO.md` at line 12, Update the Auto Completion checklist item text to
hyphenate “built-in” in the phrase inside the markdown list item (the line
containing "- [ ] **Auto Completion** (using the built in CodeMirror tools)");
change "built in" to "built-in" so the item reads "(using the built-in
CodeMirror tools)".
Summary by CodeRabbit
Release Notes
New Features
Refactor
Documentation