From f15830e3e78a4206774ee81e54e5eb2dd7e808c3 Mon Sep 17 00:00:00 2001 From: darwincereska Date: Mon, 24 Nov 2025 11:36:15 -0500 Subject: [PATCH] feat(colors): colored output; removed colorutils --- build.gradle.kts | 4 +- src/main/kotlin/org/notevc/NoteVC.kt | 11 ++- .../kotlin/org/notevc/cli/CommandParser.kt | 1 - .../org/notevc/commands/CommitCommand.kt | 24 ++--- .../kotlin/org/notevc/commands/DiffCommand.kt | 97 +++++++++---------- .../kotlin/org/notevc/commands/InitCommand.kt | 9 +- .../kotlin/org/notevc/commands/LogCommand.kt | 20 ++-- .../org/notevc/commands/RestoreCommand.kt | 35 ++++--- .../kotlin/org/notevc/commands/ShowCommand.kt | 59 ++++++----- .../org/notevc/commands/StatusCommand.kt | 25 +++-- .../kotlin/org/notevc/utils/ColorUtils.kt | 77 --------------- 11 files changed, 138 insertions(+), 224 deletions(-) delete mode 100644 src/main/kotlin/org/notevc/cli/CommandParser.kt delete mode 100644 src/main/kotlin/org/notevc/utils/ColorUtils.kt diff --git a/build.gradle.kts b/build.gradle.kts index 70c2302..b03f559 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } group = "org.notevc" -version = "1.0.7" +version = "1.0.8" buildConfig { buildConfigField("String", "VERSION", "\"${project.version}\"") @@ -26,7 +26,7 @@ dependencies { val junitVersion = "5.10.0" implementation(kotlin("stdlib")) implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") - implementation("org.kargs:kargs:1.0.4") + implementation("org.kargs:kargs:1.0.8") testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion") diff --git a/src/main/kotlin/org/notevc/NoteVC.kt b/src/main/kotlin/org/notevc/NoteVC.kt index 4ecbbdc..3b002bc 100644 --- a/src/main/kotlin/org/notevc/NoteVC.kt +++ b/src/main/kotlin/org/notevc/NoteVC.kt @@ -2,12 +2,17 @@ package org.notevc import org.notevc.core.Repository import org.notevc.commands.* -import org.notevc.utils.ColorUtils import org.kargs.* fun main(args: Array) { + val argsList = args.toMutableList() + // Create argument parser - val parser = Parser("notevc", ParserConfig(programVersion = Repository.VERSION)) + val parser = Parser("notevc", ParserConfig(programVersion = Repository.VERSION, colorsEnabled = true)) + + if (argsList.remove("--no-color")) { + Colors.setGlobalColorsEnabled(false) + } else Colors.setGlobalColorsEnabled(true) // Register subcommands parser.subcommands( @@ -22,7 +27,7 @@ fun main(args: Array) { // Parse arguments try { - parser.parse(args) + parser.parse(argsList.toTypedArray()) } catch (e: Exception) { kotlin.system.exitProcess(1) } diff --git a/src/main/kotlin/org/notevc/cli/CommandParser.kt b/src/main/kotlin/org/notevc/cli/CommandParser.kt deleted file mode 100644 index 243daff..0000000 --- a/src/main/kotlin/org/notevc/cli/CommandParser.kt +++ /dev/null @@ -1 +0,0 @@ -package org.notevc.cli diff --git a/src/main/kotlin/org/notevc/commands/CommitCommand.kt b/src/main/kotlin/org/notevc/commands/CommitCommand.kt index d127507..a6dcad4 100644 --- a/src/main/kotlin/org/notevc/commands/CommitCommand.kt +++ b/src/main/kotlin/org/notevc/commands/CommitCommand.kt @@ -7,12 +7,8 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.nio.file.Files import java.time.Instant -import org.notevc.utils.ColorUtils import kotlin.io.path.* -import org.kargs.Subcommand -import org.kargs.ArgType -import org.kargs.Option -import org.kargs.Argument +import org.kargs.* class CommitCommand : Subcommand("commit", description = "Create a commit of changed files") { val targetFile by Option(ArgType.readableFile(), longName = "file", shortName = "f", description = "Commit only a specific file") @@ -30,7 +26,7 @@ class CommitCommand : Subcommand("commit", description = "Create a commit of cha } result.onSuccess { message -> println(message) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } private fun createSingleFileCommit(repo: Repository, targetFile: String, message: String): String { @@ -77,9 +73,9 @@ class CommitCommand : Subcommand("commit", description = "Create a commit of cha updateRepositoryHead(repo, commitHash, timestamp, message) return buildString { - appendLine("${ColorUtils.success("Created commit")} ${ColorUtils.hash(commitHash)}") - appendLine("${ColorUtils.bold("Message:")} $message") - appendLine("${ColorUtils.bold("File:")} ${ColorUtils.filename(relativePath)} ${ColorUtils.dim("(${snapshot.blocks.size} blocks)")}") + appendLine("${Colors.success("Created commit")} ${Colors.yellow(commitHash)}") + appendLine("${Colors.bold("Message:")} $message") + appendLine("${Colors.bold("File:")} ${Colors.filename(relativePath)} ${Colors.dim("(${snapshot.blocks.size} blocks)")}") } } @@ -140,16 +136,16 @@ class CommitCommand : Subcommand("commit", description = "Create a commit of cha updateRepositoryHead(repo, commitHash, timestamp, message) return buildString { - appendLine("${ColorUtils.success("Created commit")} ${ColorUtils.hash(commitHash)}") - appendLine("${ColorUtils.bold("Message:")} $message") - appendLine("${ColorUtils.bold("Files committed:")} ${changedFiles.size}") - appendLine("${ColorUtils.bold("Total blocks:")} $totalBlocksStored") + appendLine("${Colors.success("Created commit")} ${Colors.yellow(commitHash)}") + appendLine("${Colors.bold("Message:")} $message") + appendLine("${Colors.bold("Files committed:")} ${changedFiles.size}") + appendLine("${Colors.bold("Total blocks:")} $totalBlocksStored") appendLine() changedFiles.forEach { fileInfo -> val parts = fileInfo.split(" (") val filename = parts[0] val blockInfo = if (parts.size > 1) " (${parts[1]}" else "" - appendLine(" ${ColorUtils.filename(filename)}${ColorUtils.dim(blockInfo)}") + appendLine(" ${Colors.filename(filename)}${Colors.dim(blockInfo)}") } } } diff --git a/src/main/kotlin/org/notevc/commands/DiffCommand.kt b/src/main/kotlin/org/notevc/commands/DiffCommand.kt index b90dff9..4b2a85a 100644 --- a/src/main/kotlin/org/notevc/commands/DiffCommand.kt +++ b/src/main/kotlin/org/notevc/commands/DiffCommand.kt @@ -1,7 +1,6 @@ package org.notevc.commands import org.notevc.core.* -import org.notevc.utils.ColorUtils import kotlinx.serialization.json.Json import java.nio.file.Files import java.time.Instant @@ -53,7 +52,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } result.onSuccess { message -> println(message) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } private fun compareSpecificBlock(repo: Repository, commitHash: String?, blockHash: String, targetFile: String?): String { @@ -66,13 +65,13 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } val result = StringBuilder() - result.appendLine("${ColorUtils.bold("Block comparison:")} ${ColorUtils.hash(blockHash.take(8))}") + result.appendLine("${Colors.bold("Block comparison:")} ${Colors.yellow(blockHash.take(8))}") result.appendLine() // Get the commit snapshot if provided, otherwise use latest val commitSnapshot = if (commitHash != null) { val commit = findCommit(repo, commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(commitHash)} not found") val commitTime = Instant.parse(commit.timestamp) blockStore.getLatestBlockSnapshotBefore(targetFile, commitTime) } else { @@ -94,41 +93,41 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c val newBlock = currentSnapshot.blocks.find { it.id.startsWith(blockHash) } if (oldBlock == null && newBlock == null) { - throw Exception("Block ${ColorUtils.hash(blockHash)} not found") + throw Exception("Block ${Colors.yellow(blockHash)} not found") } val headingText = (newBlock?.heading ?: oldBlock?.heading ?: "").replace(Regex("^#+\\s*"), "").trim() - result.appendLine("${ColorUtils.heading(headingText)} ${ColorUtils.dim("[${blockHash.take(8)}]")}") - result.appendLine("${ColorUtils.dim("─".repeat(70))}") + result.appendLine("${Colors.heading(headingText)} ${Colors.dim("[${blockHash.take(8)}]")}") + result.appendLine(Colors.dim("─".repeat(70))) result.appendLine() when { oldBlock == null && newBlock != null -> { - result.appendLine("${ColorUtils.success("This block was ADDED")}") + result.appendLine(Colors.green("This block was ADDED")) result.appendLine() val newContent = objectStore.getContent(newBlock.contentHash) if (newContent != null) { newContent.lines().forEach { line -> - result.appendLine("${ColorUtils.success("+ ")} $line") + result.appendLine("${Colors.green("+ ")} $line") } } } oldBlock != null && newBlock == null -> { - result.appendLine("${ColorUtils.error("This block was DELETED")}") + result.appendLine(Colors.error("This block was DELETED")) result.appendLine() val oldContent = objectStore.getContent(oldBlock.contentHash) if (oldContent != null) { oldContent.lines().forEach { line -> - result.appendLine("${ColorUtils.error("- ")} $line") + result.appendLine("${Colors.error("- ")} $line") } } } oldBlock != null && newBlock != null -> { if (oldBlock.contentHash == newBlock.contentHash) { - result.appendLine("${ColorUtils.dim("No changes")}") + result.appendLine(Colors.dim("No changes")) } else { - result.appendLine("${ColorUtils.warning("Block was MODIFIED")}") + result.appendLine(Colors.warn("Block was MODIFIED")) result.appendLine() val oldContent = objectStore.getContent(oldBlock.contentHash) val newContent = objectStore.getContent(newBlock.contentHash) @@ -152,9 +151,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c // Find commits val commit1 = findCommit(repo, hash1) - ?: throw Exception("Commit ${ColorUtils.hash(hash1)} not found") + ?: throw Exception("Commit ${Colors.yellow(hash1)} not found") val commit2 = findCommit(repo, hash2) - ?: throw Exception("Commit ${ColorUtils.hash(hash2)} not found") + ?: throw Exception("Commit ${Colors.yellow(hash2)} not found") val time1 = Instant.parse(commit1.timestamp) val time2 = Instant.parse(commit2.timestamp) @@ -170,9 +169,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } val result = StringBuilder() - result.appendLine("${ColorUtils.bold("Comparing commits:")}") - result.appendLine(" ${ColorUtils.hash(hash1.take(8))} ${ColorUtils.dim(commit1.message)}") - result.appendLine(" ${ColorUtils.hash(hash2.take(8))} ${ColorUtils.dim(commit2.message)}") + result.appendLine(Colors.bold("Comparing commits:")) + result.appendLine(" ${Colors.yellow(hash1.take(8))} ${Colors.dim(commit1.message)}") + result.appendLine(" ${Colors.yellow(hash2.take(8))} ${Colors.dim(commit2.message)}") result.appendLine() var totalChanges = 0 @@ -184,7 +183,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c if (snapshot1 != null || snapshot2 != null) { val changes = blockStore.compareBlocks(snapshot1, snapshot2) if (changes.isNotEmpty()) { - result.appendLine("${ColorUtils.filename(filePath)}:") + result.appendLine("${Colors.filename(filePath)}:") result.append(formatBlockChanges(changes, objectStore)) result.appendLine() totalChanges += changes.size @@ -193,9 +192,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } if (totalChanges == 0) { - result.appendLine("${ColorUtils.dim("No differences found")}") + result.appendLine(Colors.dim("No differences found")) } else { - result.appendLine("${ColorUtils.bold("Total changes:")} $totalChanges") + result.appendLine("${Colors.bold("Total changes:")} $totalChanges") } return result.toString() @@ -208,7 +207,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c // Find commit val commit = findCommit(repo, hash) - ?: throw Exception("Commit ${ColorUtils.hash(hash)} not found") + ?: throw Exception("Commit ${Colors.yellow(hash)} not found") val commitTime = Instant.parse(commit.timestamp) @@ -225,8 +224,8 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } val result = StringBuilder() - result.appendLine("${ColorUtils.bold("Comparing working directory to commit:")}") - result.appendLine(" ${ColorUtils.hash(hash.take(8))} ${ColorUtils.dim(commit.message)}") + result.appendLine(Colors.bold("Comparing working directory to commit:")) + result.appendLine(" ${Colors.yellow(hash.take(8))} ${Colors.lightGray(commit.message)}") result.appendLine() var totalChanges = 0 @@ -247,7 +246,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c val changes = blockStore.compareBlocks(commitSnapshot, currentSnapshot) if (changes.isNotEmpty()) { - result.appendLine("${ColorUtils.filename(filePath)}:") + result.appendLine("${Colors.filename(filePath)}:") result.append(formatBlockChanges(changes, objectStore)) result.appendLine() totalChanges += changes.size @@ -256,9 +255,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } if (totalChanges == 0) { - result.appendLine("${ColorUtils.dim("No differences found")}") + result.appendLine(Colors.dim("No differences found")) } else { - result.appendLine("${ColorUtils.bold("Total changes:")} $totalChanges") + result.appendLine("${Colors.bold("Total changes:")} $totalChanges") } return result.toString() @@ -282,7 +281,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } val result = StringBuilder() - result.appendLine("${ColorUtils.bold("Changes in working directory:")}") + result.appendLine(Colors.bold("Changes in working directory:")) result.appendLine() var totalChanges = 0 @@ -294,16 +293,14 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c val parsedFile = blockParser.parseFile(content, filePath) // Skip disabled files - if (parsedFile.frontMatter?.isEnabled == false) { - return@forEach - } + if (parsedFile.frontMatter?.isEnabled == false) return@forEach val latestSnapshot = blockStore.getLatestBlockSnapshot(filePath) val currentSnapshot = createCurrentSnapshot(parsedFile, objectStore) val changes = blockStore.compareBlocks(latestSnapshot, currentSnapshot) if (changes.isNotEmpty()) { - result.appendLine("${ColorUtils.filename(filePath)}:") + result.appendLine("${Colors.filename(filePath)}:") result.append(formatBlockChanges(changes, objectStore)) result.appendLine() totalChanges += changes.size @@ -312,9 +309,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c } if (totalChanges == 0) { - result.appendLine("${ColorUtils.dim("No changes detected - working directory clean")}") + result.appendLine(Colors.lightGray("No changes detected - working directory clean")) } else { - result.appendLine("${ColorUtils.bold("Total changes:")} $totalChanges") + result.appendLine("${Colors.bold("Total changes:")} $totalChanges") } return result.toString() @@ -330,42 +327,42 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c when (change.type) { BlockChangeType.ADDED -> { result.appendLine() - result.appendLine(" ${ColorUtils.success("+++")} ${ColorUtils.bold("ADDED")} ${ColorUtils.success("+++")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") - result.appendLine(" ${ColorUtils.dim("─".repeat(60))}") + result.appendLine(" ${Colors.green("+++")} ${Colors.bold("ADDED")} ${Colors.green("+++")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}") + result.appendLine(" ${Colors.dim("─".repeat(60))}") if (change.newHash != null) { val content = objectStore.getContent(change.newHash) if (content != null) { content.lines().take(5).forEach { line -> - result.appendLine(" ${ColorUtils.success("+")} $line") + result.appendLine(" ${Colors.green("+")} $line") } if (content.lines().size > 5) { - result.appendLine(" ${ColorUtils.dim(" ... ${content.lines().size - 5} more lines")}") + result.appendLine(" ${Colors.dim(" ... ${content.lines().size - 5} more lines")}") } } } } BlockChangeType.DELETED -> { result.appendLine() - result.appendLine(" ${ColorUtils.error("---")} ${ColorUtils.bold("DELETED")} ${ColorUtils.error("---")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") - result.appendLine(" ${ColorUtils.dim("─".repeat(60))}") + result.appendLine(" ${Colors.error("---")} ${Colors.bold("DELETED")} ${Colors.error("---")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}") + result.appendLine(" ${Colors.dim("─".repeat(60))}") if (change.oldHash != null) { val content = objectStore.getContent(change.oldHash) if (content != null) { content.lines().take(5).forEach { line -> - result.appendLine(" ${ColorUtils.error("-")} $line") + result.appendLine(" ${Colors.error("-")} $line") } if (content.lines().size > 5) { - result.appendLine(" ${ColorUtils.dim(" ... ${content.lines().size - 5} more lines")}") + result.appendLine(" ${Colors.dim(" ... ${content.lines().size - 5} more lines")}") } } } } BlockChangeType.MODIFIED -> { result.appendLine() - result.appendLine(" ${ColorUtils.warning("~~~")} ${ColorUtils.bold("MODIFIED")} ${ColorUtils.warning("~~~")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") - result.appendLine(" ${ColorUtils.dim("─".repeat(60))}") + result.appendLine(" ${Colors.warn("~~~")} ${Colors.bold("MODIFIED")} ${Colors.warn("~~~")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}") + result.appendLine(" ${Colors.dim("─".repeat(60))}") // Show detailed diff if (change.oldHash != null && change.newHash != null) { @@ -405,27 +402,27 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c when { oldLine == null && newLine != null -> { // Addition - diff.add("${ColorUtils.success("+ ")} $newLine") + diff.add("${Colors.green("+ ")} $newLine") newIndex++ displayedLines++ } oldLine != null && newLine == null -> { // Deletion - diff.add("${ColorUtils.error("- ")} $oldLine") + diff.add("${Colors.boldRed("- ")} $oldLine") oldIndex++ displayedLines++ } oldLine == newLine -> { // Unchanged line (context) - diff.add("${ColorUtils.dim(" ")} ${ColorUtils.dim(oldLine ?: "")}") + diff.add("${Colors.dim(" ")} ${Colors.dimWhite(oldLine ?: "")}") oldIndex++ newIndex++ displayedLines++ } else -> { // Modified line - diff.add("${ColorUtils.error("- ")} $oldLine") - diff.add("${ColorUtils.success("+ ")} $newLine") + diff.add("${Colors.boldRed("- ")} $oldLine") + diff.add("${Colors.green("+ ")} $newLine") oldIndex++ newIndex++ displayedLines += 2 @@ -435,7 +432,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c val remainingLines = (oldLines.size - oldIndex) + (newLines.size - newIndex) if (remainingLines > 0) { - diff.add("${ColorUtils.dim(" ... $remainingLines more lines")}") + diff.add(Colors.dim(" ... $remainingLines more lines")) } return diff diff --git a/src/main/kotlin/org/notevc/commands/InitCommand.kt b/src/main/kotlin/org/notevc/commands/InitCommand.kt index 6c7d9d2..240b787 100644 --- a/src/main/kotlin/org/notevc/commands/InitCommand.kt +++ b/src/main/kotlin/org/notevc/commands/InitCommand.kt @@ -2,10 +2,7 @@ package org.notevc.commands import org.notevc.core.Repository import java.nio.file.Path -import org.notevc.utils.ColorUtils -import org.kargs.Subcommand -import org.kargs.Argument -import org.kargs.ArgType +import org.kargs.* class InitCommand : Subcommand("init", description = "Initialize a repository", aliases = listOf("i")) { val path by Argument(ArgType.existingDirectory(), "path", description = "Initialize in a specified directory", required = false) @@ -19,13 +16,13 @@ class InitCommand : Subcommand("init", description = "Initialize a repository", repo.init().fold( onSuccess = { val absolutePath = repo.path.toAbsolutePath().toString() - "Initialized notevc repository in ${ColorUtils.filename(absolutePath)}" + "Initialized notevc repository in ${Colors.filename(absolutePath)}" }, onFailure = { error -> throw Exception(error) } ) } result.onSuccess { message -> println(message) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } } diff --git a/src/main/kotlin/org/notevc/commands/LogCommand.kt b/src/main/kotlin/org/notevc/commands/LogCommand.kt index 2a0038b..38c786d 100644 --- a/src/main/kotlin/org/notevc/commands/LogCommand.kt +++ b/src/main/kotlin/org/notevc/commands/LogCommand.kt @@ -4,7 +4,6 @@ import org.notevc.core.* import kotlinx.serialization.json.Json import java.nio.file.Files import java.time.Instant -import org.notevc.utils.ColorUtils import java.time.ZoneId import java.time.format.DateTimeFormatter import kotlin.io.path.* @@ -27,7 +26,7 @@ class LogCommand : Subcommand("log", description = "Show commit history with det } result.onSuccess { message -> println(message) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } private fun generateLog(repo: Repository, options: LogOptions): String { @@ -111,10 +110,10 @@ class LogCommand : Subcommand("log", description = "Show commit history with det return commits.joinToString("\n") { commit -> val fileInfo = if (options.showFiles) { val stats = getCommitStats(repo, commit, options.targetFile) - ColorUtils.dim(" (${stats.filesChanged} files, ${stats.totalBlocks} blocks)") + Colors.dim(" (${stats.filesChanged} files, ${stats.totalBlocks} blocks)") } else "" - "${ColorUtils.hash(commit.hash)} ${commit.message}$fileInfo" + "${Colors.yellow(commit.hash)} ${commit.message}$fileInfo" } } @@ -127,22 +126,23 @@ class LogCommand : Subcommand("log", description = "Show commit history with det val formattedDate = formatter.format(timestamp) buildString { - appendLine("${ColorUtils.bold("commit")} ${ColorUtils.hash(commit.hash)}") - appendLine("${ColorUtils.bold("Author:")} ${ColorUtils.author(commit.author)}") - appendLine("${ColorUtils.bold("Date:")} ${ColorUtils.date(formattedDate)}") + appendLine("${Colors.bold("commit")} ${Colors.yellow(commit.hash)}") + appendLine("${Colors.bold("Author:")} ${Colors.green(commit.author)}") + appendLine("${Colors.bold("Date:")} ${Colors.dim(formattedDate)}") if (options.showFiles) { val stats = getCommitStats(repo, commit, options.targetFile) - appendLine("${ColorUtils.info("Files changed:")} ${stats.filesChanged}, ${ColorUtils.info("Total blocks:")} ${stats.totalBlocks}") + appendLine("${Colors.info("Files changed:")} ${stats.filesChanged}, ${Colors.info("Total blocks:")} ${stats.totalBlocks}") if (stats.fileDetails.isNotEmpty()) { appendLine() stats.fileDetails.forEach { (file, blocks) -> - appendLine(" ${ColorUtils.filename(file)} ${ColorUtils.dim("(${blocks.size} blocks)")}") + appendLine(" ${Colors.filename(file)} ${Colors.dim("(${blocks.size} blocks)")}") blocks.forEach { block -> val heading = block.heading.replace(Regex("^#+\\s*"), "").trim() - appendLine(" - ${ColorUtils.hash(block.id.take(8))}: ${ColorUtils.heading(heading)}") + appendLine(" - ${Colors.yellow(block.id.take(8))}: ${Colors.magenta(heading)}") } + appendLine() } } } diff --git a/src/main/kotlin/org/notevc/commands/RestoreCommand.kt b/src/main/kotlin/org/notevc/commands/RestoreCommand.kt index 4105985..a990b3d 100644 --- a/src/main/kotlin/org/notevc/commands/RestoreCommand.kt +++ b/src/main/kotlin/org/notevc/commands/RestoreCommand.kt @@ -1,7 +1,6 @@ package org.notevc.commands import org.notevc.core.* -import org.notevc.utils.ColorUtils import kotlinx.serialization.json.Json import java.nio.file.Files import java.time.Instant @@ -35,7 +34,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo } result.onSuccess { message -> println(message) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } private fun restoreSpecificBlock(repo: Repository, commitHash: String, blockHash: String, targetFile: String): String { @@ -45,21 +44,21 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo // Find the commit val commit = findCommit(repo, commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(commitHash)} not found") // Find the block snapshot for this file at the commit time val commitTime = Instant.parse(commit.timestamp) val snapshot = blockStore.getBlocksAtTime(targetFile, commitTime) - ?: throw Exception("No snapshot found for ${ColorUtils.filename(targetFile)} at commit ${ColorUtils.hash(commitHash)}") + ?: throw Exception("No snapshot found for ${Colors.filename(targetFile)} at commit ${Colors.yellow(commitHash)}") // Find the specific block val targetBlock = snapshot.find { it.id.startsWith(blockHash) } - ?: throw Exception("Block ${ColorUtils.hash(blockHash)} not found in ${ColorUtils.filename(targetFile)} at commit ${ColorUtils.hash(commitHash)}") + ?: throw Exception("Block ${Colors.yellow(blockHash)} not found in ${Colors.filename(targetFile)} at commit ${Colors.yellow(commitHash)}") // Read current file val filePath = repo.path.resolve(targetFile) if (!filePath.exists()) { - throw Exception("File ${ColorUtils.filename(targetFile)} does not exist") + throw Exception("File ${Colors.filename(targetFile)} does not exist") } val currentContent = Files.readString(filePath) @@ -68,7 +67,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo // Find the block to replace in current file val currentBlockIndex = currentParsedFile.blocks.indexOfFirst { it.id.startsWith(blockHash) } if (currentBlockIndex == -1) { - throw Exception("Block ${ColorUtils.hash(blockHash)} not found in current ${ColorUtils.filename(targetFile)}") + throw Exception("Block ${Colors.yellow(blockHash)} not found in current ${Colors.filename(targetFile)}") } // Replace the block @@ -82,7 +81,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo Files.writeString(filePath, restoredContent) val blockHeading = targetBlock.heading.replace(Regex("^#+\\s*"), "").trim() - return "${ColorUtils.success("Restored block")} ${ColorUtils.hash(blockHash.take(8))} ${ColorUtils.heading("\"$blockHeading\"")} in ${ColorUtils.filename(targetFile)} from commit ${ColorUtils.hash(commitHash)}" + return "${Colors.success("Restored block")} ${Colors.yellow(blockHash.take(8))} ${Colors.heading("\"$blockHeading\"")} in ${Colors.filename(targetFile)} from commit ${Colors.yellow(commitHash)}" } private fun restoreSpecificFile(repo: Repository, commitHash: String, targetFile: String): String { @@ -92,15 +91,15 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo // Find the commit val commit = findCommit(repo, commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(commitHash)} not found") // Find the block snapshot for this file at the commit time val commitTime = Instant.parse(commit.timestamp) val snapshot = blockStore.getLatestBlockSnapshotBefore(targetFile, commitTime) - ?: throw Exception("No snapshot found for ${ColorUtils.filename(targetFile)} at commit ${ColorUtils.hash(commitHash)}") + ?: throw Exception("No snapshot found for ${Colors.filename(targetFile)} at commit ${Colors.yellow(commitHash)}") val blocks = blockStore.getBlocksAtTime(targetFile, commitTime) - ?: throw Exception("No blocks found for ${ColorUtils.filename(targetFile)} at commit ${ColorUtils.hash(commitHash)}") + ?: throw Exception("No blocks found for ${Colors.filename(targetFile)} at commit ${Colors.yellow(commitHash)}") // Reconstruct the file from blocks with frontmatter from snapshot val parsedFile = ParsedFile( @@ -116,7 +115,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo Files.createDirectories(filePath.parent) Files.writeString(filePath, restoredContent) - return "${ColorUtils.success("Restored file")} ${ColorUtils.filename(targetFile)} ${ColorUtils.dim("(${blocks.size} blocks)")} from commit ${ColorUtils.hash(commitHash)}" + return "${Colors.success("Restored file")} ${Colors.filename(targetFile)} ${Colors.dim("(${blocks.size} blocks)")} from commit ${Colors.yellow(commitHash)}" } private fun restoreEntireRepository(repo: Repository, commitHash: String): String { @@ -126,7 +125,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo // Find the commit val commit = findCommit(repo, commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(commitHash)} not found") val commitTime = Instant.parse(commit.timestamp) @@ -134,7 +133,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo val trackedFiles = getTrackedFilesAtCommit(repo, commitTime) if (trackedFiles.isEmpty()) { - throw Exception("No files found at commit ${ColorUtils.hash(commitHash)}") + throw Exception("No files found at commit ${Colors.yellow(commitHash)}") } var restoredFiles = 0 @@ -162,10 +161,10 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo } return buildString { - appendLine("${ColorUtils.success("Restored repository")} to commit ${ColorUtils.hash(commitHash)}") - appendLine("${ColorUtils.bold("Files restored:")} $restoredFiles") - appendLine("${ColorUtils.bold("Total blocks:")} $totalBlocks") - appendLine("${ColorUtils.bold("Commit message:")} ${commit.message}") + appendLine("${Colors.success("Restored repository")} to commit ${Colors.yellow(commitHash)}") + appendLine("${Colors.bold("Files restored:")} $restoredFiles") + appendLine("${Colors.bold("Total blocks:")} $totalBlocks") + appendLine("${Colors.bold("Commit message:")} ${commit.message}") } } diff --git a/src/main/kotlin/org/notevc/commands/ShowCommand.kt b/src/main/kotlin/org/notevc/commands/ShowCommand.kt index 9e6e6ba..b31bc51 100644 --- a/src/main/kotlin/org/notevc/commands/ShowCommand.kt +++ b/src/main/kotlin/org/notevc/commands/ShowCommand.kt @@ -1,7 +1,6 @@ package org.notevc.commands import org.notevc.core.* -import org.notevc.utils.ColorUtils import kotlinx.serialization.json.Json import java.nio.file.Files import java.time.Instant @@ -30,7 +29,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information } result.onSuccess { message -> println(message) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } private fun showCommit(repo: Repository, commitHash: String, targetFile: String?): String { @@ -39,7 +38,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information // Find the commit val commit = findCommit(repo, commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(commitHash)} not found") val commitTime = Instant.parse(commit.timestamp) val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") @@ -48,11 +47,11 @@ class ShowCommand : Subcommand("show", description = "Show detailed information val result = StringBuilder() // Show commit header - result.appendLine("${ColorUtils.bold("Commit:")} ${ColorUtils.hash(commit.hash)}") - result.appendLine("${ColorUtils.bold("Author:")} ${commit.author}") - result.appendLine("${ColorUtils.bold("Date:")} ${formatter.format(commitTime)}") + result.appendLine("${Colors.bold("Commit:")} ${Colors.yellow(commit.hash)}") + result.appendLine("${Colors.bold("Author:")} ${commit.author}") + result.appendLine("${Colors.bold("Date:")} ${formatter.format(commitTime)}") if (commit.parent != null) { - result.appendLine("${ColorUtils.bold("Parent:")} ${ColorUtils.hash(commit.parent)}") + result.appendLine("${Colors.bold("Parent:")} ${Colors.yellow(commit.parent)}") } result.appendLine() result.appendLine(" ${commit.message}") @@ -66,12 +65,12 @@ class ShowCommand : Subcommand("show", description = "Show detailed information } if (filesToShow.isEmpty()) { - result.appendLine("${ColorUtils.dim("No files found at this commit")}") + result.appendLine("${Colors.dim("No files found at this commit")}") return result.toString() } // Show changes for each file - result.appendLine("${ColorUtils.bold("Changes:")}") + result.appendLine("${Colors.bold("Changes:")}") result.appendLine() var totalAdded = 0 @@ -94,7 +93,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information val changes = blockStore.compareBlocks(parentSnapshot, currentSnapshot) if (changes.isNotEmpty()) { - result.appendLine("${ColorUtils.filename(filePath)}:") + result.appendLine("${Colors.filename(filePath)}:") val added = changes.count { it.type == BlockChangeType.ADDED } val modified = changes.count { it.type == BlockChangeType.MODIFIED } @@ -104,9 +103,9 @@ class ShowCommand : Subcommand("show", description = "Show detailed information totalModified += modified totalDeleted += deleted - if (added > 0) result.appendLine(" ${ColorUtils.success("+")} $added ${if (added == 1) "block" else "blocks"} added") - if (modified > 0) result.appendLine(" ${ColorUtils.warning("~")} $modified ${if (modified == 1) "block" else "blocks"} modified") - if (deleted > 0) result.appendLine(" ${ColorUtils.error("-")} $deleted ${if (deleted == 1) "block" else "blocks"} deleted") + if (added > 0) result.appendLine(" ${Colors.boldGreen("+")} $added ${if (added == 1) "block" else "blocks"} added") + if (modified > 0) result.appendLine(" ${Colors.warn("~")} $modified ${if (modified == 1) "block" else "blocks"} modified") + if (deleted > 0) result.appendLine(" ${Colors.error("-")} $deleted ${if (deleted == 1) "block" else "blocks"} deleted") result.appendLine() @@ -117,13 +116,13 @@ class ShowCommand : Subcommand("show", description = "Show detailed information when (change.type) { BlockChangeType.ADDED -> { - result.appendLine(" ${ColorUtils.success("+")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") + result.appendLine(" ${Colors.boldGreen("+")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}") } BlockChangeType.DELETED -> { - result.appendLine(" ${ColorUtils.error("-")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") + result.appendLine(" ${Colors.error("-")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}") } BlockChangeType.MODIFIED -> { - result.appendLine(" ${ColorUtils.warning("~")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") + result.appendLine(" ${Colors.warn("~")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}") } } } @@ -134,8 +133,8 @@ class ShowCommand : Subcommand("show", description = "Show detailed information // Summary if (totalAdded + totalModified + totalDeleted > 0) { - result.appendLine("${ColorUtils.bold("Summary:")}") - result.appendLine(" ${ColorUtils.success("+")} $totalAdded added, ${ColorUtils.warning("~")} $totalModified modified, ${ColorUtils.error("-")} $totalDeleted deleted") + result.appendLine("${Colors.bold("Summary:")}") + result.appendLine(" ${Colors.boldGreen("+")} $totalAdded added, ${Colors.warn("~")} $totalModified modified, ${Colors.error("-")} $totalDeleted deleted") } return result.toString() @@ -190,7 +189,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information val blockStore = BlockStore(objectStore, repo.path.resolve("${Repository.NOTEVC_DIR}/blocks")) val commit = findCommit(repo, options.commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(options.commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(options.commitHash)} not found") val commitTime = Instant.parse(commit.timestamp) @@ -202,7 +201,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information ?: throw Exception("No snapshot found for ${options.targetFile} at commit ${options.commitHash}") val block = snapshot.blocks.find { it.id.startsWith(options.blockHash!!) } - ?: throw Exception("Block ${ColorUtils.hash(options.blockHash!!)} not found") + ?: throw Exception("Block ${Colors.yellow(options.blockHash!!)} not found") val content = objectStore.getContent(block.contentHash) ?: throw Exception("Content not found for block") @@ -210,12 +209,12 @@ class ShowCommand : Subcommand("show", description = "Show detailed information val headingText = block.heading.replace(Regex("^#+\\s*"), "").trim() val result = StringBuilder() - result.appendLine("${ColorUtils.bold("Block:")} ${ColorUtils.hash(block.id.take(8))}") - result.appendLine("${ColorUtils.bold("Heading:")} ${ColorUtils.heading(headingText)}") - result.appendLine("${ColorUtils.bold("File:")} ${ColorUtils.filename(options.targetFile)}") - result.appendLine("${ColorUtils.bold("Commit:")} ${ColorUtils.hash(commit.hash)}") + result.appendLine("${Colors.bold("Block:")} ${Colors.yellow(block.id.take(8))}") + result.appendLine("${Colors.bold("Heading:")} ${Colors.heading(headingText)}") + result.appendLine("${Colors.bold("File:")} ${Colors.filename(options.targetFile)}") + result.appendLine("${Colors.bold("Commit:")} ${Colors.yellow(commit.hash)}") result.appendLine() - result.appendLine("${ColorUtils.dim("─".repeat(70))}") + result.appendLine("${Colors.dim("─".repeat(70))}") result.appendLine() result.append(content) @@ -228,7 +227,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information val blockParser = BlockParser() val commit = findCommit(repo, options.commitHash) - ?: throw Exception("Commit ${ColorUtils.hash(options.commitHash)} not found") + ?: throw Exception("Commit ${Colors.yellow(options.commitHash)} not found") val commitTime = Instant.parse(commit.timestamp) @@ -261,11 +260,11 @@ class ShowCommand : Subcommand("show", description = "Show detailed information val reconstructedContent = blockParser.reconstructFile(parsedFile) val result = StringBuilder() - result.appendLine("${ColorUtils.bold("File:")} ${ColorUtils.filename(options.targetFile)}") - result.appendLine("${ColorUtils.bold("Commit:")} ${ColorUtils.hash(commit.hash)}") - result.appendLine("${ColorUtils.bold("Blocks:")} ${blocks.size}") + result.appendLine("${Colors.bold("File:")} ${Colors.filename(options.targetFile)}") + result.appendLine("${Colors.bold("Commit:")} ${Colors.yellow(commit.hash)}") + result.appendLine("${Colors.bold("Blocks:")} ${blocks.size}") result.appendLine() - result.appendLine("${ColorUtils.dim("─".repeat(70))}") + result.appendLine("${Colors.dim("─".repeat(70))}") result.appendLine() result.append(reconstructedContent) diff --git a/src/main/kotlin/org/notevc/commands/StatusCommand.kt b/src/main/kotlin/org/notevc/commands/StatusCommand.kt index d23cf52..1383597 100644 --- a/src/main/kotlin/org/notevc/commands/StatusCommand.kt +++ b/src/main/kotlin/org/notevc/commands/StatusCommand.kt @@ -2,10 +2,9 @@ package org.notevc.commands import org.notevc.core.* import org.notevc.utils.FileUtils -import org.notevc.utils.ColorUtils import org.notevc.core.Repository.Companion.NOTEVC_DIR import java.time.Instant -import org.kargs.Subcommand +import org.kargs.* class StatusCommand : Subcommand("status", description = "Show status of tracked files", aliases = listOf("st")) { override fun execute() { @@ -21,7 +20,7 @@ class StatusCommand : Subcommand("status", description = "Show status of tracked } result.onSuccess { output -> println(output) } - result.onFailure { error -> println("${ColorUtils.error("Error:")} ${error.message}") } + result.onFailure { error -> println("${Colors.error("Error:")} ${error.message}") } } private fun getRepositoryStatus(repo: Repository): RepositoryStatus { @@ -111,35 +110,35 @@ class StatusCommand : Subcommand("status", description = "Show status of tracked // Modified files grouped[FileStatusType.MODIFIED]?.let { modifiedFiles -> - output.appendLine(ColorUtils.bold("Modified files:")) + output.appendLine(Colors.bold("Modified files:")) modifiedFiles.forEach { fileStatus -> - output.appendLine(" ${ColorUtils.filename(fileStatus.path)}") + output.appendLine(" ${Colors.filename(fileStatus.path)}") fileStatus.blockChanges?.forEach { change -> val symbol = when (change.type) { - BlockChangeType.MODIFIED -> ColorUtils.modified("~") - BlockChangeType.ADDED -> ColorUtils.added("+") - BlockChangeType.DELETED -> ColorUtils.deleted("-") + BlockChangeType.MODIFIED -> Colors.boldYellow("~") + BlockChangeType.ADDED -> Colors.boldGreen("+") + BlockChangeType.DELETED -> Colors.boldRed("-") } val heading = change.heading.replace(Regex("^#+\\s*"), "").trim() - output.appendLine(" $symbol ${ColorUtils.heading(heading)}") + output.appendLine(" $symbol ${Colors.heading(heading)}") } } } // Untracked files grouped[FileStatusType.UNTRACKED]?.let { untrackedFiles -> - output.appendLine(ColorUtils.bold("Untracked files:")) + output.appendLine(Colors.bold("Untracked files:")) untrackedFiles.forEach { fileStatus -> - output.appendLine(" ${ColorUtils.untracked(fileStatus.path)} ${ColorUtils.dim("(${fileStatus.blockCount} blocks)")}") + output.appendLine(" ${Colors.dimWhite(fileStatus.path)} ${Colors.dim("(${fileStatus.blockCount} blocks)")}") } output.appendLine() } // Deleted files grouped[FileStatusType.DELETED]?.let { deletedFiles -> - output.appendLine(ColorUtils.bold("Deleted files:")) + output.appendLine(Colors.bold("Deleted files:")) deletedFiles.forEach { fileStatus -> - output.appendLine(" ${ColorUtils.deleted(fileStatus.path)}") + output.appendLine(" ${Colors.boldRed(fileStatus.path)}") } output.appendLine() } diff --git a/src/main/kotlin/org/notevc/utils/ColorUtils.kt b/src/main/kotlin/org/notevc/utils/ColorUtils.kt deleted file mode 100644 index 2bb4434..0000000 --- a/src/main/kotlin/org/notevc/utils/ColorUtils.kt +++ /dev/null @@ -1,77 +0,0 @@ -package org.notevc.utils - -object ColorUtils { - // ANSI color codes - private const val RESET = "\u001B[0m" - private const val BOLD = "\u001B[1m" - private const val DIM = "\u001B[2m" - - // Colors - private const val BLACK = "\u001B[30m" - private const val RED = "\u001B[31m" - private const val GREEN = "\u001B[32m" - private const val YELLOW = "\u001B[33m" - private const val BLUE = "\u001B[34m" - private const val MAGENTA = "\u001B[35m" - private const val CYAN = "\u001B[36m" - private const val WHITE = "\u001B[37m" - - // Bright colors - private const val BRIGHT_RED = "\u001B[91m" - private const val BRIGHT_GREEN = "\u001B[92m" - private const val BRIGHT_YELLOW = "\u001B[93m" - private const val BRIGHT_BLUE = "\u001B[94m" - private const val BRIGHT_MAGENTA = "\u001B[95m" - private const val BRIGHT_CYAN = "\u001B[96m" - - // Flag to force disable colors via --no-color flag - var forceDisableColors = false - - // Check if colors should be enabled (disable in CI/pipes or via flag) - private val colorsEnabled: Boolean - get() = !forceDisableColors && - System.getenv("NO_COLOR") == null && - System.getenv("CI") == null && - System.console() != null - - // Function to disable colors programmatically - fun disableColors() { - forceDisableColors = true - } - - // Public color functions - fun red(text: String): String = if (colorsEnabled) "$RED$text$RESET" else text - fun green(text: String): String = if (colorsEnabled) "$GREEN$text$RESET" else text - fun yellow(text: String): String = if (colorsEnabled) "$YELLOW$text$RESET" else text - fun blue(text: String): String = if (colorsEnabled) "$BLUE$text$RESET" else text - fun magenta(text: String): String = if (colorsEnabled) "$MAGENTA$text$RESET" else text - fun cyan(text: String): String = if (colorsEnabled) "$CYAN$text$RESET" else text - - fun brightRed(text: String): String = if (colorsEnabled) "$BRIGHT_RED$text$RESET" else text - fun brightGreen(text: String): String = if (colorsEnabled) "$BRIGHT_GREEN$text$RESET" else text - fun brightYellow(text: String): String = if (colorsEnabled) "$BRIGHT_YELLOW$text$RESET" else text - fun brightBlue(text: String): String = if (colorsEnabled) "$BRIGHT_BLUE$text$RESET" else text - fun brightMagenta(text: String): String = if (colorsEnabled) "$BRIGHT_MAGENTA$text$RESET" else text - fun brightCyan(text: String): String = if (colorsEnabled) "$BRIGHT_CYAN$text$RESET" else text - - fun bold(text: String): String = if (colorsEnabled) "$BOLD$text$RESET" else text - fun dim(text: String): String = if (colorsEnabled) "$DIM$text$RESET" else text - - // Semantic colors for version control - fun success(text: String): String = brightGreen(text) - fun error(text: String): String = brightRed(text) - fun warning(text: String): String = brightYellow(text) - fun info(text: String): String = brightBlue(text) - fun hash(text: String): String = yellow(text) - fun filename(text: String): String = cyan(text) - fun heading(text: String): String = brightMagenta(text) - fun author(text: String): String = green(text) - fun date(text: String): String = dim(text) - - // Status-specific colors - fun added(text: String): String = brightGreen(text) - fun modified(text: String): String = brightYellow(text) - fun deleted(text: String): String = brightRed(text) - fun untracked(text: String): String = red(text) -} -