{"id":16964950,"url":"https://github.com/propensive/profanity","last_synced_at":"2025-03-22T14:31:01.175Z","repository":{"id":47438289,"uuid":"399208425","full_name":"propensive/profanity","owner":"propensive","description":"A library for realtime interactive terminal software in Scala","archived":false,"fork":false,"pushed_at":"2025-02-11T23:47:31.000Z","size":2442,"stargazers_count":7,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-10T19:53:48.443Z","etag":null,"topics":["console","curses","interactive","ncurses","scala","terminal","tty"],"latest_commit_sha":null,"homepage":"https://soundness.dev/profanity/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/propensive.png","metadata":{"files":{"readme":".github/readme.md","changelog":null,"contributing":".github/contributing.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-23T18:23:09.000Z","updated_at":"2025-02-11T23:47:35.000Z","dependencies_parsed_at":"2023-10-12T15:22:28.528Z","dependency_job_id":"d0d61c60-a3c9-48c9-a82d-08d0b49f39f1","html_url":"https://github.com/propensive/profanity","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fprofanity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fprofanity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fprofanity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fprofanity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/propensive","download_url":"https://codeload.github.com/propensive/profanity/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244217806,"owners_count":20417665,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["console","curses","interactive","ncurses","scala","terminal","tty"],"created_at":"2024-10-13T23:44:40.625Z","updated_at":"2025-03-22T14:31:01.156Z","avatar_url":"https://github.com/propensive.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[\u003cimg alt=\"GitHub Workflow\" src=\"https://img.shields.io/github/actions/workflow/status/propensive/profanity/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/propensive/profanity/actions)\n[\u003cimg src=\"https://img.shields.io/discord/633198088311537684?color=8899f7\u0026label=DISCORD\u0026style=for-the-badge\" height=\"24\"\u003e](https://discord.com/invite/MBUrkTgMnA)\n\u003cimg src=\"/doc/images/github.png\" valign=\"middle\"\u003e\n\n# Profanity\n\n__A library for realtime interactive terminal software__\n\n_Profanity_ makes it possible to write real-time applications that interact through the terminal by\nconverting STDIN into an event stream of keypresses.\n\n## Features\n\n- capture a TTY terminal's individual keypresses\n- interprets standard keys and control keys\n- simple interactive line editor supporting standard keypresses\n- simple interactive navigable menu\n\n\n## Availability\n\n\n\n\n\n\n\n## Getting Started\n\nJava does not provide native support for direct access to keypress events. While Standard input (`STDIN`)\nis accessible as an input stream, it is buffered until a newline is sent, which makes it impossible for a\nScala application to respond immediately to a keypress event, unless that keypress is the `Enter` key.\n\nProfanity uses the Java Native Interface (JNI) to turn off buffering so that each keypress is received as\nsoon as it happens.\n\n### Capturing the TTY\n\nBefore keypresses can be streamed as events, the TTY must be \"captured\". This is as simple as,\n```scala\nTty.capture {\n  // TTY operations are available here\n}\n```\nbut may not always succeed, for example if the JVM is not running inside a TTY, or if the TTY has already\nbeen captured, or if the JNI calls fail for another reason. These exceptions are checked.\n\n### Streaming keypresses\n\nWithin a `Tty.capture` block, a contextual `Tty` instance is made available, and `Tty.stream` can be\ncalled which will, by default, return a `LazyList[Keypress]`, where `Keypress` is Profanity's standard\nrepresentation of a keypress event. `Keypress` is an enumeration providing the following cases:\n- `Printable(c: Char)`, a keypress of a printable character, for example, `Shift+T` is `Printable('T')`\n- `Function(i: Int)`, a function key keypress, where `i` is the function key number, for example `F2` is `Function(2)`\n- `Ctrl(c: Char)`, a key combination of `Ctrl` and another character, for example, `Ctrl+C` is `Ctrl('c')`\n- a keypress of one of the following keys: `Enter`, `Escape`, `Tab`, `Backspace`, `Delete`, `PageUp`,\n  `PageDown`, `LeftArrow`, `RightArrow`, `UpArrow`, `DownArrow`, `CtrlLeftArrow`, `CtrlRightArrow`,\n  `CtrlUpArrow`, `CtrlDownArrow`, `End`, `Home`, `Insert`\n- `Escape(bytes: Byte*)`, any other escape sequence that hasn't been identified as one of the above\n\nAdditionally, the `Resize(rows: Int, cols: Int)` case represents the escape sequence that reports\nthe width and height of the console, and may be triggered by calling the `Tty.reportSize()`, or when a\n`SIGWINCH` event occurs, i.e. when the terminal window's size changes. So a `Resize` event can be handled\njust like a keypress event, where an action make be taken to ignore the event or redraw the screen, or\nsomething else.\n\n#### Alternative keyboards\n\n`Tty.stream` takes an optional type parameter, `K`, which determines the type used to represent keypress events,\nby resolving a contextual `Keyboard[K]` instance that interprets the bytes arriving in `STDIN` (either\nindividually, or as short sequences) as instances of `K`.\n\nBy default, only a single `Keyboard` given instance is defined, parameterized on the event type, `Keypress`,\nwhich means that `Tty.stream` may be invoked without specifying its type parameter. Nevertheless, it is\npossible to define alternative interpreters for the byte input to `STDIN`.\n\n### Line Editor\n\nProfanity provides a simple line editor that may be used inside a `Tty.capture` block, and will handle\ncommon keypresses that may be used inside an editable field, including printable characters, arrow keys,\n`Ctrl` key combinations such as `Ctrl+W` (delete word) and `Ctrl+U` (delete line).\n\nThis can be invoked inside a `Tty.capture` block with `LineEditor.ask()`, or pre-filled with an initial\nvalue, `LineEditor.ask(\"initial\")`.\n\n#### Rendering\n\nIt is also possible to control how the line editor displays the text by overriding the default `render`\nmethod of `LineEditor.ask`. This parameter is a `String =\u003e String` lambda, mapping from the current\nvalue (i.e. the accumulation of several keypresses into a string) to the value that should be printed.\n\nWhile `render` would normally use the identity function, it is possible to use the `LineEditor.concealed`\nmethod to display each character as an `*` or even to include ANSI escape characters (e.g. from\n[Escapade](https://github.com/propensive/escapade)) in the string. It is executed, and the line is\nredrawn, for every keypress.\n\n### Menus\n\nA simple menu of two or more options is provided through the `SelectMenu` object. Its `ask` method,\nwhich can only be called inside a `Tty.capture` block, will present a set of options to the user, of\nwhich exactly one must be chosen using the arrow keys and the `Enter` key. In addition to the list of\nchoices being supplied as the first parameter of `SelectMenu.ask`, the initially-selected value may be\nprovided as the second.\n\nAdditionally, two lambdas, `renderOn` and `renderOff`, allow the rendering of each menu item (whether\n_on_ or _off_) to be specified.\n\n### Limitations\n\nProfanity does not currently support Windows.\n\n\n\n\n\n## Status\n\nProfanity is classified as __fledgling__. For reference, Soundness projects are\ncategorized into one of the following five stability levels:\n\n- _embryonic_: for experimental or demonstrative purposes only, without any guarantees of longevity\n- _fledgling_: of proven utility, seeking contributions, but liable to significant redesigns\n- _maturescent_: major design decisions broady settled, seeking probatory adoption and refinement\n- _dependable_: production-ready, subject to controlled ongoing maintenance and enhancement; tagged as version `1.0.0` or later\n- _adamantine_: proven, reliable and production-ready, with no further breaking changes ever anticipated\n\nProjects at any stability level, even _embryonic_ projects, can still be used,\nas long as caution is taken to avoid a mismatch between the project's stability\nlevel and the required stability and maintainability of your own project.\n\nProfanity is designed to be _small_. Its entire source code currently consists\nof 448 lines of code.\n\n## Building\n\nProfanity will ultimately be built by Fury, when it is published. In the\nmeantime, two possibilities are offered, however they are acknowledged to be\nfragile, inadequately tested, and unsuitable for anything more than\nexperimentation. They are provided only for the necessity of providing _some_\nanswer to the question, \"how can I try Profanity?\".\n\n1. *Copy the sources into your own project*\n   \n   Read the `fury` file in the repository root to understand Profanity's build\n   structure, dependencies and source location; the file format should be short\n   and quite intuitive. Copy the sources into a source directory in your own\n   project, then repeat (recursively) for each of the dependencies.\n\n   The sources are compiled against the latest nightly release of Scala 3.\n   There should be no problem to compile the project together with all of its\n   dependencies in a single compilation.\n\n2. *Build with [Wrath](https://github.com/propensive/wrath/)*\n\n   Wrath is a bootstrapping script for building Profanity and other projects in\n   the absence of a fully-featured build tool. It is designed to read the `fury`\n   file in the project directory, and produce a collection of JAR files which can\n   be added to a classpath, by compiling the project and all of its dependencies,\n   including the Scala compiler itself.\n   \n   Download the latest version of\n   [`wrath`](https://github.com/propensive/wrath/releases/latest), make it\n   executable, and add it to your path, for example by copying it to\n   `/usr/local/bin/`.\n\n   Clone this repository inside an empty directory, so that the build can\n   safely make clones of repositories it depends on as _peers_ of `profanity`.\n   Run `wrath -F` in the repository root. This will download and compile the\n   latest version of Scala, as well as all of Profanity's dependencies.\n\n   If the build was successful, the compiled JAR files can be found in the\n   `.wrath/dist` directory.\n\n## Contributing\n\nContributors to Profanity are welcome and encouraged. New contributors may like\nto look for issues marked\n[beginner](https://github.com/propensive/profanity/labels/beginner).\n\nWe suggest that all contributors read the [Contributing\nGuide](/contributing.md) to make the process of contributing to Profanity\neasier.\n\nPlease __do not__ contact project maintainers privately with questions unless\nthere is a good reason to keep them private. While it can be tempting to\nrepsond to such questions, private answers cannot be shared with a wider\naudience, and it can result in duplication of effort.\n\n## Author\n\nProfanity was designed and developed by Jon Pretty, and commercial support and\ntraining on all aspects of Scala 3 is available from [Propensive\nO\u0026Uuml;](https://propensive.com/).\n\n\n\n## Name\n\nA __profanity__ is an expletive or curse-word, and _Profanity_ imitates many of the features of the popular terminal library, Curses.\n\nIn general, Soundness project names are always chosen with some rationale,\nhowever it is usually frivolous. Each name is chosen for more for its\n_uniqueness_ and _intrigue_ than its concision or catchiness, and there is no\nbias towards names with positive or \"nice\" meanings—since many of the libraries\nperform some quite unpleasant tasks.\n\nNames should be English words, though many are obscure or archaic, and it\nshould be noted how willingly English adopts foreign words. Names are generally\nof Greek or Latin origin, and have often arrived in English via a romance\nlanguage.\n\n## Logo\n\nThe logo shows the horns of a devil; the epitome of the profane.\n\n## License\n\nProfanity is copyright \u0026copy; 2025 Jon Pretty \u0026 Propensive O\u0026Uuml;, and\nis made available under the [Apache 2.0 License](/license.md).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fprofanity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpropensive%2Fprofanity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fprofanity/lists"}