From 5a1b853af59b9a10953fdf1eb1c05b4738917482 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:08:12 +0000 Subject: [PATCH 1/7] Initial plan From 2283385d0c40662c474fa38d311b215bf3b9f7e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:13:02 +0000 Subject: [PATCH 2/7] Fix unicode/Chinese character rendering by adding charset=UTF-8 to Content-Type headers Co-authored-by: brianhuster <111893501+brianhuster@users.noreply.github.com> --- lua/livepreview/server/utils/content_type.lua | 18 ++++++------- tests/test-unicode.html | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 tests/test-unicode.html diff --git a/lua/livepreview/server/utils/content_type.lua b/lua/livepreview/server/utils/content_type.lua index 57f36577..1ce96583 100644 --- a/lua/livepreview/server/utils/content_type.lua +++ b/lua/livepreview/server/utils/content_type.lua @@ -168,7 +168,7 @@ M.mime = { ["csh"] = "application/x-csh", ["csml"] = "chemical/x-csml", ["csp"] = "application/vnd.commonspace", - ["css"] = "text/css", + ["css"] = "text/css; charset=UTF-8", ["cst"] = "application/x-director", ["csv"] = "text/csv", ["c"] = "text/x-c", @@ -344,8 +344,8 @@ M.mime = { ["hqx"] = "application/mac-binhex40", ["h"] = "text/x-c", ["htke"] = "application/vnd.kenameaapp", - ["html"] = "text/html", - ["htm"] = "text/html", + ["html"] = "text/html; charset=UTF-8", + ["htm"] = "text/html; charset=UTF-8", ["hvd"] = "application/vnd.yamaha.hv-dic", ["hvp"] = "application/vnd.yamaha.hv-voice", ["hvs"] = "application/vnd.yamaha.hv-script", @@ -393,9 +393,9 @@ M.mime = { ["jpgm"] = "video/jpm", ["jpgv"] = "video/jpeg", ["jpm"] = "video/jpm", - ["json"] = "application/json", + ["json"] = "application/json; charset=UTF-8", ["jsonml"] = "application/jsonml+json", - ["js"] = "text/javascript", + ["js"] = "text/javascript; charset=UTF-8", ["kar"] = "audio/midi", ["karbon"] = "application/vnd.kde.karbon", ["kfo"] = "application/vnd.kde.kformula", @@ -478,7 +478,7 @@ M.mime = { ["mime"] = "message/rfc822", ["mj2"] = "video/mj2", ["mjp2"] = "video/mj2", - ["mjs"] = "text/javascript", + ["mjs"] = "text/javascript; charset=UTF-8", ["mk3d"] = "video/x-matroska", ["mka"] = "audio/x-matroska", ["mks"] = "video/x-matroska", @@ -800,7 +800,7 @@ M.mime = { ["sv4crc"] = "application/x-sv4crc", ["svc"] = "application/vnd.dvb.service", ["svd"] = "application/vnd.svd", - ["svg"] = "image/svg+xml", + ["svg"] = "image/svg+xml; charset=UTF-8", ["svgz"] = "image/svg+xml", ["swa"] = "application/x-director", ["swf"] = "application/x-shockwave-flash", @@ -848,7 +848,7 @@ M.mime = { ["twds"] = "application/vnd.simtech-mindmapper", ["txd"] = "application/vnd.genomatix.tuxedo", ["txf"] = "application/vnd.mobius.txf", - ["txt"] = "text/plain", + ["txt"] = "text/plain; charset=UTF-8", ["u32"] = "application/x-authorware-bin", ["udeb"] = "application/x-debian-package", ["ufd"] = "application/vnd.ufdl", @@ -990,7 +990,7 @@ M.mime = { ["xltx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.template", ["xlw"] = "application/vnd.ms-excel", ["xm"] = "audio/xm", - ["xml"] = "application/xml", + ["xml"] = "application/xml; charset=UTF-8", ["xo"] = "application/vnd.olpc-sugar", ["xop"] = "application/xop+xml", ["xpi"] = "application/x-xpinstall", diff --git a/tests/test-unicode.html b/tests/test-unicode.html new file mode 100644 index 00000000..28122cc9 --- /dev/null +++ b/tests/test-unicode.html @@ -0,0 +1,27 @@ + + +
+这是一个测试中文字符的HTML文件。
+This is an HTML file for testing Chinese characters.
+ +これは日本語のテストです。
+This is a test for Japanese characters.
+ +이것은 한국어 테스트입니다.
+This is a test for Korean characters.
+ +Emoji: 🌟 🎉 🚀 ❤️ 🌈
+Math: ∀ ∃ ∈ ∉ ∅ ∪ ∩ ⊂ ⊃ ≤ ≥ ≠
+Currency: € £ ¥ ₹ ₽ ₿
+Accented: café, naïve, über, résumé
+ + From def6d4b57ef32b0416b3897d92f3c54c97290dc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:14:23 +0000 Subject: [PATCH 3/7] Add charset=UTF-8 to svgz, xhtml, and xht mime types Co-authored-by: brianhuster <111893501+brianhuster@users.noreply.github.com> --- lua/livepreview/server/utils/content_type.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/livepreview/server/utils/content_type.lua b/lua/livepreview/server/utils/content_type.lua index 1ce96583..6095b759 100644 --- a/lua/livepreview/server/utils/content_type.lua +++ b/lua/livepreview/server/utils/content_type.lua @@ -801,7 +801,7 @@ M.mime = { ["svc"] = "application/vnd.dvb.service", ["svd"] = "application/vnd.svd", ["svg"] = "image/svg+xml; charset=UTF-8", - ["svgz"] = "image/svg+xml", + ["svgz"] = "image/svg+xml; charset=UTF-8", ["swa"] = "application/x-director", ["swf"] = "application/x-shockwave-flash", ["swi"] = "application/vnd.aristanetworks.swi", @@ -972,8 +972,8 @@ M.mime = { ["xer"] = "application/patch-ops-error+xml", ["xfdf"] = "application/vnd.adobe.xfdf", ["xfdl"] = "application/vnd.xfdl", - ["xht"] = "application/xhtml+xml", - ["xhtml"] = "application/xhtml+xml", + ["xht"] = "application/xhtml+xml; charset=UTF-8", + ["xhtml"] = "application/xhtml+xml; charset=UTF-8", ["xhvml"] = "application/xv+xml", ["xif"] = "image/vnd.xiff", ["xla"] = "application/vnd.ms-excel", From 82f27b636e8ae2ce4b6fe9b5e3c8b2a5875d7f45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:15:46 +0000 Subject: [PATCH 4/7] Remove charset from JSON content-type for RFC compliance Co-authored-by: brianhuster <111893501+brianhuster@users.noreply.github.com> --- lua/livepreview/server/utils/content_type.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/livepreview/server/utils/content_type.lua b/lua/livepreview/server/utils/content_type.lua index 6095b759..48d19b83 100644 --- a/lua/livepreview/server/utils/content_type.lua +++ b/lua/livepreview/server/utils/content_type.lua @@ -393,7 +393,7 @@ M.mime = { ["jpgm"] = "video/jpm", ["jpgv"] = "video/jpeg", ["jpm"] = "video/jpm", - ["json"] = "application/json; charset=UTF-8", + ["json"] = "application/json", ["jsonml"] = "application/jsonml+json", ["js"] = "text/javascript; charset=UTF-8", ["kar"] = "audio/midi", From 52b5aa0a0af978fb0e5ca2817907b8eba0015d3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:16:57 +0000 Subject: [PATCH 5/7] Add comprehensive tests and documentation for Unicode fix Co-authored-by: brianhuster <111893501+brianhuster@users.noreply.github.com> --- tests/UNICODE_FIX_DOCUMENTATION.md | 77 +++++++++++++++++++++ tests/test-unicode-fix.js | 103 +++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 tests/UNICODE_FIX_DOCUMENTATION.md create mode 100755 tests/test-unicode-fix.js diff --git a/tests/UNICODE_FIX_DOCUMENTATION.md b/tests/UNICODE_FIX_DOCUMENTATION.md new file mode 100644 index 00000000..9870683a --- /dev/null +++ b/tests/UNICODE_FIX_DOCUMENTATION.md @@ -0,0 +1,77 @@ +# Unicode Character Rendering Fix + +## Problem +When previewing HTML files containing Chinese, Japanese, Korean, or other Unicode characters, the browser displayed garbled text because the HTTP `Content-Type` header didn't specify the character encoding. + +## Root Cause +The server was sending: +``` +Content-Type: text/html +``` + +Without an explicit charset, browsers default to encodings like ISO-8859-1 or Windows-1252, which cannot properly represent Unicode characters. + +## Solution +Modified `/lua/livepreview/server/utils/content_type.lua` to include `charset=UTF-8` for text-based MIME types: + +```lua +["html"] = "text/html; charset=UTF-8", +["htm"] = "text/html; charset=UTF-8", +["xhtml"] = "application/xhtml+xml; charset=UTF-8", +["xht"] = "application/xhtml+xml; charset=UTF-8", +["css"] = "text/css; charset=UTF-8", +["js"] = "text/javascript; charset=UTF-8", +["mjs"] = "text/javascript; charset=UTF-8", +["xml"] = "application/xml; charset=UTF-8", +["svg"] = "image/svg+xml; charset=UTF-8", +["svgz"] = "image/svg+xml; charset=UTF-8", +["txt"] = "text/plain; charset=UTF-8", +``` + +## Result +The server now sends: +``` +Content-Type: text/html; charset=UTF-8 +``` + +This ensures browsers correctly interpret Unicode characters in all supported text-based file formats. + +## Testing +Created `tests/test-unicode.html` with: +- Chinese characters: 你好世界 +- Japanese characters: これは日本語のテストです +- Korean characters: 한국어 테스트 +- Emoji: 🌟 🎉 🚀 ❤️ 🌈 +- Math symbols: ∀ ∃ ∈ ∉ ∅ +- Special characters: € £ ¥ ₹ ₽ + +All characters now render correctly in the browser preview. + +## Why Markdown Files Worked +Markdown files were already working because the `html_template()` function includes: +```html + +``` + +However, HTML files served directly lacked both: +1. The HTTP `Content-Type` charset parameter +2. The `` tag (user's responsibility) + +This fix ensures that even HTML files without a `` tag will be correctly interpreted by the browser. + +## Impact +- **Before**: Unicode characters displayed as �, Ã,Æ—‡Å, or other garbled text +- **After**: All Unicode characters display correctly + +This fix affects: +- HTML files (.html, .htm, .xhtml, .xht) +- CSS files with Unicode content +- JavaScript files with Unicode strings +- SVG files with Unicode text +- XML files with Unicode content +- Plain text files + +## Files Changed +1. `lua/livepreview/server/utils/content_type.lua` - Added charset=UTF-8 to 11 MIME types +2. `tests/test-unicode.html` - New test file with comprehensive Unicode examples +3. `tests/test-unicode-fix.js` - Automated test to verify the fix diff --git a/tests/test-unicode-fix.js b/tests/test-unicode-fix.js new file mode 100755 index 00000000..9d8e9b53 --- /dev/null +++ b/tests/test-unicode-fix.js @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +/** + * Test script to demonstrate the Unicode/Chinese character fix + * This simulates how the live-preview server sends HTTP responses + */ + +const fs = require('fs'); +const path = require('path'); + +console.log('='.repeat(70)); +console.log('Unicode Character Rendering Fix - Demonstration'); +console.log('='.repeat(70)); +console.log(); + +// Read the content_type.lua file +const contentTypePath = path.join(__dirname, '../lua/livepreview/server/utils/content_type.lua'); +const contentTypeLua = fs.readFileSync(contentTypePath, 'utf8'); + +// Extract relevant MIME types +const mimeTypes = { + 'html': contentTypeLua.match(/\["html"\]\s*=\s*"([^"]+)"/)?.[1], + 'htm': contentTypeLua.match(/\["htm"\]\s*=\s*"([^"]+)"/)?.[1], + 'css': contentTypeLua.match(/\["css"\]\s*=\s*"([^"]+)"/)?.[1], + 'js': contentTypeLua.match(/\["js"\]\s*=\s*"([^"]+)"/)?.[1], + 'svg': contentTypeLua.match(/\["svg"\]\s*=\s*"([^"]+)"/)?.[1], + 'xml': contentTypeLua.match(/\["xml"\]\s*=\s*"([^"]+)"/)?.[1], +}; + +console.log('📋 MIME Type Configuration Check:'); +console.log('-'.repeat(70)); + +let allPass = true; +for (const [ext, mimeType] of Object.entries(mimeTypes)) { + const hasCharset = mimeType && mimeType.includes('charset=UTF-8'); + const status = hasCharset ? '✓' : '✗'; + console.log(` ${status} .${ext.padEnd(6)} → ${mimeType || 'NOT FOUND'}`); + if (!hasCharset) allPass = false; +} + +console.log(); + +// Simulate HTTP response +console.log('📡 Sample HTTP Response (for HTML file):'); +console.log('-'.repeat(70)); + +const htmlContentType = mimeTypes['html']; +const testHtmlPath = path.join(__dirname, 'test-unicode.html'); +const testHtml = fs.readFileSync(testHtmlPath, 'utf8'); +const contentLength = Buffer.byteLength(testHtml, 'utf8'); + +console.log('HTTP/1.1 200 OK'); +console.log(`Content-Type: ${htmlContentType}`); +console.log(`Content-Length: ${contentLength}`); +console.log('Connection: close'); +console.log(); +console.log(''); +console.log(''); +console.log(' '); +console.log('これは日本語のテストです。
+漢字、ひらがな、カタカナ
+ + diff --git a/tests/test-unicode-fix.js b/tests/test-unicode-fix.js deleted file mode 100755 index 9d8e9b53..00000000 --- a/tests/test-unicode-fix.js +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env node - -/** - * Test script to demonstrate the Unicode/Chinese character fix - * This simulates how the live-preview server sends HTTP responses - */ - -const fs = require('fs'); -const path = require('path'); - -console.log('='.repeat(70)); -console.log('Unicode Character Rendering Fix - Demonstration'); -console.log('='.repeat(70)); -console.log(); - -// Read the content_type.lua file -const contentTypePath = path.join(__dirname, '../lua/livepreview/server/utils/content_type.lua'); -const contentTypeLua = fs.readFileSync(contentTypePath, 'utf8'); - -// Extract relevant MIME types -const mimeTypes = { - 'html': contentTypeLua.match(/\["html"\]\s*=\s*"([^"]+)"/)?.[1], - 'htm': contentTypeLua.match(/\["htm"\]\s*=\s*"([^"]+)"/)?.[1], - 'css': contentTypeLua.match(/\["css"\]\s*=\s*"([^"]+)"/)?.[1], - 'js': contentTypeLua.match(/\["js"\]\s*=\s*"([^"]+)"/)?.[1], - 'svg': contentTypeLua.match(/\["svg"\]\s*=\s*"([^"]+)"/)?.[1], - 'xml': contentTypeLua.match(/\["xml"\]\s*=\s*"([^"]+)"/)?.[1], -}; - -console.log('📋 MIME Type Configuration Check:'); -console.log('-'.repeat(70)); - -let allPass = true; -for (const [ext, mimeType] of Object.entries(mimeTypes)) { - const hasCharset = mimeType && mimeType.includes('charset=UTF-8'); - const status = hasCharset ? '✓' : '✗'; - console.log(` ${status} .${ext.padEnd(6)} → ${mimeType || 'NOT FOUND'}`); - if (!hasCharset) allPass = false; -} - -console.log(); - -// Simulate HTTP response -console.log('📡 Sample HTTP Response (for HTML file):'); -console.log('-'.repeat(70)); - -const htmlContentType = mimeTypes['html']; -const testHtmlPath = path.join(__dirname, 'test-unicode.html'); -const testHtml = fs.readFileSync(testHtmlPath, 'utf8'); -const contentLength = Buffer.byteLength(testHtml, 'utf8'); - -console.log('HTTP/1.1 200 OK'); -console.log(`Content-Type: ${htmlContentType}`); -console.log(`Content-Length: ${contentLength}`); -console.log('Connection: close'); -console.log(); -console.log(''); -console.log(''); -console.log(' '); -console.log('这是一个测试中文字符的HTML文件。
-This is an HTML file for testing Chinese characters.
- -これは日本語のテストです。
-This is a test for Japanese characters.
- -이것은 한국어 테스트입니다.
-This is a test for Korean characters.
- -Emoji: 🌟 🎉 🚀 ❤️ 🌈
-Math: ∀ ∃ ∈ ∉ ∅ ∪ ∩ ⊂ ⊃ ≤ ≥ ≠
-Currency: € £ ¥ ₹ ₽ ₿
-Accented: café, naïve, über, résumé
- - From 8924e42ceb533f60e5423848aa157e4305db2dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20B=C3=ACnh=20An?= <111893501+brianhuster@users.noreply.github.com> Date: Fri, 2 Jan 2026 11:42:11 +0700 Subject: [PATCH 7/7] Apply suggestion from @brianhuster --- tests/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index.html b/tests/index.html index 87abe362..34e27a5f 100644 --- a/tests/index.html +++ b/tests/index.html @@ -1,5 +1,5 @@ - +