diff --git a/layouts/shortcodes/agent-memory-demo.html b/layouts/shortcodes/agent-memory-demo.html index 825bc24604..3e9c9546d0 100644 --- a/layouts/shortcodes/agent-memory-demo.html +++ b/layouts/shortcodes/agent-memory-demo.html @@ -20,11 +20,12 @@ {{- /* ?v= is a hash of the file, so a reload never runs a stale cached copy. */ -}} {{- $v := dict -}} -{{- range slice "css/context-retriever-demo.css" "css/agent-memory-demo.css" "js/agent-memory-demo.js" -}} +{{- range slice "css/context-retriever-demo.css" "css/agent-memory-demo.css" "js/demo-highlight.js" "js/agent-memory-demo.js" -}} {{- $v = merge $v (dict . (substr (md5 (os.ReadFile (print "static/" .))) 0 10)) -}} {{- end }} +
diff --git a/layouts/shortcodes/context-retriever-demo.html b/layouts/shortcodes/context-retriever-demo.html index 21ab600f05..fa53ea545d 100644 --- a/layouts/shortcodes/context-retriever-demo.html +++ b/layouts/shortcodes/context-retriever-demo.html @@ -11,6 +11,7 @@ {{- if not (.Page.Store.Get "rcrAssetsEmitted") -}} + {{- .Page.Store.Set "rcrAssetsEmitted" true -}} {{- end -}} diff --git a/layouts/shortcodes/langcache-demo.html b/layouts/shortcodes/langcache-demo.html index 266a171b6b..8773fed1aa 100644 --- a/layouts/shortcodes/langcache-demo.html +++ b/layouts/shortcodes/langcache-demo.html @@ -21,11 +21,12 @@ {{- /* ?v= is a hash of the file, so a reload never runs a stale cached copy. */ -}} {{- $v := dict -}} -{{- range slice "css/context-retriever-demo.css" "css/langcache-demo.css" "js/langcache-demo.js" -}} +{{- range slice "css/context-retriever-demo.css" "css/langcache-demo.css" "js/demo-highlight.js" "js/langcache-demo.js" -}} {{- $v = merge $v (dict . (substr (md5 (os.ReadFile (print "static/" .))) 0 10)) -}} {{- end }} +
diff --git a/layouts/shortcodes/rdi-demo.html b/layouts/shortcodes/rdi-demo.html index 5e84b197a0..c6ca8cbe0a 100644 --- a/layouts/shortcodes/rdi-demo.html +++ b/layouts/shortcodes/rdi-demo.html @@ -20,11 +20,12 @@ {{- /* ?v= is a hash of the file, so a reload never runs a stale cached copy. */ -}} {{- $v := dict -}} -{{- range slice "css/context-retriever-demo.css" "css/rdi-demo.css" "js/rdi-demo.js" -}} +{{- range slice "css/context-retriever-demo.css" "css/rdi-demo.css" "js/demo-highlight.js" "js/rdi-demo.js" -}} {{- $v = merge $v (dict . (substr (md5 (os.ReadFile (print "static/" .))) 0 10)) -}} {{- end }} +
diff --git a/layouts/shortcodes/redisvl-demo.html b/layouts/shortcodes/redisvl-demo.html index 8cdbd8af9f..8872e6b601 100644 --- a/layouts/shortcodes/redisvl-demo.html +++ b/layouts/shortcodes/redisvl-demo.html @@ -21,11 +21,12 @@ {{- /* ?v= is a hash of the file, so a reload never runs a stale cached copy. */ -}} {{- $v := dict -}} -{{- range slice "css/context-retriever-demo.css" "css/redisvl-demo.css" "js/redisvl-demo.js" -}} +{{- range slice "css/context-retriever-demo.css" "css/redisvl-demo.css" "js/demo-highlight.js" "js/redisvl-demo.js" -}} {{- $v = merge $v (dict . (substr (md5 (os.ReadFile (print "static/" .))) 0 10)) -}} {{- end }} +
diff --git a/layouts/shortcodes/search-demo.html b/layouts/shortcodes/search-demo.html index 830ac148c0..df53a41fb6 100644 --- a/layouts/shortcodes/search-demo.html +++ b/layouts/shortcodes/search-demo.html @@ -22,12 +22,13 @@ {{- /* ?v= is a hash of the file, so a reload never runs a stale cached copy. */ -}} {{- $v := dict -}} -{{- range slice "css/context-retriever-demo.css" "css/search-demo.css" "css/search-index-demo.css" "js/search-demo.js" -}} +{{- range slice "css/context-retriever-demo.css" "css/search-demo.css" "css/search-index-demo.css" "js/demo-highlight.js" "js/search-demo.js" -}} {{- $v = merge $v (dict . (substr (md5 (os.ReadFile (print "static/" .))) 0 10)) -}} {{- end }} +
diff --git a/layouts/shortcodes/search-index-demo.html b/layouts/shortcodes/search-index-demo.html index e98cf86ca1..ac38955fc6 100644 --- a/layouts/shortcodes/search-index-demo.html +++ b/layouts/shortcodes/search-index-demo.html @@ -21,12 +21,13 @@ {{- /* ?v= is a hash of the file, so a reload never runs a stale cached copy. */ -}} {{- $v := dict -}} -{{- range slice "css/context-retriever-demo.css" "css/search-demo.css" "css/search-index-demo.css" "js/search-index-demo.js" -}} +{{- range slice "css/context-retriever-demo.css" "css/search-demo.css" "css/search-index-demo.css" "js/demo-highlight.js" "js/search-index-demo.js" -}} {{- $v = merge $v (dict . (substr (md5 (os.ReadFile (print "static/" .))) 0 10)) -}} {{- end }} +
diff --git a/static/css/context-retriever-demo.css b/static/css/context-retriever-demo.css index a7a81c4014..61ebeb1737 100644 --- a/static/css/context-retriever-demo.css +++ b/static/css/context-retriever-demo.css @@ -99,6 +99,22 @@ .rcr-jn { color: #b35900; } .rcr-jl { color: #c62828; } +/* Syntax highlighting from demo-highlight.js, in every demo's code blocks. The colors are Redis Insight's + Workbench colors, darkened where they're too light to read on a light background. */ +.rcr .hl-cmd { color: #7547de; } /* a command or language keyword */ +.rcr .hl-kw { color: #2c7d5d; } /* an option; Insight's #8cd7b9 */ +.rcr .hl-key { color: #c724a1; } /* an index name or URL; Insight's #de47bb */ +.rcr .hl-field { color: #b02c30; } /* an @field */ +.rcr .hl-query { color: #4c68d5; } /* a query; Insight's #7b90e0 */ +.rcr .hl-op { color: #167b80; } /* a query operator; Insight's #b9f0f3 */ +.rcr .hl-fn { color: #7d53e0; } /* a function in a query; Insight's #9e7ee8 */ +.rcr .hl-str { color: #a65b3c; } /* a string; Insight's dark #ce9178 */ +.rcr .hl-num { color: #567844; } /* a number; Insight's dark #b5cea8, as its light #098658 matches the option green */ +.rcr .hl-com { color: var(--rcr-faint); font-style: italic; } +.rcr .hl-faint { color: var(--rcr-faint); } +.rcr .hl-ok { color: #567844; font-weight: 600; } +.rcr .hl-err { color: var(--rcr-bad); } + /* ---- step 1: keys ---- */ .rcr-keylist { max-height: 420px; overflow: auto; padding: 6px; } .rcr-group summary { diff --git a/static/css/iris-dark.css b/static/css/iris-dark.css index 0015c2f474..cbef4fbbbd 100644 --- a/static/css/iris-dark.css +++ b/static/css/iris-dark.css @@ -275,6 +275,17 @@ body.iris-dark main .rcr .ram-type-episodic { body.iris-dark main .rcr .rcr-jl { color: #ff6b6b; } +/* Syntax highlighting, in Redis Insight's dark Workbench colors. Its @field red is lightened to read on the dark code background. */ +body.iris-dark main .rcr .hl-cmd { color: #c8b5f2; } +body.iris-dark main .rcr .hl-kw { color: #8cd7b9; } +body.iris-dark main .rcr .hl-key { color: #de47bb; } +body.iris-dark main .rcr .hl-field { color: #d65a5e; } +body.iris-dark main .rcr .hl-query { color: #7b90e0; } +body.iris-dark main .rcr .hl-op { color: #b9f0f3; } +body.iris-dark main .rcr .hl-fn { color: #9e7ee8; } +body.iris-dark main .rcr .hl-str { color: #ce9178; } +body.iris-dark main .rcr .hl-num, +body.iris-dark main .rcr .hl-ok { color: #b5cea8; } /* The Iris overview demo is also .rcr. Its highlighter chips and reply parts keep their pastel service colors, so the text on them stays dark. */ diff --git a/static/js/agent-memory-demo.js b/static/js/agent-memory-demo.js index 7c655f09da..05db732835 100644 --- a/static/js/agent-memory-demo.js +++ b/static/js/agent-memory-demo.js @@ -709,7 +709,7 @@ if (request) { body.appendChild(el("div", "rcr-lbl", "Request body")); body.appendChild(pre(request)); } body.appendChild(el("div", "rcr-lbl", "Response")); body.appendChild(pre(response)); - body.appendChild(el("pre", "rcr-code rcr-code-sm", esc(curl(method, path, request)))); + body.appendChild(el("pre", "rcr-code rcr-code-sm", window.DemoHL.shell(curl(method, path, request)))); } function chatCard(defs, n, extra) { var card = el("div", "rcr-card ram-chatcard"); @@ -1021,7 +1021,7 @@ body.appendChild(pre(response)); var cd = el("details", "rcr-curl"); cd.appendChild(el("summary", "", "Run this call yourself with curl")); - cd.appendChild(el("pre", "rcr-code rcr-code-sm", esc(curl(method, path, request)))); + cd.appendChild(el("pre", "rcr-code rcr-code-sm", window.DemoHL.shell(curl(method, path, request)))); body.appendChild(cd); d.appendChild(body); return d; diff --git a/static/js/context-retriever-demo.js b/static/js/context-retriever-demo.js index 3e94e9fb36..2285bfa975 100644 --- a/static/js/context-retriever-demo.js +++ b/static/js/context-retriever-demo.js @@ -641,7 +641,7 @@ renderTools(right); var idx = el("details", "rcr-card rcr-behind"); idx.appendChild(el("summary", "", "Behind the scenes: the search indexes the service creates")); - idx.appendChild(el("pre", "rcr-code", esc(indexCommands(state.model, state.surface).join("\n\n") || "No indexed fields, so no index is created."))); + idx.appendChild(el("pre", "rcr-code", indexHtml())); idx.appendChild(el("p", "rcr-note", "Tag fields use the INDEXMISSING option, so the database must run Redis 7.4 or later. " + "Tag fields used for access control, which this demo doesn't include, also get INDEXEMPTY. The index names are illustrative.")); p.appendChild(idx); @@ -678,7 +678,12 @@ var right = panels.model.querySelector(".rcr-tools"); if (right) { renderTools(right); right.classList.remove("rcr-flash"); void right.offsetWidth; right.classList.add("rcr-flash"); } var beh = panels.model.querySelector(".rcr-behind pre"); - if (beh) beh.textContent = indexCommands(state.model, state.surface).join("\n\n") || "No indexed fields, so no index is created."; + if (beh) beh.innerHTML = indexHtml(); + } + /* The FT.CREATE commands behind the tools, highlighted, or why there are none. */ + function indexHtml() { + var cmds = indexCommands(state.model, state.surface); + return cmds.length ? window.DemoHL.redis(cmds.join("\n\n")) : esc("No indexed fields, so no index is created."); } function entityCard(ent) { @@ -909,7 +914,7 @@ body.appendChild(el("pre", "rcr-code rcr-code-sm", jsonHtml(failed ? res : c.result))); var cd = el("details", "rcr-curl"); cd.appendChild(el("summary", "", "Run this call yourself with curl")); - cd.appendChild(el("pre", "rcr-code rcr-code-sm", esc(curlFor(c.name, c.args, c.id)))); + cd.appendChild(el("pre", "rcr-code rcr-code-sm", window.DemoHL.shell(curlFor(c.name, c.args, c.id)))); body.appendChild(cd); d.appendChild(body); return d; diff --git a/static/js/demo-highlight.js b/static/js/demo-highlight.js new file mode 100644 index 0000000000..e6cf3135a3 --- /dev/null +++ b/static/js/demo-highlight.js @@ -0,0 +1,170 @@ +/* ========================================================================= + Syntax highlighting for the scripted demos' code blocks + Dependency-free and shared: each demo's shortcode loads this before the + demo itself. Every function takes text exactly as a demo shows it and + returns it escaped, with spans the shared stylesheet colors. Redis commands + are colored the way Redis Insight's Workbench colors them, and the other + languages follow the same roles: + hl-cmd a command or language keyword, such as FT.SEARCH, curl, or import + hl-kw an option, such as SCHEMA or -H + hl-key an index name or URL + hl-field an @field + hl-query a query, quotes and all + hl-op a comparison or logical operator in a query; YAML list dashes + hl-fn a function in a query, such as ismissing + hl-str a string + hl-num a number + hl-com a comment + hl-faint framing that isn't the point: reply index numbers, (integer), + line continuations, and + Anything else, such as a key or a field name, stays plain, as in Insight. + The text itself never changes: strip the spans and it reads the same. + ========================================================================= */ +(function () { + "use strict"; + if (window.DemoHL) return; // loaded twice: the first copy already defined everything + + function esc(s) { + return String(s).replace(/[&<>"']/g, function (c) { + return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; + }); + } + function span(cls, s) { return cls ? '' + esc(s) + "" : esc(s); } + var NUM = /^(-?\d+(\.\d+)?|[+-]inf)$/; + var UPPER = /^[A-Z][A-Z0-9_]*$/; + + /* ---- Redis ---- */ + + /* One argument of a Redis command, by its role. `t` is the argument as you'd type it, quotes and all. + As in Insight, options are colored only in FT commands. */ + function arg(argv, i, t) { + var a = argv[i], cmd = argv[0], ft = /^FT\./i.test(cmd), cls = ""; + if (i === 0) cls = "hl-cmd"; + else if (ft && i === 1) cls = "hl-key"; // the index + else if (ft && isQuery(cmd, i, argv[i - 1], t !== a)) return query(t); + else if (t !== a) cls = "hl-str"; + else if (NUM.test(a)) cls = "hl-num"; + else if (a.charAt(0) === "@") cls = "hl-field"; + else if (ft && UPPER.test(a)) cls = "hl-kw"; + return span(cls, t); + } + /* Whether an FT command's argument is a query: FT.SEARCH's and FT.AGGREGATE's, or the quoted expression after FILTER or APPLY. */ + function isQuery(cmd, i, prev, quoted) { + return i === 2 && /^FT\.(SEARCH|AGGREGATE)$/i.test(cmd) || quoted && /^(FILTER|APPLY)$/i.test(prev); + } + /* A query, quotes and all. Insight colors it as one, with @fields, comparison and logical operators, and functions picked out, + and parentheses plain. */ + function query(t) { + var re = /(@[A-Za-z_$][\w.$]*)|(==|!=|<=|>=|&&|\|\||[<>])|([A-Za-z_]\w*(?=\())|([()])/g, out = "", at = 0, m; + while ((m = re.exec(t))) { + if (m.index > at) out += span("hl-query", t.slice(at, m.index)); + out += span(m[1] ? "hl-field" : m[2] ? "hl-op" : m[3] ? "hl-fn" : "", m[0]); + at = re.lastIndex; + } + return out + (at < t.length ? span("hl-query", t.slice(at)) : ""); + } + /* A reply as redis-cli prints it: index numbers and (integer) faint, strings, integers, OK, and errors colored. */ + function reply(text) { + return text.split("\n").map(function (ln) { + var m = ln.match(/^((?:\s*\d+\)\s)*)(.*)$/), idx = m[1], rest = m[2], body; + if (/^\(integer\) /.test(rest)) body = span("hl-faint", "(integer)") + " " + span("hl-num", rest.slice(10)); + else if (/^\(error\) /.test(rest)) body = span("hl-err", rest); + else if (/^"/.test(rest)) body = span("hl-str", rest); + else if (/^\((nil|empty array)\)$/.test(rest)) body = span("hl-faint", rest); + else if (rest === "OK") body = span("hl-ok", rest); + else body = esc(rest); + return (idx ? span("hl-faint", idx) : "") + body; + }).join("\n"); + } + /* Redis commands written out as text: one or more, separated by blank lines, with \ continuations and . + As in Insight, a query that starts a line of its own is colored as a plain string. */ + function redis(text) { + var re = /(\s+)|(\\(?=\s|$))|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(<[^>\n]*>)|([^\s"']+)|([\s\S])/g, m, out = "", n = 0, cmd = "", ft = false, prev = "", nl = false; + while ((m = re.exec(text))) { + if (m[1]) { if (/\n\s*\n/.test(m[1])) n = 0; nl = /\n/.test(m[1]); out += esc(m[1]); continue; } + if (m[2]) { out += span("hl-faint", m[2]); continue; } + if (m[6]) { out += esc(m[6]); continue; } // a quote with no closing quote, kept as it is + var t = m[0], cls = ""; + if (n === 0) { cmd = t; ft = /^FT\./i.test(t); cls = "hl-cmd"; } + else if (m[4]) cls = "hl-faint"; + else if (ft && n === 1) cls = "hl-key"; + else if (ft && !nl && isQuery(cmd, n, prev, !!m[3])) { out += query(t); prev = t; n++; continue; } + else if (m[3]) cls = "hl-str"; + else if (NUM.test(t)) cls = "hl-num"; + else if (t.charAt(0) === "@") cls = "hl-field"; + else if (ft && UPPER.test(t)) cls = "hl-kw"; + out += span(cls, t); + prev = t; + nl = false; + n++; + } + return out; + } + + /* ---- Python ---- */ + + var PY_KW = /^(from|import|as|def|return|class|if|elif|else|for|while|in|not|and|or|is|with|try|except|finally|raise|lambda|yield|pass|None|True|False)$/; + /* Python: keywords, strings, numbers, and comments. */ + function python(text) { + var re = /(#[^\n]*)|("""[\s\S]*?"""|'''[\s\S]*?'''|"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*')|(\b\d+(?:\.\d+)?\b)|([A-Za-z_][\w]*)|([\s\S])/g, m, out = ""; + while ((m = re.exec(text))) { + if (m[1]) out += span("hl-com", m[1]); + else if (m[2]) out += span("hl-str", m[2]); + else if (m[3]) out += span("hl-num", m[3]); + else if (m[4]) out += span(PY_KW.test(m[4]) ? "hl-cmd" : "", m[4]); + else out += esc(m[5]); + } + return out; + } + + /* ---- YAML ---- */ + + /* One line of YAML: list dashes, the value after a key, and comments. */ + function yaml(line) { + var m = line.match(/^(\s*)(- )?(?:([\w.$-]+)(:)(?=\s|$))?(\s*)(.*)$/), out = esc(m[1]); + if (m[2]) out += span("hl-op", m[2]); + if (m[3]) out += esc(m[3] + m[4]); + out += esc(m[5]); + var v = m[6], c = v.match(/^(.*?)(\s+#.*|^#.*)$/), com = ""; + if (c) { v = c[1]; com = c[2]; } + if (v) out += span(/^["']/.test(v) ? "hl-str" : NUM.test(v) ? "hl-num" : /^(true|false|null|~)$/.test(v) ? "hl-cmd" : m[3] || m[2] ? "hl-str" : "", v); + if (com) out += span("hl-com", com); + return out; + } + + /* ---- shell and SQL ---- */ + + /* A shell command such as curl: the command, its flags and methods, URLs, strings, and \ continuations. */ + function shell(text) { + var re = /(\s+)|(\\(?=\n|$))|("(?:[^"\\]|\\.)*"|'(?:[^']|'\\'')*')|([^\s"']+)|([\s\S])/g, m, out = "", first = true; + while ((m = re.exec(text))) { + if (m[1]) { out += esc(m[1]); continue; } + if (m[2]) { out += span("hl-faint", m[2]); continue; } + if (m[5]) { out += esc(m[5]); continue; } // a quote with no closing quote, kept as it is + var t = m[0], cls; + if (m[3]) cls = /^["'](https?:|\$\w+\/)/.test(t) ? "hl-key" : "hl-str"; // a URL, including one that starts with a $VARIABLE + else if (first) cls = "hl-cmd"; + else if (/^--?[A-Za-z]/.test(t)) cls = "hl-kw"; + else if (/^(GET|POST|PUT|PATCH|DELETE)$/.test(t)) cls = "hl-kw"; + else if (/^https?:/.test(t)) cls = "hl-key"; + else cls = ""; + first = false; + out += span(cls, t); + } + return out; + } + var SQL_KW = /^(INSERT|INTO|VALUES|UPDATE|SET|WHERE|DELETE|FROM|SELECT|AND|OR|NOT|NULL|AS|ON|JOIN|ORDER|BY|LIMIT|RETURNING|BEGIN|COMMIT|TRUE|FALSE)$/i; + /* SQL: keywords, strings, and numbers. */ + function sql(text) { + var re = /('(?:[^']|'')*')|(\b\d+(?:\.\d+)?\b)|([A-Za-z_][\w]*)|([\s\S])/g, m, out = ""; + while ((m = re.exec(text))) { + if (m[1]) out += span("hl-str", m[1]); + else if (m[2]) out += span("hl-num", m[2]); + else if (m[3]) out += span(SQL_KW.test(m[3]) ? "hl-cmd" : "", m[3]); + else out += esc(m[4]); + } + return out; + } + + window.DemoHL = { esc: esc, arg: arg, query: query, reply: reply, redis: redis, python: python, yaml: yaml, shell: shell, sql: sql }; +})(); diff --git a/static/js/langcache-demo.js b/static/js/langcache-demo.js index 06aa15e983..947db5f6c6 100644 --- a/static/js/langcache-demo.js +++ b/static/js/langcache-demo.js @@ -532,7 +532,7 @@ body.appendChild(response == null ? el("p", "rcr-faint", "No content.") : pre(response)); var cd = el("details", "rcr-curl"); cd.appendChild(el("summary", "", "Run this call yourself with curl")); - cd.appendChild(el("pre", "rcr-code rcr-code-sm", esc(curl(method, path, request)))); + cd.appendChild(el("pre", "rcr-code rcr-code-sm", window.DemoHL.shell(curl(method, path, request)))); body.appendChild(cd); d.appendChild(body); return d; @@ -780,7 +780,7 @@ body.appendChild(pre(r.response)); var cd = el("details", "rcr-curl"); cd.appendChild(el("summary", "", "Run this call yourself with curl")); - cd.appendChild(el("pre", "rcr-code rcr-code-sm", esc(curl("POST", "/entries/search", r.request)))); + cd.appendChild(el("pre", "rcr-code rcr-code-sm", window.DemoHL.shell(curl("POST", "/entries/search", r.request)))); body.appendChild(cd); d.appendChild(body); rows.appendChild(d); diff --git a/static/js/rdi-demo.js b/static/js/rdi-demo.js index 584d48f1d1..b5ce239bd9 100644 --- a/static/js/rdi-demo.js +++ b/static/js/rdi-demo.js @@ -679,7 +679,7 @@ var cfg = remember(el("details", "rdi-file"), "config"); cfg.appendChild(el("summary", "", "Show " + code("config.yaml"))); cfg.appendChild(el("pre", "rcr-code rdi-yaml", '' + configYaml(st.structure).split("\n").map(function (l) { - return '' + esc(l) + ""; }).join("") + "")); + return '' + window.DemoHL.yaml(l) + ""; }).join("") + "")); files.appendChild(cfg); if (st.deployed && ctx.done) { var stx = remember(el("details", "rdi-file"), "status"); @@ -820,7 +820,7 @@ li.appendChild(el("div", "rdi-stephead", '' + (i + 1) + '' + esc(x.stage) + "" + esc(x.title))); var body = el("div", "rdi-stepbody"); - if (x.sql) body.appendChild(el("code", "rdi-stepsql", esc(x.sql))); + if (x.sql) body.appendChild(el("code", "rdi-stepsql", window.DemoHL.sql(x.sql))); if (x.text) body.appendChild(el("div", "rdi-steptext", x.text)); if (x.diff.length) body.appendChild(el("div", "rdi-diff", x.diff.map(function (f) { return '
' + esc(f.field) + ' ' + esc(f.from) + @@ -911,7 +911,7 @@ "through the pipeline into Redis.")); p.appendChild(pipelineCard(ctx)); p.appendChild(optionsCard("Run a statement in PostgreSQL", STATEMENTS, function (s) { return st.ran.indexOf(s.key) >= 0; }, - function (s) { return "" + esc(sqlOf(s)) + ""; }, runStatement)); + function (s) { return "" + window.DemoHL.sql(sqlOf(s)) + ""; }, runStatement)); var ran = st.ran.map(function (k) { return byKey(STATEMENTS, k); }); if (ran.length) p.appendChild(changesCard(ctx, ran, STATEMENTS.length)); p.appendChild(grid(sourceCard(ctx), redisCard(ctx))); @@ -961,7 +961,7 @@ var start = /^transform:/.test(line) ? 1 : /^ key:/.test(line) ? 2 : 0; if (start) block = start; else if (/^output:/.test(line)) block = 0; - html += '' + (start ? '' + start + "" : "") + esc(line) + ""; + html += '' + (start ? '' + start + "" : "") + window.DemoHL.yaml(line) + ""; }); /* YAML depends on indentation, so lines never wrap: the file scrolls sideways instead, and the body spans the widest line so the diff --git a/static/js/redisvl-demo.js b/static/js/redisvl-demo.js index abe493df5e..e5afa05519 100644 --- a/static/js/redisvl-demo.js +++ b/static/js/redisvl-demo.js @@ -175,12 +175,13 @@ var m = line.match(/^ - name: (\S+)/); if (m) cur = m[1]; else if (!/^ {4}/.test(line)) cur = null; - return used.indexOf(cur) >= 0 ? '' + esc(line) + "" : esc(line); + var html = window.DemoHL.yaml(line); + return used.indexOf(cur) >= 0 ? '' + html + "" : html; }).join("\n"); } - /* Code with "restaurants.yaml" as a link to the schema. */ + /* The Python, highlighted, with "restaurants.yaml" as a link to the schema. */ function withYamlLink(text) { - return esc(text).replace(/"restaurants\.yaml"/g, ''); + return window.DemoHL.python(text).replace(/"restaurants\.yaml"/g, ''); } /* --------------------------------------------------------------- the widget */ @@ -382,7 +383,7 @@ card.appendChild(head); var used = fieldsUsed(q, built); if (st.view !== "data") { - var pre = el("pre", "rcr-code rvd-pre", st.view === "python" ? withYamlLink(pythonFor(q, built)) : st.view === "redis" ? esc(commandFor(built)) : schemaHtml(used)); + var pre = el("pre", "rcr-code rvd-pre", st.view === "python" ? withYamlLink(pythonFor(q, built)) : st.view === "redis" ? window.DemoHL.redis(commandFor(built)) : schemaHtml(used)); pre.addEventListener("click", function (e) { if (e.target.closest(".rvd-yaml")) { st.view = "schema"; render(); } }); card.appendChild(pre); } diff --git a/static/js/search-demo.js b/static/js/search-demo.js index f0539f0316..07fa0eca7a 100644 --- a/static/js/search-demo.js +++ b/static/js/search-demo.js @@ -155,6 +155,10 @@ } function cmdline(argv) { return argv.map(function (a, i) { return token(argv, i); }).join(" "); } + /* Syntax highlighting comes from demo-highlight.js, which the shortcode loads first. */ + function hl(argv, i) { return window.DemoHL.arg(argv, i, token(argv, i)); } + function hlReply(text) { return window.DemoHL.reply(text); } + /* ------------------------------------------------ step 3: filter and sort */ function filterQuery(f) { @@ -416,7 +420,7 @@ /* A transcript line: the prompt, the command, and its reply. */ function line(argv, reply) { - return '> ' + cmdHtml(argv) + "\n" + esc(reply); + return '> ' + cmdHtml(argv) + "\n" + hlReply(reply); } /* A key as written and as read back, exactly as redis-cli prints it. */ function docBox(key) { @@ -445,7 +449,7 @@ box.appendChild(el("div", "rsi-createhead", "The index, part by part")); lines.forEach(function (l) { var toks = []; - for (var j = l.from; j <= l.to; j++) toks.push(esc(token(argv, j))); + for (var j = l.from; j <= l.to; j++) toks.push(hl(argv, j)); box.appendChild(el("div", "rsi-cline", '' + toks.join(" ") + '' + l.note + "")); }); var foot = el("div", "rcr-faint rsi-createcap", "One part per line here. In redis-cli, it's a single line. "), copy = el("button", "rsi-copy", "Copy as one line"); @@ -491,7 +495,7 @@ function outputBlock(c) { var box = el("div", "rsi-create rsi-output"); box.appendChild(el("div", "rsi-createhead", "The output")); - box.appendChild(el("pre", "rcr-code rsq-pre rsq-cmdline", esc(c.reply.replace(/\n$/, "")))); + box.appendChild(el("pre", "rcr-code rsq-pre rsq-cmdline", hlReply(c.reply.replace(/\n$/, "")))); box.appendChild(el("div", "rcr-faint rsi-createcap", esc("Exactly what redis-cli prints." + (st.tab === "text" ? " The tags around matched words come from HIGHLIGHT." : "")))); return box; @@ -506,7 +510,7 @@ } function cmdHtml(argv) { var m = marks(argv); - return argv.map(function (a, i) { var t = esc(token(argv, i)); return m[i] ? '' + t + "" : t; }).join(" "); + return argv.map(function (a, i) { var t = hl(argv, i); return m[i] ? '' + t + "" : t; }).join(" "); } render(); diff --git a/static/js/search-index-demo.js b/static/js/search-index-demo.js index ef66dd3cd3..7d06d7f24b 100644 --- a/static/js/search-index-demo.js +++ b/static/js/search-index-demo.js @@ -233,6 +233,10 @@ } function cmdline(argv) { return argv.map(function (a, i) { return token(argv, i); }).join(" "); } + /* Syntax highlighting comes from demo-highlight.js, which the shortcode loads first. */ + function hl(argv, i) { return window.DemoHL.arg(argv, i, token(argv, i)); } + function hlReply(text) { return window.DemoHL.reply(text); } + /* --------------------------------------------------------------- the widget */ var STEPS = [ @@ -529,7 +533,7 @@ /* A transcript line: the prompt, the command, and, when asked, its reply. */ function line(argv, reply, withReply) { - return '> ' + cmdHtml(argv) + "\n" + (withReply ? esc(reply) : ""); + return '> ' + cmdHtml(argv) + "\n" + (withReply ? hlReply(reply) : ""); } /* A key as written and as read back, exactly as redis-cli prints it. */ function docBox(key) { @@ -567,7 +571,7 @@ box.appendChild(el("div", "rsi-createhead", "The index, part by part")); lines.forEach(function (l) { var toks = []; - for (var j = l.from; j <= l.to; j++) { var t = esc(token(argv, j)); toks.push(m[j] ? '' + t + "" : t); } + for (var j = l.from; j <= l.to; j++) { var t = hl(argv, j); toks.push(m[j] ? '' + t + "" : t); } box.appendChild(el("div", "rsi-cline", '' + toks.join(" ") + '' + l.note + "")); }); var foot = el("div", "rcr-faint rsi-createcap", "One part per line here. In redis-cli, it's a single line. "), copy = el("button", "rsi-copy", "Copy as one line"); @@ -609,7 +613,7 @@ function outputBlock(c) { var box = el("div", "rsi-create rsi-output"), cap = "Exactly what redis-cli prints."; box.appendChild(el("div", "rsi-createhead", "The output")); - box.appendChild(el("pre", "rcr-code rsq-pre rsq-cmdline" + (c.error ? " is-err" : ""), esc(c.reply.replace(/\n$/, "")))); // an error reply in red + box.appendChild(el("pre", "rcr-code rsq-pre rsq-cmdline" + (c.error ? " is-err" : ""), hlReply(c.reply.replace(/\n$/, "")))); // an error reply in red if (c.query[0] === "FT.INFO") cap += " Sizes, timings, and counters vary from server to server."; else if (st.tab === "keys") cap += " The keys come back in Redis's own order."; box.appendChild(el("div", "rcr-faint rsi-createcap", cap)); @@ -630,7 +634,7 @@ } function cmdHtml(argv) { var m = marks(argv); - return argv.map(function (a, i) { var t = esc(token(argv, i)); return m[i] ? '' + t + "" : t; }).join(" "); + return argv.map(function (a, i) { var t = hl(argv, i); return m[i] ? '' + t + "" : t; }).join(" "); } render(); }