Redis Workbench - #3862
Conversation
"Try it" no longer sends the reader to redis.io/cli. It opens a console docked to
the bottom of the page — a full-width "Workbench" bar that expands into a live
terminal plus a Redis Insight-style key browser — and runs the snippet there.
layouts/partials/cli-assets.html loads cli.js + the workbench, in one place
so a new page type picks up all of it
static/js/redis-workbench.js the dock
static/css/redis-workbench.css its styles, all scoped under .rwb
layouts/partials/tabs/wrapper.html the button prefers the dock, and falls back
layouts/shortcodes/redis-cli.html to opening redis.io/cli as before
The dock is one per page, not one per snippet: each "Try it" runs into the same
session on top of what is already there, keys and transcript accumulating, so a
reader can follow a page's examples in order and watch the keyspace build up.
"Clear keys" (FLUSHDB, transcript with it) and "Clear terminal" are the ways back.
Keys are discovered, not scanned. The sandbox database is shared by every visitor
(~300k keys) and the backend rewrites SCAN to MATCH <session>:*, which Redis
applies after walking the table — one iteration at COUNT 10000 comes back empty,
so enumerating a handful of keys would take dozens of round trips. Instead the
dock asks the server where each executed command put its keys (COMMAND GETKEYS)
and tracks those names; indexes come from FT._LIST, which the backend scopes to
the session. Nothing is probed until the reader opens the panel: batches that run
while it is closed are only remembered, and discovery catches up in one pass.
Value views read the way someone would read by hand — GET, HGETALL, SMEMBERS, the
whole LRANGE — and only fall back to a bounded or cursor form when the value is
too big to show, saying what it left out. That matters because the commands are
shown to the reader ("Read with"), which is the part of the browser that teaches.
Two things worth knowing:
* The dock needs window.RedisCli, which the /cli backend's widget only
publishes from an unreleased version (see the redis-clinterwebz branch of the
same name). Against production it stays dormant and "Try it" behaves exactly
as it does today, so this is safe to ship ahead of that backend.
* static/js/cli.js therefore points a LOCALLY served docs site at a local
backend by default — marked TEMPORARY, with the one line to delete once the
backend ships. Until then, `hugo server` without that backend running has no
working terminals.
Verified in a headless browser against the real sandbox: all thirteen key types
render, the bar spans the viewport and reserves its strip so the footer is not
covered, the panel clears the sticky header at full height (both learned the hard
way — the footer is z-50 and the header sticky z-50), drag and keyboard resize,
state persists across pages, and the old-backend fallback still opens the
standalone CLI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds a Notebook pane to the workbench dock for pages built from the
jupyter-example shortcode (/develop/clients/redis-py and friends), and
moves running out of the page and into it.
The page goes back to being ordinary code blocks. Thebe rewrites what it
activates — highlighted code becomes an editor with its own run/restart
controls — so the runnable cell is adopted into the dock at load and the
page keeps a plain clone of the block (.thebe-static), with one "Try it"
that opens the dock on the Notebook tab. "Run here" and "Run in browser"
are gone; Try it falls back to opening the BinderHub notebook where the
CLI widget has no workbench API. The full-file view is a single unmarked
listing: rendering it per section to highlight the current step drew a
band over part of the file and gave each section its own padding, which
read as blank lines the file does not have.
The pane reads Thebe's own record rather than guessing. An output wrapper
appears when a request is SENT, the driver clears its `loading` class on
every queued cell as soon as any one goes idle, and an editor exists
before a kernel is ever asked for — all of which had cells ticked off as
run when the connection had dropped. `executionCount` from the kernel's
execute_reply is the one signal that means "this ran", and it supplies
the In[n]/Out[n] a real notebook would print. A dropped execution says so
instead of showing a count.
Run all hands the whole queue to the kernel instead of waiting for each
cell in turn, and Clear notebook goes through Thebe's cell.clear() —
emptying the container detaches the widget Thebe renders into, so every
later result landed in an orphaned node.
Two fixes to the Thebe integration behind "if I refresh, keep the
kernel":
- saveSession() stores no appendToken, so on restore makeSettings()
recomputes it as false (page and server share a hostname through
/jupyterhub/) and the kernel websocket goes out without its token.
The user server refuses cookie-only auth for a websocket, so the
handshake was rejected and retried forever: a kernel that reported
ready and could not run anything. The flag now goes into the saved
record, which makeSettings spreads over its own default.
- The session was discarded on any "disconnected"/"Connection lost"
signal, including transient ones, so a refresh launched a new pod.
Expiry now asks the server first, with redirect: 'manual' — a pod
that is gone answers 302 to a login page, which a followed redirect
reports as alive.
Clear keys and Clear terminal move into the Terminal pane, beside what
they act on, as Run all and Clear notebook are in the Notebook pane.
build/dev-proxy.js serves the docs and the hub from one origin for local
work, since /binder/build/* sends no CORS headers and an EventSource
cannot be given an Authorization header. Credentials come from the
environment only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ning commands The dock's Terminal tab is now a three-column split: the CLI, the keys it writes, and the value of whichever key is selected. The keys used to be a sidebar shown beside every pane — including the notebook's, whose kernel talks to a different Redis entirely — and the value hid behind its own tab, so reading a key meant leaving the terminal that had just written it. The Value tab is gone, the notebook gets the full width, and a divider between each pair drags (and takes arrow keys), with the proportions remembered alongside the height. Columns grow from a zero basis rather than sitting at fixed percentages: three at "0 0 N%" summing to 100 plus two dividers add up to more than there is, and the last column overflowed the dock. "Clear keys" only clears keys now. It used to wipe the transcript too, which is what the button next to it is for; the FLUSHDB still goes through the terminal, so the transcript records why the keys went away. The dock has a gutter down both sides, applied on the root so the background and top border still span the page while everything inside is inset. That exposed a latent bug: .rwb-facts had flex-wrap but no min-width: 0, and a flex item's min-width is `auto` — so it never shrank, its own wrap never applied, and the last badge was clipped. The redis-cli shortcode and the redis-cli tab of clients-example render a static code block instead of a live terminal. They used to emit form.redis-cli, which cli.js executed as the page loaded: reading a page of prose ran commands against a shared sandbox whether or not the reader wanted it, and what was on screen was the sandbox's output rather than the example as written. Running happens in the workbench now, behind "Try it", where the reader asks for it and can watch the keys it creates. Loading /develop/data-types/strings/ issues no command batches at all. runnable="false" still means no "Try it" (unsafe blocking commands like BRPOP), try_it="false" still hides the button on its own, and copy still works — codetabs.js already read div.redis-cli-static > pre. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lbars Key-type badges use Redis Insight's own colours, so a type reads the same here as in the tool a reader is likely to open next. Taken from its source rather than guessed at a screenshot: GROUP_TYPES_COLORS in redisinsight/ui/src/constants/keys.ts maps each type to a CSS variable, resolved in styles/themes/dark_theme/_theme_color.scss (redis/RedisInsight@main). Labels turn white, which is the pairing those fills are designed for. Two of ours have no Insight equivalent. The probabilistic types (Bloom, cuckoo, count-min, top-k, t-digest) get --defaultTypeColor, which is what Insight's badge falls back to for a type it has no colour for, so they look the same there as here. A search index is not a key at all, so it takes --typeGraphColor: the one colour in the palette no type we list uses. The terminal follows its output again. The widget scrolls its own box when it runs standalone, but in the dock that box is told to grow and the CLI column does the scrolling, so nothing was pinning it to the end: a snippet appended to a full transcript landed below the fold and the reader had to go looking for what their click had done. Now "Try it" pins to the end — it is a request to see a result — and typing follows the end too, while someone who has scrolled up to re-read an earlier reply is left alone until they come back. Resizing keeps the last line in view. Scrollbars in the dock are thin, dark and trackless, scoped so the docs' own are untouched. Not black, though it was asked for and tried: on the dock's #0f172a surface a black thumb cannot be seen, so this is --rwb-bg-3, the slate already used for raised surfaces. The standard scrollbar-width/-color come first because Blink and Firefox use them — including for the overlay scrollbars macOS Chrome shows, which ::-webkit-scrollbar cannot colour — and the explicit 7px rounded thumb still applies in Safari and older Blink. Also: the key browser forgets names that no longer exist. A deleted or expired key stayed on the list to describe and was re-probed on every later sweep, spending commands on a shared backend for a key that is not there. The sweep's own result says which survived; the list is snapshotted first so a batch landing mid-sweep is not mistaken for a key that vanished. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The key browser said several things that were true of the sandbox and of
nowhere else, which a reader comparing notes with their own terminal would
read as the docs being wrong:
- MEMORY USAGE. Every key here is stored as `<session-uuid>:<key>`, so
it counted 37 bytes of prefix: 72 B where redis-cli and Redis Insight
both say 40 B for `SET bike:1 Deimos`. The gap is not a constant to
subtract — 72 against 40 because the longer name crosses a jemalloc
size class — so the figure is gone rather than corrected.
- OBJECT ENCODING. The same value that a plain SET stores as `embstr`
comes back `raw` through this backend, verified against the object
itself, so it is the write path rather than a mangled reply. Also
gone; the cause is a question for redis-clinterwebz.
- The remaining size chip was a bare "6 B" that read as the key's
footprint when it is the length of the value. It now reads
"Length: 6", with Insight's own field names from
browser.keyDetails.length.* — Length, Entries for a stream, Samples
for a time series — and every chip says on hover which command
produced it.
TTLs count down where they are shown, and a key that expires leaves the
list on its own. The countdown is arithmetic on the TTL the last sweep
read and the moment it read it; asking the server every second would
spend a command per key per tick to learn what subtraction already knows.
At zero the row goes and one sweep follows to confirm it — which also puts
the row back if the key outlived our clock. Only the TTL text is
rewritten, never the row, so it cannot fight the pointer.
Arrays and vector sets are now first-class rather than unknown types
falling through to "no enumerable value":
- An array badges in Insight's --typeArrayColor and previews as index
and element via ARSCAN, which skips empty slots — ARGETRANGE over
`ARSET a 1000000 x` would answer with a million nils. ARLEN gives the
logical length, ARCOUNT the number that exist.
- A vector set carries Quant type and Vector dim from VINFO beside its
length, lists its elements (VRANDMEMBER, sorted so the same set does
not reshuffle on each open), and each element opens onto its vector
and attributes — VEMB and VGETATTR, the pair behind Insight's
magnifier. That view survives a command now: a sweep re-reads the open
element instead of replacing it with the key's own view, so a
VSETATTR shows through where it used to close the panel.
A list's preview says Index / Element, the word the docs beside it use
sixty-six times against nineteen for "items".
Redis Insight shows nothing at all for a time series — it points at its
Workbench — but a reader who has just run TS.ADD is better served by the
samples, so that view stays and only borrows the label.
The notebook cells drop the step name and the "needs connect" chip. The
name is a build-time identifier from examples.json and the prose above
each cell already says what it does; the gate is on the Run button, which
is inert and explains itself on hover. The name stays as data-cell,
because the depends rules are written in those names.
Also gone: the "Full CLI" link, and a dead vectorset probe that a fuller
one had shadowed since it was added.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Type the start of a command in the workbench terminal and the matches appear with the syntax each one takes; choose with the arrow keys, insert with Enter or Tab. Once a command is settled, its remaining arguments trail the cursor dimmed, the way redis-cli hints them. The list is the docs' own. Every content/commands/*.md already carries the syntax line its page prints (syntax_fmt), a summary and a group, so a new partial publishes those 583 commands as one file and the completion cannot drift from the reference beside it. It is fetched on the first keystroke and cached, so a reader who never types pays nothing. Matching follows what Insight offers rather than a simpler rule that would have been easier to write: exact first, then prefix, then subsequence — so "hget" reaches HMGET, whose letters appear in order though not as a substring, and "xinfostr" reaches XINFO STREAM. Matched letters are marked individually. Separators are ignored, which is what makes "jsonget" find JSON.GET. A space settles the command, and only a subcommand can follow it: "XINFO " offers its four, "ACL " its thirteen, and "HDEL " offers nothing, because HDEL takes none. Getting this wrong is what put the picker back on screen every time a space was typed after choosing a command — the subsequence tier ignores separators, so it still matched the command just chosen. The inline hint counts arguments as redis-cli does: a part-typed argument counts, so `hget bike` hints "field" rather than "key field", and a trailing repeating group stays hinted while the reader is inside it. It is a span laid over the prompt line, because an <input> cannot hold styled text of its own and the terminal belongs to the /cli widget: everything here attaches from outside it. user-select is off, or a reader copying their session would take the syntax hints with it. Placement never covers the line being typed. Below the prompt if it fits, above if it fits there, and on the roomier side capped and scrolling when neither does — a dock at its minimum height is 63px of terminal. A ResizeObserver on the column re-places it, so growing the dock while the list is open gives the list the new room instead of leaving it capped. Two bugs found by looking rather than by the suite passing: the first keystroke of a visit is what fetches the list, so the hint drew before it arrived and showed nothing until another key followed; and "\_" in syntax_fmt is a YAML escape that arrives as U+00A0, so SET's "IFEQ\_ifeq-value" needed flattening. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The list was being clipped rather than capped. Shrinking the dock could leave the prompt below the fold, and a list placed against a prompt nobody can see took its room from a figure larger than the column: the top rows went outside the column, which hides its overflow silently, so typing SIN showed the loose matches at the end of the list and no scrollbar to say SINTER was above them. The prompt is brought back into view first, both rooms are measured inside the visible slice of the column, and the cap is written on every placement — left unwritten, a cap from a short dock survived the dock being grown. The one-row floor is gone with it: it was what pushed the list past the column's edge. Placement also re-runs on scroll and settles across frames, because a resize clamps the column's scroll a frame later with no scroll event to hear, and it measures the content instead of clearing the cap and re-measuring inside a ResizeObserver — that forced re-layout is the loop Blink cuts short by dropping the notifications after it. The list scrolls back to the top so a cap never leaves it showing its tail, and "N more — keep typing" sticks to the foot so a scrolling list still says there is more. And the keys pane drops "Length: N" from its rows: it is on the value beside them, and a list of keys is for finding one, not for reading it twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three things a reader could not do before: read an index, find it, and see what
it holds.
Reading it. FT.INFO was dumped as a Field/Value table of whichever internal
counters seemed useful, nested lists and all. It is now Insight's "View index"
panel: the definition as one sentence ("Indexing JSON documents whose keys start
with "product:"."), the schema as a table of identifier, attribute and type with
Weight only when a field carries one, and the counts Insight puts under it. Field
types are badged in the order Insight badges them — TEXT informative, TAG notice,
NUMERIC attention, VECTOR success, GEO default — with the nearest hues in the
palette the dock already uses, since those variants resolve inside the redis-ui
package rather than in the app. Flags like SORTABLE sit beside the type they
qualify rather than in a fifth column that a narrow pane would push off the edge.
Finding it. Indexes were rows under a heading in the middle of the key list,
which read as a key called "Indexes" and scrolled against the keys above it — on
a search page, an index sat below the fold of the dozen documents it indexed.
KEYS and INDEXES are now two sections of that column, each with its own count and
its own scroll, and the divider between them drags like the ones between the
columns. The section appears once the session has an index: most pages never
create one, and an empty box with a heading would take a third of a list that a
short dock has little enough of.
Seeing what it holds. Selecting an index narrows the key list to its documents,
as Insight's index selector does, with a chip in the KEYS heading saying so and
clearing it. The documents come from FT.SEARCH in the sweep's own batch, and ones
the dock never watched a command touch are adopted, so the list is the index's
contents rather than the part of them this session happened to see. Any command
the reader runs drops the filter — what they just wrote is what they want to see —
and so does dropping the index, flushing the sandbox, or clicking the chip.
Two bugs fixed on the way. A sweep used to replace an open index with "Select a
key to see its value", because it only knew how to re-open keys; it re-reads the
index instead, which also keeps its counts current as documents arrive. And the
sandbox strips its session prefix out of FT.SEARCH replies assuming the
with-fields layout, stepping two elements at a time, so a NOCONTENT reply comes
back with every other name still prefixed — those are stripped here rather than
matched against the key list and missed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Try it" on step 3 of the search tutorial ran FT.SEARCH against an index the sandbox had never been told to build, so the reader got SEARCH_INDEX_NOT_FOUND where the page prints a count. The commands to fix that were already on the page — every step keeps a "Reload products data and re-create index" block in a <details> — but nothing connected the two. A block marked prereq="true" now publishes its commands as the page's setup, and every other "Try it" there runs them first. Once per sandbox session, not per click: re-creating an index and twelve documents on every snippet would put a wall of output between the reader and what they asked for. Clicking the setup block itself counts, so nobody loads the data twice; "Clear keys" empties the sandbox and forgets; a run that fails does not claim to have happened. The status line says "setting this page up first" and the commands appear in the transcript, so nothing happens invisibly. openTryItCli was two inline copies — one in the redis-cli shortcode, one in tabs/wrapper.html — both guarded with `|| function`, so which one a page got depended on which shortcode Hugo rendered first, and only one of them knew about prerequisites at all. They now share layouts/partials/tryit-script.html. The older per-set needs_prereq model still works for pages that use it without a page-level setup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three things went wrong in the notebook pane, all of them in what the reader could see. The toolbar. It was sticky inside the scrolling pane, resting at the pane's padding edge — 8px short of the top, so cell content scrolled through the strip above it. Moving the padding onto the toolbar fixed that until the cells ran: a cell that has executed carries Thebe's own editor and output widgets, and those paint over a sticky sibling however it is layered, so the toolbar appeared to go transparent with code sliding through it. It is no longer in the scroller at all — the pane is a flex column with a fixed toolbar row and the cells scrolling beneath it, which nothing in the scroller can paint over. "No response" on a cell whose result was on screen. The last cell of the node-redis notebook prints nothing by the page's reckoning, so it has no output container — but the JavaScript kernel echoes the value of an awaited expression, and `await client.quit()` answers OK into Thebe's own output area. The pane looked only in its own container, found nothing, and called the kernel silent. It now reads either place, and waits another second and a half before saying anything: the execution count is recorded after the future resolves, so it and its output can both land after the cell's turn looks over. A cell with output and no number ran — the output is the evidence. "No response" is left for a cell that produced neither. Scrolling. A cell's inner boxes overflow their own frames, which browsers treat differently for the wheel: scrollable-in-the-DOM but not by the reader. Chrome passes the event to the scroller behind; where it does not, the notebook stops moving a few cells from the end. The scroller takes the wheel first and hands it back only if what is under the pointer has room left in that direction. Also: the two footer notes are gone, and the display rule for the notebook pane is scoped with :not(.rwb-hidden) — without that it outranked the rule that hides an inactive pane, and the notebook rendered under the terminal, squashing it to a single line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The dev proxy is a local tool and does not belong in the repo, so nothing here should point at a path that is not in it. Two comments and one status line now say what is needed — the docs served from an origin that also has /binder/ — instead of naming the script that does it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The CLI shim defaulted to a backend on localhost:5000 when the page was served locally, and the Thebe config let a local page aim at another BinderHub through a query parameter, a localStorage key, or a flag a dev proxy set. All of it was hostname-gated and inert on redis.io, and all of it was local-development plumbing living in the shipped files. Both now name one endpoint: https://redis.io/cli and https://redis.io/binder/. The cross-origin warning goes with them — it only ever fired on a local page aimed elsewhere, which is no longer a thing this code does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All four were real; each is a case where the code assumed something the DOM or the run had not got round to yet. A first "Try it" handed its batch to a terminal that was still starting. createCli is async, and open() dropped its promise, so the very first click raced the terminal's own startup — and RedisWorkbench.open had already returned true, so the standalone-CLI fallback could not step in either. The promise is kept now and the batch waits for it. A setup whose run failed was remembered as done. Clicking the setup block's own "Try it" claimed setupRan[provides] before running, and the failure path only took back a claim made by the prepending branch. A snippet that needed the setup afterwards would skip it and search an index that was never built. Both claims are tracked the same way and both are given back. Copy threw on the static twin. copyJupyterCodeToClipboard resolved .thebe-container only, while the visibility toggle and the link copy beside it already accepted .thebe-static — which is what the page keeps once the workbench adopts a runnable cell. Same selector as its neighbours now, and a null guard behind it. "Reconnecting" was never taken off. expireSessionIfDead adds it to every container when it keeps a session the server still answers for, and nothing removed it: the workbench reads that class before any other, so the header stayed on "kernel reconnecting…" with a working kernel behind it. The attached handler clears it alongside marking the containers ready. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two more from the review. The setup block is labelled "Reload products data and re-create index", and it starts with FT.CREATE. Once an earlier "Try it" had applied the setup for the reader, clicking that block answered with "Index already exists" — the reload it offers, reporting an error. It now drops the indexes it is about to re-create first: exactly those, parsed from the setup's own commands, and only the ones this session actually has, so neither the drop nor the create has anything to complain about. Its documents overwrite themselves as they always did. A first run drops nothing, because there is nothing to drop. And the driver's own messages about the session — "please refresh the page", the rate-limit notice — go into each cell's codetabs header, which the pane hides along with the rest of the page's chrome. So a reader whose kernel had died was told nothing, in the one place they were looking. The notebook mirrors whatever is there above its cells, and stops when the driver takes it away. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The data-modeling step writes product:1 in the example that introduces JSON.SET and product:2 onwards in the block that loads the rest, so marking only the second one as the page's setup left the first document out: a reader clicking "read one document back" on a fresh session asked for product:1 and got nil, where the page prints its name. A page can now mark as many blocks as its setup takes. They register as parts in document order, each remembered on its own, and a "Try it" runs the parts this sandbox has not had — so nothing is replayed twice and nothing is missing. A part clicked directly runs only itself: it *is* setup, so it needs none. A failure gives back every part that run had claimed, not just one. Only data-modeling has a split setup today; the other four pages keep their single block and behave exactly as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@paoloredis Looking very good :-) Just a few things come to mind so far:
These are just minor things, though. I'm happy to approve it as it is - really great work! |
andy-stark-redis
left a comment
There was a problem hiding this comment.
Approved (but see comments for a few thoughts). I'll re-add docs as reviewers so you can get everyone else's thoughts about this.
… clearing Three unrelated things, all about what the reader is looking at. The key list waited for the last answer before drawing anything. Describing a key takes three round trips the sandbox cannot fold together — which keys did those commands touch, what are they, how long are they — and against the deployment that is ~300ms each. So the list is drawn as the answers arrive: a placeholder row per name as soon as COMMAND GETKEYS reports it, then the real row when TYPE and TTL land. The sizes no longer hold it up at all, since Length moved to the value pane. Measured with the deployment's latency: a key appears at ~640ms and is described at ~940ms, where nothing appeared before ~1250ms. Same requests; only the waiting changed. The resize grip was between the title bar and the panel, so the resize cursor showed along the bottom of the bar while the dock's top edge — the one a panel that grows upwards is grabbed by — did nothing. It is on that edge now, and hidden while the dock is shut, where there is nothing to resize. And "Clear keys" asks first. It is the one control here that destroys something, what it destroys took a snippet to make, and a reader who hit it by accident had to go back up the page to find the "Try it" that filled the sandbox. The question appears in the toolbar the button came from rather than in a browser dialog, with the confirm focused for the keyboard; Escape or Cancel leaves everything alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A page whose examples are all notebook cells still got a "Terminal" tab, and since it was the only tab it looked like a control that did nothing. Worse, the pane behind it offered a key browser for a sandbox those cells never touch — they run in a Jupyter kernel with its own Redis. So the terminal pane is built only where the page has commands for it: a CLI block, or a "Try it" that is not the notebook's (a client page's Try it carries .thebe-tryit and opens a cell). Where it is absent the dock also asks the sandbox nothing at all, instead of sweeping a keyspace that cannot have keys — one fewer round trip and one fewer session minted on every client page. And the tab strip appears only when there is a choice to make. One pane needs no tabs, whichever pane it is. Nothing in the docs ships both today; a snippet arriving through RedisWorkbench.open still mounts a terminal on demand, and the tabs turn up with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 56310f6. Configure here.
| var thebe = window.thebe; | ||
| var cells = thebe && thebe.notebook && thebe.notebook.cells; | ||
| return cells && cells.length ? cells : null; | ||
| } |
There was a problem hiding this comment.
Notebook reads the wrong Thebe global
Medium Severity
The notebook pane looks up window.thebe for execution counts, kernel id, and cell-idle events, but this site loads Thebe 0.9.0-rc.12, which exposes thebelab and never assigns window.thebe. Count tracking, refresh gating, and restart detection therefore always take the weak DOM fallback the file itself describes as unreliable.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 56310f6. Configure here.
|
Thanks for the feedback @andy-stark-redis ,
I've moved the slide up/down cursor to the top of bar, it does make more sense this way. I'm not sure on removing the
I've added the "Are you sure" check on the clear button, that's a good idea. I'm not sure on the "Restart Example" button, how would that work when there are several code snippets, which one would it re-run?
I've now removed the Terminal from the pages that have the Jupyter Notebook example.
What about hiding the Workbench if any client language is selected?
|


Note
Medium Risk
Large, user-facing interactive docs surface (workbench, Try it, Thebe/Binder session persistence) with many moving parts; failures affect tutorials and in-browser runs but not core product auth or data paths.
Overview
Introduces an on-page Redis Workbench — a bottom dock with a CLI terminal, key/value browser, command completion (built from command reference pages), and a Notebook pane for runnable Jupyter cells — wired in through a shared
cli-assetspartial on docs layouts.Try it no longer auto-executes
redis-clion page load or embeds live terminals in prose. Examples render as static code; clicking Try it runs commands in the workbench (or falls back toredis.io/cli), with page setup from blocks markedprereq="true"so mid-tutorial snippets get indexes/data first. The Try It implementation is consolidated intryit-script.html.Jupyter shortcodes swap visible Run here / Run in browser for Try it (workbench notebook, Binder URL fallback), keep hidden driver run buttons, and leave static twins on the page when cells move into the dock.
Thebe/Binder integration in
baseof.htmlis hardened: fixed production binder URL, binder ref/kernel fallbacks from cell attributes,appendTokenon restored sessions, server-alive checks before expiring sessions, and safer bootstrap/copy/expand behavior for.thebe-staticclones.Search tutorial pages tag reload/setup examples with
prereq="true"so aggregation/search steps seed the sandbox correctly.Reviewed by Cursor Bugbot for commit 56310f6. Bugbot is set up for automated code reviews on this repo. Configure here.