Skip to content
Open
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
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@
## 2024-08-01 - 네이티브 브라우저 UI의 다크 모드 지원 강제
**학습:** CSS 미디어 쿼리(`@media (prefers-color-scheme: dark)`)를 통해 다크 모드를 지원하더라도, 브라우저의 네이티브 UI 요소(스크롤바, 기본 폼 컨트롤, 기본 백그라운드 등)는 테마 변경을 인식하지 못해 어두운 테마 환경에서 밝은 스크롤바가 표시되는 등 시각적 불일치를 초래합니다.
**조치:** 항상 HTML 문서의 `<head>` 영역에 `<meta name="color-scheme" content="light dark">` 메타 태그를 명시적으로 추가하여 브라우저 수준에서 사용자의 시스템 테마(다크 모드 등)를 완전히 상속받아 일관성 있는 네이티브 UI를 렌더링하도록 보장하십시오.

## 2024-05-25 - 디렉토리 목록 빈 폴더 시각화 향상
**학습:** 디렉토리 목록에서 비어있는 디렉토리에 대해 일반 디렉토리와 동일한 닫힌 폴더 아이콘과 '디렉토리' 라벨을 제공하면, 사용자가 불필요하게 비어있는 폴더를 클릭하여 탐색하게 되는 UX 저하가 발생합니다.
**조치:** 디렉토리 트리를 렌더링할 때 하위 디렉토리가 비어있는지 확인(성능을 위해 `Files.newDirectoryStream` 사용)하고, 비어있는 경우 열린 폴더 아이콘(`&#128194;`)과 명확한 라벨('빈 디렉토리')을 제공하여 사용자에게 즉각적인 시각적 피드백을 제공하십시오.
21 changes: 18 additions & 3 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array
padding: 0.5rem;
opacity: 0.7;
font-style: italic;
display: flex;
align-items: flex-start;
gap: 0.5rem;
}
"""

Expand Down Expand Up @@ -360,17 +363,29 @@ ${cssContent} </style>
} catch (e: Exception) {
}
if (!isSymbolicLink) {
var isEmptyDir = false
if (isLinkedDirectory) {
try {
val stream = Files.newDirectoryStream(it.toPath())
try {
isEmptyDir = !stream.iterator().hasNext()
} finally {
stream.close()
}
} catch (e: Exception) {
}
}
Comment on lines +366 to +377
val encodedHref = if (isLinkedDirectory) { "./${fileName.urlEncodePath()}/" } else { "./${fileName.urlEncodePath()}" }
val ariaLabel = "${fileName} ${if (isLinkedDirectory) { "디렉토리" } else { "파일" }}".escapeHtml()
val icon = if (isLinkedDirectory) { "&#128193;" } else { "&#128196;" }
val ariaLabel = "${fileName} ${if (isLinkedDirectory) { if (isEmptyDir) "빈 디렉토리" else "디렉토리" } else { "파일" }}".escapeHtml()
val icon = if (isLinkedDirectory) { if (isEmptyDir) "&#128194;" else "&#128193;" } else { "&#128196;" }
l.append(""" <li><a class="dir-link" href="${encodedHref}" aria-label="${ariaLabel}" title="${ariaLabel}"><span class="icon" aria-hidden="true">${icon}</span> <span>${fileName.escapeHtml()}</span></a></li>""")
l.append('\n')
}
}
}

if(l.isEmpty()){
l.append(""" <li><div class="empty-dir">이 디렉토리는 비어 있습니다.</div></li>""")
l.append(""" <li><div class="empty-dir"><span class="icon" aria-hidden="true">&#128194;</span> <span>이 디렉토리는 비어 있습니다.</span></div></li>""")
l.append('\n')
}

Expand Down
8 changes: 8 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class MainTest {
val htmlContent = indexFile.readText()
assertTrue(htmlContent.contains("<html lang=\"ko\">"))
assertTrue(htmlContent.contains("이 디렉토리는 비어 있습니다."))
assertTrue(htmlContent.contains("&#128194;"))
assertTrue(htmlContent.contains("role=\"list\""))
}

Expand Down Expand Up @@ -298,6 +299,9 @@ class MainTest {
fun testProcessDir() {
val subdir = File(tempDir, "subdir")
subdir.mkdir()
File(subdir, "child.txt").createNewFile()
val emptydir = File(tempDir, "emptydir")
emptydir.mkdir()
File(tempDir, "file1.txt").createNewFile()
File(tempDir, "test.ignore").createNewFile()
File(tempDir, ".html4ignore").writeText("*.ignore")
Expand All @@ -320,9 +324,13 @@ class MainTest {
assertTrue(htmlContent.contains("title=\"file1.txt 파일\""))
assertTrue(htmlContent.contains("aria-label=\"subdir 디렉토리\""))
assertTrue(htmlContent.contains("title=\"subdir 디렉토리\""))
assertTrue(htmlContent.contains("aria-label=\"emptydir 빈 디렉토리\""))
assertTrue(htmlContent.contains("title=\"emptydir 빈 디렉토리\""))
assertTrue(htmlContent.contains("file1.txt"))
assertTrue(htmlContent.contains("subdir/"))
assertTrue(htmlContent.contains("emptydir/"))
assertTrue(htmlContent.contains("&#128193;"))
assertTrue(htmlContent.contains("&#128194;"))
assertFalse(htmlContent.contains("test.ignore"))
assertTrue(htmlContent.contains("Content-Security-Policy"))
val hashMatch = Regex("""style-src '(sha256-[A-Za-z0-9+/=]+)'""").find(htmlContent)?.groupValues?.get(1)
Expand Down
Loading