{"id":51167434,"url":"https://github.com/yiheng-yu/iced-command-runner","last_synced_at":"2026-06-28T23:00:39.185Z","repository":{"id":366697821,"uuid":"1277444786","full_name":"Yiheng-Yu/iced-command-runner","owner":"Yiheng-Yu","description":"An iced widget that spawns child command line process, execute commands, streams its outputs, and displays them in a mocked terminal window.","archived":false,"fork":false,"pushed_at":"2026-06-25T11:06:21.000Z","size":355,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-26T21:05:04.333Z","etag":null,"topics":["gui","iced","iced-rs","iced-rust","rust","widget"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Yiheng-Yu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-22T22:47:00.000Z","updated_at":"2026-06-25T11:06:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Yiheng-Yu/iced-command-runner","commit_stats":null,"previous_names":["yiheng-yu/iced-command-runner"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Yiheng-Yu/iced-command-runner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiheng-Yu%2Ficed-command-runner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiheng-Yu%2Ficed-command-runner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiheng-Yu%2Ficed-command-runner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiheng-Yu%2Ficed-command-runner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Yiheng-Yu","download_url":"https://codeload.github.com/Yiheng-Yu/iced-command-runner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiheng-Yu%2Ficed-command-runner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34869004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["gui","iced","iced-rs","iced-rust","rust","widget"],"created_at":"2026-06-26T21:00:13.831Z","updated_at":"2026-06-27T22:00:32.640Z","avatar_url":"https://github.com/Yiheng-Yu.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n`iced_command_runner` is an iced widget for used for executing terminal commands and stream outputs. \n\nLinks to documentation:\n\n- [Crates.io](https://crates.io/crates/iced_command_runner)\n\n- [Doc for the latest release](https://docs.rs/iced_command_runner/latest/iced_command_runner/)\n\n\nNote that this is still a bit of work-in-progress. Current API is done and won't change (at least too much) in the forseeable future. There might be some small bugs here and there but the crate itself should be functional for majorities of the use case. They will get fixed one by one in my free time.\n\nIf you found any issues, please do feel free to [submit them](https://github.com/Yiheng-Yu/iced-command-runner/issues) or do some [pull requests](https://github.com/Yiheng-Yu/iced-command-runner/pulls) on your fixes/ improvements, thanks in advance.\n\n`iced` it self is also in active development. I will try my best to udpate this crate trakcing the most recent stable release of `iced` as soon as I could.\n\nCheers.\n\n## Overview\n\nThe main stuff of the `iced_command_runner` crate is `CommandRunner`, it is a struct that holds data for `program` and `args` to execute, as well as `buffer` that stores data streamed from previous and currently running processes. `CommandRunner` provides a default `create_view()` function that renders terminal output in ways similar to a shell.\n\nCommand execution is trigged by `Event::Execute` enum.\n\nInstance of CommandRunner itself does not work on its own, as you will need to incorporate it into other iced widgets to make it work.\n\nFeel free referring to the `demo.rs` in the provided `examples` for a working example.\n\n## Getting started\n\nHere is the easiest way of doing it:\n\n### **Step 1.** Add `Event` and `CommandRunner` to your application\n\nAdd `Event` to your application's `Message`:\n\n```rust\nuse iced_command_runner::{\n    CommandRunner,\n    event::Event\n};\n\n#[derive(Clone)]\nenum Message {\n    Runner(Event),  // used for passing command execution events\n    ...  // the rest of messages in your module\n}\n```\n\nAdd `CommandRunner`:\n\n```rust\nstruct App {\n    runner: CommandRunner,\n    ..  // the rest of the app\n}\n```\n\nTo init a new `CommandRunner` instance, use `CommandRunner::new` or `create_runner`.\nExample:\n\n```rust\nuse iced_command_runner::create_runner;\n\nimpl App {\n    pub fn new(\n        ..  // your codes\n        command: impl Into\u003cString\u003e, \n        args: impl IntoIterator\u003cItem = impl Into\u003cString\u003e\u003e\n    ) -\u003e Self {\n        let runner = create_runner(command, args);  // or CommandRunner::new(command, args)\n\n        Self {\n            ...  // your codes\n            runner: runner\n        }\n    }\n}\n```\n\n### **Step 2.** Set up your `view()` function\n\nThe `iced_command_runner` module comes with a complimentary helper function `terminal_container` that provides a good enough (at least for me) out-of-box solution that renders a default layout. A demo of the default layout can be found in `examples/default_layout` (`cargo run --example default_layout`).\n\nTo implement, simply put this in your `App`'s `view`:\n\n```rust\n...\nlet terminal_window = terminal_container(\u0026runner, Message::Runner, \"Run\", \"Clear history\")\n.spacing(..)\n.align_x(..)\n.align_y(..)\n.width(..)\n.height(..);\n...\n```\n\nAlternatively, you can set up your own layout as follow:\n\n#### Setting up your own layout\n\nThe `iced_command_runner` comes with several widgets that meant to work together, and you can arrange/ use them however you like. A demo of an example custom layout can be found in `examples/custom_layout` (`cargo run --example custom_layout`).\n\nHere's a walkthrough of the widgets:\n\n##### Live stream terminal output\n\n`CommandRunner` impls `crate_view()` method that renders a simulated termianl window that streams termianl output as they got produced:\n\n```rust\npub fn view\u003c'a\u003e(\u0026'a self) -\u003e Element\u003c'a, Message\u003e {\n    ...\n    let terminal_window = self.runner.crate_view(\n        Message::Runner\n        );\n    ...\n}\n```\n\n##### Triggering command execution\n\nCommand execution is triggered by `Event::Execute`. You need to manually implement widgets to trigger this event and pass the event to to the `CommandRunner` instance via the `CommandRunner::update` methdod.\n\nYou can also use the (very handy!) function that comes with `iced_command_runner`, `run_button`:\n\n```rust\n// in your view() function:\nlet trigger = run_button(\n    widget::text(\"Run!\"),   // or just plain \u0026str\n    \u0026self.runner.status,  // current status of the command runner \n    Message::Runner // wraps Event inside your application `Message`\n    )\n    .style(..);  // function that returns button::Style\n```\n\n###### Clear buffer history\n\n`clear_buffer_button()` returns a button that clears history stored in `CommandRunner`:\n\n```rust\nuse iced_command_runner::clear_buffer_button;\n...\n    // in your view() function:\n    let clear_history = clear_buffer_button(\n        widget::text(\"Wipe!\"),   // or just plain \u0026str\n        \u0026self.runner.status,  // current status of the command runner \n        Message::Runner // wraps Event inside your application `Message`\n        )\n        .style(..);  // function that returns button::Style\n```\n\n###### Executation status\n\n`iced_command_runner` also comes with another (very handy!! and very beautiful!!) widget that displays current execution status:\n\n```rust\nlet status_bar = status_bar::\u003cMessage\u003e(\u0026self.runner.status);\n```\n\n### **Step 3.** Set up `update()`\n\nSimply pass `event` back to the runner in your `update()` function, note that:\n\n1) the `update()` function NEEDS TO return `iced::Task` in order for this module to work\n\n2) you need to map the output of `runner.update()` back into your `Message`\n\nExample:\n\n```rust\npub fn update(\n    \u0026mut self, \n    message: Message\n) -\u003e iced::Task\u003cMessage\u003e {  // note that the `update()` function NEEDS TO return `iced::Task` in order for this module to work.\n    match message {\n        Message::Runner(event) =\u003e \n            self.runner.update(event)  // pass event to your runner\n            .map(Message::Runner),  //! don't forget to map the output of `runner.update()` back into your Message::Runner\n        // the rest of the update()\n        ... \n    }\n}\n```\n\nThat's it, there's nothing else you need to do, you don't need to set up `subscribe` etc. Just `run()` your application and see the result.\n\n## Styling\n\nTwo button functions (`clear_buffer_button` and `run_button`) return an `iced::widget::button::Button` instance that allows you to style them however you like.\n\nThe `CommandRunner` styling is pretty similar too:\n\n```rust\nlet runner = create_runner(command, args)\n    .prompt(\"yourname@localhost:\")\n    .text_size(8.0)\n    .background(..)\n    .border_idle(..)\n    .font(..);\n```\n\n## Dev notes\n\n### Alternative ways of rendering terminal output\n\nIf you don't like currently rendering setups and wishing to render things differently, you can implement your own `view()` functions that renders terminal outputs stored in the `buffer` field:\n\n```rust\nlet content: Vec\u003cElement\u003c'_, Message\u003e\u003e = runner.buffer.iter().map(\n    |output| {\n        match output {\n            // implement your own styling functions here\n            Terminal::StdIn(text) =\u003e ...,\n            Terminal::StdOut(text) =\u003e ...,\n            Terminal::StdErr(text) =\u003e ...,\n            Terminal::Error(text) =\u003e ...,\n        }\n    }\n    ).collect();\n```\n\n#### Carriage return\n\nBy default, `CommandRunner`'s `update()` function would automatically remove previous message if the new message starts with carriage return (`\\r`). This is particularly useful to correctly printing progress bars (like the one in python's `tqdm`). The current solution *works* but not as ideal. Proper handling this is planned for future releases.\n\n### Redirect terminal output elsewhere\n\nYou can use `CommandRunner` solely as a backend that simply executes command in a child process, and provides [`Stream`](https://docs.rs/futures-core/0.3.32/futures_core/stream/trait.Stream.html) that pushes data to your application. You can trigger the command execution \u0026 data streaming in your `update()` method and handling rendeirng etc. as you wish.\n\n## Similar crates\n\nThe main motivation for writing this is being able to create a GUI app that runs various pre-written CLI scripts in parallel. An alternative choice would be the [iced_term](https://github.com/Harzu/iced_term). It is a terminal emulator widget that more or less does the same job. The main difference between `iced_term` and the current crate is that `iced_command_runner` does not use `subscribe`. It spawns a child process, execute the command and shuts itself down after running it.\n\nThe good thing about this apporach is you don't have some command line process that constantly runs in the background. This means it's much easier to spawn lots and lots and lots of child processes that gets executed in parallel. The bad thing about this approach is also, you don't have some command line process that constantly runs in the background, which can be *really* handy some times. For example, implementing `stdin` turned out to be a bit of a headacahe.\n\n## Future (to do list)\n\n- Set up alternative displaying options (i.e., use text/ rich_text instead of iced::text_editor)\n- Set up 'terminate' button that terminates/ kills currently running process.\n- Set up support for `stdin`","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyiheng-yu%2Ficed-command-runner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyiheng-yu%2Ficed-command-runner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyiheng-yu%2Ficed-command-runner/lists"}