Checking in an editor
Put "$schema" with the schema’s URL at the top of the file, as in the example at the end. VS Code and other editors then complete and check it. The app reads past "$schema" when it is this URL and refuses any other value.
The file
One JSON object with these fields. A file holds 1 to 32 themes.
| Field | Value |
|---|---|
$schema | Optional. Only the schema’s URL. |
format | Always "ssh-term-theme". |
formatVersion | 1. A file of a newer version is refused, asking to update the app. |
themes | An array of 1 to 32 themes. |
A theme
| Field | Value |
|---|---|
id | A UUID v4 in lowercase. Give each new theme a new random one: a theme sent again with the same id is offered as a replacement for the one the reader has. |
name | The theme’s name. |
author | Who made it. |
license | CC0-1.0, CC-BY-4.0 or MIT (see Licenses). |
basedOn | Optional. The theme this one was made from, one step back: its name, author and license. For one of the app’s presets, its name alone. |
colors | The colors, below. |
Colors
Write a color as #RRGGBB or #RRGGBBAA (alpha last, as in CSS), in upper or lower case, with no spaces. Foreground, background and the 16 ANSI colors are required; the app fills in the others when they are missing. The app itself writes all 23, and #RRGGBB when a color is opaque.
| Key | Color | When missing |
|---|---|---|
foreground | Foreground (text) | Required |
background | Background | Required |
cursor | Cursor | The foreground |
selection | Selection | The foreground at 40% opacity (alpha 66) |
black | ANSI 0 | Required |
red | ANSI 1 | Required |
green | ANSI 2 | Required |
yellow | ANSI 3 | Required |
blue | ANSI 4 | Required |
magenta | ANSI 5 | Required |
cyan | ANSI 6 | Required |
white | ANSI 7 | Required |
brightBlack | ANSI 8 | Required |
brightRed | ANSI 9 | Required |
brightGreen | ANSI 10 | Required |
brightYellow | ANSI 11 | Required |
brightBlue | ANSI 12 | Required |
brightMagenta | ANSI 13 | Required |
brightCyan | ANSI 14 | Required |
brightWhite | ANSI 15 | Required |
searchHitBackground | Search hits | #FFB86C |
searchHitBackgroundCurrent | The current search hit | #FF79C6 |
searchHitForeground | Text of search hits | #282A36 |
Rules
The app, the theme link page and the theme gallery check the same rules. The schema says all of them but the file size, the trimming of names and the unique ids.
- A file is at most 64 KB (65,536 bytes of UTF-8).
- No fields other than the ones above, at any level. null is not a value: leave an optional field out instead.
- Names and authors (also in basedOn) lose their surrounding white space, then are 1 to 40 characters (Unicode code points).
- They are plain text: no control characters (U+0000–U+001F, U+007F–U+009F) and none that turn the direction of text (U+061C, U+200E, U+200F, U+202A–U+202E, U+2066–U+2069).
- The themes of a file each have their own id.
- A file with a required color missing or a color written wrong is refused, naming the color.
Licenses
Pick one of these SPDX IDs, written exactly so:
- CC0-1.0: anyone may use and change the theme, without credit.
- CC-BY-4.0 (the app’s default): anyone may use and change it, crediting you.
- MIT: anyone may use and change it, keeping the copyright and license notice.
When you make a theme from someone else’s, put theirs in basedOn. If theirs is CC-BY-4.0 or MIT, yours keeps the same license; if it is CC0-1.0, choose any.
Theme links
A theme link opens one theme in SSH Term: https://sshterm.iru-yo.com/theme#t=… where t is a v1 file holding one theme, compressed with deflate-raw (DEFLATE without a zlib or gzip header) and written in base64url without padding. The part after # never reaches the server.
It must expand to no more than 64 KB. In JavaScript (browsers and Node):
async function themeLink(theme) {
const doc = { format: 'ssh-term-theme', formatVersion: 1, themes: [theme] };
const json = new TextEncoder().encode(JSON.stringify(doc));
const stream = new Blob([json]).stream().pipeThrough(new CompressionStream('deflate-raw'));
const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
let binary = '';
for (const b of bytes) binary += String.fromCharCode(b);
const t = btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
return `https://sshterm.iru-yo.com/theme#t=${t}`;
}
In Python:
import base64, json, zlib
def theme_link(theme):
doc = {"format": "ssh-term-theme", "formatVersion": 1, "themes": [theme]}
deflate = zlib.compressobj(9, zlib.DEFLATED, -15)
raw = deflate.compress(json.dumps(doc, ensure_ascii=False).encode()) + deflate.flush()
t = base64.urlsafe_b64encode(raw).rstrip(b"=").decode()
return "https://sshterm.iru-yo.com/theme#t=" + t
From Windows Terminal and iTerm2
SSH Term also reads a Windows Terminal color scheme (JSON) and an iTerm2 .itermcolors file; the author is added in the app. To convert one yourself, take the colors from these keys:
| v1 | Windows Terminal | iTerm2 |
|---|---|---|
foreground | foreground | Foreground Color |
background | background | Background Color |
cursor | cursorColor | Cursor Color |
selection | selectionBackground | Selection Color |
black | black | Ansi 0 Color |
red | red | Ansi 1 Color |
green | green | Ansi 2 Color |
yellow | yellow | Ansi 3 Color |
blue | blue | Ansi 4 Color |
magenta | purple | Ansi 5 Color |
cyan | cyan | Ansi 6 Color |
white | white | Ansi 7 Color |
brightBlack | brightBlack | Ansi 8 Color |
brightRed | brightRed | Ansi 9 Color |
brightGreen | brightGreen | Ansi 10 Color |
brightYellow | brightYellow | Ansi 11 Color |
brightBlue | brightBlue | Ansi 12 Color |
brightMagenta | brightPurple | Ansi 13 Color |
brightCyan | brightCyan | Ansi 14 Color |
brightWhite | brightWhite | Ansi 15 Color |
iTerm2 gives each component as a number from 0 to 1: multiply it by 255 and round. Its Alpha Component, if there, is the alpha. The search hit colors have no counterpart: leave them out. An opaque selection color would hide the text under it, so SSH Term writes it with alpha 66 (40% opacity): do the same.
Example
A file with one theme:
{
"$schema": "https://sshterm.iru-yo.com/themes/schema/v1.json",
"format": "ssh-term-theme",
"formatVersion": 1,
"themes": [
{
"id": "8b0e5c3a-2f6d-4e91-a7c4-5d1f0b9e3a62",
"name": "Harbor Night",
"author": "SSH Term",
"license": "CC0-1.0",
"basedOn": {
"name": "Dracula"
},
"colors": {
"foreground": "#E6E8EF",
"background": "#14181F",
"cursor": "#7FD1C7",
"selection": "#E6E8EF66",
"black": "#1C222B",
"red": "#E86A6A",
"green": "#7FCB8A",
"yellow": "#E8C66A",
"blue": "#6A9FE8",
"magenta": "#C08AE6",
"cyan": "#6ACFD8",
"white": "#C9CED8",
"brightBlack": "#4A5363",
"brightRed": "#F28B8B",
"brightGreen": "#9BE0A5",
"brightYellow": "#F2D88B",
"brightBlue": "#8BB8F2",
"brightMagenta": "#D4A8F0",
"brightCyan": "#8BE3EA",
"brightWhite": "#F4F6FA",
"searchHitBackground": "#FFB86C",
"searchHitBackgroundCurrent": "#FF79C6",
"searchHitForeground": "#282A36"
}
}
]
}