SHLS (Sebis Hierarchical List Structure) writes structured data without quotes, brackets, colons or repeated keys: each section names its keys once, and the records below list only the values. The SHLS Editor shows the code and, next to it, each section's key block and records. Change values, keys and records right there, see problems by line, convert to and from JSON, YAML and CSV, and see how many tokens SHLS saves against JSON.
Everything runs in your browser. Documents are not uploaded, there is no sign-up and no advertising.
A section starts with its name. Its key block lists the keys, indented by one space; the keys of a nested list one level deeper. After exactly one blank line follow the records: one value per line, in the order of the keys. A nested list has a line of its own (its key, or PH as a placeholder) with its records one level deeper. All values are text - converting them to numbers or dates is up to the program that reads them.
Product
Name
Price
Smartphone A1
599.99
Laptop B2
1299.99
Company
Name
Location
Country
City
TechCorp
PH
USA
San Francisco
The same data as JSON repeats every key in every record and needs quotes, brackets and colons. SHLS was made for local and cloud AI models, where every token counts. SHLS by Sebastian Grünwald - the format, its rules and the reference SHLSDecoder and SHLSEncoder (Python) are in the SHLS repository on GitHub.
Highlighted SHLS on the left - sections, keys, values, nesting levels and PH placeholders. In the middle each section's key block and its records, the keys as labels. A click jumps to the code, the code cursor marks its record.
Change values, keys and section names; add, duplicate, move and remove records and keys. Records follow their key block: a new key gets "-" in every record. One step for Undo each.
The editor reads SHLS exactly as SHLSDecoder does and lists what it would read differently than it looks: a record with a value too few or too many, indentation, blank lines after the key block, a key twice.
The Convert tab counts the approximate tokens of your SHLS and of the same data as JSON, so you see the saving at a glance.
To and from JSON exactly as SHLSDecoder and SHLSEncoder, YAML, CSV (one section as a table) and JSON Lines. JSON, YAML, CSV and JSON Lines files open as SHLS.
Keep any number of documents in your browser, with their own undo history, and download them all as a ZIP.
Any https page (and localhost) can embed the editor with an <iframe> and ?embed=1 - no sign-up, no key. Embedded, the editor hides this text and shows a Save button. There are two ways to get the document in and out.
src= opens a file (your server must allow CORS), webhook= makes "Save" (Ctrl+S) send the document to that address. Add &token=… for an Authorization: Bearer … header and &auto to save after every change.
<iframe src="https://shls.giit.ch/?embed=1&src=https://your-site.com/data/products.shls&webhook=https://your-site.com/save.php"
allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
The webhook gets a POST with JSON { editor: "shls", event: "save", id, name, content, updated }; content is the document as a string. A small PHP script that stores it:
<?php
header('Access-Control-Allow-Origin: https://shls.giit.ch');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
header('Access-Control-Allow-Methods: POST, OPTIONS');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
if (($_SERVER['HTTP_AUTHORIZATION'] ?? '') !== 'Bearer SECRET') { http_response_code(403); exit; }
$d = json_decode(file_get_contents('php://input'), true);
$name = preg_replace('/[^\w\- ]/u', '_', $d['name']);
file_put_contents(__DIR__ . "/data/$name.shls", $d['content']);
echo '{"ok":true}';
The editor says shlseditor:ready, your page answers with shlseditor:load, the editor reports shlseditor:loaded, shlseditor:change on real edits and shlseditor:save when the user saves. Your page may send shlseditor:get (the current state) or shlseditor:save (press Save). Saving then goes through your own login and session.
<iframe id="ed" src="https://shls.giit.ch/?embed=1" allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
<script>
const ed = document.getElementById('ed');
const ORIGIN = 'https://shls.giit.ch';
addEventListener('message', async e => {
if (e.source !== ed.contentWindow || e.origin !== ORIGIN) return;
const m = e.data;
if (m.type === 'shlseditor:ready') { // the editor is ready: hand it the document
ed.contentWindow.postMessage({
type: 'shlseditor:load',
name: 'Products',
content: await (await fetch('/data/products.shls')).text()
}, ORIGIN);
}
if (m.type === 'shlseditor:change') document.title = '● not saved'; // a real edit
if (m.type === 'shlseditor:save') { // the user clicked "Save"
await fetch('/api/save', { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: m.name, content: m.content }) });
document.title = 'Saved';
}
});
// A save button on your own page: the answer comes as shlseditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'shlseditor:get' }, ORIGIN); }
</script>
AI Assistant inside your page: it works in the frame too. With allow="clipboard-write" on the iframe, "Copy prompt" uses the clipboard directly (otherwise the editor falls back to the old copy command, or shows the prompt to copy by hand). A link the AI sends cannot reach an editor inside another website, so the editor tells the AI and the user to paste the AI's whole answer into the editor instead - or open the editor in a window of its own.
window.open("https://shls.giit.ch/?embed=window", "shlseditor-17") opens the full editor in its own window and speaks the same messages with window.opener. Links from an AI reach it there. shlseditor:load may also carry context: { host, title, rules } - the AI prompt then says where the document belongs and follows your rules - and hook: { url, token }, a webhook for this document only that is never stored. Closing the window with unsaved changes sends shlseditor:closing.
Hand the editor your own prompt, an address on your site and a key for this editor session. The AI dialog then shows "Connected to" your site, what you tell the AI, and the choice change the current document or make a new one; Copy prompt puts it all together. The AI posts its result with the key to your server, and your page opens it in the editor - a new document opens separately and stays in the user's browser. One key per editor session: it stays valid while the editor is open, and only while it has none the editor asks for it with shlseditor:key-request. The editor never sends the key itself and never stores it; the address must be https on your own host or a subdomain of it.
const key = async () => (await fetch('/api/ai-key.php?doc=17')).json(); // one key per editor session
const k = await key();
ed.contentWindow.postMessage({ type: 'shlseditor:load', name: 'Products', content, ai: {
label: 'My Site', prompt: 'Keep every key. Only add records.',
endpoint: 'https://your-site.com/api/ai-result.php', key: k.key } }, ORIGIN);
// remember the id from shlseditor:loaded and ignore change/save/closing with another id
// only while the editor has no key:
if (m.type === 'shlseditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'shlseditor:ai', key: k.key }, ORIGIN); }
// while the editor is open, tell your server every minute; closing the editor or the page voids the key
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// your server has a result (polling, SSE …): target is "current" or "new"
ed.contentWindow.postMessage({ type: 'shlseditor:load', name: 'Products', content: draft, from: 'ai', target }, ORIGIN);
Make keys with at least 128 random bits, store only their hash, bind each to the logged-in user, the document and this editor session, make one per session, keep it valid only while your page reports the session as open, and void it when the editor or the page closes.
The whole protocol is in llms.txt - give it to an AI and it builds the connection for your system. The JSON Editor works the same way with the prefix jsoneditor: - json.giit.ch.
SHLS (Sebis Hierarchical List Structure) is a text format for structured data by Sebastian Grünwald. Each section names its keys once in a key block; the records below list only the values, one per line, and indentation shows the nesting. Without quotes, brackets, colons or repeated keys, the same data needs far fewer tokens than JSON - which makes it a good format for AI models.
No. The editor runs entirely in your browser. Your documents are never sent to a server, unless you set up a webhook to your own server yourself.
Yes. The editor reads and writes SHLS exactly as SHLSDecoder and SHLSEncoder of the SHLS repository do - the JSON in the Convert tab is the data the Python classes give. Where they read a document differently than it looks (a record with a value too few, a blank line with spaces), the editor lists it as a problem with its line.
Yes. Click a value, a key or a section name in the middle, type, and press Enter. The buttons above the records add, duplicate, move and remove records and keys; when you add or remove a key, every record of the section gets or loses its value. Each change is one step for Undo.
The Convert tab counts the approximate tokens of your document as SHLS and of the same data as JSON. For tables of records SHLS often needs less than half of the tokens of indented JSON, because the keys are written only once.
Yes. Open a .json, .yaml, .csv or .jsonl file and it becomes SHLS, written as SHLSEncoder writes it; the editor says what SHLS cannot keep (empty values, values with line breaks). The Convert tab and Export write JSON, YAML, CSV, JSON Lines and plain text.
Yes, any AI - ChatGPT, Claude, Gemini, Copilot or the AI in your coding tool. Click "AI Assistant", then "Copy prompt": the prompt explains SHLS, so any AI writes valid SHLS. The AI answers with a link; click it and the document appears in your editor. "Undo" takes every change back.
No. The editor is free, without ads and without sign-up.