From 5b78e596ed2888b4d670425afbbcf00a7618f4c5 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Wed, 17 Jun 2026 18:37:01 +0300 Subject: [PATCH] Fix hx-prompt unicode header encoding --- src/ext/hx-prompt.js | 2 +- test/tests/ext/hx-prompt.js | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/ext/hx-prompt.js b/src/ext/hx-prompt.js index 4f77c3a9b..443080369 100644 --- a/src/ext/hx-prompt.js +++ b/src/ext/hx-prompt.js @@ -17,7 +17,7 @@ let answer = (window.htmxPrompt || window.prompt)(question); if (answer === null) return false; if (!htmx.trigger(ctx.sourceElement, 'htmx:prompt', { prompt: answer, target: ctx.target })) return false; - ctx.request.headers['HX-Prompt'] = answer; + ctx.request.headers['HX-Prompt'] = encodeURI(answer); } }); })(); diff --git a/test/tests/ext/hx-prompt.js b/test/tests/ext/hx-prompt.js index 0257417c3..32037f497 100644 --- a/test/tests/ext/hx-prompt.js +++ b/test/tests/ext/hx-prompt.js @@ -42,6 +42,28 @@ describe('hx-prompt extension', function() { assert.equal(lastFetch().request.headers['HX-Prompt'], 'because'); }); + it('URI-encodes unicode prompt responses for fetch headers', async function() { + window.prompt = () => '中文 reason'; + + let fetchCalled = false; + let encodedHeader; + let btn = createProcessedHTML(''); + btn.addEventListener('htmx:before:request', (e) => { + e.detail.ctx.fetch = async (action, request) => { + fetchCalled = true; + encodedHeader = request.headers['HX-Prompt']; + new Request(new URL(action, location.href), request); + return new Response('ok'); + }; + }); + + btn.click(); + await forRequest(); + + assert.isTrue(fetchCalled); + assert.equal(encodedHeader, encodeURI('中文 reason')); + }); + it('aborts the request when the user cancels', async function() { window.prompt = () => null; mockResponse('DELETE', '/items/1', 'ok'); @@ -105,7 +127,7 @@ describe('hx-prompt extension', function() { btn.click(); await forRequest(); - assert.equal(lastFetch().request.headers['HX-Prompt'], 'a reason'); + assert.equal(lastFetch().request.headers['HX-Prompt'], 'a%20reason'); window.confirm = originalConfirm; }); @@ -155,7 +177,7 @@ describe('hx-prompt extension', function() { window.prompt = () => 'long enough'; btn.click(); await forRequest(); - assert.equal(lastFetch().request.headers['HX-Prompt'], 'long enough'); + assert.equal(lastFetch().request.headers['HX-Prompt'], 'long%20enough'); }); it('uses window.htmxPrompt when defined', async function() { @@ -167,7 +189,7 @@ describe('hx-prompt extension', function() { btn.click(); await forRequest(); - assert.equal(lastFetch().request.headers['HX-Prompt'], 'answered: Q?'); + assert.equal(lastFetch().request.headers['HX-Prompt'], 'answered:%20Q?'); }); it('null from window.htmxPrompt aborts the request', async function() {