Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2025-02-25 - Avoid leaving random test files when generating patches
**Learning:** Even when working carefully on isolated performance optimizations using a temporary Gradle setup (`tempTest/`) or ad-hoc scripts (`patch_hint.sh`), it's incredibly important to clean up these files before requesting a code review or submitting a PR. Leaving them pollutes the git working tree, turning an otherwise excellent PR into a blockable state.
**Action:** Before running `request_code_review` or `submit`, explicitly run a `git status` and actively `rm -rf` all scripts, test files, and temporary directories that are not intended to be part of the final commit.
13 changes: 10 additions & 3 deletions halogen-core/src/commonMain/kotlin/halogen/SchemaParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public object SchemaParser {
isLenient = true
}

private val HEX_COLOR_REGEX: Regex = Regex("^#[0-9A-Fa-f]{6}$")

/**
* Parse a JSON string (potentially wrapped in markdown code fences) into
* a validated [HalogenThemeSpec].
Expand Down Expand Up @@ -67,7 +65,7 @@ public object SchemaParser {
)

for ((name, value) in colorFields) {
if (!HEX_COLOR_REGEX.matches(value)) {
if (!isValidHexColor(value)) {
return Result.failure(
IllegalArgumentException(
"Invalid hex color for $name: \"$value\". Expected format: #RRGGBB",
Expand All @@ -85,4 +83,13 @@ public object SchemaParser {

return Result.success(clamped)
}

private fun isValidHexColor(value: String): Boolean {
if (value.length != 7 || value[0] != '#') return false
for (i in 1 until 7) {
val c = value[i]
if (c !in '0'..'9' && c !in 'a'..'f' && c !in 'A'..'F') return false
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,78 @@ package halogen.engine
*/
internal object HintExtractor {

private val PREFIX_PATTERN = Regex("""^(?:/r/|/category/|/topic/|/|#)""")
private val CAMEL_SPLIT = Regex("""(?<=[a-z])(?=[A-Z])""")
private val ID_PATTERN = Regex("""^[0-9a-f]{8,}$""", RegexOption.IGNORE_CASE)
private val NUMERIC_ONLY = Regex("""^\d+$""")
private val WHITESPACE_PATTERN = Regex("""\s+""")

fun extract(key: String): String? {
if (key.isBlank()) return null
val trimmed = key.trim()
var start = 0
val len = trimmed.length

// Strip common prefixes
var cleaned = PREFIX_PATTERN.replace(key.trim(), "")
if (trimmed.startsWith("/r/")) start = 3
else if (trimmed.startsWith("/category/")) start = 10
else if (trimmed.startsWith("/topic/")) start = 7
else if (trimmed.startsWith("/") || trimmed.startsWith("#")) start = 1

// Remove leading/trailing slashes
cleaned = cleaned.trim('/')
var end = len
while (start < end && trimmed[start] == '/') start++
while (start < end && trimmed[end - 1] == '/') end--

// Take the last meaningful segment if it looks like a path
if ('/' in cleaned) {
cleaned = cleaned.substringAfterLast('/')
val lastSlash = trimmed.lastIndexOf('/', end - 1)
if (lastSlash >= start) {
start = lastSlash + 1
}

// Split camelCase
cleaned = CAMEL_SPLIT.replace(cleaned, " ")
if (start >= end) return null

val sb = StringBuilder()
var hasValidChar = false
var isAllHex = true
var isAllDigit = true
var charCount = 0
var spacePending = false
var prevWasLower = false

for (i in start until end) {
val c = trimmed[i]
if (c == '_' || c == '-' || c.isWhitespace()) {
if (hasValidChar) {
spacePending = true
}
prevWasLower = false
} else {
val isUpper = c.isUpperCase()
val isLower = c.isLowerCase()

// Split snake_case and kebab-case
cleaned = cleaned.replace('_', ' ').replace('-', ' ')
// Split camelCase
if (prevWasLower && isUpper) {
spacePending = true
}

// Normalize whitespace
cleaned = cleaned.trim().replace(WHITESPACE_PATTERN, " ")
if (spacePending && hasValidChar) {
sb.append(' ')
spacePending = false
}

if (cleaned.isBlank()) return null
sb.append(c.lowercaseChar())
hasValidChar = true
prevWasLower = isLower
charCount++

// Validation checks
if (c !in '0'..'9') {
isAllDigit = false
if (c !in 'a'..'f' && c !in 'A'..'F') {
isAllHex = false
}
}
}
}

// Reject things that look like IDs
val noSpaces = cleaned.replace(" ", "")
if (ID_PATTERN.matches(noSpaces)) return null
if (NUMERIC_ONLY.matches(noSpaces)) return null
if (!hasValidChar) return null
if (isAllDigit) return null
if (isAllHex && charCount >= 8) return null

return cleaned.lowercase()
return sb.toString()
}
}
Loading