├── CODE_OF_CONDUCT.md ├── CHANGELOG.md ├── templates ├── workspace │ ├── crates │ │ └── PROJECT_NAME │ │ │ ├── _Cargo.toml │ │ │ └── src │ │ │ └── main.rs │ ├── README.md │ └── _Cargo.toml └── default │ ├── _Cargo.toml │ ├── README.md │ └── src │ └── main.rs ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── cliff.toml └── src └── main.rs /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The Code of Conduct for this repository can be found online at [zed.dev/docs/code-of-conduct](https://zed.dev/docs/code-of-conduct). 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [0.1.2] - 2024-08-05 6 | 7 | ### 🚀 Features 8 | 9 | - Automate CHANGELOG generation with `cliff` (#16) 10 | 11 | 12 | -------------------------------------------------------------------------------- /templates/workspace/crates/PROJECT_NAME/_Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "PROJECT_NAME" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "PROJECT_NAME" 9 | path = "src/main.rs" 10 | 11 | [dependencies] 12 | gpui.workspace = true 13 | -------------------------------------------------------------------------------- /templates/default/_Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "PROJECT_NAME" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | gpui = { git = "https://github.com/zed-industries/zed" } 8 | # smallvec is included here for convenience, it is used by gpui when creating 9 | # components that can have children. uncomment this line or 10 | # use `cargo add smallvec` to add it to your project 11 | #smallvec = "1.13.2" 12 | -------------------------------------------------------------------------------- /templates/default/README.md: -------------------------------------------------------------------------------- 1 | # PROJECT_NAME 2 | 3 | Created with Create GPUI App. 4 | 5 | - [`gpui`](https://www.gpui.rs/) 6 | - [GPUI documentation](https://github.com/zed-industries/zed/tree/main/crates/gpui/docs) 7 | - [GPUI examples](https://github.com/zed-industries/zed/tree/main/crates/gpui/examples) 8 | 9 | ## Usage 10 | 11 | - Ensure Rust is installed - [Rustup](https://rustup.rs/) 12 | - Run your app with `cargo run` 13 | -------------------------------------------------------------------------------- /templates/workspace/README.md: -------------------------------------------------------------------------------- 1 | # PROJECT_NAME 2 | 3 | Created with Create GPUI App. 4 | 5 | - [`gpui`](https://www.gpui.rs/) 6 | - [GPUI documentation](https://github.com/zed-industries/zed/tree/main/crates/gpui/docs) 7 | - [GPUI examples](https://github.com/zed-industries/zed/tree/main/crates/gpui/examples) 8 | 9 | ## Usage 10 | 11 | - Ensure Rust is installed - [Rustup](https://rustup.rs/) 12 | - Run your app with `cargo run` 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | `create-gpui-app` is open source software, feel free to contribute. 2 | 3 | Contributions should be made through Pull Requests. 4 | 5 | Low effort issues & contributions, bike shedding, and overly-specific contributions will be closed. 6 | 7 | Feel free to organize discussion in another channel, such as our Zed channels or our community Discord. 8 | 9 | All interations in this project are subject to our [Code of Conduct](https://zed.dev/docs/code-of-conduct), also duplicated in this repository. 10 | -------------------------------------------------------------------------------- /templates/workspace/_Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["crates/PROJECT_NAME"] 3 | default-members = ["crates/PROJECT_NAME"] 4 | resolver = "2" 5 | 6 | [workspace.dependencies] 7 | PROJECT_NAME = { path = "crates/PROJECT_NAME" } 8 | 9 | gpui = { git = "https://github.com/zed-industries/zed" } 10 | # smallvec is included here for convenience, it is used by gpui when creating 11 | # components that can have children. uncomment this line or 12 | # use `cargo add smallvec` to add it to your project 13 | #smallvec = "1.13.2" 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "create-gpui-app" 3 | description = "A CLI tool to create a new gpui app." 4 | repository = "https://github.com/zed-industries/create-gpui-app" 5 | authors = [ 6 | "Nate Butler ", 7 | "Zed Industries (@zed-industries)", 8 | ] 9 | version = "0.1.5" 10 | edition = "2021" 11 | license = "MIT" 12 | pre-release-hook = ["git", "cliff", "-o", "CHANGELOG.md", "--tag", "{{version}}" ] 13 | 14 | [dependencies] 15 | clap = { version = "4.5.4", features = ["derive"] } 16 | include_dir = "0.7.4" 17 | 18 | [[bin]] 19 | name = "create-gpui-app" 20 | path = "src/main.rs" 21 | -------------------------------------------------------------------------------- /templates/workspace/crates/PROJECT_NAME/src/main.rs: -------------------------------------------------------------------------------- 1 | use gpui::*; 2 | 3 | struct HelloWorld { 4 | text: SharedString, 5 | } 6 | 7 | impl Render for HelloWorld { 8 | fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { 9 | div() 10 | .flex() 11 | .bg(rgb(0x2e7d32)) 12 | .size_full() 13 | .justify_center() 14 | .items_center() 15 | .text_xl() 16 | .text_color(rgb(0xffffff)) 17 | .child(format!("Hello, {}!", &self.text)) 18 | } 19 | } 20 | 21 | fn main() { 22 | Application::new().run(|cx: &mut App| { 23 | cx.open_window(WindowOptions::default(), |_, cx| { 24 | cx.new(|_cx| HelloWorld { 25 | text: "World".into(), 26 | }) 27 | }) 28 | .unwrap(); 29 | }); 30 | } -------------------------------------------------------------------------------- /templates/default/src/main.rs: -------------------------------------------------------------------------------- 1 | use gpui::*; 2 | 3 | struct HelloWorld { 4 | text: SharedString, 5 | } 6 | 7 | impl Render for HelloWorld { 8 | fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { 9 | div() 10 | .flex() 11 | .bg(rgb(0x2e7d32)) 12 | .size_full() 13 | .justify_center() 14 | .items_center() 15 | .text_xl() 16 | .text_color(rgb(0xffffff)) 17 | .child(format!("Hello, {}!", &self.text)) 18 | } 19 | } 20 | 21 | fn main() { 22 | Application::new().run(|cx: &mut App| { 23 | cx.open_window(WindowOptions::default(), |_, cx| { 24 | cx.new(|_cx| HelloWorld { 25 | text: "World".into(), 26 | }) 27 | }) 28 | .unwrap(); 29 | }); 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Zed Industries 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create GPUI App 2 | 3 | Create a new [GPUI](https://www.gpui.rs/) app in a single command. 4 | 5 | GPUI is a fast, productive UI framework for Rust from the creators of [Zed](https://zed.dev/). 6 | 7 | ## Quick Start 8 | 9 | ```sh 10 | cargo install create-gpui-app 11 | create-gpui-app --name my-app 12 | cd my-app 13 | ``` 14 | 15 | ## Creating an App 16 | 17 | **You'll need to have Rust and Cargo installed on your machine**. You can install Rust through [rustup](https://rustup.rs/). 18 | 19 | To create a new app, run: 20 | 21 | ```sh 22 | create-gpui-app --name my-app 23 | cd my-app 24 | ``` 25 | 26 | By default this will output: 27 | 28 | ``` 29 | my-app 30 | ├── src 31 | │ ├── main.rs 32 | ├── Cargo.toml 33 | ├── README.md 34 | ``` 35 | 36 | To set up your application as a workspace, run: 37 | 38 | ```sh 39 | create-gpui-app --workspace --name my-app 40 | cd my-app 41 | ``` 42 | 43 | This will output a directory structure like this: 44 | 45 | ``` 46 | my-app 47 | ├── Cargo.toml 48 | ├── crates 49 | │   └── my-app 50 | │   ├── Cargo.toml 51 | │   └── src 52 | │   └── main.rs 53 | └── README.md 54 | ``` 55 | 56 | `create-gpui-app` with no arguments will create a new app called `gpui-app`. 57 | 58 | ### Running the App 59 | 60 | - During development: `cargo run` 61 | - For production/performance testing: `cargo build --release` 62 | 63 | ### Troubleshooting 64 | See the [zed development troubleshooting guide](https://github.com/zed-industries/zed/blob/main/docs/src/development/macos.md#troubleshooting) for assistance with common errors. 65 | 66 | ## Contributing 67 | 68 | Your contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for more details. 69 | 70 | ## License 71 | 72 | `create-gpui-app` is open source software [licensed as MIT](LICENSE). 73 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | [changelog] 2 | # template for the changelog footer 3 | header = """ 4 | # Changelog\n 5 | All notable changes to this project will be documented in this file.\n 6 | """ 7 | # template for the changelog body 8 | # https://keats.github.io/tera/docs/#introduction 9 | body = """ 10 | {% if version %}\ 11 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 12 | {% else %}\ 13 | ## [unreleased] 14 | {% endif %}\ 15 | {% for group, commits in commits | group_by(attribute="group") %} 16 | ### {{ group | striptags | trim | upper_first }} 17 | {% for commit in commits %} 18 | - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ 19 | {% if commit.breaking %}[**breaking**] {% endif %}\ 20 | {{ commit.message | upper_first }}\ 21 | {% endfor %} 22 | {% endfor %}\n 23 | """ 24 | # template for the changelog footer 25 | footer = """ 26 | 27 | """ 28 | # remove the leading and trailing s 29 | trim = true 30 | # postprocessors 31 | postprocessors = [ 32 | { pattern = '', replace = "https://github.com/zed-industries/create-gpui-app" }, # replace repository URL 33 | ] 34 | 35 | [git] 36 | # parse the commits based on https://www.conventionalcommits.org 37 | conventional_commits = true 38 | # filter out the commits that are not conventional 39 | filter_unconventional = true 40 | # process each line of a commit as an individual commit 41 | split_commits = false 42 | # regex for preprocessing the commit messages 43 | commit_preprocessors = [ 44 | # Replace issue numbers 45 | #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, 46 | # Check spelling of the commit with https://github.com/crate-ci/typos 47 | # If the spelling is incorrect, it will be automatically fixed. 48 | #{ pattern = '.*', replace_command = 'typos --write-changes -' }, 49 | ] 50 | # regex for parsing and grouping commits 51 | commit_parsers = [ 52 | { message = "^feat", group = "🚀 Features" }, 53 | { message = "^fix", group = "🐛 Bug Fixes" }, 54 | { message = "^doc", group = "📚 Documentation" }, 55 | { message = "^perf", group = "⚡ Performance" }, 56 | { message = "^refactor", group = "🚜 Refactor" }, 57 | { message = "^style", group = "🎨 Styling" }, 58 | { message = "^test", group = "🧪 Testing" }, 59 | { message = "^chore\\(release\\): prepare for", skip = true }, 60 | { message = "^chore\\(deps.*\\)", skip = true }, 61 | { message = "^chore\\(pr\\)", skip = true }, 62 | { message = "^chore\\(pull\\)", skip = true }, 63 | { message = "^chore|^ci", group = "⚙️ Miscellaneous Tasks" }, 64 | { body = ".*security", group = "🛡️ Security" }, 65 | { message = "^revert", group = "◀️ Revert" }, 66 | ] 67 | # protect breaking changes from being skipped due to matching a skipping commit_parser 68 | protect_breaking_commits = false 69 | # filter out the commits that are not matched by commit parsers 70 | filter_commits = false 71 | # regex for matching git tags 72 | # tag_pattern = "v[0-9].*" 73 | # regex for skipping tags 74 | # skip_tags = "" 75 | # regex for ignoring tags 76 | # ignore_tags = "" 77 | # sort the tags topologically 78 | topo_order = false 79 | # sort the commits inside sections by oldest/newest order 80 | sort_commits = "oldest" 81 | # limit the number of commits included in the changelog. 82 | # limit_commits = 42 83 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use include_dir::{include_dir, Dir, DirEntry}; 3 | use std::{ 4 | fs, 5 | io::Result, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | static TEMPLATES_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/templates"); 10 | static DEFAULT_PROJECT_NAME: &str = "gpui-app"; 11 | 12 | #[derive(Parser, Debug)] 13 | #[clap(version, about, long_about = None)] 14 | struct Args { 15 | /// Name of the new project 16 | #[clap(short, long, default_value = DEFAULT_PROJECT_NAME, value_parser = parse_name)] 17 | name: Option, 18 | /// Setup your project as a workspace 19 | #[clap(short, long)] 20 | workspace: bool, 21 | /// Setup a zed style project 22 | #[clap(short, long)] 23 | zed: bool, 24 | } 25 | 26 | fn parse_name(name: &str) -> Result { 27 | if name.is_empty() { 28 | Ok(DEFAULT_PROJECT_NAME.to_string()) 29 | } else { 30 | Ok(name.to_string()) 31 | } 32 | } 33 | 34 | fn copy_and_replace( 35 | destination_path: &mut PathBuf, 36 | is_workspace: bool, 37 | is_zed_styled: bool, 38 | project_name: &str, 39 | source_dir: &Dir, 40 | ) -> Result<()> { 41 | const WORD_TO_REPLACE: &str = "PROJECT_NAME"; 42 | if destination_path.file_name().unwrap() == WORD_TO_REPLACE { 43 | destination_path.set_file_name(project_name) 44 | }; 45 | 46 | fs::create_dir_all(&destination_path)?; 47 | for entry in source_dir.entries() { 48 | let relative_path = entry.path().strip_prefix(source_dir.path()).unwrap(); 49 | let mut entry_path = destination_path.to_owned().join(relative_path); 50 | match entry { 51 | DirEntry::Dir(dir) => copy_and_replace( 52 | &mut entry_path, 53 | is_workspace, 54 | is_zed_styled, 55 | project_name, 56 | &dir, 57 | )?, 58 | DirEntry::File(file) => { 59 | if let Some(content) = file.contents_utf8() { 60 | let mut content_string = content.to_string(); 61 | match file.path() { 62 | path if path.file_name().unwrap() == "main.rs" => { 63 | if is_zed_styled { 64 | entry_path.set_file_name(format!("{}.rs", project_name)) 65 | }; 66 | } 67 | path if path.file_name().unwrap() == "_Cargo.toml" => { 68 | if is_zed_styled { 69 | let additional_content = "\n[[bin]]\nname = \"PROJECT_NAME\"\npath = \"src/PROJECT_NAME.rs\""; 70 | if is_workspace { 71 | if path 72 | .components() 73 | .any(|component| component.as_os_str() == "crates") 74 | { 75 | content_string = content_string.replace( 76 | "src/main.rs", 77 | format!("src/{}.rs", project_name).as_str(), 78 | ); 79 | } 80 | } else { 81 | content_string.push_str(additional_content); 82 | } 83 | } 84 | 85 | entry_path.set_file_name("Cargo.toml") 86 | } 87 | _ => {} 88 | } 89 | content_string = content_string.replace(WORD_TO_REPLACE, project_name); 90 | fs::write(&entry_path, content_string)?; 91 | } 92 | } 93 | } 94 | } 95 | Ok(()) 96 | } 97 | 98 | /// Create a new gpui app 99 | fn main() -> Result<()> { 100 | let args = Args::parse(); 101 | 102 | let project_name = args.name.unwrap(); 103 | let mut project_path = Path::new(&project_name).to_owned(); 104 | let is_workspace = args.workspace; 105 | let is_zed_styled = args.zed; 106 | 107 | if project_path.exists() { 108 | println!("'{}' already exists.", project_name); 109 | return Ok(()); 110 | } 111 | 112 | copy_and_replace( 113 | &mut project_path, 114 | is_workspace, 115 | is_zed_styled, 116 | &project_name, 117 | if is_workspace { 118 | TEMPLATES_DIR.get_dir("workspace").unwrap() 119 | } else { 120 | TEMPLATES_DIR.get_dir("default").unwrap() 121 | }, 122 | )?; 123 | 124 | println!( 125 | "Successfully created new gpui app project '{}'", 126 | project_name 127 | ); 128 | 129 | Ok(()) 130 | } 131 | --------------------------------------------------------------------------------