feat(colors): colored output; removed colorutils

This commit is contained in:
darwincereska
2025-11-24 11:36:15 -05:00
parent 28f65d3886
commit f15830e3e7
11 changed files with 138 additions and 224 deletions

View File

@@ -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")

View File

@@ -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<String>) {
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<String>) {
// Parse arguments
try {
parser.parse(args)
parser.parse(argsList.toTypedArray())
} catch (e: Exception) {
kotlin.system.exitProcess(1)
}

View File

@@ -1 +0,0 @@
package org.notevc.cli

View File

@@ -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)}")
}
}
}

View File

@@ -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

View File

@@ -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}") }
}
}

View File

@@ -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()
}
}
}

View File

@@ -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}")
}
}

View File

@@ -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)

View File

@@ -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()
}

View File

@@ -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)
}