Remove markdown include (which used an external binary), add list templates
This commit is contained in:
parent
e19fe276c4
commit
775abb29d1
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.bak
|
||||||
|
out/
|
|
@ -93,7 +93,6 @@ case class Presentation(
|
||||||
case Clear => Terminal.clear()
|
case Clear => Terminal.clear()
|
||||||
case PauseKey => waitkey(using Keymap.empty)
|
case PauseKey => waitkey(using Keymap.empty)
|
||||||
case Pause(msec) => Thread.sleep(msec)
|
case Pause(msec) => Thread.sleep(msec)
|
||||||
case incMd @ IncludeMarkdown(_) => println(incMd.markdownBlock())
|
|
||||||
case Image(file, None) => println(KittyGraphicsProtocol.showImage(file))
|
case Image(file, None) => println(KittyGraphicsProtocol.showImage(file))
|
||||||
case Image(file, Some(ImageSize(w, h, aspect))) =>
|
case Image(file, Some(ImageSize(w, h, aspect))) =>
|
||||||
import KittyGraphicsProtocol.Sizing.*
|
import KittyGraphicsProtocol.Sizing.*
|
||||||
|
@ -121,6 +120,6 @@ case class Presentation(
|
||||||
case Group(slides) => slides.foreach(executeSilent(pos))
|
case Group(slides) => slides.foreach(executeSilent(pos))
|
||||||
case lios @ LazyIOSlide(_, display) =>
|
case lios @ LazyIOSlide(_, display) =>
|
||||||
executeSilent(pos)(lios.genSlide())
|
executeSilent(pos)(lios.genSlide())
|
||||||
case Paragraph(_) | Image(_, _) | Clear | IncludeMarkdown(_) | Meta(_) => ()
|
case Paragraph(_) | Image(_, _) | Clear | Meta(_) => ()
|
||||||
case _ => executeQuick(pos)(slide)
|
case _ => executeQuick(pos)(slide)
|
||||||
end Presentation
|
end Presentation
|
||||||
|
|
|
@ -1,224 +0,0 @@
|
||||||
package de.qwertyuiop.copret
|
|
||||||
|
|
||||||
import de.qwertyuiop.copret.Terminal.*
|
|
||||||
|
|
||||||
import de.qwertyuiop.copret.*
|
|
||||||
import de.qwertyuiop.copret.syntax.*
|
|
||||||
import TypedCommand.{interactive, shell => sh}
|
|
||||||
import os.Path
|
|
||||||
|
|
||||||
given presentationRoot: Path = os.Path("/home/crater2150/org/opencolloq/termescape")
|
|
||||||
val imgs = presentationRoot / "img"
|
|
||||||
given theme: Theme = Theme.default
|
|
||||||
|
|
||||||
def figlet(str: String, font: String): String = String(
|
|
||||||
os.proc("figlet", "-t", "-f", font, str).call().out.bytes,
|
|
||||||
)
|
|
||||||
|
|
||||||
def lolcat(str: String): String = String(
|
|
||||||
os.proc("lolcat", "-b", "-f", "-h", "0.7", "-r").call(stdin = str).out.bytes,
|
|
||||||
)
|
|
||||||
|
|
||||||
def lollet(str: String, font: String): String = lolcat(figlet(str, font))
|
|
||||||
|
|
||||||
// debug mode
|
|
||||||
val noInteractiveCommands = false
|
|
||||||
|
|
||||||
val titleSlide =
|
|
||||||
Group(
|
|
||||||
Clear,
|
|
||||||
TypedCommand(TypedCommand.runInteractive(using presentationRoot), "", Vector("./title.zsh"), true, false),
|
|
||||||
Clear,
|
|
||||||
header,
|
|
||||||
Paragraph(
|
|
||||||
lollet("Terminal", "mono12").block ++ lollet("basics", "mono12").block
|
|
||||||
++ "\n" ++ "Was tut so eine Terminalanwendung eigentlich?".right(10),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
/* copret templates are simply normal methods that return slides */
|
|
||||||
def chapter(title: String, subtitle: String): Group =
|
|
||||||
chapter(title, "", subtitle)
|
|
||||||
def chapter(title1: String, title2: String, subtitle: String): Group = {
|
|
||||||
val font =
|
|
||||||
if ((title1.length max title2.length) < columns / 10) "mono12" else "mono9"
|
|
||||||
Group(
|
|
||||||
Clear,
|
|
||||||
header, // a built in template
|
|
||||||
Paragraph(
|
|
||||||
lollet(title1, font).block ++ lollet(title2, font).block ++ "\n" ++ subtitle.right(10),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val parentSHApattern = raw"parent ([a-f0-9]{40})".r
|
|
||||||
val treeSHApattern = raw"tree ([a-f0-9]{40})".r
|
|
||||||
val blobSHApattern = raw"blob ([a-f0-9]{40})".r
|
|
||||||
|
|
||||||
val meta = Map(
|
|
||||||
"author" -> "Alexander Gehrke",
|
|
||||||
"title" -> "Terminals",
|
|
||||||
)
|
|
||||||
|
|
||||||
def sgr(code: String): String = s"\u001b[${code}m"
|
|
||||||
|
|
||||||
def sgr(code: Int*): String = sgr(code.mkString(";"))
|
|
||||||
|
|
||||||
val reset = sgr(0)
|
|
||||||
|
|
||||||
extension (s: String)
|
|
||||||
def style(code: String): String = sgr(code) + s + reset
|
|
||||||
def style(code: Int*): String = sgr(code*) + s + reset
|
|
||||||
def italic = s.style(3)
|
|
||||||
def bold = s.style(1)
|
|
||||||
|
|
||||||
def underline(style: Int = 1, color: Int = 7) =
|
|
||||||
sgr("4:" + style) + sgr(58, 5, color) + s + reset
|
|
||||||
|
|
||||||
val rainbow = Vector(
|
|
||||||
"\u001b[38;5;196m",
|
|
||||||
"\u001b[38;5;202m",
|
|
||||||
"\u001b[38;5;208m",
|
|
||||||
"\u001b[38;5;214m",
|
|
||||||
"\u001b[38;5;220m",
|
|
||||||
"\u001b[38;5;226m",
|
|
||||||
"\u001b[38;5;190m",
|
|
||||||
"\u001b[38;5;154m",
|
|
||||||
"\u001b[38;5;118m",
|
|
||||||
"\u001b[38;5;82m",
|
|
||||||
"\u001b[38;5;46m",
|
|
||||||
"\u001b[38;5;47m",
|
|
||||||
"\u001b[38;5;48m",
|
|
||||||
"\u001b[38;5;49m",
|
|
||||||
"\u001b[38;5;50m",
|
|
||||||
"\u001b[38;5;51m",
|
|
||||||
"\u001b[38;5;45m",
|
|
||||||
"\u001b[38;5;39m",
|
|
||||||
"\u001b[38;5;33m",
|
|
||||||
)
|
|
||||||
|
|
||||||
def list(bullet: (Int, Int) => String, continued: (Int, Int) => String)(items: String*)(using Theme): Paragraph =
|
|
||||||
val randbow = util.Random.shuffle(rainbow)
|
|
||||||
items.zipWithIndex
|
|
||||||
.map((str, index) => {
|
|
||||||
def formatFirst(first: String) =
|
|
||||||
s" ${randbow(index % randbow.length)}${bullet(index, items.size)}\u001b[0m $first"
|
|
||||||
|
|
||||||
str.split("\n").toList match
|
|
||||||
case first :: Nil => formatFirst(first)
|
|
||||||
case first :: cont =>
|
|
||||||
formatFirst(first)
|
|
||||||
+ cont.map(line => s" ${continued(index, items.size)}${line}").mkString("\n", "\n", "")
|
|
||||||
case Nil => ""
|
|
||||||
})
|
|
||||||
.mkString("\n")
|
|
||||||
.par
|
|
||||||
|
|
||||||
def ulist(items: String*)(using Theme) = list(bullet = (_, _) => "✦ ", continued = (_, _) => " ")(items*)
|
|
||||||
|
|
||||||
def digits(i: Int): Int = math.ceil(math.log10(i)).toInt
|
|
||||||
|
|
||||||
def olist(items: String*)(using Theme) =
|
|
||||||
list(
|
|
||||||
bullet = (i, max) => (" " * (digits(max + 1) - digits(i + 1) - 1)) + s"${i + 1}.",
|
|
||||||
continued = (i, max) => " " * digits(max + 1),
|
|
||||||
)(items*)
|
|
||||||
|
|
||||||
def emph(str: String) = sgr(3) + lolcat(str) + reset
|
|
||||||
def strong(str: String) = sgr(1) + lolcat(str) + reset
|
|
||||||
|
|
||||||
val slides = Vector(
|
|
||||||
titleSlide,
|
|
||||||
slide("Terminal IO")(
|
|
||||||
"Eine Anwendung in einem Terminal kommuniziert (fast) nur via Standard-IO mit diesem:".par,
|
|
||||||
ulist(
|
|
||||||
"stdin, für Benutzereingaben und Nachrichten vom Terminal",
|
|
||||||
"stdout, für Textausgabe und Nachrichten an das Terminal",
|
|
||||||
"stderr, wird wie stdout behandelt",
|
|
||||||
),
|
|
||||||
s"=> Alle Steuersignale vom Programm ans Terminal oder umgekehrt sind ${emph("inband")}".par,
|
|
||||||
),
|
|
||||||
slide("Steuersignale")(
|
|
||||||
s"""|Wie unterscheiden Terminal und Programm normalen IO von Steuersignalen?
|
|
||||||
|Durch ${emph("Steuerzeichen")}, aufgeteilt in zwei Bereiche:""".par,
|
|
||||||
s"""|${strong("C0")}: alle Zeichen im Bereich von 0x00 bis 0x1F
|
|
||||||
|Größtenteils veraltet. Noch relevant sind u.a.:""".par,
|
|
||||||
ulist(
|
|
||||||
"Whitespaces (\\n, \\r, \\t, ...) und Backspace (0x08)",
|
|
||||||
"Bell (\\a), führt bei den meisten Desktops zu blinkendem Icon o.ä. (Urgent Flag)",
|
|
||||||
"XON/XOFF (0x11/0x13), schaltet Ausgabe an/aus (Flow Control)",
|
|
||||||
"Escape (\\e, 0x1B), das wichtigste für diesen Talk, startet Escapesequenzen",
|
|
||||||
),
|
|
||||||
s"""|${strong("C1")}: Zeichen im Bereich von 0x80 bis 0x9F
|
|
||||||
|Wegen Kollisionen mit Unicode inkompatibel.
|
|
||||||
|Heute üblicherweise durch Escapesequenzen ersetzt""".par,
|
|
||||||
),
|
|
||||||
chapter("Escape-", "sequenzen", "die verschiedenen Typen"),
|
|
||||||
slide("CSI")(
|
|
||||||
PauseKey,
|
|
||||||
Image(imgs / "csi.png"),
|
|
||||||
),
|
|
||||||
slide("CSI")(
|
|
||||||
"""|Control Sequence Introducer (CSI) ist der vermutlich häufigste Typ.
|
|
||||||
|Wird benutzt für:""".par,
|
|
||||||
ulist(
|
|
||||||
"Cursor-Steuerung",
|
|
||||||
"Löschen von Zeichen oder Zeilen",
|
|
||||||
s"${lolcat("Farben")} ${"und".italic} ${"andere".style(42)} ${"Formatierung".underline(style = 3, color = 1)}",
|
|
||||||
),
|
|
||||||
"|CSI-Sequenzen haben folgendes Format:\n".par,
|
|
||||||
s"${"ESC [".style(94)} ${"arg1 ; arg2 ; ... ; argn".style(93)} ${"c".style(92)}".block.text,
|
|
||||||
s"""|
|
|
||||||
|${"✦ ESC [".style(94)} ist der Teil, der CSI genannt wird
|
|
||||||
|${"✦ c".style(92)} gibt an, was gemacht werden soll. Immer ein Zeichen zwischen 0x40 (@) und 0x7F (~)
|
|
||||||
|${"✦ arg1 ; arg2 ; ... ; argn".style(93)} sind Parameter, meistens Zahlen.
|
|
||||||
| Zeichen, die für ${"c".style(92)} erlaubt sind, sind nicht als Parameter erlaubt.
|
|
||||||
|\n""".stripMargin.block.text,
|
|
||||||
s"""\nBeispiel: ${"\\u001b[31m".style(31)} schaltet auf rote Schrift um""".par,
|
|
||||||
),
|
|
||||||
slide("OSC")(
|
|
||||||
"""|Operating System Commands (OSC) haben verschiedenste Anwendungszwecke,
|
|
||||||
|z.B. Setzen des Fenstertitels, Zugriff aufs Clipboard, etc.
|
|
||||||
|Im Gegensatz zu CSI Escapes können sie die meisten Zeichen enthalten
|
|
||||||
| => komplexere Befehle möglich.""".par,
|
|
||||||
s"${"ESC ]".style(94)} ${"<Zahl>".style(92)} ; ${"args...".style(93)} ${"ST".style(96)}".block.text,
|
|
||||||
s"""|
|
|
||||||
|${"✦ ESC ]".style(94)} startet ein OSC
|
|
||||||
|${"✦ <Zahl>".style(92)} gibt an, was gemacht werden soll, ein- bis vierstellige Zahl
|
|
||||||
|${"✦ args...".style(93)} sind je nach Befehl die Argumente. Hier ist alles außer ST erlaubt
|
|
||||||
|${"✦ ST".style(96)} ist das String Terminator Zeichen. Entweder BEL (\\a) oder ESC \\
|
|
||||||
|""".stripMargin.block.text,
|
|
||||||
s"""\nBeispiel: TODO irgendwas mit clipboard""".par,
|
|
||||||
),
|
|
||||||
slide("APC")(
|
|
||||||
"Application Program Commands (APC) sind ähnlich zu OSC, aber größtenteils\nspezifisch für einzelne Terminals".par,
|
|
||||||
ulist(
|
|
||||||
s"fangen mit ${"ESC _".style(3, 94)} an statt mit ${"ESC ]".style(3, 94)}",
|
|
||||||
s"Format bis zum ${"ST".style(3, 96)} ist uneinheitlich",
|
|
||||||
"Benutzt z.B. bei screen und tmux zum Setzen von Statusleisten",
|
|
||||||
"Beispiel für mittlerweile verbreitetere Anwendung: Kitty Graphics Protocol",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
chapter("Ende", "Noch Fragen?", ""),
|
|
||||||
slide("Sonstige")(
|
|
||||||
"Ein paar weitere Typen von Escapesequenzen, die wir uns nicht genauer anschauen".par,
|
|
||||||
ulist(
|
|
||||||
"DCS (Device Control String): ähnlich wie OSC, kaum noch genutzt.\nU.a. für ältere Grafikprotokolle",
|
|
||||||
"SCS, DOCS: für Zeichensatzwechsel. Wir haben jetzt Unicode.",
|
|
||||||
"DEC: Für Dinge wie doppelte Zeilenhöhe. In manchen Terminals noch supported.",
|
|
||||||
"SM, RM: Setzen von Terminalmodi, war für Hardwareterminals relevant",
|
|
||||||
"DECSET, DECRST: Noch mehr Terminalmodi, vendorspezifisch.\nWerden noch genutzt, z.B. für Maus-Support",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val presentation = Presentation(slides, meta = meta)
|
|
||||||
|
|
||||||
@main def main =
|
|
||||||
// enterRawMode()
|
|
||||||
// println(Terminal.getSize())
|
|
||||||
presentation.start(using
|
|
||||||
Keymap.default ++ Map(
|
|
||||||
Key('i') -> SlideAction.runForeground("tmux"),
|
|
||||||
),
|
|
||||||
)
|
|
|
@ -16,15 +16,6 @@ case class Paragraph(contents: String) extends Slide:
|
||||||
object Paragraph:
|
object Paragraph:
|
||||||
def apply(str: fansi.Str): Paragraph = Paragraph(str.toString)
|
def apply(str: fansi.Str): Paragraph = Paragraph(str.toString)
|
||||||
|
|
||||||
case class IncludeMarkdown(path: Path) extends Slide:
|
|
||||||
def markdownBlock() =
|
|
||||||
%%%(
|
|
||||||
"/usr/bin/mdcat",
|
|
||||||
"--columns",
|
|
||||||
(columns * 0.8).toInt.toString,
|
|
||||||
path.toString,
|
|
||||||
)(using os.pwd).block
|
|
||||||
|
|
||||||
case class Image(path: Path, sizing: Option[ImageSize]) extends Slide
|
case class Image(path: Path, sizing: Option[ImageSize]) extends Slide
|
||||||
object Image:
|
object Image:
|
||||||
def apply(path: Path) = new Image(path, None)
|
def apply(path: Path) = new Image(path, None)
|
||||||
|
|
|
@ -32,6 +32,31 @@ trait Templates:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def list(bullet: (Int, Int) => String, continued: (Int, Int) => String)(items: String*)(using Theme): Paragraph =
|
||||||
|
items.zipWithIndex
|
||||||
|
.map((str, index) => {
|
||||||
|
def formatFirst(first: String) =
|
||||||
|
s" ${bullet(index, items.size)} $first"
|
||||||
|
|
||||||
|
str.split("\n").toList match
|
||||||
|
case first :: Nil => formatFirst(first)
|
||||||
|
case first :: cont =>
|
||||||
|
formatFirst(first)
|
||||||
|
+ cont.map(line => s" ${continued(index, items.size)}${line}").mkString("\n", "\n", "")
|
||||||
|
case Nil => ""
|
||||||
|
})
|
||||||
|
.mkString("\n")
|
||||||
|
.par
|
||||||
|
|
||||||
|
def ulist(items: String*)(using Theme) = list(bullet = (_, _) => "✦ ", continued = (_, _) => " ")(items*)
|
||||||
|
|
||||||
|
def olist(items: String*)(using Theme) =
|
||||||
|
def digits(i: Int): Int = math.ceil(math.log10(i)).toInt
|
||||||
|
list(
|
||||||
|
bullet = (i, max) => (" " * (digits(max + 1) - digits(i + 1) - 1)) + s"${i + 1}.",
|
||||||
|
continued = (i, max) => " " * digits(max + 1),
|
||||||
|
)(items*)
|
||||||
|
|
||||||
lazy val --- = Paragraph("\n" + ("-" * columns).yellow.toString + "\n")
|
lazy val --- = Paragraph("\n" + ("-" * columns).yellow.toString + "\n")
|
||||||
lazy val === = Paragraph("\n" + ("═" * columns).yellow.toString + "\n")
|
lazy val === = Paragraph("\n" + ("═" * columns).yellow.toString + "\n")
|
||||||
end Templates
|
end Templates
|
||||||
|
|
Loading…
Reference in a new issue