The colouring alone, with none of the machinery: no Web Worker, no validation, and no
xql-data.json. This page loads xql-lang.js and the
xql-highlight.json word lists, and nothing else.
const xql = XQLLang.registerHighlighting(monaco, highlightData);
const html = await monaco.editor.colorize(query, xql.languageId, {});
colorize() returns an HTML string, so these are real tokenizer output rather than
editor instances — cheap enough to put dozens on a page.
The whole thing, start to finish. Serve it next to the lib/ folder from
xql-monaco.zip — it needs xql-lang.js and
xql-highlight.json, and nothing else.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Highlighted XQL block</title>
<style>
body { background: #1e293b; color: #eaebeb; font-family: sans-serif; padding: 24px; }
.xql {
background: #172032;
border: 1px solid #334155;
border-radius: 6px;
padding: 12px 14px;
overflow-x: auto;
font: 13px/1.55 "SF Mono", Menlo, Consolas, monospace;
white-space: pre;
}
</style>
</head>
<body>
<div class="xql" id="out">loading…</div>
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js"></script>
<script src="lib/xql-lang.js"></script>
<script>
var VS = "https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs";
var QUERY = [
"// Rare user-agent strings, by process",
"dataset = xdr_data",
"| filter event_type = ENUM.NETWORK and action_process_image_name contains \"powershell\"",
"| alter ua = json_extract(action_network_http, \"$.headers.User-Agent\")",
"| comp count() as hits by action_process_image_name, ua",
"| filter hits < 10",
"| sort desc hits"
].join("\n");
require.config({ paths: { vs: VS } });
require(["vs/editor/editor.main"], function () {
// xql-highlight.json is just the word lists the tokenizer matches on (~175 KB). The full
// xql-data.json is only needed for completion and hover — see snippet-editor.html.
fetch("lib/xql-highlight.json").then(function (r) { return r.json(); }).then(function (data) {
var xql = XQLLang.registerHighlighting(monaco, data);
monaco.editor.setTheme(xql.themes.dark); // or xql.themes.light
// colorize() returns an HTML string, so there is no editor instance here — cheap enough
// to put dozens of these on one page.
monaco.editor.colorize(QUERY, xql.languageId, {}).then(function (html) {
document.getElementById("out").innerHTML = html;
});
});
});
</script>
</body>
</html>
colorize() emits <span class="mtkN">
elements and Monaco supplies the colours for them, so your container only needs a monospace
font, white-space: pre and a background. Switching theme with
monaco.editor.setTheme() changes that colour map, so call colorize()
again afterwards.
Pick one to colourise it.