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" group = "org.notevc"
version = "1.0.7" version = "1.0.8"
buildConfig { buildConfig {
buildConfigField("String", "VERSION", "\"${project.version}\"") buildConfigField("String", "VERSION", "\"${project.version}\"")
@@ -26,7 +26,7 @@ dependencies {
val junitVersion = "5.10.0" val junitVersion = "5.10.0"
implementation(kotlin("stdlib")) implementation(kotlin("stdlib"))
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") 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") testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$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.core.Repository
import org.notevc.commands.* import org.notevc.commands.*
import org.notevc.utils.ColorUtils
import org.kargs.* import org.kargs.*
fun main(args: Array<String>) { fun main(args: Array<String>) {
val argsList = args.toMutableList()
// Create argument parser // 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 // Register subcommands
parser.subcommands( parser.subcommands(
@@ -22,7 +27,7 @@ fun main(args: Array<String>) {
// Parse arguments // Parse arguments
try { try {
parser.parse(args) parser.parse(argsList.toTypedArray())
} catch (e: Exception) { } catch (e: Exception) {
kotlin.system.exitProcess(1) 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 kotlinx.serialization.json.Json
import java.nio.file.Files import java.nio.file.Files
import java.time.Instant import java.time.Instant
import org.notevc.utils.ColorUtils
import kotlin.io.path.* import kotlin.io.path.*
import org.kargs.Subcommand import org.kargs.*
import org.kargs.ArgType
import org.kargs.Option
import org.kargs.Argument
class CommitCommand : Subcommand("commit", description = "Create a commit of changed files") { 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") 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.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 { 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) updateRepositoryHead(repo, commitHash, timestamp, message)
return buildString { return buildString {
appendLine("${ColorUtils.success("Created commit")} ${ColorUtils.hash(commitHash)}") appendLine("${Colors.success("Created commit")} ${Colors.yellow(commitHash)}")
appendLine("${ColorUtils.bold("Message:")} $message") appendLine("${Colors.bold("Message:")} $message")
appendLine("${ColorUtils.bold("File:")} ${ColorUtils.filename(relativePath)} ${ColorUtils.dim("(${snapshot.blocks.size} blocks)")}") 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) updateRepositoryHead(repo, commitHash, timestamp, message)
return buildString { return buildString {
appendLine("${ColorUtils.success("Created commit")} ${ColorUtils.hash(commitHash)}") appendLine("${Colors.success("Created commit")} ${Colors.yellow(commitHash)}")
appendLine("${ColorUtils.bold("Message:")} $message") appendLine("${Colors.bold("Message:")} $message")
appendLine("${ColorUtils.bold("Files committed:")} ${changedFiles.size}") appendLine("${Colors.bold("Files committed:")} ${changedFiles.size}")
appendLine("${ColorUtils.bold("Total blocks:")} $totalBlocksStored") appendLine("${Colors.bold("Total blocks:")} $totalBlocksStored")
appendLine() appendLine()
changedFiles.forEach { fileInfo -> changedFiles.forEach { fileInfo ->
val parts = fileInfo.split(" (") val parts = fileInfo.split(" (")
val filename = parts[0] val filename = parts[0]
val blockInfo = if (parts.size > 1) " (${parts[1]}" else "" 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 package org.notevc.commands
import org.notevc.core.* import org.notevc.core.*
import org.notevc.utils.ColorUtils
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.nio.file.Files import java.nio.file.Files
import java.time.Instant import java.time.Instant
@@ -53,7 +52,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
result.onSuccess { message -> println(message) } 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 { 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() 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() result.appendLine()
// Get the commit snapshot if provided, otherwise use latest // Get the commit snapshot if provided, otherwise use latest
val commitSnapshot = if (commitHash != null) { val commitSnapshot = if (commitHash != null) {
val commit = findCommit(repo, commitHash) 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 commitTime = Instant.parse(commit.timestamp)
blockStore.getLatestBlockSnapshotBefore(targetFile, commitTime) blockStore.getLatestBlockSnapshotBefore(targetFile, commitTime)
} else { } else {
@@ -94,41 +93,41 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
val newBlock = currentSnapshot.blocks.find { it.id.startsWith(blockHash) } val newBlock = currentSnapshot.blocks.find { it.id.startsWith(blockHash) }
if (oldBlock == null && newBlock == null) { 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() val headingText = (newBlock?.heading ?: oldBlock?.heading ?: "").replace(Regex("^#+\\s*"), "").trim()
result.appendLine("${ColorUtils.heading(headingText)} ${ColorUtils.dim("[${blockHash.take(8)}]")}") result.appendLine("${Colors.heading(headingText)} ${Colors.dim("[${blockHash.take(8)}]")}")
result.appendLine("${ColorUtils.dim("─".repeat(70))}") result.appendLine(Colors.dim("".repeat(70)))
result.appendLine() result.appendLine()
when { when {
oldBlock == null && newBlock != null -> { oldBlock == null && newBlock != null -> {
result.appendLine("${ColorUtils.success("This block was ADDED")}") result.appendLine(Colors.green("This block was ADDED"))
result.appendLine() result.appendLine()
val newContent = objectStore.getContent(newBlock.contentHash) val newContent = objectStore.getContent(newBlock.contentHash)
if (newContent != null) { if (newContent != null) {
newContent.lines().forEach { line -> newContent.lines().forEach { line ->
result.appendLine("${ColorUtils.success("+ ")} $line") result.appendLine("${Colors.green("+ ")} $line")
} }
} }
} }
oldBlock != null && newBlock == null -> { oldBlock != null && newBlock == null -> {
result.appendLine("${ColorUtils.error("This block was DELETED")}") result.appendLine(Colors.error("This block was DELETED"))
result.appendLine() result.appendLine()
val oldContent = objectStore.getContent(oldBlock.contentHash) val oldContent = objectStore.getContent(oldBlock.contentHash)
if (oldContent != null) { if (oldContent != null) {
oldContent.lines().forEach { line -> oldContent.lines().forEach { line ->
result.appendLine("${ColorUtils.error("- ")} $line") result.appendLine("${Colors.error("- ")} $line")
} }
} }
} }
oldBlock != null && newBlock != null -> { oldBlock != null && newBlock != null -> {
if (oldBlock.contentHash == newBlock.contentHash) { if (oldBlock.contentHash == newBlock.contentHash) {
result.appendLine("${ColorUtils.dim("No changes")}") result.appendLine(Colors.dim("No changes"))
} else { } else {
result.appendLine("${ColorUtils.warning("Block was MODIFIED")}") result.appendLine(Colors.warn("Block was MODIFIED"))
result.appendLine() result.appendLine()
val oldContent = objectStore.getContent(oldBlock.contentHash) val oldContent = objectStore.getContent(oldBlock.contentHash)
val newContent = objectStore.getContent(newBlock.contentHash) val newContent = objectStore.getContent(newBlock.contentHash)
@@ -152,9 +151,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
// Find commits // Find commits
val commit1 = findCommit(repo, hash1) 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) 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 time1 = Instant.parse(commit1.timestamp)
val time2 = Instant.parse(commit2.timestamp) val time2 = Instant.parse(commit2.timestamp)
@@ -170,9 +169,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
val result = StringBuilder() val result = StringBuilder()
result.appendLine("${ColorUtils.bold("Comparing commits:")}") result.appendLine(Colors.bold("Comparing commits:"))
result.appendLine(" ${ColorUtils.hash(hash1.take(8))} ${ColorUtils.dim(commit1.message)}") result.appendLine(" ${Colors.yellow(hash1.take(8))} ${Colors.dim(commit1.message)}")
result.appendLine(" ${ColorUtils.hash(hash2.take(8))} ${ColorUtils.dim(commit2.message)}") result.appendLine(" ${Colors.yellow(hash2.take(8))} ${Colors.dim(commit2.message)}")
result.appendLine() result.appendLine()
var totalChanges = 0 var totalChanges = 0
@@ -184,7 +183,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
if (snapshot1 != null || snapshot2 != null) { if (snapshot1 != null || snapshot2 != null) {
val changes = blockStore.compareBlocks(snapshot1, snapshot2) val changes = blockStore.compareBlocks(snapshot1, snapshot2)
if (changes.isNotEmpty()) { if (changes.isNotEmpty()) {
result.appendLine("${ColorUtils.filename(filePath)}:") result.appendLine("${Colors.filename(filePath)}:")
result.append(formatBlockChanges(changes, objectStore)) result.append(formatBlockChanges(changes, objectStore))
result.appendLine() result.appendLine()
totalChanges += changes.size totalChanges += changes.size
@@ -193,9 +192,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
if (totalChanges == 0) { if (totalChanges == 0) {
result.appendLine("${ColorUtils.dim("No differences found")}") result.appendLine(Colors.dim("No differences found"))
} else { } else {
result.appendLine("${ColorUtils.bold("Total changes:")} $totalChanges") result.appendLine("${Colors.bold("Total changes:")} $totalChanges")
} }
return result.toString() return result.toString()
@@ -208,7 +207,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
// Find commit // Find commit
val commit = findCommit(repo, hash) 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) val commitTime = Instant.parse(commit.timestamp)
@@ -225,8 +224,8 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
val result = StringBuilder() val result = StringBuilder()
result.appendLine("${ColorUtils.bold("Comparing working directory to commit:")}") result.appendLine(Colors.bold("Comparing working directory to commit:"))
result.appendLine(" ${ColorUtils.hash(hash.take(8))} ${ColorUtils.dim(commit.message)}") result.appendLine(" ${Colors.yellow(hash.take(8))} ${Colors.lightGray(commit.message)}")
result.appendLine() result.appendLine()
var totalChanges = 0 var totalChanges = 0
@@ -247,7 +246,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
val changes = blockStore.compareBlocks(commitSnapshot, currentSnapshot) val changes = blockStore.compareBlocks(commitSnapshot, currentSnapshot)
if (changes.isNotEmpty()) { if (changes.isNotEmpty()) {
result.appendLine("${ColorUtils.filename(filePath)}:") result.appendLine("${Colors.filename(filePath)}:")
result.append(formatBlockChanges(changes, objectStore)) result.append(formatBlockChanges(changes, objectStore))
result.appendLine() result.appendLine()
totalChanges += changes.size totalChanges += changes.size
@@ -256,9 +255,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
if (totalChanges == 0) { if (totalChanges == 0) {
result.appendLine("${ColorUtils.dim("No differences found")}") result.appendLine(Colors.dim("No differences found"))
} else { } else {
result.appendLine("${ColorUtils.bold("Total changes:")} $totalChanges") result.appendLine("${Colors.bold("Total changes:")} $totalChanges")
} }
return result.toString() return result.toString()
@@ -282,7 +281,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
val result = StringBuilder() val result = StringBuilder()
result.appendLine("${ColorUtils.bold("Changes in working directory:")}") result.appendLine(Colors.bold("Changes in working directory:"))
result.appendLine() result.appendLine()
var totalChanges = 0 var totalChanges = 0
@@ -294,16 +293,14 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
val parsedFile = blockParser.parseFile(content, filePath) val parsedFile = blockParser.parseFile(content, filePath)
// Skip disabled files // Skip disabled files
if (parsedFile.frontMatter?.isEnabled == false) { if (parsedFile.frontMatter?.isEnabled == false) return@forEach
return@forEach
}
val latestSnapshot = blockStore.getLatestBlockSnapshot(filePath) val latestSnapshot = blockStore.getLatestBlockSnapshot(filePath)
val currentSnapshot = createCurrentSnapshot(parsedFile, objectStore) val currentSnapshot = createCurrentSnapshot(parsedFile, objectStore)
val changes = blockStore.compareBlocks(latestSnapshot, currentSnapshot) val changes = blockStore.compareBlocks(latestSnapshot, currentSnapshot)
if (changes.isNotEmpty()) { if (changes.isNotEmpty()) {
result.appendLine("${ColorUtils.filename(filePath)}:") result.appendLine("${Colors.filename(filePath)}:")
result.append(formatBlockChanges(changes, objectStore)) result.append(formatBlockChanges(changes, objectStore))
result.appendLine() result.appendLine()
totalChanges += changes.size totalChanges += changes.size
@@ -312,9 +309,9 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
} }
if (totalChanges == 0) { if (totalChanges == 0) {
result.appendLine("${ColorUtils.dim("No changes detected - working directory clean")}") result.appendLine(Colors.lightGray("No changes detected - working directory clean"))
} else { } else {
result.appendLine("${ColorUtils.bold("Total changes:")} $totalChanges") result.appendLine("${Colors.bold("Total changes:")} $totalChanges")
} }
return result.toString() return result.toString()
@@ -330,42 +327,42 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
when (change.type) { when (change.type) {
BlockChangeType.ADDED -> { BlockChangeType.ADDED -> {
result.appendLine() result.appendLine()
result.appendLine(" ${ColorUtils.success("+++")} ${ColorUtils.bold("ADDED")} ${ColorUtils.success("+++")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") result.appendLine(" ${Colors.green("+++")} ${Colors.bold("ADDED")} ${Colors.green("+++")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}")
result.appendLine(" ${ColorUtils.dim("─".repeat(60))}") result.appendLine(" ${Colors.dim("─".repeat(60))}")
if (change.newHash != null) { if (change.newHash != null) {
val content = objectStore.getContent(change.newHash) val content = objectStore.getContent(change.newHash)
if (content != null) { if (content != null) {
content.lines().take(5).forEach { line -> content.lines().take(5).forEach { line ->
result.appendLine(" ${ColorUtils.success("+")} $line") result.appendLine(" ${Colors.green("+")} $line")
} }
if (content.lines().size > 5) { 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 -> { BlockChangeType.DELETED -> {
result.appendLine() result.appendLine()
result.appendLine(" ${ColorUtils.error("---")} ${ColorUtils.bold("DELETED")} ${ColorUtils.error("---")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") result.appendLine(" ${Colors.error("---")} ${Colors.bold("DELETED")} ${Colors.error("---")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}")
result.appendLine(" ${ColorUtils.dim("─".repeat(60))}") result.appendLine(" ${Colors.dim("─".repeat(60))}")
if (change.oldHash != null) { if (change.oldHash != null) {
val content = objectStore.getContent(change.oldHash) val content = objectStore.getContent(change.oldHash)
if (content != null) { if (content != null) {
content.lines().take(5).forEach { line -> content.lines().take(5).forEach { line ->
result.appendLine(" ${ColorUtils.error("-")} $line") result.appendLine(" ${Colors.error("-")} $line")
} }
if (content.lines().size > 5) { 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 -> { BlockChangeType.MODIFIED -> {
result.appendLine() result.appendLine()
result.appendLine(" ${ColorUtils.warning("~~~")} ${ColorUtils.bold("MODIFIED")} ${ColorUtils.warning("~~~")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") result.appendLine(" ${Colors.warn("~~~")} ${Colors.bold("MODIFIED")} ${Colors.warn("~~~")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}")
result.appendLine(" ${ColorUtils.dim("─".repeat(60))}") result.appendLine(" ${Colors.dim("─".repeat(60))}")
// Show detailed diff // Show detailed diff
if (change.oldHash != null && change.newHash != null) { if (change.oldHash != null && change.newHash != null) {
@@ -405,27 +402,27 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
when { when {
oldLine == null && newLine != null -> { oldLine == null && newLine != null -> {
// Addition // Addition
diff.add("${ColorUtils.success("+ ")} $newLine") diff.add("${Colors.green("+ ")} $newLine")
newIndex++ newIndex++
displayedLines++ displayedLines++
} }
oldLine != null && newLine == null -> { oldLine != null && newLine == null -> {
// Deletion // Deletion
diff.add("${ColorUtils.error("- ")} $oldLine") diff.add("${Colors.boldRed("- ")} $oldLine")
oldIndex++ oldIndex++
displayedLines++ displayedLines++
} }
oldLine == newLine -> { oldLine == newLine -> {
// Unchanged line (context) // Unchanged line (context)
diff.add("${ColorUtils.dim(" ")} ${ColorUtils.dim(oldLine ?: "")}") diff.add("${Colors.dim(" ")} ${Colors.dimWhite(oldLine ?: "")}")
oldIndex++ oldIndex++
newIndex++ newIndex++
displayedLines++ displayedLines++
} }
else -> { else -> {
// Modified line // Modified line
diff.add("${ColorUtils.error("- ")} $oldLine") diff.add("${Colors.boldRed("- ")} $oldLine")
diff.add("${ColorUtils.success("+ ")} $newLine") diff.add("${Colors.green("+ ")} $newLine")
oldIndex++ oldIndex++
newIndex++ newIndex++
displayedLines += 2 displayedLines += 2
@@ -435,7 +432,7 @@ class DiffCommand : Subcommand("diff", description = "Show differences between c
val remainingLines = (oldLines.size - oldIndex) + (newLines.size - newIndex) val remainingLines = (oldLines.size - oldIndex) + (newLines.size - newIndex)
if (remainingLines > 0) { if (remainingLines > 0) {
diff.add("${ColorUtils.dim(" ... $remainingLines more lines")}") diff.add(Colors.dim(" ... $remainingLines more lines"))
} }
return diff return diff

View File

@@ -2,10 +2,7 @@ package org.notevc.commands
import org.notevc.core.Repository import org.notevc.core.Repository
import java.nio.file.Path import java.nio.file.Path
import org.notevc.utils.ColorUtils import org.kargs.*
import org.kargs.Subcommand
import org.kargs.Argument
import org.kargs.ArgType
class InitCommand : Subcommand("init", description = "Initialize a repository", aliases = listOf("i")) { 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) 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( repo.init().fold(
onSuccess = { onSuccess = {
val absolutePath = repo.path.toAbsolutePath().toString() 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) } onFailure = { error -> throw Exception(error) }
) )
} }
result.onSuccess { message -> println(message) } 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 kotlinx.serialization.json.Json
import java.nio.file.Files import java.nio.file.Files
import java.time.Instant import java.time.Instant
import org.notevc.utils.ColorUtils
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import kotlin.io.path.* import kotlin.io.path.*
@@ -27,7 +26,7 @@ class LogCommand : Subcommand("log", description = "Show commit history with det
} }
result.onSuccess { message -> println(message) } 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 { 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 -> return commits.joinToString("\n") { commit ->
val fileInfo = if (options.showFiles) { val fileInfo = if (options.showFiles) {
val stats = getCommitStats(repo, commit, options.targetFile) val stats = getCommitStats(repo, commit, options.targetFile)
ColorUtils.dim(" (${stats.filesChanged} files, ${stats.totalBlocks} blocks)") Colors.dim(" (${stats.filesChanged} files, ${stats.totalBlocks} blocks)")
} else "" } 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) val formattedDate = formatter.format(timestamp)
buildString { buildString {
appendLine("${ColorUtils.bold("commit")} ${ColorUtils.hash(commit.hash)}") appendLine("${Colors.bold("commit")} ${Colors.yellow(commit.hash)}")
appendLine("${ColorUtils.bold("Author:")} ${ColorUtils.author(commit.author)}") appendLine("${Colors.bold("Author:")} ${Colors.green(commit.author)}")
appendLine("${ColorUtils.bold("Date:")} ${ColorUtils.date(formattedDate)}") appendLine("${Colors.bold("Date:")} ${Colors.dim(formattedDate)}")
if (options.showFiles) { if (options.showFiles) {
val stats = getCommitStats(repo, commit, options.targetFile) 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()) { if (stats.fileDetails.isNotEmpty()) {
appendLine() appendLine()
stats.fileDetails.forEach { (file, blocks) -> 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 -> blocks.forEach { block ->
val heading = block.heading.replace(Regex("^#+\\s*"), "").trim() 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 package org.notevc.commands
import org.notevc.core.* import org.notevc.core.*
import org.notevc.utils.ColorUtils
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.nio.file.Files import java.nio.file.Files
import java.time.Instant import java.time.Instant
@@ -35,7 +34,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo
} }
result.onSuccess { message -> println(message) } 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 { 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 // Find the commit
val commit = findCommit(repo, commitHash) 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 // Find the block snapshot for this file at the commit time
val commitTime = Instant.parse(commit.timestamp) val commitTime = Instant.parse(commit.timestamp)
val snapshot = blockStore.getBlocksAtTime(targetFile, commitTime) 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 // Find the specific block
val targetBlock = snapshot.find { it.id.startsWith(blockHash) } 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 // Read current file
val filePath = repo.path.resolve(targetFile) val filePath = repo.path.resolve(targetFile)
if (!filePath.exists()) { 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) 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 // Find the block to replace in current file
val currentBlockIndex = currentParsedFile.blocks.indexOfFirst { it.id.startsWith(blockHash) } val currentBlockIndex = currentParsedFile.blocks.indexOfFirst { it.id.startsWith(blockHash) }
if (currentBlockIndex == -1) { 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 // Replace the block
@@ -82,7 +81,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo
Files.writeString(filePath, restoredContent) Files.writeString(filePath, restoredContent)
val blockHeading = targetBlock.heading.replace(Regex("^#+\\s*"), "").trim() 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 { 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 // Find the commit
val commit = findCommit(repo, commitHash) 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 // Find the block snapshot for this file at the commit time
val commitTime = Instant.parse(commit.timestamp) val commitTime = Instant.parse(commit.timestamp)
val snapshot = blockStore.getLatestBlockSnapshotBefore(targetFile, commitTime) 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) 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 // Reconstruct the file from blocks with frontmatter from snapshot
val parsedFile = ParsedFile( val parsedFile = ParsedFile(
@@ -116,7 +115,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo
Files.createDirectories(filePath.parent) Files.createDirectories(filePath.parent)
Files.writeString(filePath, restoredContent) 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 { private fun restoreEntireRepository(repo: Repository, commitHash: String): String {
@@ -126,7 +125,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo
// Find the commit // Find the commit
val commit = findCommit(repo, commitHash) 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 commitTime = Instant.parse(commit.timestamp)
@@ -134,7 +133,7 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo
val trackedFiles = getTrackedFilesAtCommit(repo, commitTime) val trackedFiles = getTrackedFilesAtCommit(repo, commitTime)
if (trackedFiles.isEmpty()) { 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 var restoredFiles = 0
@@ -162,10 +161,10 @@ class RestoreCommand : Subcommand("restore", description = "Restore files or blo
} }
return buildString { return buildString {
appendLine("${ColorUtils.success("Restored repository")} to commit ${ColorUtils.hash(commitHash)}") appendLine("${Colors.success("Restored repository")} to commit ${Colors.yellow(commitHash)}")
appendLine("${ColorUtils.bold("Files restored:")} $restoredFiles") appendLine("${Colors.bold("Files restored:")} $restoredFiles")
appendLine("${ColorUtils.bold("Total blocks:")} $totalBlocks") appendLine("${Colors.bold("Total blocks:")} $totalBlocks")
appendLine("${ColorUtils.bold("Commit message:")} ${commit.message}") appendLine("${Colors.bold("Commit message:")} ${commit.message}")
} }
} }

View File

@@ -1,7 +1,6 @@
package org.notevc.commands package org.notevc.commands
import org.notevc.core.* import org.notevc.core.*
import org.notevc.utils.ColorUtils
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.nio.file.Files import java.nio.file.Files
import java.time.Instant import java.time.Instant
@@ -30,7 +29,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
} }
result.onSuccess { message -> println(message) } 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 { 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 // Find the commit
val commit = findCommit(repo, commitHash) 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 commitTime = Instant.parse(commit.timestamp)
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 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() val result = StringBuilder()
// Show commit header // Show commit header
result.appendLine("${ColorUtils.bold("Commit:")} ${ColorUtils.hash(commit.hash)}") result.appendLine("${Colors.bold("Commit:")} ${Colors.yellow(commit.hash)}")
result.appendLine("${ColorUtils.bold("Author:")} ${commit.author}") result.appendLine("${Colors.bold("Author:")} ${commit.author}")
result.appendLine("${ColorUtils.bold("Date:")} ${formatter.format(commitTime)}") result.appendLine("${Colors.bold("Date:")} ${formatter.format(commitTime)}")
if (commit.parent != null) { 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()
result.appendLine(" ${commit.message}") result.appendLine(" ${commit.message}")
@@ -66,12 +65,12 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
} }
if (filesToShow.isEmpty()) { 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() return result.toString()
} }
// Show changes for each file // Show changes for each file
result.appendLine("${ColorUtils.bold("Changes:")}") result.appendLine("${Colors.bold("Changes:")}")
result.appendLine() result.appendLine()
var totalAdded = 0 var totalAdded = 0
@@ -94,7 +93,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
val changes = blockStore.compareBlocks(parentSnapshot, currentSnapshot) val changes = blockStore.compareBlocks(parentSnapshot, currentSnapshot)
if (changes.isNotEmpty()) { if (changes.isNotEmpty()) {
result.appendLine("${ColorUtils.filename(filePath)}:") result.appendLine("${Colors.filename(filePath)}:")
val added = changes.count { it.type == BlockChangeType.ADDED } val added = changes.count { it.type == BlockChangeType.ADDED }
val modified = changes.count { it.type == BlockChangeType.MODIFIED } val modified = changes.count { it.type == BlockChangeType.MODIFIED }
@@ -104,9 +103,9 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
totalModified += modified totalModified += modified
totalDeleted += deleted totalDeleted += deleted
if (added > 0) result.appendLine(" ${ColorUtils.success("+")} $added ${if (added == 1) "block" else "blocks"} added") if (added > 0) result.appendLine(" ${Colors.boldGreen("+")} $added ${if (added == 1) "block" else "blocks"} added")
if (modified > 0) result.appendLine(" ${ColorUtils.warning("~")} $modified ${if (modified == 1) "block" else "blocks"} modified") if (modified > 0) result.appendLine(" ${Colors.warn("~")} $modified ${if (modified == 1) "block" else "blocks"} modified")
if (deleted > 0) result.appendLine(" ${ColorUtils.error("-")} $deleted ${if (deleted == 1) "block" else "blocks"} deleted") if (deleted > 0) result.appendLine(" ${Colors.error("-")} $deleted ${if (deleted == 1) "block" else "blocks"} deleted")
result.appendLine() result.appendLine()
@@ -117,13 +116,13 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
when (change.type) { when (change.type) {
BlockChangeType.ADDED -> { BlockChangeType.ADDED -> {
result.appendLine(" ${ColorUtils.success("+")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") result.appendLine(" ${Colors.boldGreen("+")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}")
} }
BlockChangeType.DELETED -> { BlockChangeType.DELETED -> {
result.appendLine(" ${ColorUtils.error("-")} ${ColorUtils.heading(headingText)} ${ColorUtils.dim("[$blockId]")}") result.appendLine(" ${Colors.error("-")} ${Colors.heading(headingText)} ${Colors.dim("[$blockId]")}")
} }
BlockChangeType.MODIFIED -> { 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 // Summary
if (totalAdded + totalModified + totalDeleted > 0) { if (totalAdded + totalModified + totalDeleted > 0) {
result.appendLine("${ColorUtils.bold("Summary:")}") result.appendLine("${Colors.bold("Summary:")}")
result.appendLine(" ${ColorUtils.success("+")} $totalAdded added, ${ColorUtils.warning("~")} $totalModified modified, ${ColorUtils.error("-")} $totalDeleted deleted") result.appendLine(" ${Colors.boldGreen("+")} $totalAdded added, ${Colors.warn("~")} $totalModified modified, ${Colors.error("-")} $totalDeleted deleted")
} }
return result.toString() 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 blockStore = BlockStore(objectStore, repo.path.resolve("${Repository.NOTEVC_DIR}/blocks"))
val commit = findCommit(repo, options.commitHash) 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) 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}") ?: throw Exception("No snapshot found for ${options.targetFile} at commit ${options.commitHash}")
val block = snapshot.blocks.find { it.id.startsWith(options.blockHash!!) } 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) val content = objectStore.getContent(block.contentHash)
?: throw Exception("Content not found for block") ?: 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 headingText = block.heading.replace(Regex("^#+\\s*"), "").trim()
val result = StringBuilder() val result = StringBuilder()
result.appendLine("${ColorUtils.bold("Block:")} ${ColorUtils.hash(block.id.take(8))}") result.appendLine("${Colors.bold("Block:")} ${Colors.yellow(block.id.take(8))}")
result.appendLine("${ColorUtils.bold("Heading:")} ${ColorUtils.heading(headingText)}") result.appendLine("${Colors.bold("Heading:")} ${Colors.heading(headingText)}")
result.appendLine("${ColorUtils.bold("File:")} ${ColorUtils.filename(options.targetFile)}") result.appendLine("${Colors.bold("File:")} ${Colors.filename(options.targetFile)}")
result.appendLine("${ColorUtils.bold("Commit:")} ${ColorUtils.hash(commit.hash)}") result.appendLine("${Colors.bold("Commit:")} ${Colors.yellow(commit.hash)}")
result.appendLine() result.appendLine()
result.appendLine("${ColorUtils.dim("─".repeat(70))}") result.appendLine("${Colors.dim("─".repeat(70))}")
result.appendLine() result.appendLine()
result.append(content) result.append(content)
@@ -228,7 +227,7 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
val blockParser = BlockParser() val blockParser = BlockParser()
val commit = findCommit(repo, options.commitHash) 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) val commitTime = Instant.parse(commit.timestamp)
@@ -261,11 +260,11 @@ class ShowCommand : Subcommand("show", description = "Show detailed information
val reconstructedContent = blockParser.reconstructFile(parsedFile) val reconstructedContent = blockParser.reconstructFile(parsedFile)
val result = StringBuilder() val result = StringBuilder()
result.appendLine("${ColorUtils.bold("File:")} ${ColorUtils.filename(options.targetFile)}") result.appendLine("${Colors.bold("File:")} ${Colors.filename(options.targetFile)}")
result.appendLine("${ColorUtils.bold("Commit:")} ${ColorUtils.hash(commit.hash)}") result.appendLine("${Colors.bold("Commit:")} ${Colors.yellow(commit.hash)}")
result.appendLine("${ColorUtils.bold("Blocks:")} ${blocks.size}") result.appendLine("${Colors.bold("Blocks:")} ${blocks.size}")
result.appendLine() result.appendLine()
result.appendLine("${ColorUtils.dim("─".repeat(70))}") result.appendLine("${Colors.dim("─".repeat(70))}")
result.appendLine() result.appendLine()
result.append(reconstructedContent) result.append(reconstructedContent)

View File

@@ -2,10 +2,9 @@ package org.notevc.commands
import org.notevc.core.* import org.notevc.core.*
import org.notevc.utils.FileUtils import org.notevc.utils.FileUtils
import org.notevc.utils.ColorUtils
import org.notevc.core.Repository.Companion.NOTEVC_DIR import org.notevc.core.Repository.Companion.NOTEVC_DIR
import java.time.Instant import java.time.Instant
import org.kargs.Subcommand import org.kargs.*
class StatusCommand : Subcommand("status", description = "Show status of tracked files", aliases = listOf("st")) { class StatusCommand : Subcommand("status", description = "Show status of tracked files", aliases = listOf("st")) {
override fun execute() { override fun execute() {
@@ -21,7 +20,7 @@ class StatusCommand : Subcommand("status", description = "Show status of tracked
} }
result.onSuccess { output -> println(output) } 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 { private fun getRepositoryStatus(repo: Repository): RepositoryStatus {
@@ -111,35 +110,35 @@ class StatusCommand : Subcommand("status", description = "Show status of tracked
// Modified files // Modified files
grouped[FileStatusType.MODIFIED]?.let { modifiedFiles -> grouped[FileStatusType.MODIFIED]?.let { modifiedFiles ->
output.appendLine(ColorUtils.bold("Modified files:")) output.appendLine(Colors.bold("Modified files:"))
modifiedFiles.forEach { fileStatus -> modifiedFiles.forEach { fileStatus ->
output.appendLine(" ${ColorUtils.filename(fileStatus.path)}") output.appendLine(" ${Colors.filename(fileStatus.path)}")
fileStatus.blockChanges?.forEach { change -> fileStatus.blockChanges?.forEach { change ->
val symbol = when (change.type) { val symbol = when (change.type) {
BlockChangeType.MODIFIED -> ColorUtils.modified("~") BlockChangeType.MODIFIED -> Colors.boldYellow("~")
BlockChangeType.ADDED -> ColorUtils.added("+") BlockChangeType.ADDED -> Colors.boldGreen("+")
BlockChangeType.DELETED -> ColorUtils.deleted("-") BlockChangeType.DELETED -> Colors.boldRed("-")
} }
val heading = change.heading.replace(Regex("^#+\\s*"), "").trim() val heading = change.heading.replace(Regex("^#+\\s*"), "").trim()
output.appendLine(" $symbol ${ColorUtils.heading(heading)}") output.appendLine(" $symbol ${Colors.heading(heading)}")
} }
} }
} }
// Untracked files // Untracked files
grouped[FileStatusType.UNTRACKED]?.let { untrackedFiles -> grouped[FileStatusType.UNTRACKED]?.let { untrackedFiles ->
output.appendLine(ColorUtils.bold("Untracked files:")) output.appendLine(Colors.bold("Untracked files:"))
untrackedFiles.forEach { fileStatus -> 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() output.appendLine()
} }
// Deleted files // Deleted files
grouped[FileStatusType.DELETED]?.let { deletedFiles -> grouped[FileStatusType.DELETED]?.let { deletedFiles ->
output.appendLine(ColorUtils.bold("Deleted files:")) output.appendLine(Colors.bold("Deleted files:"))
deletedFiles.forEach { fileStatus -> deletedFiles.forEach { fileStatus ->
output.appendLine(" ${ColorUtils.deleted(fileStatus.path)}") output.appendLine(" ${Colors.boldRed(fileStatus.path)}")
} }
output.appendLine() 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)
}