-
Notifications
You must be signed in to change notification settings - Fork 332
feat: add Try It button to CLI code examples #3380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7ec429a
629daa4
9549911
aac21dd
b1580b3
d69cbd1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| {{ $description := .description }} | ||
| {{ $difficulty := .difficulty }} | ||
| {{ $buildsUpon := .buildsUpon }} | ||
| {{ $tryItCommands := .tryItCommands }} | ||
|
|
||
| {{- /* Build metadata map for each language/client combination */ -}} | ||
| {{- $codetabsMeta := dict -}} | ||
|
|
@@ -73,7 +74,7 @@ | |
| {{- if $exampleId -}} | ||
| <a id="{{ $exampleId }}" class="relative"{{ if $description }} data-description="{{ $description | htmlEscape }}"{{ end }}{{ if $difficulty }} data-difficulty="{{ $difficulty | htmlEscape }}"{{ end }}{{ if $buildsUpon }} data-builds-upon="{{ delimit $buildsUpon "," }}"{{ end }} data-codetabs-id="{{ $id }}"></a> | ||
| {{- end -}} | ||
| <div class="codetabs cli group box-border rounded-lg mt-0 mb-0 mx-auto bg-slate-900" id="{{ $id }}" data-codetabs-meta="{{ $codetabsMetaJson | htmlEscape }}"> | ||
| <div class="codetabs cli group box-border rounded-lg mt-0 mb-0 mx-auto bg-slate-900" id="{{ $id }}" data-codetabs-meta="{{ $codetabsMetaJson | htmlEscape }}"{{ if $tryItCommands }} {{ printf "data-tryit-commands=\"%s\"" ($tryItCommands | jsonify | htmlEscape) | safeHTMLAttr }}{{ end }}> | ||
| <!-- Language selector dropdown with controls --> | ||
| <div class="codetabs-header flex flex-col px-4 py-2 bg-slate-900 rounded-t-lg"> | ||
| <div class="flex items-start justify-between"> | ||
|
|
@@ -101,6 +102,21 @@ | |
| </div> | ||
|
|
||
| <div class="flex items-center gap-2"> | ||
| {{/* "Try it" button - opens redis.io/cli with pre-populated commands */}} | ||
| {{ if $tryItCommands }} | ||
| <button tabindex="1" | ||
| type="button" | ||
| class="tryit-button flex items-center gap-1 text-slate-300 hover:text-white bg-red-700 hover:bg-red-600 h-7 px-3 rounded text-xs font-medium transition duration-150 ease-in-out" | ||
| title="Run these commands in the Redis CLI" | ||
| data-codetabs-id="{{ $id }}" | ||
| onclick="openTryItCli(this)" | ||
| aria-label="Try these Redis commands in an interactive CLI"> | ||
| <svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> | ||
| <path d="M8 5v14l11-7z"/> | ||
| </svg> | ||
| <span>Try it</span> | ||
| </button> | ||
| {{ end }} | ||
| {{/* BinderHub "Run in browser" link - shown conditionally based on current tab's binderId */}} | ||
| <div id="binder-link-container-{{ $id }}" class="flex items-center"> | ||
| {{/* Link will be shown/hidden by JavaScript based on selected tab */}} | ||
|
|
@@ -251,6 +267,25 @@ | |
| <!-- spellchecker-enable --> | ||
|
|
||
| <script> | ||
| // Registry for Try It CLI commands (populated per codetabs instance) | ||
| window._tryItCommands = window._tryItCommands || {}; | ||
|
|
||
| // Global function to open Try It CLI with pre-populated commands | ||
| window.openTryItCli = window.openTryItCli || function(button) { | ||
| const codetabsId = button.getAttribute('data-codetabs-id'); | ||
| const commands = window._tryItCommands[codetabsId]; | ||
| if (!commands || !commands.length) return; | ||
|
|
||
| // URL-safe base64 of the JSON payload, no padding. Avoids the %5C%22 | ||
| // patterns produced by raw JSON+encodeURIComponent, which trigger | ||
| // Cloudflare WAF SQLi/XSS-bypass rules. | ||
| const json = JSON.stringify(commands); | ||
| const b64 = btoa(unescape(encodeURIComponent(json))) | ||
| .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); | ||
| const url = 'https://redis.io/cli?commands=' + b64 + '&autorun=true'; | ||
| window.open(url, '_blank', 'noopener,noreferrer'); | ||
| }; | ||
|
|
||
| // Global function to toggle commands foldout | ||
| window.toggleCommandsFoldout = window.toggleCommandsFoldout || function(button) { | ||
| const details = button.nextElementSibling; | ||
|
|
@@ -299,6 +334,28 @@ | |
| } | ||
| }; | ||
|
|
||
| // Global function to show/hide Try It buttons based on selected language | ||
| // The button should only be visible when the Redis CLI tab is active | ||
| window.updateAllTryItButtons = window.updateAllTryItButtons || function() { | ||
| const buttons = document.querySelectorAll('.tryit-button'); | ||
| buttons.forEach((button) => { | ||
| const codetabsId = button.getAttribute('data-codetabs-id'); | ||
| const langSelect = document.getElementById('lang-select-' + codetabsId); | ||
| if (!langSelect) return; | ||
|
|
||
| const selectedOption = langSelect.options[langSelect.selectedIndex]; | ||
| const selectedLang = selectedOption ? selectedOption.value : ''; | ||
|
|
||
| if (selectedLang === 'redis-cli') { | ||
| button.classList.remove('hidden'); | ||
| button.classList.add('flex'); | ||
| } else { | ||
| button.classList.add('hidden'); | ||
| button.classList.remove('flex'); | ||
| } | ||
| }); | ||
| }; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Try It button update in tab switching pathMedium Severity The Additional Locations (1)Reviewed by Cursor Bugbot for commit aac21dd. Configure here. |
||
|
|
||
| // Global function to update all binder links on the page | ||
| // This is called whenever the language selection changes | ||
| window.updateAllBinderLinks = window.updateAllBinderLinks || function() { | ||
|
|
@@ -423,15 +480,19 @@ | |
| }; | ||
|
|
||
| (function() { | ||
| // Initialize BinderHub link for this codetabs instance | ||
| // Register Try It commands for this codetabs instance | ||
| const codetabsId = '{{ $id }}'; | ||
| {{ if $tryItCommands }} | ||
| window._tryItCommands[codetabsId] = {{ $tryItCommands | jsonify | safeJS }}; | ||
| {{ end }} | ||
| const langSelect = document.getElementById('lang-select-' + codetabsId); | ||
|
|
||
| // Initialize on page load - use a delay to allow codetabs.js to restore localStorage selection | ||
| // codetabs.js is deferred, so it runs after DOM is ready, but we need to wait for it to complete | ||
| setTimeout(() => { | ||
| window.updateAllBinderLinks(); | ||
| window.updateAllCliOutputToggles(); | ||
| window.updateAllTryItButtons(); | ||
| }, 100); | ||
|
|
||
| // Update all binder links when language changes | ||
|
|
@@ -441,6 +502,7 @@ | |
| setTimeout(() => { | ||
| window.updateAllBinderLinks(); | ||
| window.updateAllCliOutputToggles(); | ||
| window.updateAllTryItButtons(); | ||
| }, 10); | ||
| }); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,36 @@ | ||
| <div class="bg-slate-900 border-b border-slate-700 rounded-t-xl px-4 py-3 w-full flex"> | ||
| {{- $commands := slice -}} | ||
| {{- range split (trim .Inner " \n\t\r") "\n" -}} | ||
| {{- $line := trim . " \t\r" -}} | ||
| {{- if $line -}} | ||
| {{- $commands = $commands | append $line -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- $id := printf "redis-cli-%s" (substr (.Inner | md5) 0 8) -}} | ||
|
|
||
| <div class="bg-slate-900 border-b border-slate-700 rounded-t-xl px-4 py-3 w-full flex items-center"> | ||
| {{ partial "icons/cli-circle" (dict "class" "shrink-0 h-[1.0625rem] w-[1.0625rem] fill-slate-50") }} | ||
| {{ partial "icons/cli-triangle" (dict "class" "shrink-0 h-[1.0625rem] w-[1.0625rem] fill-slate-50") }} | ||
| {{ partial "icons/cli-star" (dict "class" "shrink-0 h-[1.0625rem] w-[1.0625rem] fill-slate-50") }} | ||
| </div> | ||
| {{- if $commands -}} | ||
| <button tabindex="1" type="button" class="tryit-button ml-auto flex items-center gap-1 text-slate-300 hover:text-white bg-red-700 hover:bg-red-600 h-7 px-3 rounded text-xs font-medium transition duration-150 ease-in-out" title="Run these commands in the Redis CLI" data-codetabs-id="{{ $id }}" onclick="openTryItCli(this)" aria-label="Try these Redis commands in an interactive CLI"><svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg><span>Try it</span></button> | ||
| {{- end -}} | ||
| </div> | ||
| <form class="redis-cli overflow-y-auto max-h-80"> | ||
| {{- .Inner -}} | ||
| </form> | ||
| </form> | ||
| {{- if $commands -}} | ||
| <script> | ||
| window._tryItCommands = window._tryItCommands || {}; | ||
| window._tryItCommands['{{ $id }}'] = {{ $commands | jsonify | safeJS }}; | ||
| window.openTryItCli = window.openTryItCli || function(button) { | ||
| const codetabsId = button.getAttribute('data-codetabs-id'); | ||
| const commands = window._tryItCommands[codetabsId]; | ||
| if (!commands || !commands.length) return; | ||
| const json = JSON.stringify(commands); | ||
| const b64 = btoa(unescape(encodeURIComponent(json))) | ||
| .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); | ||
| const url = 'https://redis.io/cli?commands=' + b64 + '&autorun=true'; | ||
| window.open(url, '_blank', 'noopener,noreferrer'); | ||
| }; | ||
| </script> | ||
| {{- end -}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,21 @@ function copyCodeToClipboardForCodetabs(button) { | |
| const visiblePanel = codetabsContainer.querySelector('.panel:not(.panel-hidden)'); | ||
| if (!visiblePanel) return; | ||
|
|
||
| // The redis-cli panel is an interactive terminal (form.redis-cli) with no | ||
| // <code> element, so copy the registered commands instead of reading the DOM. | ||
| const cliForm = visiblePanel.querySelector('form.redis-cli'); | ||
| if (cliForm) { | ||
| const cliCode = cliForm.getAttribute('data-cli-source') || cliForm.textContent; | ||
| navigator.clipboard.writeText(cliCode.trim()); | ||
|
|
||
| const cliTooltip = button.querySelector('.tooltiptext'); | ||
| if (cliTooltip) { | ||
| cliTooltip.style.display = 'block'; | ||
| setTimeout(() => cliTooltip.style.display = 'none', 1000); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try It button visibility not updated on URL navigationMedium Severity The Additional Locations (1)Reviewed by Cursor Bugbot for commit d69cbd1. Configure here. |
||
| let code; | ||
| const isCliTrimmed = visiblePanel.getAttribute('data-cli-trimmable') === 'true'; | ||
| const cliPreviewLines = parseInt(visiblePanel.getAttribute('data-cli-preview-lines') || '0', 10); | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused
data-tryit-commandsHTML data attributeLow Severity
The
data-tryit-commandsattribute is written to the HTML div but never read by any JavaScript on the page. The actual mechanism useswindow._tryItCommands[codetabsId](populated at line 486) andopenTryItClireads from that registry — not from the DOM attribute. This is unused/dead output that adds JSON payload bloat to every codetabs container with Try It commands.Reviewed by Cursor Bugbot for commit b1580b3. Configure here.