From cb0d9745c86e9f8b893c7e6be6fd1c6227acae1a Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:19:53 +0000 Subject: [PATCH 01/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐ก What: - ํ์ ํผ ์ ๋ ฅ ํ๋๊ฐ ๋น์์ง ๋ ์๋ฌด๋ฐ ๊ฒฝ๊ณ ๋ ์ฃผ์ง ์๊ณ ์นจ๋ฌตํ๋ ๋ฌธ์ ๋ฅผ ์์ ํ์ฌ, "This field is required."๋ผ๋ ๋ช ์์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค. - `aria-invalid="true"` ์์ฑ์ ์ค์ ํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋ ๋ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์ ์ค๋ฅ ์ํ๋ฅผ ๋ช ํํ ์ ๋ฌํ๋๋ก ๊ฐ์ ํ์ต๋๋ค. - ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ง์ ์ธ๋ผ์ธ ์คํ์ผ(`preview.style.color = '#dc3545'`)์ ์ง์ ํ๋ ๋ฐฉ์์ ์ ๊ฑฐํ๊ณ , ์ธ๋ผ์ธ ์คํ์ผ ์ถฉ๋์ ํผํ๊ธฐ ์ํด `preview.style.color = ''`๋ก ์ด๊ธฐํํ ๋ค ๊ธฐ์กด ํด๋์ค์ธ `.required-star`๋ฅผ ์ ์ฉํ๋๋ก ์์ ํ์ต๋๋ค. ๐ฏ Why: - ํ์ ๊ฐ์ด ๋น์์ง ์ํ๋ฅผ ์ฌ์ฉ์ ๋ฐ ์คํฌ๋ฆฐ ๋ฆฌ๋๊ฐ ๋ช ํํ๊ฒ ์ธ์งํ ์ ์๋๋ก ์ ๊ทผ์ฑ๊ณผ ํผ ์ ํจ์ฑ ๊ฒ์ฌ ๊ฒฝํ์ ํฅ์์ํค๊ธฐ ์ํจ์ ๋๋ค. - ์ธ๋ผ์ธ ์คํ์ผ์ CSS ํด๋์ค๋ณด๋ค ์ฐ์ ์์๊ฐ ๋์ ์คํ์ผ ๋ฎ์ด์ฐ๊ธฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ๊ธฐ์กด ํด๋์ค๋ฅผ ํ์ฉํจ์ผ๋ก์จ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ธฐ ์ํจ์ ๋๋ค. ๐ธ Before/After: - Before: ํ์ผ ์ ๋ ฅ์ด๋ ๋ฐ์ดํธ ๋ชฉํ์น ์ ๋ ฅ๋์ด ์ง์์ง ๋ ์๊ฐ์ , ๊ตฌ์กฐ์ ์ผ๋ก ์๋ฌด๋ฐ ํผ๋๋ฐฑ์ด ์ ๊ณต๋์ง ์๊ณ ๋จ์ํ ์ค๋ฅ ํ ์คํธ๊ฐ ์ฌ๋ผ์ก์ผ๋ฉฐ, ์๋ฌ ๋ฐ์ ์ ์ปค์คํ ์ธ๋ผ์ธ ์์์ด ์ค์ ๋์์ต๋๋ค. - After: ์ ๋ ฅ๋์ด ์ง์์ง๋ฉด ๋นจ๊ฐ์ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ํ๋๋ฉฐ, ์คํฌ๋ฆฐ ๋ฆฌ๋์ฉ `aria-invalid="true"` ์ํ๊ฐ ์ ์ฉ๋ฉ๋๋ค. ๋ํ, ์ปค์คํ ์ธ๋ผ์ธ CSS ๋์ `.required-star` ํด๋์ค๊ฐ ํ์ฉ๋ฉ๋๋ค. โฟ Accessibility: - ๋น ํ์ ํผ ์ ๋ ฅ ์ํ์์ `aria-invalid="true"`๋ฅผ ๋์ ์ผ๋ก ๋ถ์ฌํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋๊ฐ ํผ ์๋ฌ๋ฅผ ์ ํํ๊ฒ ์ฝ์ ์ ์๋๋ก ์ ๊ทผ์ฑ์ ๊ฐํํ์ต๋๋ค. --- saas_web.py | 32 ++++++++++++++++++++------- tests/test_empty_target_validation.py | 6 ++--- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/saas_web.py b/saas_web.py index 63265e94..d077b361 100644 --- a/saas_web.py +++ b/saas_web.py @@ -223,9 +223,14 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('file_size_preview'); input.setCustomValidity(''); input.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#0f6674'; if (!file) { - preview.innerText = ''; + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + input.setCustomValidity('This field is required.'); + input.setAttribute('aria-invalid', 'true'); return; } const text = formatBinaryBytes(file.size); @@ -245,6 +250,7 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('target_bytes_preview'); this.setCustomValidity(''); this.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#1e7e34'; const buttons = document.querySelectorAll('#preset_buttons_container .preset-btn'); @@ -257,9 +263,11 @@ async def add_security_headers(request: Request, call_next): }); if (this.value === '') { - preview.innerText = ''; - this.setCustomValidity(''); - this.removeAttribute('aria-invalid'); + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + this.setCustomValidity('This field is required.'); + this.setAttribute('aria-invalid', 'true'); return; } @@ -279,6 +287,7 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('batch_target_bytes_preview'); this.setCustomValidity(''); this.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#1e7e34'; const buttons = document.querySelectorAll('#batch_preset_buttons_container .preset-btn'); @@ -291,9 +300,11 @@ async def add_security_headers(request: Request, call_next): }); if (this.value === '') { - preview.innerText = ''; - this.setCustomValidity(''); - this.removeAttribute('aria-invalid'); + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + this.setCustomValidity('This field is required.'); + this.setAttribute('aria-invalid', 'true'); return; } @@ -320,11 +331,16 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('batch_files_preview'); input.setCustomValidity(''); input.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#0f6674'; const files = input.files; if (!files || files.length === 0) { - preview.innerText = ''; + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + input.setCustomValidity('This field is required.'); + input.setAttribute('aria-invalid', 'true'); return; } diff --git a/tests/test_empty_target_validation.py b/tests/test_empty_target_validation.py index 56374432..37afb0cf 100644 --- a/tests/test_empty_target_validation.py +++ b/tests/test_empty_target_validation.py @@ -51,9 +51,9 @@ def _assert_empty_branch(self, handler: str) -> None: empty_marker = "if (this.value === '') {" invalid_marker = "if (isNaN(val) || val <= 0) {" self.assertIn(empty_marker, handler) - self.assertIn("preview.innerText = '';", handler) - self.assertIn("this.setCustomValidity('');", handler) - self.assertIn("this.removeAttribute('aria-invalid');", handler) + self.assertIn("preview.innerText = 'This field is required.';", handler) + self.assertIn("this.setCustomValidity('This field is required.');", handler) + self.assertIn("this.setAttribute('aria-invalid', 'true');", handler) self.assertIn( "return;", handler[handler.index(empty_marker) : handler.index(invalid_marker)], From cfe17a1ab1d63d65b111ed6040cd31f5716afed7 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:06:34 +0000 Subject: [PATCH 02/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐ก What: - ํ์ ํผ ์ ๋ ฅ ํ๋๊ฐ ๋น์์ง ๋ ์๋ฌด๋ฐ ๊ฒฝ๊ณ ๋ ์ฃผ์ง ์๊ณ ์นจ๋ฌตํ๋ ๋ฌธ์ ๋ฅผ ์์ ํ์ฌ, "This field is required."๋ผ๋ ๋ช ์์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค. - `aria-invalid="true"` ์์ฑ์ ์ค์ ํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋ ๋ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์ ์ค๋ฅ ์ํ๋ฅผ ๋ช ํํ ์ ๋ฌํ๋๋ก ๊ฐ์ ํ์ต๋๋ค. - ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ง์ ์ธ๋ผ์ธ ์คํ์ผ(`preview.style.color = '#dc3545'`)์ ์ง์ ํ๋ ๋ฐฉ์์ ์ ๊ฑฐํ๊ณ , ์ธ๋ผ์ธ ์คํ์ผ ์ถฉ๋์ ํผํ๊ธฐ ์ํด `preview.style.color = ''`๋ก ์ด๊ธฐํํ ๋ค ๊ธฐ์กด ํด๋์ค์ธ `.required-star`๋ฅผ ์ ์ฉํ๋๋ก ์์ ํ์ต๋๋ค. ๐ฏ Why: - ํ์ ๊ฐ์ด ๋น์์ง ์ํ๋ฅผ ์ฌ์ฉ์ ๋ฐ ์คํฌ๋ฆฐ ๋ฆฌ๋๊ฐ ๋ช ํํ๊ฒ ์ธ์งํ ์ ์๋๋ก ์ ๊ทผ์ฑ๊ณผ ํผ ์ ํจ์ฑ ๊ฒ์ฌ ๊ฒฝํ์ ํฅ์์ํค๊ธฐ ์ํจ์ ๋๋ค. - ์ธ๋ผ์ธ ์คํ์ผ์ CSS ํด๋์ค๋ณด๋ค ์ฐ์ ์์๊ฐ ๋์ ์คํ์ผ ๋ฎ์ด์ฐ๊ธฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ๊ธฐ์กด ํด๋์ค๋ฅผ ํ์ฉํจ์ผ๋ก์จ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ธฐ ์ํจ์ ๋๋ค. ๐ธ Before/After: - Before: ํ์ผ ์ ๋ ฅ์ด๋ ๋ฐ์ดํธ ๋ชฉํ์น ์ ๋ ฅ๋์ด ์ง์์ง ๋ ์๊ฐ์ , ๊ตฌ์กฐ์ ์ผ๋ก ์๋ฌด๋ฐ ํผ๋๋ฐฑ์ด ์ ๊ณต๋์ง ์๊ณ ๋จ์ํ ์ค๋ฅ ํ ์คํธ๊ฐ ์ฌ๋ผ์ก์ผ๋ฉฐ, ์๋ฌ ๋ฐ์ ์ ์ปค์คํ ์ธ๋ผ์ธ ์์์ด ์ค์ ๋์์ต๋๋ค. - After: ์ ๋ ฅ๋์ด ์ง์์ง๋ฉด ๋นจ๊ฐ์ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ํ๋๋ฉฐ, ์คํฌ๋ฆฐ ๋ฆฌ๋์ฉ `aria-invalid="true"` ์ํ๊ฐ ์ ์ฉ๋ฉ๋๋ค. ๋ํ, ์ปค์คํ ์ธ๋ผ์ธ CSS ๋์ `.required-star` ํด๋์ค๊ฐ ํ์ฉ๋ฉ๋๋ค. โฟ Accessibility: - ๋น ํ์ ํผ ์ ๋ ฅ ์ํ์์ `aria-invalid="true"`๋ฅผ ๋์ ์ผ๋ก ๋ถ์ฌํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋๊ฐ ํผ ์๋ฌ๋ฅผ ์ ํํ๊ฒ ์ฝ์ ์ ์๋๋ก ์ ๊ทผ์ฑ์ ๊ฐํํ์ต๋๋ค. From ea60b2b73cec048495cb14647007d6ccfbe0c389 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:48:27 +0000 Subject: [PATCH 03/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐ก What: - ํ์ ํผ ์ ๋ ฅ ํ๋๊ฐ ๋น์์ง ๋ ์๋ฌด๋ฐ ๊ฒฝ๊ณ ๋ ์ฃผ์ง ์๊ณ ์นจ๋ฌตํ๋ ๋ฌธ์ ๋ฅผ ์์ ํ์ฌ, "This field is required."๋ผ๋ ๋ช ์์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค. - `aria-invalid="true"` ์์ฑ์ ์ค์ ํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋ ๋ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์ ์ค๋ฅ ์ํ๋ฅผ ๋ช ํํ ์ ๋ฌํ๋๋ก ๊ฐ์ ํ์ต๋๋ค. - ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ง์ ์ธ๋ผ์ธ ์คํ์ผ(`preview.style.color = '#dc3545'`)์ ์ง์ ํ๋ ๋ฐฉ์์ ์ ๊ฑฐํ๊ณ , ์ธ๋ผ์ธ ์คํ์ผ ์ถฉ๋์ ํผํ๊ธฐ ์ํด `preview.style.color = ''`๋ก ์ด๊ธฐํํ ๋ค ๊ธฐ์กด ํด๋์ค์ธ `.required-star`๋ฅผ ์ ์ฉํ๋๋ก ์์ ํ์ต๋๋ค. ๐ฏ Why: - ํ์ ๊ฐ์ด ๋น์์ง ์ํ๋ฅผ ์ฌ์ฉ์ ๋ฐ ์คํฌ๋ฆฐ ๋ฆฌ๋๊ฐ ๋ช ํํ๊ฒ ์ธ์งํ ์ ์๋๋ก ์ ๊ทผ์ฑ๊ณผ ํผ ์ ํจ์ฑ ๊ฒ์ฌ ๊ฒฝํ์ ํฅ์์ํค๊ธฐ ์ํจ์ ๋๋ค. - ์ธ๋ผ์ธ ์คํ์ผ์ CSS ํด๋์ค๋ณด๋ค ์ฐ์ ์์๊ฐ ๋์ ์คํ์ผ ๋ฎ์ด์ฐ๊ธฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ๊ธฐ์กด ํด๋์ค๋ฅผ ํ์ฉํจ์ผ๋ก์จ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ธฐ ์ํจ์ ๋๋ค. ๐ธ Before/After: - Before: ํ์ผ ์ ๋ ฅ์ด๋ ๋ฐ์ดํธ ๋ชฉํ์น ์ ๋ ฅ๋์ด ์ง์์ง ๋ ์๊ฐ์ , ๊ตฌ์กฐ์ ์ผ๋ก ์๋ฌด๋ฐ ํผ๋๋ฐฑ์ด ์ ๊ณต๋์ง ์๊ณ ๋จ์ํ ์ค๋ฅ ํ ์คํธ๊ฐ ์ฌ๋ผ์ก์ผ๋ฉฐ, ์๋ฌ ๋ฐ์ ์ ์ปค์คํ ์ธ๋ผ์ธ ์์์ด ์ค์ ๋์์ต๋๋ค. - After: ์ ๋ ฅ๋์ด ์ง์์ง๋ฉด ๋นจ๊ฐ์ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ํ๋๋ฉฐ, ์คํฌ๋ฆฐ ๋ฆฌ๋์ฉ `aria-invalid="true"` ์ํ๊ฐ ์ ์ฉ๋ฉ๋๋ค. ๋ํ, ์ปค์คํ ์ธ๋ผ์ธ CSS ๋์ `.required-star` ํด๋์ค๊ฐ ํ์ฉ๋ฉ๋๋ค. โฟ Accessibility: - ๋น ํ์ ํผ ์ ๋ ฅ ์ํ์์ `aria-invalid="true"`๋ฅผ ๋์ ์ผ๋ก ๋ถ์ฌํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋๊ฐ ํผ ์๋ฌ๋ฅผ ์ ํํ๊ฒ ์ฝ์ ์ ์๋๋ก ์ ๊ทผ์ฑ์ ๊ฐํํ์ต๋๋ค. From 55da6a757a0714b4103ebacab0e4e3bf876f4829 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 23:14:31 +0000 Subject: [PATCH 04/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐ก What: - ํ์ ํผ ์ ๋ ฅ ํ๋๊ฐ ๋น์์ง ๋ ์๋ฌด๋ฐ ๊ฒฝ๊ณ ๋ ์ฃผ์ง ์๊ณ ์นจ๋ฌตํ๋ ๋ฌธ์ ๋ฅผ ์์ ํ์ฌ, "This field is required."๋ผ๋ ๋ช ์์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค. - `aria-invalid="true"` ์์ฑ์ ์ค์ ํ์ฌ ์คํฌ๋ฆฐ ๋ฆฌ๋ ๋ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์ ์ค๋ฅ ์ํ๋ฅผ ๋ช ํํ ์ ๋ฌํ๋๋ก ๊ฐ์ ํ์ต๋๋ค. - ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ง์ ์ธ๋ผ์ธ ์คํ์ผ(`preview.style.color = '#dc3545'`)์ ์ง์ ํ๋ ๋ฐฉ์์ ์ ๊ฑฐํ๊ณ , ์ธ๋ผ์ธ ์คํ์ผ ์ถฉ๋์ ํผํ๊ธฐ ์ํด `preview.style.color = ''`๋ก ์ด๊ธฐํํ ๋ค ๊ธฐ์กด ํด๋์ค์ธ `.required-star`๋ฅผ ์ ์ฉํ๋๋ก ์์ ํ์ต๋๋ค. - ์ฝ๋ ๋ฆฌ๋ทฐ ๋ฐ์: ์คํฌ๋ฆฝํธ ์คํ ์์ ์ ๋ณด์ฅํ๊ธฐ ์ํด ` - -