├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo ├── 01-terminal.md ├── 02-code.md └── 03-try.md ├── docs ├── 01-intro.md ├── 02-terminal-shell.md ├── 03-terminal-repl.md ├── 04-notebook-code-bash.md ├── 05-notebook-code-others.md ├── 06-explorer.md ├── 07-options.md ├── 08-reference.md └── 09-feedback.md ├── src ├── bootstrap.sh ├── execute.sh ├── explorer.sh ├── handler.sh ├── helpers.sh ├── init.sh ├── notebook.sh ├── parser.sh ├── printer.sh ├── warning.sh ├── zellij.sh └── zellij │ ├── layout.kdl │ └── loader.sh └── wandersage /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.1 - 2024-11-12 4 | 5 | - Correctly set system or bootstrapped binaries path, `$ZEL` & `$GUM` 6 | - Handle folders with no notebooks in it 7 | 8 | ## 0.2.0 - 2024-11-07 9 | 10 | - Initally set `$NOTEBOOK` and `$NOTEBOOKS` variables for interactive terminal 11 | - Fixed 'bad substition' confirmation bug 12 | - Bootstrapping now works for Linux and MacOS, with downloading for the correct arch 13 | - Set notebook name when notebook explorer is only shown 14 | 15 | ## 0.1.0 - 2024-08-15 16 | 17 | Initial release. Booya! 18 | 19 | - Bootstrapping Zellij & Gum dependencies 20 | - Notebook and interactive terminal pane 21 | - Global scratchpad terminal 22 | - Markdown rendering 23 | - Send commands/code to interactive terminal 24 | - User confirmation for sending commands/code 25 | - Execute inline code: Bash, JavaScript, TypeScript, Python, Perl 26 | - Warning/confirmation for inline code 27 | - Notebooks explorer 28 | - Command line options: `--help`, `--no-confirm`, `--no-warn` 29 | - Interactive documentation and demo 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Sebastian Senf 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 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 | # Wandersage 2 | 3 | A interactive notebook for the terminal, powered by Bash, [Zellij](https://zellij.dev) and [Gum](https://github.com/charmbracelet/gum). 4 | 5 | [demo.webm](https://github.com/user-attachments/assets/0dd20673-5fdb-4f97-8b13-515f12bfafd5) 6 | 7 | ## Getting started 8 | 9 | The fastest way to try out Wandersage, is by cloning the repo and just run it. This downloads the dependencies Zellij and Gum, and loads the interactive [documentation](./docs/): 10 | 11 | ```shell 12 | git clone https://github.com/mustardamus/wandersage.git 13 | cd wandersage 14 | ./wandersage 15 | ``` 16 | 17 | After learning about the features, write your own notebook and load it: 18 | 19 | ```shell 20 | ./wandersage path/to/my-notebook.md 21 | ``` 22 | 23 | ## Features 24 | 25 | ### Bootstrapping dependencies for Linux and MacOS 26 | 27 | Zellij and Gum binaries are downloaded to a local folder, no installation or `sudo` needed. Only depends on `bash`, `curl` and `tar`. 28 | 29 | ### Multiplexing 30 | 31 | Fancy word for two terminals, side by side, provided by Zellij. On the left side we have the notebook, and on the right side we have the interactive terminal. 32 | 33 | ### Global scratchpad terminal 34 | 35 | Since the interactive terminal is cleared with each notebook loaded, there is a global overlaying terminal for you to use. 36 | 37 | ### Markdown 38 | 39 | Notebooks are written in Markdown, and rendered by Gum. Wandersage provides two extensions to code blocks: 40 | 41 | - `:terminal` to send commands or code to the interactive terminal 42 | - `:notebook` to execute inline code for extending the notebook rendering 43 | 44 | ### Interactive terminal 45 | 46 | The notebook can interact with the terminal, by sending commands or code via simulated key strokes - just as you would type or copy/paste the content. Since it is a real terminal, it is possible to enter REPLs, Docker, or even remote shells via SSH. 47 | 48 | ### Confirmation for sending commands or code 49 | 50 | Before sending commands or code to the interactive terminal, the user has to confirm it. This makes the notebook a nice step-by-step experience. It can be turned off with `--no-confirm`. 51 | 52 | ### Notebook inline code 53 | 54 | Notebooks can be extended by inline code in Bash, JavaScript, TypeScript, Python and Perl. If Bash is used, the inline code has access to Wandersage's variables and functions. This way, for example, you could load additional resources or request user input. 55 | 56 | ### Inline code warning 57 | 58 | If the loaded notebook has inline code, it is shown, along with a warning, before the notebook starts to render. This way, the user can review the code. It can be turned off with `--no-warn`. 59 | 60 | ### Notebooks explorer 61 | 62 | If there are multiple `.md` notebook files in the same directory, a menu of all these files is shown at the end of each notebook. 63 | 64 | ### Interactive documentation 65 | 66 | The documentation for Wandersage itself is a collection of interactive notebooks. Just run it without any arguments: `./wandersage`. 67 | -------------------------------------------------------------------------------- /demo/01-terminal.md: -------------------------------------------------------------------------------- 1 | # Demo - Interactive Terminal 2 | 3 | ## Markdown 4 | 5 | What you see here is a notebook, written in Markdown. It has multiple code blocks. We can send them to the interactive terminal on the right. 6 | 7 | ## Send shell commands 8 | 9 | ```shell:terminal 10 | sleep 2 11 | echo "Wake up, Neo..." 12 | ``` 13 | 14 | ## Run code in a REPL 15 | 16 | ```shell:terminal 17 | node 18 | ``` 19 | 20 | ```javascript:terminal 21 | console.log("The Matrix probably has a JS port..."); 22 | process.exit(); 23 | ``` 24 | 25 | ## Notebooks Explorer 26 | 27 | Below you see the notebooks explorer. `Return` loads the next notebook. 28 | -------------------------------------------------------------------------------- /demo/02-code.md: -------------------------------------------------------------------------------- 1 | # Demo - Inline Code 2 | 3 | We can make notebooks even more interactive by using inline code. 4 | 5 | ## Bash inline code 6 | 7 | Using `bash`, we can tap into Wandersage's variables and functions: 8 | 9 | ```bash 10 | user_input="$($GUM input --value 'Hi there!')" 11 | print_in_borders "User input in notebook: $user_input" 12 | ``` 13 | 14 | ```bash:notebook 15 | user_input="$($GUM input --value 'Hi there!')" 16 | print_in_borders "User input in notebook: $user_input" 17 | ``` 18 | 19 | ## Other inline code 20 | 21 | We can write inline code with `JavaScript`, `TypeScript`, `Python` and `Perl`, as long as a interpreter is installed on the system: 22 | 23 | ```python 24 | print("Hello from Python!") 25 | ``` 26 | 27 | ```python:notebook 28 | print("Hello from Python!") 29 | ``` 30 | -------------------------------------------------------------------------------- /demo/03-try.md: -------------------------------------------------------------------------------- 1 | # Demo - Try it yourself! 2 | 3 | All you need to get started is `bash`, `git` and a internet connection. The dependencies `Zellij` and `Gum` are conveniently downloaded by Wandersage, no installation needed. 4 | 5 | ```shell:terminal 6 | cd /tmp 7 | git clone https://github.com/mustardamus/wandersage.git 8 | cd wandersage 9 | ./wandersage 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/01-intro.md: -------------------------------------------------------------------------------- 1 | # Wandersage 2 | 3 | A interactive and extendable notebook for the terminal. 4 | 5 | Scroll up and down with `Alt+Up` and `Alt+Down`. `Ctrl+Q` to quit. 6 | 7 | ## Written in markdown 8 | 9 | Notebooks are ordinary markdown `*.md` files which are rendered by [Gum](https://github.com/charmbracelet/gum), which gives you all the features you would expect: headings, lists, code highlighting, etc. 10 | 11 | ## Interactive terminal 12 | 13 | Next to the notebook is the terminal. This multi-pane feature is provided by [Zellij](https://zellij.dev). It is interactive, which means you can send commands and code from the notebook via simulated keystrokes to it. 14 | 15 | You can also use the terminal! Focus via `Alt+Right`, and back to the notebook with `Alt+Left`. 16 | 17 | ## Scratchpad terminal 18 | 19 | The interactive terminal is cleared with each new notebook loaded. For a persistent terminal with your default shell, hit `Alt+S` to toggle the scratchpad. 20 | 21 | ## Inline notebook code 22 | 23 | Notebooks can be extended by inserting scripting code (Bash, Python, JS, etc.) into the rendering process. That way, requesting additional input from the user or fetching remote data, for example, becomes possible. 24 | 25 | ## Notebooks explorer 26 | 27 | Below you see the notebooks explorer. It is visible if other `*.md` files are in the same folder as the notebook that is currently loaded. 28 | 29 | For convenience, the next notebook in the list is already selected. Just hit `Enter` to continue this documentation and dive deeper into the features. 30 | -------------------------------------------------------------------------------- /docs/02-terminal-shell.md: -------------------------------------------------------------------------------- 1 | # Terminal - Shell Commands 2 | 3 | ## Default code blocks 4 | 5 | Markdown code blocks don't do anything by default, they are just highlighted and printed. For example this ` ```shell` code block: 6 | 7 | ```shell 8 | echo "Hello, world!" 9 | ``` 10 | 11 | ## Sending shell commands to the terminal 12 | 13 | If you append `:terminal` to the code block definition, the shell command will be sent to the interactive terminal, after being approved by the user. For example, this ` ```shell:terminal` code block: 14 | 15 | ```shell:terminal 16 | echo "Hello, world!" 17 | ``` 18 | 19 | ## Long running commands 20 | 21 | The interactive terminal is locked while the command is running, i.e. the command prompt is available again. For example: 22 | 23 | ```shell:terminal 24 | sleep 3 25 | echo "Wake up, Neo..." 26 | ``` 27 | 28 | ## Turn off confirmations 29 | 30 | Before any shell commands are run, as you've seen, the user is asked to confirm it. This is the default behavior for two reason: 31 | 32 | - makes a step-by-step notebook, so the reader can follow what is happening 33 | - security, so notebooks can't execute unreviewed commands 34 | 35 | However, you can turn off this behavior by passing `--no-confirm` as a command line argument: 36 | 37 | ```shell 38 | # will run commands in this notebook without confirmation 39 | ./wandersage --no-confirm docs/02-terminal-shell.md 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/03-terminal-repl.md: -------------------------------------------------------------------------------- 1 | # Terminal - REPL Example 2 | 3 | You are not limited to just sending shell commands! Append `:terminal` to any code block, and it will be sent to the interactive terminal. 4 | 5 | This way, you can interact with REPL's, Docker, or even remote shells over SSH. 6 | 7 | The notebook must provide the commands to enter the REPL or other shell, and to return to the base shell, so the next notebook can work with it. 8 | 9 | ## Python REPL example 10 | 11 | First we need to send the shell command to enter the Python REPL, which we guess are either at `python` or `python3`. 12 | 13 | ```shell:terminal 14 | command -v python && python || python3 15 | ``` 16 | 17 | Now that we are inside the Python REPL, we send the code we want to execute with the markdown code block definition of ` ```python:terminal`. For this example, let's just print some text and exit the REPL: 18 | 19 | ```python:terminal 20 | print("Hello, Python!") 21 | exit() 22 | ``` 23 | 24 | ## How it works 25 | 26 | When sending a command to the interactive terminal, Wandersage somehow has to know when this command is finished, or if it entered a REPL (for example), to continue rendering the notebook. 27 | 28 | First, the last (non-whitespace) character of the terminals content is extracted. It may be `$`, for example. 29 | 30 | After the command is executed, the content of the terminal is dumped periodically, and the last character extracted. 31 | 32 | It is checked against the first extracted character, if they match, the command is deemed finished. 33 | 34 | Since the prompt character could change, when entering a REPL or another shell, it is also checked against these known characters: `#`, `$` and `>`. If one of them match, the command is deemed finished. 35 | -------------------------------------------------------------------------------- /docs/04-notebook-code-bash.md: -------------------------------------------------------------------------------- 1 | # Inline Notebook Code - Bash 2 | 3 | ## Introduction 4 | 5 | By appending `:notebook` to a code block definition, the code is run with the appropriate interpreter. For example ` ```bash:notebook` or ` ```python:notebook`, etc. 6 | 7 | Inline notebook code blocks are not printed, but executed. After the execution is finished, the notebook continues to render. 8 | 9 | ```bash 10 | echo "Hello from inline bash code in the notebook!" 11 | ``` 12 | 13 | ```bash:notebook 14 | echo "Hello from inline bash code in the notebook!" 15 | ``` 16 | 17 | ## User Confirmation 18 | 19 | The inline code and a warning message is shown before the notebook starts to render. That way, the user is given the opportunity to review the code, before running it. 20 | 21 | However, you can turn off this behavior by passing `--no-warn` as a command line argument: 22 | 23 | ```shell 24 | # will run inline code in this notebook without a warning 25 | ./wandersage --no-warn docs/04-notebook-code-bash.md 26 | ``` 27 | 28 | ## How it works 29 | 30 | The `bash` code is written to a temporary file and then `source`ed into the current script, which is the notebook renderer. This makes the already defined functions and variables available to the inline code. 31 | 32 | The temporary file is then deleted, and the notebook continues to render. 33 | 34 | The current working directory is the directory of the currently loaded notebook: 35 | 36 | ```shell 37 | echo "Current working directory: $(pwd)" 38 | echo "Loaded notebook path: $NOTEBOOK" 39 | ``` 40 | 41 | ```shell:notebook 42 | echo "Current working directory: $(pwd)" 43 | echo "Loaded notebook path: $NOTEBOOK" 44 | ``` 45 | 46 | Variables are also available in the terminal, but only the initial values. 47 | 48 | ```shell:terminal 49 | echo "$NOTEBOOKS" 50 | ``` 51 | 52 | See the documentation notebook [Reference](./08-reference.md) for a full list of functions and variables you have access to. 53 | 54 | ## Example accessing functions and variables 55 | 56 | First we read user input with Gum (the binary path is set in the variable `$GUM`), print back the input in the notebook, and run the command to print the input in the interactive terminal: 57 | 58 | ```bash 59 | export user_input="$($GUM input --value 'Hi there!')" 60 | print_in_borders "User input in notebook: $user_input" 61 | run_terminal_command "echo 'User input in terminal: $user_input'" 62 | ``` 63 | 64 | ```bash:notebook 65 | export user_input="$($GUM input --value 'Hi there!')" 66 | print_in_borders "User input in notebook: $user_input" 67 | run_terminal_command "echo 'User input in terminal: $user_input'" 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/05-notebook-code-others.md: -------------------------------------------------------------------------------- 1 | # Inline Notebook Code - Others 2 | 3 | ## How it works 4 | 5 | If the inline code is not `bash` or `shell`, but any other supported language, these steps are taken to execute the inline code: 6 | 7 | - find path of one of the defined interpreters for the language 8 | - show error message if no interpreter was found, or unknown language 9 | - write code to a temporary file 10 | - run temporary file with the interpreter 11 | - delete temporary file 12 | - continue to render the notebook 13 | 14 | The current working directory is the directory of the currently loaded notebook: 15 | 16 | ```python 17 | from pathlib import Path 18 | print("Current working directory:", Path.cwd()) 19 | ``` 20 | 21 | ```python:notebook 22 | from pathlib import Path 23 | print("Current working directory:", Path.cwd()) 24 | ``` 25 | 26 | ## Supported Languages 27 | 28 | | Code Block Definition | Interpreters | 29 | | --------------------- | -------------------------------------- | 30 | | bash, shell | bash | 31 | | python | python, python3, python3.5, python3.11 | 32 | | javascript, js | node, deno, bun | 33 | | typescript, ts | deno, bun | 34 | | perl | perl | 35 | 36 | The first interpreter from the list, which is installed on the system, is used to run the inline code. 37 | 38 | ## Examples 39 | 40 | ### python 41 | 42 | ```python:notebook 43 | print("Hello from Python!") 44 | ``` 45 | 46 | ### javascript 47 | 48 | ```javascript:notebook 49 | console.log("Hello from JavaScript!") 50 | ``` 51 | 52 | ### typescript 53 | 54 | ```typescript:notebook 55 | console.log("Hello from TypeScript!") 56 | ``` 57 | 58 | ### perl 59 | 60 | ```perl:notebook 61 | print("Hello from Perl!") 62 | ``` 63 | -------------------------------------------------------------------------------- /docs/06-explorer.md: -------------------------------------------------------------------------------- 1 | # Notebooks Explorer 2 | 3 | The notebooks explorer helps to navigate notebooks, if there are multiple `.md` in the same folder. 4 | 5 | It is shown if a folder is passed to Wandersage, and at the end of a notebook, but only if there are multiple `.md` notebooks in the same folder as the currently loaded notebook. 6 | 7 | The entries are sorted by the filename. So if you want a specific order of notebooks, lead the filenames with a number: `01-intro.md`, `02-next.md`, etc. 8 | 9 | For convenience, the next entry from the currently loaded notebook is pre-selected, so a `Return` press brings the user right to the next notebook in the list. If the loaded notebook is the last in the list, the first entry is pre-selected. 10 | 11 | You can also filter the entries by pressing `/` and entering the search term. `ESC` twice, to go back to the whole list. 12 | -------------------------------------------------------------------------------- /docs/07-options.md: -------------------------------------------------------------------------------- 1 | # Command Line Options 2 | 3 | ```shell 4 | ./wandersage [OPTIONS] Notebooks Folder / Notebook File 5 | ``` 6 | 7 | ## Options 8 | 9 | ### `--help` 10 | 11 | Show help text. 12 | 13 | ```shell 14 | ./wandersage --help 15 | ``` 16 | 17 | ### `--no-confirm` 18 | 19 | Turns off the confirmation for sending commands and code to the interactive terminal, so they are sent right away when rendering the notebook. 20 | 21 | ```shell 22 | ./wandersage --no-confirm notebook.md 23 | ``` 24 | 25 | ### `--no-warn` 26 | 27 | Turns off warning and confirmation for inline code, so it is executed right away when rendering the notebook. 28 | 29 | ```shell 30 | ./wandersage --no-warn notebook.md 31 | ``` 32 | 33 | ## Notebooks Folder 34 | 35 | If the last argument passed is a folder, the notebooks explorer is shown, which in turn will show all `.md` files in the folder. The user selects the notebook he wishes to view, and it is rendered. 36 | 37 | ```shell 38 | ./wandersage docs/ 39 | ``` 40 | 41 | ## Notebook File 42 | 43 | If the last argument is a `.md` notebook file, it is rendered. If there are multiple `.md` files in the parent folder, the notebooks explorer is shown, with the next entry from the current `.md` notebook selected. 44 | 45 | ```shell 46 | ./wandersage docs/01-intro.md 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/08-reference.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | ## Variables 4 | 5 | Variables are available both, in the notebook renderer and the interactive terminal. 6 | 7 | ### `$GUM` 8 | 9 | The full path of the [Gum](https://github.com/charmbracelet/gum) binary (either provided by the system, or bootstrapped). 10 | You can use it via `bash` inline code to do all kinds of things: request user input, format text, render markdown, etc. 11 | 12 | ### `$ZEL` 13 | 14 | The full path of [Zellij](https://zellij.dev) binary (either provided by the system, or bootstrapped). 15 | You could use it, for example, to send [actions](https://zellij.dev/documentation/cli-actions) to the current Zellij session. 16 | 17 | ### `$NOTEBOOK` 18 | 19 | Notebook renderer: The full path of the currently loaded notebook. 20 | Interactive terminal: The full path of the initially loaded notebook. This will not change when loading a new notebook! 21 | 22 | ### `$NOTEBOOKS` 23 | 24 | Notebook renderer: The full path of the parent directory of the currently loaded notebook. 25 | Interactive terminal: The full path of the parent directory of the initially loaded notebook. This will not change when loading a new notebook! 26 | 27 | ## Functions 28 | 29 | ### `run_terminal_command` (src/execute.sh) 30 | 31 | Focuses the interactive terminal pane, sends the string via simulated key strokes, followed by a return to execute it. Once the prompt is available again, the notebook pane is focused. 32 | 33 | #### Arguments 34 | 35 | - `command`: string of command to execute 36 | 37 | ```bash 38 | run_terminal_command "echo 'Hello from the terminal!'" 39 | ``` 40 | 41 | ### `run_terminal_commands` (src/execute.sh) 42 | 43 | Runs multiple commands, separated by a new line, with `run_terminal_command`. 44 | 45 | #### Arguments 46 | 47 | - `commands`: string of commands to execute, separated by a new line 48 | 49 | ```bash 50 | run_terminal_commands "echo 1\necho 2" 51 | ``` 52 | 53 | ### `run_notebook_code` (src/execute.sh) 54 | 55 | Finds a path of an installed interpreter, and executes code with it. If the language is `bash`, it is executed with the context of the notebook renderer, i.e. variables and functions you see here are available to your script. 56 | 57 | If no interpreter is found, a error message is shown. 58 | 59 | #### Arguments 60 | 61 | - `code`: string of code block to run 62 | - `language`: string of the language of the code 63 | - `interpreters`: string of one or more interpreters to check if installed and used to execute the `code`, separated by space 64 | 65 | ```bash 66 | run_notebook_code "echo $NOTEBOOK" "bash" "bash" 67 | run_notebook_code "print('Hello from Python!')" "python" "python3 python" 68 | ``` 69 | 70 | ### `trim` (src/helpers.sh) 71 | 72 | Trims whitespace and line-breaks from the start and end of a string, and prints it. 73 | 74 | #### Arguments 75 | 76 | - `str`: the string to be trimmed 77 | 78 | ```bash 79 | str="$(trim " I am clean! ")" 80 | echo $str # -> "I am clean!" 81 | ``` 82 | 83 | ### `get_last_char` (src/helpers.sh) 84 | 85 | Gets the last character of a string and prints it. 86 | 87 | #### Arguments 88 | 89 | - `str`: the string to get the last character from 90 | 91 | ```bash 92 | str="$(get_last_char "Hi!")" 93 | echo $str # -> "!" 94 | ``` 95 | 96 | ### `get_last_line` (src/helpers.sh) 97 | 98 | Gets the last line in a multiline string, trims it, and prints it. 99 | 100 | #### Arguments 101 | 102 | - `str`: the string to get the last line from 103 | 104 | ```bash 105 | str="$(get_last_line " 1 \n 2 ")" 106 | echo $str # -> "2" 107 | ``` 108 | 109 | ### `find_binary_path` (src/helpers.sh) 110 | 111 | Find the first binary that exists in `$PATH` from a space separated string, and prints it. 112 | 113 | #### Arguments 114 | 115 | - `binaries`: string of space seprated binary names 116 | 117 | ```bash 118 | path="$(find_binary_path "python python3")" 119 | echo $path # -> "/usr/bin/python3" 120 | ``` 121 | 122 | ### `load_notebook` (src/notebook.sh) 123 | 124 | Load a notebook markdown file into the notebook pane and clears the interactive terminal. Sets the variables `$NOTEBOOK` and `$NOTEBOOKS`. 125 | 126 | #### Arguments 127 | 128 | - `notebook`: the abslolute or relative path of the notebook markdown file to load 129 | 130 | ```bash 131 | load_notebook "path/to/notebook.md" 132 | echo $NOTEBOOK # -> "/full/path/to/notebook.md" 133 | echo $NOTEBOOKS # -> "/full/path/to" 134 | ``` 135 | 136 | ### `print_markdown` (src/printer.sh) 137 | 138 | Renders and prints markdown. Formats it to fit the width of the terminal. 139 | 140 | #### Arguments 141 | 142 | - `markdown`: string of markdown to render 143 | 144 | ```bash 145 | md="$(print_markdown "# Hi!")" 146 | echo $md # -> [Heading 1] "Hi!" 147 | ``` 148 | 149 | ### `print_in_borders` (src/printer.sh) 150 | 151 | Prints a message in borders, optionally with a different color, a label and a left/right padding. 152 | 153 | #### Arguments 154 | 155 | - `message`: string to put in borders 156 | - `color` (optional): number of the color to use, `8` (grey) by default [256 colors cheat sheet](https://www.ditig.com/publications/256-colors-cheat-sheet) 157 | - `label` (optional): a label to put on the lower border 158 | - `padding` (optional): number of charcacters for left/right padding inside the borders, default is `0` 159 | 160 | ```bash 161 | just_border="$(print_in_borders "Hi!")" 162 | alt_border="$(print_in_borders "Hi!" 12 "From Sky" 2)" 163 | 164 | echo $just_border # -> [Gray borders] "Hi!" 165 | echo $alt_border # -> [Blue borders, label "From Sky", 2 padding] "Hi!" 166 | ``` 167 | 168 | ### `print_code` (src/printer.sh) 169 | 170 | Highlights code and prints it in borders. 171 | 172 | #### Arguments 173 | 174 | - `code`: string of code to highlight 175 | - `language` (optional): string of language to use for highlighting and as label on lower border 176 | 177 | ```bash 178 | bash="$(print_code "echo 'Hi!'")" 179 | python="$(print_code "print('Hi!')" "python")" 180 | 181 | echo $bash # -> [generic highlight] "echo 'Hi!'" 182 | echo $python # -> [python highlight with label] "print('Hi!')" 183 | ``` 184 | 185 | ### `print_spinner` (src/printer.sh) 186 | 187 | Prints a `pulse` spinner for one second. 188 | 189 | #### Arguments 190 | 191 | - `title`: string of spinner title 192 | 193 | ```bash 194 | print_spinner "Hold on..." 195 | ``` 196 | 197 | ### `print_error` (src/printer.sh) 198 | 199 | Prints a message in red borders with "Error" label. 200 | 201 | #### Arguments 202 | 203 | ```bash 204 | print_error "Oh no!" 205 | ``` 206 | 207 | ### `set_notebook_name` (src/zellij.sh) 208 | 209 | Renames the currently focused pane. 210 | 211 | #### Arguments 212 | 213 | - `name` - string of the name to set 214 | 215 | ```bash 216 | set_notebook_name "Title of the notebook" 217 | ``` 218 | 219 | ### `focus_terminal` (src/zellij.sh) 220 | 221 | Focuses the interactive terminal pane. 222 | 223 | ```bash 224 | focus_terminal 225 | ``` 226 | 227 | ### `focus_notebook` (src/zellij.sh) 228 | 229 | Focuses the notebook pane. 230 | 231 | ```bash 232 | focus_notebook 233 | ``` 234 | 235 | ### `type_on_focused_pane` (src/zellij.sh) 236 | 237 | Sends string as simulated keystrokes and a return key to the focused pane. 238 | 239 | #### Arguments 240 | 241 | - `str`: string of the text to type 242 | 243 | ```bash 244 | type_on_focused_pane "echo 'Hi!'" 245 | ``` 246 | 247 | ### `dump_screen` (src/zellij.sh) 248 | 249 | Dumps the screen content of the currently focused pane and prints it. 250 | 251 | ```bash 252 | screen="$(dump_screen)" 253 | echo $screen # -> [content of the focused pane] 254 | ``` 255 | 256 | ### `dump_last_char` (src/zellij.sh) 257 | 258 | Use `dump_screen` and prints the last character of the content. 259 | 260 | ```bash 261 | char="$(dump_last_char)" 262 | echo $char # -> "$" 263 | ``` 264 | 265 | ### `has_prompt` (src/zellij.sh) 266 | 267 | Determines if the currently focused pane has a awaiting prompt by dumping the content and check the last character for known prompt characters. 268 | 269 | Known prompt characters are `#`, `$` and `>`. 270 | 271 | Prints the string "true" of a awaiting prompt was found, otherwise it prints "false". 272 | 273 | #### Arguments 274 | 275 | - `check_char`: first character to check to indicate a command prompt 276 | 277 | ```bash 278 | check_char="$(dump_last_char)" 279 | 280 | if [ "$(has_prompt $check_char)" = "true" ]; then 281 | echo "Found awaiting prompt." 282 | fi 283 | ``` 284 | -------------------------------------------------------------------------------- /docs/09-feedback.md: -------------------------------------------------------------------------------- 1 | # Feedback 2 | 3 | Feedback, bugs or feature requests are welcome at: 4 | 5 | - https://github.com/mustardamus/wandersage/issues 6 | 7 | ## If you are looking for a developer that can make kewl stuff like this, hit me up at contact@mustardamus.com 8 | -------------------------------------------------------------------------------- /src/bootstrap.sh: -------------------------------------------------------------------------------- 1 | function download_extract_copy() { 2 | local url="$1" 3 | local glob="$2" 4 | local dest="$3" 5 | local archive="$(mktemp -d)/$(basename $url)" 6 | local tmp="$(mktemp -d)" 7 | 8 | curl -sSLo "$archive" "$url" 9 | tar -C "$tmp" -zxf "$archive" 10 | mkdir -p "$dest" 11 | cp "$(find $tmp/$glob)" "$dest/" 12 | rm "$archive" 13 | rm -rf "$tmp" 14 | } 15 | 16 | function to_lowercase() { 17 | local str="$1" 18 | echo "$str" | tr '[:upper:]' '[:lower:]' 19 | } 20 | 21 | function ensure_dependency() { 22 | local command="$1" 23 | 24 | if [ ! -x "$(command -v "$command")" ]; then 25 | echo "Need '$command' to bootstrap. Please install it." 26 | exit 1 27 | fi 28 | } 29 | 30 | function bootstrap_binary() { 31 | local command="$1" 32 | local version="$2" 33 | local size="$3" 34 | local repo="$4" 35 | local archive_linux="$5" 36 | local archive_darwin="$6" 37 | local extract="$7" 38 | local archive="" 39 | local os="$(to_lowercase "$(uname -s)")" 40 | 41 | if [ "$os" = "linux" ]; then 42 | archive="$archive_linux" 43 | elif [ "$os" = "darwin" ]; then 44 | archive="$archive_darwin" 45 | else 46 | echo "Could not bootstrap '$command' for '$os'. Please install it yourself." 47 | return 48 | fi 49 | 50 | local url="https://github.com/$repo/releases/download/v$version/$archive" 51 | 52 | read -r -n 1 -p "Need to download dependency https://github.com/$repo ($version $size). Continue? [Y/n]: " response 53 | response="$(to_lowercase $response)" 54 | 55 | if [[ $response =~ ^(y| ) ]] || [[ -z $response ]]; then 56 | echo "Downloading '$archive'..." 57 | download_extract_copy "$url" "$extract" "$BIN_PATH" 58 | fi 59 | } 60 | 61 | function bootstrap() { 62 | local arch="$(to_lowercase "$(uname -m)")" 63 | 64 | ensure_dependency "curl" 65 | ensure_dependency "tar" 66 | 67 | if [ ! -x "$(command -v "zellij")" ] && [ ! -f "$ZEL" ]; then 68 | local zellij_linux="zellij-$arch-unknown-linux-musl.tar.gz" 69 | local zellij_darwin="zellij-$arch-apple-darwin.tar.gz" 70 | 71 | bootstrap_binary "zellij" "0.41.1" "~11MB" "zellij-org/zellij" "$zellij_linux" "$zellij_darwin" "zellij" 72 | fi 73 | 74 | if [ ! -x "$(command -v "gum")" ] && [ ! -f "$GUM" ]; then 75 | local gum_version="0.14.5" 76 | local gum_linux="gum_$(echo $gum_version)_Linux_$(echo $arch).tar.gz" 77 | local gum_darwin="gum_$(echo $gum_version)_Darwin_$(echo $arch).tar.gz" 78 | 79 | bootstrap_binary "gum" "$gum_version" "~4MB" "charmbracelet/gum" "$gum_linux" "$gum_darwin" "**/gum" 80 | fi 81 | } 82 | -------------------------------------------------------------------------------- /src/execute.sh: -------------------------------------------------------------------------------- 1 | function run_terminal_command() { 2 | local cmd="$1" 3 | local label="$2" 4 | local check_char="$(dump_last_char)" 5 | 6 | focus_terminal 7 | type_on_focused_pane "$cmd" 8 | 9 | while [ true ]; do 10 | if [ "$(has_prompt $check_char)" = "true" ]; then 11 | break 12 | fi 13 | 14 | print_spinner "Waiting for command prompt" 15 | done 16 | 17 | focus_notebook 18 | } 19 | 20 | function run_terminal_commands() { 21 | local block="$1" 22 | 23 | while IFS= read -r cmd; do 24 | if [ ! -z "$cmd" ]; then 25 | run_terminal_command "$cmd" 26 | fi 27 | done <<< "$(echo -e $block)" 28 | } 29 | 30 | function run_notebook_code() { 31 | local block="$1" 32 | local type="$2" 33 | local interpreters="$3" 34 | local interpreter="$(find_binary_path "$interpreters")" 35 | 36 | if [ -z "$interpreter" ]; then 37 | print_error "Could not find a interpreter for '$type'. Looked for '$interpreters'." 38 | return 39 | fi 40 | 41 | local tmp="$(mktemp)" 42 | local pwd="$(pwd)" 43 | 44 | echo -e "$(trim "$block")" > "$tmp" 45 | cd $NOTEBOOKS 46 | 47 | if [ "$type" = "shell" ] || [ "$type" = "bash" ]; then 48 | source "$tmp" 49 | else 50 | "$interpreter" "$tmp" 51 | fi 52 | 53 | rm "$tmp" 54 | cd "$pwd" 55 | } 56 | -------------------------------------------------------------------------------- /src/explorer.sh: -------------------------------------------------------------------------------- 1 | function notebooks_explorer() { 2 | local choices="" 3 | local selected="" 4 | local selected_switch="" 5 | local md_count="$(find $NOTEBOOKS -maxdepth 1 -type f -name '*.md' | sed '/^\s*$/d' | wc -l)" 6 | 7 | if [ "$md_count" = "1" ]; then 8 | return 9 | fi 10 | 11 | for notebook in $NOTEBOOKS/*.md; do 12 | notebook_file="$(basename $notebook)" 13 | first_line="$(cat "$notebook" | head -1)" 14 | title="" 15 | 16 | if [ "${first_line:0:2}" = "# " ]; then 17 | title="${first_line:2:${#first_line}-2}" 18 | fi 19 | 20 | if [ -z "$title" ]; then 21 | entry="[$notebook_file]" 22 | else 23 | entry="$title $(echo "[$notebook_file]" | $GUM style --foreground 8)" 24 | fi 25 | 26 | if [ "$selected_switch" = "true" ] && [ -z "$selected" ]; then 27 | selected="$entry" 28 | fi 29 | 30 | if [ "$(realpath $notebook)" = "$NOTEBOOK" ]; then 31 | selected_switch="true" 32 | entry="$(echo "$entry" | $GUM style --foreground 8)" 33 | fi 34 | 35 | choices="$choices\n$entry" 36 | done 37 | 38 | height=$(($md_count + 2)) 39 | 40 | if [ "$height" -gt "12" ]; then 41 | height="12" 42 | fi 43 | 44 | header="Load next notebook in '$NOTEBOOKS':" 45 | choice="$(echo -e "$(trim "$choices")" | $GUM choose --selected "$selected" --header "$header" --height "$height")" 46 | notebook="$(echo "$choice" | awk -F'[][]' '{print $2}')" 47 | 48 | load_notebook "$NOTEBOOKS/$notebook" 49 | } 50 | -------------------------------------------------------------------------------- /src/handler.sh: -------------------------------------------------------------------------------- 1 | function handle_code_terminal() { 2 | local block="$1" 3 | local type="$2" 4 | local message="" 5 | local num="$(echo -e "$(trim "$block")" | wc -l)" 6 | 7 | if [ "$type" = "shell" ]; then 8 | if [ "$num" = "1" ]; then 9 | message="Run shell command in terminal?" 10 | else 11 | message="Run $num shell commands in terminal?" 12 | fi 13 | else 14 | message="Send $type code to terminal?" 15 | fi 16 | 17 | print_code "$block" "$type" 18 | 19 | if [ "$OPT_NO_CONFIRM" = "true" ]; then 20 | run_terminal_commands "$block" 21 | else 22 | $GUM confirm "$message" && run_terminal_commands "$block" 23 | fi 24 | } 25 | 26 | function handle_code_notebook() { 27 | local block="$1" 28 | local type="$2" 29 | 30 | if [ "$OPT_NOTEBOOK_CODE_ALLOWED" = "false" ]; then 31 | print_in_borders "Inline code blocked per user request." 11 32 | return 33 | fi 34 | 35 | case "$type" in 36 | "shell"|"bash") 37 | run_notebook_code "$block" "bash" "bash" 38 | ;; 39 | "python") 40 | run_notebook_code "$block" "python" "python python3 python3.5 python3.11" 41 | ;; 42 | "javascript"|"js") 43 | run_notebook_code "$block" "javascript" "node deno bun" 44 | ;; 45 | "typescript"|"ts") 46 | run_notebook_code "$block" "typescript" "deno bun" 47 | ;; 48 | "perl") 49 | run_notebook_code "$block" "perl" "perl" 50 | ;; 51 | *) print_error "Don't know how to run '$type'." ;; 52 | esac 53 | } 54 | 55 | function handle_code() { 56 | local block="$1" 57 | local def="$2" 58 | local type="$(get_code_block_part "$def" "type")" 59 | local target="$(get_code_block_part "$def" "target")" 60 | 61 | if [ -z "$target" ]; then 62 | print_code "$block" "$type" 63 | else 64 | case "$target" in 65 | "terminal") handle_code_terminal "$block" "$type" ;; 66 | "notebook") handle_code_notebook "$block" "$type" ;; 67 | *) print_error "Unknown code target '$target'" 68 | esac 69 | fi 70 | } 71 | 72 | function handle_line() { 73 | local line="$1" 74 | 75 | if [ "${line:0:2}" = "# " ]; then 76 | title="${line:2:${#line}-2}" 77 | 78 | set_notebook_name "$title" 79 | fi 80 | } 81 | -------------------------------------------------------------------------------- /src/helpers.sh: -------------------------------------------------------------------------------- 1 | function is_script_sourced { 2 | # determines if script is sourced by another one, or run from the command line 3 | ! (return 0 2>/dev/null) 4 | } 5 | 6 | function trim(){ 7 | local str="$1" 8 | 9 | # lifted from https://github.com/jmooring/bash-function-library/blob/master/_trim.sh 10 | trimmed="$(printf "%b" "${str}" | sed -E 's/^[[:space:]]+// ; s/[[:space:]]+$// ; /./,$ !d')" 11 | printf "%s" "${trimmed}" 12 | } 13 | 14 | function get_command_label() { 15 | local block="$1" 16 | local label="commands" 17 | local num="$(echo -e "$(trim "$block")" | wc -l)" 18 | 19 | if [ "$num" = "1" ]; then 20 | label="command" 21 | fi 22 | 23 | echo "$label" 24 | } 25 | 26 | function get_last_char() { 27 | local str="$1" 28 | 29 | echo "${str:${#str}-1:1}" 30 | } 31 | 32 | function get_last_line() { 33 | local str="$1" 34 | local line="$(echo $str | tail -1)" 35 | 36 | echo "$(trim "$line")" 37 | } 38 | 39 | function find_binary_path() { 40 | local binaries="$1" 41 | local path="" 42 | 43 | for binary in $binaries; do 44 | if [ -x "$(command -v "$binary")" ]; then 45 | path="$(command -v "$binary")" 46 | break 47 | fi 48 | done 49 | 50 | echo "$path" 51 | } 52 | 53 | function get_code_block_part() { 54 | local line="$1" 55 | local part="$2" 56 | local def="$(trim "${line:3:${#line}-3}")" # remove leading ``` 57 | local def_arr=(${def//:/ }) 58 | local index=0 59 | 60 | case "$part" in 61 | type) index=0 ;; 62 | target) index=1 ;; 63 | esac 64 | 65 | echo "$(trim "${def_arr[$index]}")" 66 | } 67 | -------------------------------------------------------------------------------- /src/init.sh: -------------------------------------------------------------------------------- 1 | function init() { 2 | if [ -f "$NOTEBOOK" ]; then 3 | load_notebook "$NOTEBOOK" 4 | elif [ -d "$NOTEBOOK" ]; then 5 | export NOTEBOOKS="$NOTEBOOK" 6 | local md_files="$(find $NOTEBOOKS -maxdepth 1 -type f -name '*.md')" 7 | local md_count="$(echo "$md_files" | sed '/^\s*$/d' | wc -l)" # sed removed blank lines 8 | 9 | case "$md_count" in 10 | 0) 11 | print_error "No notebooks found in '$NOTEBOOKS'." 12 | ;; 13 | 1) 14 | load_notebook "$md_files" 15 | ;; 16 | *) 17 | set_notebook_name "Notebooks Explorer" 18 | notebooks_explorer 19 | ;; 20 | esac 21 | else 22 | print_error "'$NOTEBOOK' does not exist." 23 | fi 24 | } 25 | -------------------------------------------------------------------------------- /src/notebook.sh: -------------------------------------------------------------------------------- 1 | function load_notebook() { 2 | local notebook="$1" 3 | local content="$(cat $notebook)" 4 | 5 | export NOTEBOOK="$(realpath $notebook)" 6 | export NOTEBOOKS="$(dirname $NOTEBOOK)" 7 | 8 | set_notebook_name "Notebook" 9 | clear 10 | focus_terminal 11 | type_on_focused_pane "clear" 12 | focus_notebook 13 | notebook_code_warning "$content" 14 | 15 | for i in {1..20}; do 16 | # since the interactiveness, sleep and send a bunch of scroll-ups to make 17 | # sure we are at the top of the notebook in the background 18 | eval "sleep 2 && $ZEL action page-scroll-up &> /dev/null" & 19 | done 20 | 21 | parse "$content" "handle_code" "print_markdown" "handle_line" 22 | 23 | if [ -d "$NOTEBOOKS" ]; then 24 | echo 25 | echo 26 | notebooks_explorer 27 | fi 28 | } 29 | -------------------------------------------------------------------------------- /src/parser.sh: -------------------------------------------------------------------------------- 1 | function parse() { 2 | local content="$1" 3 | local code_block_callback="$2" 4 | local markdown_callback="$3" 5 | local line_callback="$4" 6 | local block="" 7 | local is_code_block_open="false" 8 | local code_block="" 9 | local code_block_def="" 10 | 11 | while IFS= read -r line; do 12 | if [ "${line:0:3}" = "\`\`\`" ]; then 13 | if [ "$is_code_block_open" = "false" ]; then 14 | is_code_block_open="true" 15 | code_block_def="$line" 16 | else 17 | if [ ! -z "$markdown_callback" ]; then 18 | $markdown_callback "$block" 19 | fi 20 | 21 | if [ ! -z "$code_block_callback" ]; then 22 | $code_block_callback "$code_block" "$code_block_def" 23 | fi 24 | 25 | is_code_block_open="false" 26 | block="" 27 | code_block="" 28 | code_block_def="" 29 | fi 30 | else # not a open or close code block 31 | if [ "$is_code_block_open" = "false" ]; then 32 | if [ ! -z "$line_callback" ]; then 33 | $line_callback "$line" 34 | fi 35 | 36 | block="$block\n$line" 37 | else 38 | code_block="$code_block\n$line" 39 | fi 40 | fi 41 | done <<< "$content" 42 | 43 | if [ ! -z "$block" ] && [ ! -z "$markdown_callback" ]; then 44 | $markdown_callback "$block" 45 | fi 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/printer.sh: -------------------------------------------------------------------------------- 1 | function print_markdown() { 2 | local block="$(trim "$1")" 3 | 4 | if [ ! -z "$block" ]; then 5 | local width="$(tput cols)" 6 | local md="$(echo -e "$block" | $GUM format -t markdown)" 7 | 8 | echo 9 | echo -e "$(trim "$md")" | $GUM style --width "$width" 10 | fi 11 | } 12 | 13 | function print_in_borders() { 14 | local content="$1" 15 | local border_color="$2" 16 | local label="$3" 17 | local padding_lr="$4" 18 | local term_width="$(tput cols)" 19 | local width="$(($term_width - 2))" 20 | 21 | if [ -z "$border_color" ]; then 22 | border_color=8 23 | fi 24 | 25 | if [ -z "$padding_lr" ]; then 26 | padding_lr="0" 27 | fi 28 | 29 | echo "$(trim "$content")" | $GUM style \ 30 | --width $width \ 31 | --border normal \ 32 | --border-foreground $border_color \ 33 | --padding "0 $padding_lr" 34 | 35 | if [ ! -z "$label" ]; then 36 | tput cuu 1 # cursor up one line 37 | echo "└─ $label " | $GUM style --foreground $border_color 38 | fi 39 | 40 | echo 41 | } 42 | 43 | function print_code() { 44 | local block="$1" 45 | local type="$2" 46 | local code="" 47 | local border_color=8 48 | 49 | if [ -z "$type" ]; then 50 | code="$(echo -e "$block" | $GUM format -t code)" 51 | else 52 | code="$(echo -e "$block" | $GUM format -t code -l $type)" 53 | fi 54 | 55 | print_in_borders "$code" "$border_color" "$type" 56 | } 57 | 58 | function print_spinner() { 59 | local title="$1" 60 | 61 | $GUM spin -a right --spinner pulse --title " $title" -- sleep 1 62 | } 63 | 64 | function print_error() { 65 | local msg="$1" 66 | 67 | print_in_borders "$msg" 9 "Error" 68 | } 69 | 70 | function print_error_argument() { 71 | local arg="$1" 72 | 73 | print_error "Unknown argument '$arg'. Pass '--help' to show possible arguments." 74 | } 75 | 76 | function print_help() { 77 | local help=`cat <") echo "true" ;; 56 | *) echo "false" ;; 57 | esac 58 | } 59 | -------------------------------------------------------------------------------- /src/zellij/layout.kdl: -------------------------------------------------------------------------------- 1 | layout { 2 | tab split_direction="vertical" hide_floating_panes=true { 3 | pane { 4 | command "bash" 5 | args "-c" "bash $SRC_PATH/zellij/loader.sh" 6 | // loading it this way so we have access to envs 7 | } 8 | 9 | pane { 10 | name "Terminal" 11 | command "bash" 12 | } 13 | 14 | floating_panes { 15 | pane { 16 | name "Scratchpad" 17 | x "5%" 18 | y "5%" 19 | width "90%" 20 | height "90%" 21 | } 22 | } 23 | } 24 | } 25 | 26 | keybinds clear-defaults=true { 27 | normal { 28 | bind "Alt Left" { MoveFocusOrTab "Left"; } 29 | bind "Alt Right" { MoveFocusOrTab "Right"; } 30 | bind "Alt Up" { ScrollUp; } 31 | bind "Alt Down" { ScrollDown; } 32 | bind "Alt s" { ToggleFloatingPanes; } 33 | bind "Ctrl q" { Quit; } 34 | } 35 | } 36 | 37 | on_force_close "quit" 38 | theme "dracula" // https://github.com/zellij-org/zellij/tree/main/zellij-utils/assets/themes 39 | mouse_mode false 40 | copy_on_select true 41 | -------------------------------------------------------------------------------- /src/zellij/loader.sh: -------------------------------------------------------------------------------- 1 | for script in $SRC_PATH/*.sh; do 2 | source $script 3 | done 4 | 5 | init 6 | -------------------------------------------------------------------------------- /wandersage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | 5 | export ROOT_PATH="$(realpath $(dirname $BASH_SOURCE))" 6 | export BIN_PATH="$ROOT_PATH/bin" 7 | export SRC_PATH="$ROOT_PATH/src" 8 | export NOTEBOOK="$1" 9 | 10 | if [ -x "$(command -v "zellij")" ]; then 11 | export ZEL="$(which zellij)" 12 | else 13 | export ZEL="$BIN_PATH/zellij" # $ZELLIJ is taken over by Zellij 14 | fi 15 | 16 | if [ -x "$(command -v "gum")" ]; then 17 | export GUM="$(which gum)" 18 | else 19 | export GUM="$BIN_PATH/gum" 20 | fi 21 | 22 | for script in $SRC_PATH/*.sh; do 23 | source $script 24 | done 25 | 26 | if [ "$1" = "--help" ]; then 27 | print_help 28 | exit 29 | fi 30 | 31 | bootstrap "$ROOT_PATH/bin" 32 | 33 | if ! is_script_sourced; then 34 | while [[ "$#" -gt 0 ]]; do 35 | case $1 in 36 | # -t|--theme) export OPT_THEME="$2"; shift ;; 37 | --no-confirm) export OPT_NO_CONFIRM="true" ;; 38 | --no-warn) export OPT_NO_WARN="true" ;; 39 | -*) 40 | print_error_argument "$1" 41 | exit 1 42 | ;; 43 | *) export NOTEBOOK="$1" ;; 44 | esac 45 | 46 | shift 47 | done 48 | 49 | if [ -z "$NOTEBOOK" ] || [ "${NOTEBOOK:0:1}" = "-" ]; then 50 | # show docs if no notebook is passed 51 | export NOTEBOOK="$ROOT_PATH/docs/01-intro.md" 52 | export NOTEBOOKS="$ROOT_PATH/docs" 53 | else 54 | export NOTEBOOK="$(realpath $NOTEBOOK)" 55 | 56 | if [ -f "$NOTEBOOK" ]; then 57 | export NOTEBOOKS="$(dirname $NOTEBOOK)" 58 | elif [ -d "$NOTEBOOK" ]; then 59 | export NOTEBOOKS="$NOTEBOOK" 60 | fi 61 | fi 62 | 63 | $ZEL --layout "$SRC_PATH/zellij/layout.kdl" 64 | tput cuu 1 65 | fi 66 | --------------------------------------------------------------------------------