Skip to content

Commit

Permalink
-
Browse files Browse the repository at this point in the history
  • Loading branch information
kostaskougios committed Feb 5, 2024
1 parent 1e8c82f commit 621eea0
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ If we click in a cell, we will be able to change a value. And then use the "Save
Now feel free to examine and run the rest of the scripts or create your own! You can have the server running and develop your
scripts with your favorite IDE. Run the scripts within the IDE and view the UI in a browser.

# Tutorial

To learn the basics of coding with terminal21, please see the [tutorial](docs/tutorial.md)

# Example scripts

```shell
Expand Down
Binary file added docs/images/tutorial/progress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
150 changes: 150 additions & 0 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Terminal21 tutorial

This tutorial assumes you have the terminal 21 server running (as described in the readme file of the project) and you would
like to learn how to create user interfaces.

This tutorial will use `scala-cli` but the same applies for `sbt` or `mill` projects that use the terminal21 libraries.

## Creating a folder for our scripts

Create a folder and a file `project.scala` into it. This file will help us include the library dependencies and also
scala & jdk version. It should look like this:

```scala
//> using jvm "21"
//> using scala 3

//> using dep io.github.kostaskougios::terminal21-ui-std:_VERSION_
```

Change `_VERSION_` with the terminal 21 latest version.

See [project.scala](../example-scripts/project.scala)

## Creating a hello world app

To do this we can create a [hello-world.sc](../example-scripts/hello-world.sc) in our folder.

```scala
#!/usr/bin/env -S scala-cli project.scala

import org.terminal21.client.*
import org.terminal21.client.components.*
import org.terminal21.client.components.std.*

Sessions.withNewSession("hello-world", "Hello World Example"): session =>
given ConnectedSession = session

Paragraph(text = "Hello World!").render()
session.leaveSessionOpenAfterExiting()
```

The first line, `#!/usr/bin/env -S scala-cli project.scala`, makes our script runnable from the command line.

```shell
chmod +x hello-world.sc

./hello-world.sc
```

I had issues with intellij and this line, so you may want to comment it out while you develop your scripts.

It starts `scala-cli` and also includes `project.scala` so that we get our dependencies, jdk 21 and scala 3 when running our code.


Next it creates a session. Each session has a unique id (globally unique across scripts), in this case `hello-world`. And a session
title, "Hello World Example", that will be displayed on the browser.

```scala
Sessions.withNewSession("hello-world", "Hello World Example"): session =>
...
```

![hello-world](images/hello-world.png)

Next is the actual user interface, in this example just a paragraph with a "Hello World!":

```scala
Paragraph(text = "Hello World!").render()
```

The `render()` method sends the UI to the server which in turn sends it to the terminal21 UI so that it is rendered.

Finally because this is just a presentation script (we don't expect any feedback from the user), we can terminate it but
inform terminal21 we want to leave the session open so that the user has a chance to see it.

```scala
session.leaveSessionOpenAfterExiting()
```

When we run our code, it will compile, download dependencies (if needed) and run. It will exit straight away but the UI for our script
will be available in terminal21 UI.

## Updating the UI

Let's create a script that will display a progress bar for some process that will run for some time. The script will update
the progress bar and also give an informative message regarding which stage of the process it is performing.

[progress.sc](../example-scripts/progress.sc)

![progress](images/tutorial/progress.png)

```scala
#!/usr/bin/env -S scala-cli project.scala

import org.terminal21.client.*
import org.terminal21.client.components.*
import org.terminal21.client.components.std.*
import org.terminal21.client.components.chakra.*

Sessions.withNewSession("universe-generation", "Universe Generation Progress"): session =>
given ConnectedSession = session

val msg = Paragraph(text = "Generating universe ...")
val progress = Progress(value = 1)

Seq(msg, progress).render()

for i <- 1 to 100 do
val p = progress.withValue(i)
val m =
if i < 10 then msg
else if i < 30 then msg.withText("Creating atoms")
else if i < 50 then msg.withText("Big bang!")
else if i < 80 then msg.withText("Inflating")
else msg.withText("Life evolution")

Seq(p, m).renderChanges()
Thread.sleep(100)

// clear UI
session.clear()
Paragraph(text = "Universe ready!").render()
```

Here we create a paragraph and a progress bar.

```scala
val msg = Paragraph(text = "Generating universe ...")
val progress = Progress(value = 1)
```

Then we render them for the first time on screen. When we want to add a new element to the UI, we use the `render()` method. When
we want to update an existing element we use the `renderChanges()` method.

```scala
Seq(msg, progress).render()
```

Then we have our main loop where the calculations occur. We just use a `Thread.sleep` to simulate that some important task is being calculated. And we
update the progress bar and the message in our paragraph.
```scala
val p = progress.withValue(i)
val m = ... msg.withText("Creating atoms") ...
```

Note the `e.withX()` methods. Those help us change a value on a UI element. We get a copy of the UI element which we can render as an update:

```scala
Seq(p, m).renderChanges()
```
30 changes: 30 additions & 0 deletions example-scripts/progress.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env -S scala-cli project.scala

import org.terminal21.client.*
import org.terminal21.client.components.*
import org.terminal21.client.components.std.*
import org.terminal21.client.components.chakra.*

Sessions.withNewSession("universe-generation", "Universe Generation Progress"): session =>
given ConnectedSession = session

val msg = Paragraph(text = "Generating universe ...")
val progress = Progress(value = 1)

Seq(msg, progress).render()

for i <- 1 to 100 do
val p = progress.withValue(i)
val m =
if i < 10 then msg
else if i < 30 then msg.withText("Creating atoms")
else if i < 50 then msg.withText("Big bang!")
else if i < 80 then msg.withText("Inflating")
else msg.withText("Life evolution")

Seq(p, m).renderChanges()
Thread.sleep(100)

// clear UI
session.clear()
Paragraph(text = "Universe ready!").render()
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,8 @@ case class AlertDescription(
def withKey(v: String) = copy(key = v)
def withText(v: String) = copy(text = v)

/** https://chakra-ui.com/docs/components/progress
*/
case class Progress(
key: String = Keys.nextKey,
value: Int = 50,
Expand Down

0 comments on commit 621eea0

Please sign in to comment.