An experimental tool for building console UI in Kotlin using the Jetpack Compose compiler/runtime

Related tags

Kotlin mosaic
Overview

Mosaic

An experimental tool for building console UI in Kotlin using the Jetpack Compose compiler/runtime. Inspired by Ink.

(Heads up: this SVG has a slight rendering bug)

Jump to: Introduction | Usage | Samples | FAQ | License

Introduction

The entrypoint to Mosaic is the runMosaic function. The lambda passed to this function is responsible for both output and performing work.

Output (for now) happens through the setContent function. You can call setContent multiple times, but as you'll see you probably won't need to.

fun main() = runMosaic {
  setContent {
    Text("The count is: 0")
  }
}

To change the output dynamically we can use local properties to hold state. Let's update our counter to actually count to 20.

fun main() = runMosaic {
  var count = 0

  setContent {
    Text("The count is: $count")
  }

  for (i in 1..20) {
    delay(250)
    count = i
  }
}

This will not work! Our count stays are 0 for 5 seconds instead of incrementing until 20. Instead, we have to use Compose's State objects to hold state.

-var count = 0
+var count by mutableStateOf(0)

Now, when the count value is updated, Compose will know that it needs to re-render the string.

fun main() = runMosaic {
  var count by mutableStateOf(0)

  setContent {
    Text("The count is: $count")
  }

  for (i in 1..20) {
    delay(250)
    count = i
  }
}

(Note: You may need to add imports for androidx.compose.runtime.getValue and import androidx.compose.runtime.setValue manually.)

Usage

In order to use Mosaic you must write your code in Kotlin and must apply the Compose Kotlin compiler plugin.

For Gradle users, the Mosaic Gradle plugin will take care of applying the compiler plugin.

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10'
    classpath 'com.jakewharton.mosaic:mosaic-gradle-plugin:0.1.0'
  }
}

apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'com.jakewharton.mosaic'

The runtime APIs will be made available automatically by applying the plugin. Documentation is available at jakewharton.github.io/mosaic/docs/0.x/.

Note: Any module which contains a @Composable-annotated function or lambda must apply the Mosaic plugin. While the runtime dependency will be available to downstream modules as a transitive dependency, the compiler plugin is not inherited and must be applied to every module.

Since Kotlin compiler plugins are an unstable API, certain versions of Mosaic only work with certain versions of Kotlin.

Kotlin Mosaic
1.5.10 0.1.0
1.5.20 Blocked on Compose

Versions of Kotlin older than 1.5.10 are not supported. Versions newer than those listed may be supported but are untested.

Snapshots of the development version are available in Sonatype's snapshots repository.

buildscript {
  repository {
    mavenCental()
    maven {
      url 'https://oss.sonatype.org/content/repositories/snapshots/'
    }
  }
  dependencies {
    classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10'
    classpath 'com.jakewharton.mosaic:mosaic-gradle-plugin:0.2.0-SNAPSHOT'
  }
}

apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'com.jakewharton.mosaic'

Snapshot documentation is available at jakewharton.github.io/mosaic/docs/latest/.

Samples

Run ./gradlew installDist to build the sample binaries.

  • Counter: A simple increasing number from 0 until 20.

    ./samples/counter/build/install/counter/bin/counter

  • Jest: Example output of a test framework (such as JS's 'Jest').

    ./samples/jest/build/install/jest/bin/jest

  • Robot: An interactive, game-like program with keyboard control.

    ./samples/robot/build/install/robot/bin/robot

FAQ

I thought Jetpack Compose was a UI toolkit for Android?

Compose is, at its core, a general-purpose runtime and compiler for tree and property manipulation which is trapped inside the AndroidX monorepo and under the Jetpack marketing department. This core can be used for any tree on any platform supported by Kotlin. It's an amazing piece of technology.

Compose UI is the new UI toolkit for Android (and maybe Desktop?). The lack of differentiation between these two technologies has unfortunately caused Compose UI to overshadow the core under the single "Compose" moniker in an unforced marketing error.

If you want another example of a non-Compose UI-based Compose project checkout JetBrains' Compose for Web project.

Why doesn't work take place in a LaunchedEffect?

This is the goal. It is currently blocked by issuetracker.google.com/178904648.

When that change lands, and Mosaic is updated, the counter sample will look like this:

fun main() = runMosaic {
  var count by remember { mutableStateOf(0) }

  Text("The count is: $count")

  LaunchedEffect(Unit) {
    for (i in 1..20) {
      delay(250)
      count = i
    }
  }
}

License

Copyright 2020 Jake Wharton

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Mixing console logging with mosaic

    Mixing console logging with mosaic

    I'd like to create something similar to the rich console output of Gradle with Mosaic, where some kind of table is pinned to the bottom and log messages append above. This is currently doable within Mosaic if I keep a list of these log messages as state and display them first in the content and then the table. But this seems really inefficient as both the log messages and the table are completely cleared from the console and re-written with each update.

    What I think I'm looking for is some kind of Appender which is available through MosaicScope that I can call something like println on. The internal AnsiOutput will know how to write those lines after clearing the screen of the previous output but before writing the new output, thus keeping the setContent at the bottom of the console. But this is only what I think and there's probably some better way of doing this.

    I'd be happy to mess around with this myself if you like the idea and would be open to contribution.

    opened by bnorm 6
  • Publish to MavenCentral

    Publish to MavenCentral

    Hello,

    I wanted to try out mosaic in my own project. Sadly, it seems like it is not available in the MavenCentral repository. Would it be possible to publish it there? Having to build the project locally is a major barrier to people simply wanting to try it out :)

    opened by Jonas164 4
  • Counter sample doesn't behave correctly

    Counter sample doesn't behave correctly

    Here is my build.gradle.kts in an intellij project where I created the project with the gradle cmd line app wizard

    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10")
            classpath("com.jakewharton.mosaic:mosaic-gradle-plugin:0.1.0")
        }
    }
    
    plugins {
        id("org.jetbrains.kotlin.jvm") version "1.5.10"
        id("org.jetbrains.kotlin.plugin.serialization") version "1.5.10"
        id("com.jakewharton.mosaic") version "0.1.0"
        application
    }
    
    group = "me.coltonidle"
    version = "1.0-SNAPSHOT"
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        testImplementation(kotlin("test-junit"))
    }
    
    tasks.test {
        useJUnit()
    }
    
    tasks.withType<KotlinCompile>() {
        kotlinOptions.jvmTarget = "1.8"
    }
    
    application {
        mainClassName = "MainKt"
    }
    

    Here is my MainKt

    import androidx.compose.runtime.getValue
    import androidx.compose.runtime.mutableStateOf
    import androidx.compose.runtime.setValue
    import com.jakewharton.mosaic.Column
    import com.jakewharton.mosaic.Text
    import com.jakewharton.mosaic.runMosaic
    import kotlinx.coroutines.delay
    
    fun main() = runMosaic {
        var count by mutableStateOf(0)
    
        setContent {
            Column {
                Text("The count is: $count")
            }
        }
    
        for (i in 1..20) {
            delay(250)
            count = i
        }
    }
    

    Running this gives me

    Screen Shot 2021-06-29 at 10 59 35 AM
    opened by ColtonIdle 2
  • Can't figure out how to setup with kts files

    Can't figure out how to setup with kts files

    I want to start experimenting with mosaic so I did a file > new project in intellij and followed the wizard for a gradle cmd line app. I added a few lines based on the readme and ended up with this

    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    plugins {
        kotlin("jvm") version "1.5.10"
        application
        id("com.jakewharton.mosaic")
    }
    
    group = "me.coltonidle"
    version = "1.0-SNAPSHOT"
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        testImplementation(kotlin("test-junit"))
    }
    
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10")
            classpath("com.jakewharton.mosaic:mosaic-gradle-plugin:0.1.0")
        }
    }
    
    tasks.test {
        useJUnit()
    }
    
    tasks.withType<KotlinCompile>() {
        kotlinOptions.jvmTarget = "1.8"
    }
    
    application {
        mainClassName = "MainKt"
    }
    

    when building though I get an error of Plugin [id: 'com.jakewharton.mosaic'] was not found in any of the following sources:

    I've only really only ever done android development so maybe I'm doing something really wrong here

    opened by ColtonIdle 2
  • error: has been compiled by a more recent version

    error: has been compiled by a more recent version

    Tried to run ./gradlew -p samples installDist from README and got:

    > Task :mosaic:mosaic-runtime:compileKotlin FAILED
    e: java.lang.UnsupportedClassVersionError: 
    androidx/compose/compiler/plugins/kotlin/ComposeComponentRegistrar
    has been compiled by a more recent version of the Java Runtime
    (class file version 55.0), this version of the Java Runtime only recognizes
    class file versions up to 52.0
    

    Found https://stackoverflow.com/questions/47457105/class-has-been-compiled-by-a-more-recent-version-of-the-java-environment

    Researching this now, will post an update here if I solve it.

    opened by andrewarrow 1
  • Fewer lines of output will cause display to drift downwards

    Fewer lines of output will cause display to drift downwards

    Instead of opening an issue, I thought this might be easier to demonstrate, so went straight for the PR.

    I believe the lastHeight logic of AnsiOutput to be slightly flawed. I've tested this with a modified version of the jest sample which does not display the done tests. I found that if the new output is smaller by 2 or more lines, the output will drift downwards and not clear all previous output. I've included an SVG in the first commit along with the test change which demonstrates this bug.

    I found that the extra line clearing logic leaves the cursor too far down which causes the initial cursor move up on the next frame to not move far enough and leave some of the previous output. If the cursor is moved back up after clearing extra lines, all of the previous output is cleared correctly. I've made this change in the second commit and included another SVG of the jest sample output.

    The third commit then reverts all the jest sample changes so this PR could be squash-merged and only include the minimal number of changes.

    Another possible solution is instead of moving the cursor up, add minOf(0, extraLines - 1) to lastHight in addition to lines.size. This however leaves cleared lines below the output when the program exits where moving the cursor up will not. But due to the extra line clearing having a conditional new line, the cursor will drift back up slowly each frame which makes for a slightly odd visual. I think overall moving the cursor up after the extra line clearing is better visually and logically.

    Moving cursor up:

    $ ./build/install/jest/bin/jest
    Tests: 4 failed, 6 passed, 10 total
    Time:  10s
    
    $ # next prompt here
    

    Adding extra lines to last hight:

    $ ./build/install/jest/bin/jest
    Tests: 4 failed, 6 passed, 10 total
    Time:  10s
    
    
    $ # next prompt here
    
    opened by bnorm 1
  • Empty Column or Row causes exception in render

    Empty Column or Row causes exception in render

    Example which causes exception

    runMosaic {
        setContent {
            Column {}
        }
    }
    

    Exception stack trace

    Exception in thread "main" java.lang.IllegalArgumentException: Row start value out of range [0,0): 0
            at com.jakewharton.mosaic.TextCanvas.get(canvas.kt:22)
            at com.jakewharton.mosaic.BoxNode.renderTo(nodes.kt:136)
            at com.jakewharton.mosaic.MosaicNode.render(nodes.kt:26)
            at com.jakewharton.mosaic.MosaicKt$runMosaic$1$2.invokeSuspend(mosaic.kt:55)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
            at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
            at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
            at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
            at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
            at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
            at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
            at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
            at com.jakewharton.mosaic.MosaicKt.runMosaic(mosaic.kt:28)
    

    This is actually fixed in #66 here: https://github.com/JakeWharton/mosaic/pull/66/files#diff-edb81a24f298abe6e7ff34ccd8691345abc86bed07218a3d05d49793406d9f7fR21. I could move that change into a separate PR if you aren't ready for the whole Static design yet.

    opened by bnorm 1
  • Add Static component for rendering permanent output

    Add Static component for rendering permanent output

    Resolves #65

    Probably went a little overboard on the jest sample updates so let me know if you want me to roll anything back. Also I'm not stuck on any naming, so if you have suggestions let me know.

    opened by bnorm 1
  • Bump gradle-maven-publish-plugin from 0.13.0 to 0.21.0

    Bump gradle-maven-publish-plugin from 0.13.0 to 0.21.0

    Bumps gradle-maven-publish-plugin from 0.13.0 to 0.21.0.

    Release notes

    Sourced from gradle-maven-publish-plugin's releases.

    0.21.0

    Changelog

    0.20.0

    Changelog

    0.19.0

    Changelog

    0.18.0

    Changelog

    0.17.0

    Changelog

    0.16.0

    Changelog

    0.15.1

    Changelog

    0.15.0

    Changelog

    0.14.2

    Changelog

    0.14.1

    Changelog

    0.14.0

    Changelog

    Changelog

    Sourced from gradle-maven-publish-plugin's changelog.

    Version 0.21.0 (2022-07-11)

    Minimum supported Gradle version is now 7.2.0

    Minimum supported Android Gradle Plugin versions are now 7.1.2, 7.2.0-beta02 and 7.3.0-alpha01

    Behavior changes

    The com.vanniktech.maven.publish stops adding Maven Central (Sonatype OSS) as a publishing target and will not enable GPG signing by default. To continue publishing to maven central and signing artifacts either add the following to your gradle.properties:

    SONATYPE_HOST=DEFAULT
    # SONATYPE_HOST=S01 for publishing through s01.oss.sonatype.org
    RELEASE_SIGNING_ENABLED=true
    

    or add this to your Groovy build files:

    mavenPublishing {
      publishToMavenCentral()
      // publishToMavenCentral("S01") for publishing through s01.oss.sonatype.org
      signAllPublications()
    }
    

    or the following to your kts build files:

    mavenPublishing {
      publishToMavenCentral()
      // publishToMavenCentral(SonatypeHost.S01) for publishing through s01.oss.sonatype.org
      signAllPublications()
    }
    

    The base plugin is unaffected by these changes because it already has this behavior.

    Android variant publishing

    Since version 0.19.0 the plugin was publishing a multi variant library by default for Android projects. Due to a bug in Android Studio that will cause it to not find the sources for libraries published this way the plugin will temporarily revert to publishing single variant libraries again. Unless another variant is specified by setting the ANDROID_VARIANT_TO_PUBLISH Gradle property the release variant will be published.

    To continue publishing multi variant libraries you can use the base plugin.

    Removals

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies java 
    opened by dependabot[bot] 1
  • Bump actions/setup-java from 2 to 3

    Bump actions/setup-java from 2 to 3

    Bumps actions/setup-java from 2 to 3.

    Release notes

    Sourced from actions/setup-java's releases.

    v3.0.0

    In scope of this release we changed version of the runtime Node.js for the setup-java action and updated package-lock.json file to v2.

    Breaking Changes

    With the update to Node 16 in #290, all scripts will now be run with Node 16 rather than Node 12.

    v2.5.0

    In scope of this pull request we add support for Microsoft Build of OpenJDK (actions/setup-java#252).

    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup-java
        uses: actions/setup-java@v2
        with:
          distribution: microsoft
          java-version: 11
    

    Supported distributions

    Currently, the following distributions are supported:

    Keyword Distribution Official site License
    temurin Eclipse Temurin Link Link
    zulu Zulu OpenJDK Link Link
    adopt or adopt-hotspot Adopt OpenJDK Hotspot Link Link
    adopt-openj9 Adopt OpenJDK OpenJ9 Link Link
    liberica Liberica JDK Link Link
    microsoft Microsoft Build of OpenJDK Link Link

    v2.4.0

    In scope of this pull request we add support for Liberica JDK (actions/setup-java#225).

    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup-java
        uses: actions/setup-java@v2
        with:
          distribution: liberica
          java-version: 11
    

    Supported distributions

    Currently, the following distributions are supported:

    Keyword Distribution Official site License
    zulu Zulu OpenJDK Link Link
    adopt or adopt-hotspot Adopt OpenJDK Hotspot Link Link
    adopt-openj9 Adopt OpenJDK OpenJ9 Link Link
    temurin Eclipse Temurin Link Link

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.

    Dependabot will merge this PR once CI passes on it, as requested by @JakeWharton.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • Border Modifier

    Border Modifier

    Being able to draw borders around layouts would be great. I guess Modifier.border(BorderStyle) could be good enough?

    Maybe JakeWharton/flip-tables could be used under the hood?

    I imagine being able to draw single line borders, double line border, rounded corners, square corners (defined by BorderStyle?) See https://en.wikipedia.org/wiki/Box-drawing_character#Box_Drawing

           | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
    U+250x | ─ | ━ | │ | ┃ | ┄ | ┅ | ┆ | ┇ | ┈ | ┉ | ┊ | ┋ | ┌ | ┍ | ┎ | ┏
    U+251x | ┐ | ┑ | ┒ | ┓ | └ | ┕ | ┖ | ┗ | ┘ | ┙ | ┚ | ┛ | ├ | ┝ | ┞ | ┟
    U+252x | ┠ | ┡ | ┢ | ┣ | ┤ | ┥ | ┦ | ┧ | ┨ | ┩ | ┪ | ┫ | ┬ | ┭ | ┮ | ┯
    U+253x | ┰ | ┱ | ┲ | ┳ | ┴ | ┵ | ┶ | ┷ | ┸ | ┹ | ┺ | ┻ | ┼ | ┽ | ┾ | ┿
    U+254x | ╀ | ╁ | ╂ | ╃ | ╄ | ╅ | ╆ | ╇ | ╈ | ╉ | ╊ | ╋ | ╌ | ╍ | ╎ | ╏
    U+255x | ═ | ║ | ╒ | ╓ | ╔ | ╕ | ╖ | ╗ | ╘ | ╙ | ╚ | ╛ | ╜ | ╝ | ╞ | ╟
    U+256x | ╠ | ╡ | ╢ | ╣ | ╤ | ╥ | ╦ | ╧ | ╨ | ╩ | ╪ | ╫ | ╬ | ╭ | ╮ | ╯
    U+257x | ╰ | ╱ | ╲ | ╳ | ╴ | ╵ | ╶ | ╷ | ╸ | ╹ | ╺ | ╻ | ╼ | ╽ | ╾ | ╿
    

    In terminal app, it's always painful to achieve this:

    • border length depending on content or requested width
    • support minWidth + dynamic width
    • pad end of each lines to draw right border

    Would be a nice addition to

    • #11
    • #8
    • #7
    opened by opatry 1
  • Box/Row/Column background

    Box/Row/Column background

    Might be a different solution to the same problem (#81), but probably a capability missing: image

    This knows how large the Column is horizontally, yet I have to give every single Text() a separate background = to make it look like a box (and fail because #81).

    Column {
    	Text("+----------------+", color = Black, background = Cyan)
    	menu.items.forEach { item ->
    		if (item.separator) {
    			Text("| ${item.name} |", color = Black, background = Cyan)
    		} else {
    			Text("| ${item.name} |", color = White, background = Cyan)
    		}
            }
    	Text("+----------------+", color = Black, background = Cyan)
    }
    

    I would expect to have some syntax to achieve the same (or even better fully filled box-like) UI:

    Column(background = Cyan) {
    	Text("+----------------+", color = Black)
    	menu.items.forEach { item ->
    		if (item.separator) {
    			Text("| ${item.name} |", color = Black)
    		} else {
    			Text("| ${item.name} |", color = White)
    		}
            }
    	Text("+----------------+", color = Black)
    }
    
    opened by TWiStErRob 0
  • Stretch Text

    Stretch Text

    I played around a bit trying to reproduce image image

    I found two things I couldn't do:

    • stretch each item to full width
      I think this is calculatable based on item.name lengths, but it feels like that goes against having a "layout system" around you.
    • stretch the separator and top-bottom adorning box dynamically (right now it's hard-coded)

    Also probably related, I managed to shrink the main menu (Left smaller, and Files right next to it, but it feels like I am hacking something mosaic (or maybe a custom layout) should be doing in some way: image image

    @Composable
    fun MenuDropdown(menus: List<Menu>, focus: MenuItem) {
    	Row {
    		Text("  ") // screen padding
    		menus.forEach { menu ->
    			if (focus in menu.items) {
    				ExpandedMenu(menu, focus)
    			} else {
    				Text(" ".repeat(2 + menu.name.length + 2)) // hack to add "just enough" padding
    			}
    		}
    	}
    }
    
    opened by TWiStErRob 0
  • Windows support

    Windows support

    I was playing around and I was having problems making it work out of the box, but I did it in the end. I'm opening an issue instead of a PR, because I'm not sure in what style would you apply these changes. The main fix that's absolutely necessary is patch 2 for jansi (see 4.).

    Below is what I observed and learned:

    1. IntelliJ IDEA terminal view supports color and relocation of cursor
      (Gradle output is colored and workers are shown)

      • Command Prompt (cmd)
      • Windows Powershell
      • Ubuntu Bash (from WSL)
      • image
    2. Gradle :run task is JavaExec type and it messes with System.out in some way
      See https://github.com/spring-projects/spring-boot/issues/24169 where they gave up supporting colors in Gradle, this seems to be because of https://github.com/gradle/gradle/issues/1251, but this assumption is not conclusive as they're talking about console input there.

    3. :jar in samples doesn't produce a runnable jar file even though the application plugin is applied. (easy fix)

      Patch 1: make jar runnable

      Applied to each sample:

      jar {
      	manifest {
      		attributes(
      			'Main-Class': mainClassName,
      		)
      	}
      	from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }}
      	exclude('META-INF/versions/9/module-info.class')
      }
      
    4. jansi is not set up correctly in mosaic, because it detects tty, but is not rendering correctly

      Example unprocessed ansi output
      Tests: 10 total←[K
      Time:  0s      ←[K
      ←[F←[F←[30;43m RUNS ←[39;49m tests/←[1mlogin.kt←[22m                   ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1msignup.kt←[22m                  ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1mforgot-password.kt←[22m         ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1mreset-password.kt←[22m          ←[K
                                              ←[K
      Tests: ←[33m4 running←[39m, 10 total              ←[K
      Time:  0s                               ←[K
      ←[43m                ←[100m                        ←[0m←[K
      ←[F←[F←[F←[F←[F←[F←[F←[F←[30;43m RUNS ←[39;49m tests/←[1mlogin.kt←[22m                   ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1msignup.kt←[22m                  ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1mforgot-password.kt←[22m         ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1mreset-password.kt←[22m          ←[K
                                              ←[K
      Tests: ←[33m4 running←[39m, 10 total              ←[K
      Time:  0s                               ←[K
      ←[100m                                        ←[0m←[K
      ←[F←[F←[F←[F←[F←[F←[F←[F←[30;43m RUNS ←[39;49m tests/←[1mlogin.kt←[22m                   ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1msignup.kt←[22m                  ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1mforgot-password.kt←[22m         ←[K
      ←[30;43m RUNS ←[39;49m tests/←[1mreset-password.kt←[22m          ←[K
                                              ←[K
      
      Patch 2: set up jansi as documented
      diff --git a/mosaic-runtime/src/main/kotlin/com/jakewharton/mosaic/output.kt b/mosaic-runtime/src/main/kotlin/com/jakewharton/mosaic/output.kt
      --- a/mosaic-runtime/src/main/kotlin/com/jakewharton/mosaic/output.kt	(revision 95c0826ab30a226e363ee773904fb2e7b3bb6d01)
      +++ b/mosaic-runtime/src/main/kotlin/com/jakewharton/mosaic/output.kt	(date 1667899762778)
      @@ -36,6 +36,10 @@
       internal object AnsiOutput : Output {
          private var lastHeight = 0
      
      +	init {
      +		AnsiConsole.systemInstall()
      +	}
      +
          override fun display(canvas: TextCanvas) {
              val rendered = buildString {
                  repeat(lastHeight) {
      
    5. At this point running gradlew -p samples :jest:jar and then java -jar samples\jest\build\libs\jest.jar works nicely (ignoring Unicode problems)

      • image
      • image
    opened by TWiStErRob 0
  • Adopt target markers in generated Compose code

    Adopt target markers in generated Compose code

    https://developer.android.com/jetpack/androidx/releases/compose-runtime#1.2.0-alpha04

    This will ensure no one uses our Compose code in Compose UI or other compositions.

    enhancement blocked 
    opened by JakeWharton 1
Releases(0.2.0)
  • 0.2.0(Aug 12, 2022)

    • Support Kotlin 1.7.10 via Compose compiler 1.3.0.
    • Migrate from custom build of Compose compiler and Compose runtime to Google's Compose compiler and JetBrains' multiplatform Compose runtime. Note that this will require you have the Google Maven repositories in your Gradle repositories (google()).
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Jun 26, 2021)

Owner
Jake Wharton
Jake Wharton
A CLI tool to convert multi-module Jetpack Compose compiler metrics into beautiful HTML reports

A CLI tool to convert multi-module Jetpack Compose compiler metrics into beautiful HTML reports 1. What are Jetpack Compose compiler metrics? The Comp

Jaya Surya Thotapalli 116 Jan 3, 2023
An experimental UI toolkit for generating PowerPoint presentation files using Compose

ComposePPT An experimental UI toolkit for generating PowerPoint presentation files(.pptx) using Compose. Inspired by Glance and Mosaic. Why? This proj

Fatih Giriş 252 Dec 28, 2022
Simple Design for Kotlin bridge with Javascript. Also can get javascript console.log.

SDBridgeJava is here. If your h5 partner confused about how to deal with iOS and Android. This Demo maybe help. bilibili video introduction is here. Y

null 14 Dec 19, 2022
🐅 Experimental Kotlin library for Revolt (API subject is to change)

Kairi ?? Experimental Kotlin library for Revolt

Noel 3 Jul 4, 2022
🍼Debug Bottle is an Android runtime debug / develop tools written using kotlin language.

???? 中文 / ???? 日本語 / ???? English ?? Debug Bottle An Android debug / develop tools written using Kotlin language. All the features in Debug bottle are

Yuriel Arlencloyn 846 Nov 14, 2022
This is a Kotlin multiplatform template project used to generate and deploy a natively compiled AWS lambda function using the custom runtime.

Overview This is a Kotlin multiplatform template project used to generate and deploy a natively compiled AWS Lambda function using a custom runtime. U

Greg Steckman 5 Jun 25, 2022
Various experimental proposals and extensions to Javalin 4.x used in Reposilite 3.x

Javalin RFCs Various experimental extensions to Javalin 4.x used in Reposilite 3.x. Provides basic support for Kotlin coroutines and async routes with

Reposilite Playground 5 Feb 22, 2022
Kotlin Multiplatform runtime infix expressions evaluator.

Kotlin multiplatform expressions evaluator This is a kotlin multiplatform runtime infix expressions evaluator. Overview Operators The library supports

Azamat Murzagalin 13 Dec 30, 2022
A Kotlin compiler plugin that allows Java callers to pass in null for default parameters

kotlin-null-defaults (Compiler plugin) (Gradle Plugin) ( Currently pending approval) A Kotlin compiler plugin that allows Java callers to pass in null

Youssef Shoaib 7 Oct 14, 2022
An annotation and Kotlin compiler plugin for enforcing a when statement is exhaustive

An annotation and Kotlin compiler plugin for enforcing a when statement is exhaustive

Cash App 468 Jan 4, 2023
A composite Github Action to execute the Kotlin Script with compiler plugin and dependency caching!

Kotlin Script Github Action Kotlin can also be used as a scripting language, which is more safer, concise, and fun to write than bash or python. Githu

Suresh 9 Nov 28, 2022
Build a compiler in Kotlin (based on the original tutorial by Jack Crenshaw)

Let's Build a Compiler Based on the original series "Let’s Build a Compiler!" by Jack Crenshaw. This is an adaptation of the original series to Kotlin

null 2 Oct 9, 2022
Lightweight compiler plugin intended for Kotlin/JVM library development and symbol visibility control.

Restrikt A Kotlin/JVM compiler plugin to restrict symbols access, from external project sources. This plugin offers two ways to hide symbols: An autom

Lorris Creantor 18 Nov 24, 2022
Playground project for Koin Koin Compiler - Sandbox

Koin Compiler - Sandbox The goal of Koin compiler & Annotations project is to help declare Koin definition in a very fast and intuitive way, and gener

insert-koin.io 17 Nov 22, 2021
This repository is part of a Uni-Project to write a complete Compiler for a subset of Java.

Compiler This repository is part of a Uni-Project to write a complete Compiler for a subset of Java. Features error recovery using context sensitive a

null 3 Jan 10, 2022
A Open GAL compiler based on OpenGAL 0.3.1

A Open GAL compiler based on OpenGAL 0.3.1

Li Plum 3 Dec 21, 2022
A simple example of kotlim compiler plugin with FIR and IR.

A simple Kotlin compiler plugin example This Kotlin compiler plugin generates a top level class: public final class foo.bar.MyClass { fun foo(): S

Anastasiia Birillo 10 Dec 2, 2022
Provides Kotlin libs and some features for building Kotlin plugins

Kotlin Plugin Provides Kotlin libs and some features for building awesome Kotlin plugins. Can be used instead of CreeperFace's KotlinLib (don't use to

null 3 Dec 24, 2021
Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.

Codename One - Cross Platform Native Apps with Java or Kotlin Codename One is a mobile first cross platform environment for Java and Kotlin developers

Codename One 1.4k Jan 9, 2023