varabyte / kotter Goto Github PK
View Code? Open in Web Editor NEWA declarative, Kotlin-idiomatic API for writing dynamic console applications.
License: Apache License 2.0
A declarative, Kotlin-idiomatic API for writing dynamic console applications.
License: Apache License 2.0
Otherwise, you have new blocks running up against old ones.
We should restore the cursor when the user presses Ctrl+C, and probably add handler methods to KonsoleBlock and KonsoleApp that get triggered as well so users can clean up if it happens.
Something like
box(WIDTH, HEIGHT, x, y, "TEXT")
where
box(10, 10, 3, 5, "Hello")
would render (assume X's are spaces):
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXHelloXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
This would be useful in the robot example but also for something like, say, a "screensaver", like a text string that bounces around the console.
But this is low priority, and honestly may be a very niche case.
Ctrl + U: Delete input
Ctrl + W: Delete word
Alt + Backspace (and Ctrl + Backspace?): Delete last word
so you can set state one last time before the block finishes.
The blinking cursor, for example, should internally use this, and turn off its blinker, so it isn't recorded in static history for all time.
For a CLI, these ideas may be overkill, but writing them down as food for thought.
Bounce vs. loop (i.e. A, B, C, D, C, B, A, B, ...)
Custom animation frame lengths
Easing
A library of common animations
Occasionally, a dynamic active block may want to generate static text which should go into the history. So something like:
Initially:
Compiling...
Thread #1: foo/whatever.kt
Thread #2: bar/somethingelse.kt
Thread #3: idle
Thread #3: baz/morecode.kt
Then later:
Compiled foo/whatever.kt
Compiled bar/somethingelse.kt
Compiling...
Thread #1: foo/lastone.kt
Thread #2: idle
Thread #3: idle
Thread #3: baz/morecode.kt
More later:
Compiled foo/whatever.kt
Compiled bar/somethingelse.kt
Compiled baz/morecode.kt
Compiling...
Thread #1: foo/lastone.kt
Thread #2: idle
Thread #3: idle
Thread #3: idle
And finally:
Compiled foo/whatever.kt
Compiled bar/somethingelse.kt
Compiled baz/morecode.kt
Compiled foo/lastone.kt
Compiling... DONE!
Compilation took 15.2 seconds
The idea being that even while one block is still active, it can send output to the static history:
| Compiled foo/whatever.kt
Static | Compiled bar/somethingelse.kt
| Compiled baz/morecode.kt
| Compiling...
| Thread #1: foo/lastone.kt
Active | Thread #2: idle
| Thread #3: idle
| Thread #3: idle
You can fake this by having the WHOLE thing be active, and just have the static part really be part of the active part, maybe a bunch of lines that get prepended, but the concern is for something like a compiler working on thousands of files, this could probably add up into many many many lines that Konsole is repainting unecessarily.
The API would probably look something like this (although possibly a better name?)
konsole {
/* ... this is KonsoleBlock@1234 */
}.run {
generateSideEffect {
/* ... this is KonsoneBlock@4567 and it is only run once ... */
}
}
While active:
Please enter your first name: John_
What should happen after pressing enter:
Please enter your first name: John
Please enter your last name: _
What's happening right now:
Please enter your first name: John_
Please enter your last name: _
Similar to mutableStateListOf in Compose
Right now we have
red { text(...) }
green { text(...) }
but imagine you wanted to change the color based on some enumerated type. It's awkward!
// Option 1
when(state) {
ALIVE -> white { text("*") }
DYING -> red { text("*") }
DEAD -> black { text("*") }
}
// Option 2
scopedState {
when(state) {
ALIVE -> white()
DYING -> red()
DEAD -> black()
}
text("*")
}
If we had an option that took an enum, we could do something like:
val c = when(state) {
ALIVE -> Colors.Fg.WHITE
DYING -> Colors.Fg.RED
DEAD -> Colors.Fg.BLACK
}
color(c) { text("*") }
and you might even have a set of custom themes, with "fgColor" and "bgColor" you can reference everywhere this way:
fgColor { text("...") }
bgColor { text("...") }
Of course, you could also add fgColor
and bgColor
functions in your codebase yourself:
var theme = ...
fun KonsoleScope.fgColor(block: KonsoleScope.() -> Unit) {
scopedState {
when(theme) { ... }
}
}
fun KonsoleScope.bgColor(...) { ... }
so maybe it's not critical to figure this out right now
If you press ESC and no other key for.... 200ms? You should send the ESC key directly.
Approach 1: Two newlines
textLine("Last line")
textLine()
Outputs on exit:
Last line
(This session ended blah blah blah)
Everything below this point should do the same.
Approach 2: One newline
textLine("Last line")
Outputs on exit:
Last line
(This session ended blah blah blah)
Approach 2: No newlines
text("Last line")
Outputs on exit:
Last line
(This session ended blah blah blah)
Instead, KonsoleBlock should keep a list of futures
rerenderLock = ReenterantLock()
rerenderRequests = mutableListOf<Future<*>>
rerenderLock.withLock {
if (rerenderRequests.length == 2) return
rerenderRequests.add(executor.submit { ... })
}
In theory that should work? We always have current work and (optional) followup work. If more "rerenderRequests" come in and we already have followupWork scheduled, we don't need to add it because they're already covered
This could be useful for highlighting various examples, especially those with animations.
Option 1: Automatically reset animation back to 0 when not referenced for a frame
We could possibly do this by keeping track of all animations referenced each render pass and checking if any ones weren't referenced the next time around.
Even if we could do this though, should we?
Option 2: Add a "reset" method to the KonsoleAnim class which users can trigger in the run block if they want to.
The text in the dynamic part of the block is constantly repainting, but if the user scrolls up to see the history, we should let them!
e.g.
bold()
bold()
red()
red()
or
red {
red {
}
}
Read class docs with fresh eyes, and make sure every public method is documented, except for the most obvious.
Also, trim unused methods and audit internal vs public vs private
Something like blink("Your text here")
5m to start blinking, 25m to stop
Discuss the top level packages (runtime, foundation, and terminal) and the important role played by the ConcurrentScopedData class.
Currently in Snake I'm doing something like:
var firstMove = true
level.snake.onMoved = {
if (firstMove) {
firstMove = false
addTimer(KonsoleAnim.ONE_FRAME_60FPS, repeat = true) {
...
}
}
}
which could possibly be simplified to:
level.snake.onMoved = {
addTimer(KonsoleAnim.ONE_FRAME_60FPS, repeat = true, key = level.snake) {
...
}
}
In other words, if a timer with the particular key is already found, don't add it again.
Maybe
konsole(inputCompletions = setOf("yes", "no")) {
textLine("Do you like this example?")
textLine("> $input")
}
Turns out you can do this afterall...
39m - clear fg
49m - clear bg
21m - clear bold
24m - clear underline
29m - clear strikethrough
27m - clear inverted
This should have new commands, support for them in the virtual terminal, new API functions, and.... updates to KonsoleState? Can we clean up the logic in TextSupport?
Typing characters, left/right, backspace, del, HOME/END, ENTER
onInputChanged
onInputEntered
onKeyPressed
If you see
red(BG) { textLine("Hello") }
you might expect just the text to be red, not the rest of the line.
We should probably consider searching for the last non \n and insert the reset command there?
Create a test executor and test terminal so we can verify behaviors in unit tests.
Now that we have a consistent set of rules, namely
Terminal depends on Runtime
Foundation depends on Terminal + Runtime
it would be best to break things up into three modules to enforce we don't accidentally add a class in the future which breaks them.
(Or is there some Java 9 jigsaw thing we can do?)
Paragraph is supposed to be smart about detecting newlines, and not adding more unecessarily, so:
textLine("Hello")
p { textLine("Paragraph") }
textLine("World")
is supposed to print out:
Hello
Paragraph
World
but adding ANSI codes screws it up:
red { text("Hello") }
p { green { text("Paragraph") } }
blue { text("World") }
Hello
Paragraph
World
Do we want to parse text into nodes so we can skip over non-text nodes?
It seems more descriptive, but I should sleep on it.
Also "KonsoleScope.Lifecycle" feels hard to pin down as a short lived lifecycle while "RenderScope.Lifecycle" communicates it more effectively.
We can throw the exception either when a new konsole block is defined or if the konsoleApp exists:
konsole {
textLine("...")
} // <--- whoops no run
konsole { // <--- Throw exception here
textLine("...")
}.run()
The thought is, if you don't run the block, it's probably a mistake. That being said, can we think of cases where someone might define a block that is optionally run? Maybe.
Kotter aims to be opinionated about its ansi output and (via the Virtual Console) the subset of ansi it supports, so don't let users add whatever they want into their strings
Currently we support some control keys (ENTER, HOME, etc.) but we should support all of them (TAB, F1, CTRL) and maybe even include isCtrl, isAlt, isShift methods on the onKeyPressed event.
There should be a boolean which is set to true, after which, further calls to run kill the block.
Maybe this is overkill? Perhaps just preventing two blocks from running at the same time is enough? Is there a usecase for creating a block and running it multiple times?
and use it in appropriate examples (with anims and timers)
(Note: duration-ktx doesn't exist yet...)
After the game starts animating, the color refresh is broken. Bug in NEWLINE_COMMAND handling?
The Swing code has some ugly bits and hacks because JTextPanel stutters.
center(WIDTH, string)
so center(10, "Hi") would generate (assume X's are spaces):
XXXXHiXXXX
and you could use it like textLine(center(WIDTH, "message"))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.