Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .icons/zellij.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added registry/jang2162/.images/avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions registry/jang2162/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
display_name: "Byeong-Hyun"
bio: "ㅎㅇ means Hi"
avatar: "./.images/avatar.png"
github: "jang2162"
status: "community"
---

# Byeong-Hyun

ㅎㅇ means "Hi"
113 changes: 113 additions & 0 deletions registry/jang2162/modules/zellij/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
display_name: Zellij
description: Modern terminal workspace with session management
icon: ../../../../.icons/zellij.svg
verified: false
tags: [zellij, terminal, multiplexer]
---

# Zellij

Automatically install and configure [zellij](https://github.com/zellij-org/zellij), a modern terminal workspace with session management. Supports terminal and web modes, custom configuration, and session persistence.

```tf
module "zellij" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/jang2162/zellij/coder"
version = "1.0.0"
agent_id = coder_agent.example.id
}
```

## Features

- Installs zellij if not already present (version configurable, default `0.43.1`)
- Configures zellij with sensible defaults
- Supports custom configuration (KDL format)
- Session serialization enabled by default
- **Two modes**: `terminal` (Coder built-in terminal) and `web` (browser-based via subdomain proxy)
- Cross-platform architecture support (x86_64, aarch64)

## Examples

### Basic Usage (Terminal Mode)

```tf
module "zellij" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/jang2162/zellij/coder"
version = "1.0.0"
agent_id = coder_agent.example.id
}
```

### Web Mode

```tf
module "zellij" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/jang2162/zellij/coder"
version = "1.0.0"
agent_id = coder_agent.example.id
mode = "web"
web_port = 8082
group = "Terminal"
order = 1
}
```

### Custom Configuration

```tf
module "zellij" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/jang2162/zellij/coder"
version = "1.0.0"
agent_id = coder_agent.example.id
zellij_config = <<-EOT
keybinds {
normal {
bind "Ctrl t" { NewTab; }
}
}
theme "dracula"
EOT
}
```

## How It Works

### Installation & Setup (scripts/run.sh)

1. **Version Check**: Checks if zellij is already installed with the correct version
2. **Architecture Detection**: Detects system architecture (x86_64 or aarch64)
3. **Download**: Downloads the appropriate zellij binary from GitHub releases
4. **Installation**: Installs zellij to `/usr/local/bin/zellij`
5. **Configuration**: Creates default or custom configuration at `~/.config/zellij/config.kdl`
6. **Web Mode Only**:
- Prepends a `TERM` fix to `~/.bashrc` (sets `TERM=xterm-256color` inside zellij when `TERM=dumb`)
- Starts the zellij web server as a daemon and creates an authentication token

### Session Access

- **Terminal mode**: Opens zellij in the Coder built-in terminal via `zellij attach --create default`
- **Web mode**: Accesses zellij through a subdomain proxy in the browser (authentication token required on first visit)

## Default Configuration

The default configuration includes:

- Session serialization enabled for persistence
- 10,000 line scroll buffer
- Copy on select enabled (system clipboard)
- Rounded pane frames
- Key bindings: `Ctrl+s` (new pane), `Ctrl+q` (quit)
- Default theme
- Web mode: web server IP/port automatically appended

> [!IMPORTANT]
>
> - Custom `zellij_config` replaces the default configuration entirely
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IMPORTANT note says "Custom zellij_config replaces the default configuration entirely" but does not warn that in web mode, the user must also include web_server_ip and web_server_port in their custom config, since those are only appended to the default config. Without these settings, the zellij web server won't listen on the configured port when using a custom config. This is a significant omission that should be documented here.

Suggested change
> - Custom `zellij_config` replaces the default configuration entirely
> - Custom `zellij_config` replaces the default configuration entirely
> - When using `mode = "web"`, your custom configuration **must** also define `web_server_ip` and `web_server_port`, since these are only appended to the default config. Omitting them will prevent the zellij web server from listening on the expected port.

Copilot uses AI. Check for mistakes.
> - Requires `curl` and `tar` for installation
> - Uses `sudo` to install to `/usr/local/bin/`
> - Supported architectures: x86_64, aarch64
63 changes: 63 additions & 0 deletions registry/jang2162/modules/zellij/main.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";

describe("zellij", async () => {
await runTerraformInit(import.meta.dir);

testRequiredVariables(import.meta.dir, {
agent_id: "foo",
});

it("default mode should be terminal", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
const terminalApp = state.resources.find(
(r) => r.type === "coder_app" && r.name === "zellij_terminal",
);
const webApp = state.resources.find(
(r) => r.type === "coder_app" && r.name === "zellij_web",
);
expect(terminalApp).toBeDefined();
expect(terminalApp!.instances.length).toBe(1);
expect(terminalApp!.instances[0].attributes.command).toBe(
"zellij attach --create default",
);
expect(webApp).toBeUndefined();
});

it("web mode should create web app", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
mode: "web",
});
const webApp = state.resources.find(
(r) => r.type === "coder_app" && r.name === "zellij_web",
);
const terminalApp = state.resources.find(
(r) => r.type === "coder_app" && r.name === "zellij_terminal",
);
expect(webApp).toBeDefined();
expect(webApp!.instances.length).toBe(1);
expect(webApp!.instances[0].attributes.subdomain).toBe(true);
expect(webApp!.instances[0].attributes.url).toBe("http://localhost:8082");
expect(terminalApp).toBeUndefined();
});

it("web mode should use custom port", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
mode: "web",
web_port: 9090,
});
const webApp = state.resources.find(
(r) => r.type === "coder_app" && r.name === "zellij_web",
);
expect(webApp).toBeDefined();
expect(webApp!.instances[0].attributes.url).toBe("http://localhost:9090");
});
});
104 changes: 104 additions & 0 deletions registry/jang2162/modules/zellij/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
terraform {
required_version = ">= 1.0"

required_providers {
coder = {
source = "coder/coder"
version = ">= 2.5"
}
}
}

variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}

variable "zellij_version" {
type = string
description = "The version of zellij to install."
default = "0.43.1"
}

variable "zellij_config" {
type = string
description = "Custom zellij configuration to apply."
default = ""
}

variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}

variable "group" {
type = string
description = "The name of a group that this app belongs to."
default = null
}

variable "icon" {
type = string
description = "The icon to use for the app."
default = "/icon/zellij.svg"
}

variable "mode" {
type = string
description = "How to run zellij: 'web' for web client with subdomain proxy, 'terminal' for Coder built-in terminal."
default = "terminal"

validation {
condition = contains(["web", "terminal"], var.mode)
error_message = "mode must be 'web' or 'terminal'."
}
}

variable "web_port" {
type = number
description = "The port for the zellij web server. Only used when mode is 'web'."
default = 8082
}


resource "coder_script" "zellij" {
agent_id = var.agent_id
display_name = "Zellij"
icon = "/icon/zellij.svg"
script = templatefile("${path.module}/scripts/run.sh", {
ZELLIJ_VERSION = var.zellij_version
ZELLIJ_CONFIG = var.zellij_config
MODE = var.mode
WEB_PORT = var.web_port
})
run_on_start = true
run_on_stop = false
}

# Web mode: subdomain proxy to zellij web server
resource "coder_app" "zellij_web" {
count = var.mode == "web" ? 1 : 0

agent_id = var.agent_id
slug = "zellij"
display_name = "Zellij"
icon = var.icon
order = var.order
group = var.group
url = "http://localhost:${var.web_port}"
subdomain = true
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coder_app.zellij_web resource does not include a healthcheck block. Other web-mode modules in this registry (e.g., filebrowser, jupyterlab, code-server) include a healthcheck so Coder can display the correct app status. If the Zellij web server exposes any health or status endpoint, consider adding a healthcheck block to give users visibility into whether the web server is running.

Suggested change
subdomain = true
healthcheck {
url = "/"
interval = "10s"
timeout = "5s"
}
subdomain = true

Copilot uses AI. Check for mistakes.
}

# Terminal mode: run zellij in Coder built-in terminal
resource "coder_app" "zellij_terminal" {
count = var.mode == "terminal" ? 1 : 0

agent_id = var.agent_id
slug = "zellij"
display_name = "Zellij"
icon = var.icon
order = var.order
group = var.group
command = "zellij attach --create default"
}
Comment on lines +80 to +104
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both coder_app resources hardcode slug and display_name, and the web app (zellij_web) also lacks a configurable share variable. Other modules in this registry (e.g., code-server, filebrowser, kasmvnc) expose slug, display_name, and share (with a default of "owner") as configurable variables. Without slug being configurable, users cannot run multiple instances of this module in the same workspace. Consider adding slug, display_name, and share variables to match the established convention.

Copilot uses AI. Check for mistakes.
Loading