-
Notifications
You must be signed in to change notification settings - Fork 64
feat: input_code_editor() Component
#1274
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
Open
gadenbuie
wants to merge
29
commits into
main
Choose a base branch
from
feat/input-code
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Each theme is wrapped with attribute selectors that match the editor's `data-theme-light`/`data-theme-dark` attributes, combined with the page's `data-bs-theme` attribute, using CSS nesting (supported in all modern browsers since late 2023).
gadenbuie
commented
Dec 31, 2025
gadenbuie
commented
Dec 31, 2025
gadenbuie
added a commit
to posit-dev/py-shiny
that referenced
this pull request
Dec 31, 2025
Ports the `input_code_editor()` component from bslib PR #1274 to py-shiny. ## Overview `input_code_editor()` is a lightweight code editor input with syntax highlighting powered by prism-code-editor. It supports 20+ languages, multiple themes, and automatic light/dark mode switching. ## Added - `input_code_editor()` - Create a code editor input - `update_code_editor()` - Update editor from server - `code_editor_themes()` - List available themes ## Key decisions for human review 1. **Dependency structure**: The code editor uses two HTML dependencies: - `prism-code-editor` (vendored library for syntax highlighting) - `bslib-code-editor-js` (the Shiny input binding) 2. **Themes list**: Manually hardcoded the theme list to match the vendored CSS files. The R version discovers these dynamically, but Python doesn't have easy access to the vendored directory at runtime. 3. **Language validation**: Using a hardcoded tuple of supported languages instead of dynamic discovery, matching the R implementation's approach. 4. **Manual vendoring**: Due to issues with the automated vendoring script (ionRangeSlider CSS step fails), assets were manually copied from the bslib feat/input-code branch. Updated htmlDependencies.R to include the prism-code-editor copy for future runs once bslib PR is merged. 5. **Value handling**: The `value` parameter accepts either a string or a sequence of strings (lines), matching Python idioms. 6. **Fill behavior**: Added `html-fill-container` and `html-fill-item` classes for fillable layout support, similar to other fill components. Related: rstudio/bslib#1274 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
input_code_editor()is a Shiny input that provides a lightweight code editor with syntax highlighting, powered by prism-code-editor. It supports 20+ languages (a subset of those supported by prism-code-editor), multiple themes, and automatic light/dark mode switching.File Layout
Architecture
Key Design Decisions
Custom Element: The editor is a web component (
<bslib-code-editor>) extending HTMLElement. This provides standard lifecycle hooks and attribute reflection, simplifying Shiny integration via the sharedmakeInputBinding()helper.Separate Bundle:
code-editor.jsis NOT bundled intocomponents.min.js. It's loaded only wheninput_code_editor()is used, keeping the main bslib bundle small.ES Modules: The code-editor bundle uses ESM format to support dynamic imports of language grammars at runtime.
Lazy Loading: Language grammars and themes are loaded on-demand when the editor initializes (
connectedCallback()) or when receiving an update messages from the server, not upfront.Theme Watching: Each editor instance creates a MutationObserver that watches
<html data-bs-theme>to automatically switch between light/dark themes. Theme stylesheets are shared across all instances and never unloaded.R API (
R/input-code-editor.R)Exported Functions
input_code_editor()update_code_editor()code_editor_themes()Internal Functions
code_editor_dependencies()code_editor_dependency_prism()code_editor_dependency_js()arg_match_language()arg_match_theme()check_value_line_count()HTML Output Structure
The component uses a custom element
<bslib-code-editor>with kebab-case attributes:TypeScript Web Component (
srcts/src/components/codeEditor.ts)The editor is implemented as a custom element (
<bslib-code-editor>) that extendsHTMLElementand implementsCustomElementInputGetValue<string>for Shiny integration.Class:
BslibCodeEditorStatic Properties:
tagName = "bslib-code-editor": Custom element tag nameisShinyInput = true: Marks this as a Shiny input formakeInputBinding()observedAttributes: List of attributes that triggerattributeChangedCallback()Static Methods (shared across all instances):
#getBasePath(): Locates and caches path to prism-code-editor assets#loadLanguage(): Dynamically imports language grammars (cached)#loadTheme(): Loads theme stylesheets (cached, never unloaded)Instance Properties (reflect to/from HTML attributes):
language,readonly,lineNumbers,wordWrap,tabSize,insertSpacesthemeLight,themeDarkvalue: Current editor content (get/set onprismEditor)Lifecycle Methods:
connectedCallback(): Initializes editor when element is added to DOMdisconnectedCallback(): Cleans up MutationObserver when removedattributeChangedCallback(): Responds to attribute changes by updating prism-code-editorKey Instance Methods:
getValue(): Returns current content (for Shiny input binding)receiveMessage(): Handlesupdate_code_editor()calls from R_initializeEditor(): Creates prism-code-editor instance, sets up Ctrl+Enter and blur handlers_setupThemeWatcher(): Watchesdata-bs-themeon<html>to switch themes_handleLanguageChange(): Loads new grammar and updates editorShiny Integration
The Shiny input binding is created via the shared
makeInputBinding()helper:The
makeInputBinding()helper (fromwebcomponents/_makeInputBinding.ts) creates a standard Shiny input binding that:getValue()andreceiveMessage()to the custom element instanceonChangeCallbackfor value updatestsconfig.json Note
The
module: esnextsetting is required for dynamic imports. Thets-nodesection overrides this tocommonjsfor the build script only.Vendoring (
tools/yarn_install.R)How It Works
inst/package.jsondeclaresprism-code-editor-fulldependency (aliased fromprism-code-editor-lightweight)yarn installininst/downloads tonode_modules/node_modules/is moved tolib/prism-code-editor-full/dist/toprism-code-editor/R/versions.RUpdating prism-code-editor
Update version in
inst/package.json:Run
Rscript tools/yarn_install.RorRscript tools/main.RVerify
R/versions.Rhas updated versionRun tests:
devtools::test(filter = "code-editor")Adding New Languages
Languages are loaded dynamically from
inst/lib/prism-code-editor/prism/languages/. To add a new language:prism/languages/{lang}.jscode_editor_bundled_languagesintools/yarn_install.Rand re-run the yarn install script.code_editor_bundled_languagesinR/versions.Ris updated.@param languagedocumentation inR/input-code-editor.R.Adding New Themes
Themes are CSS files in
inst/lib/prism-code-editor/themes/. Available themes are auto-discovered bycode_editor_themes().To add a custom theme:
{theme-name}.csstoinst/lib/prism-code-editor/themes/code_editor_themes()outputTesting
Unit tests of
input_code_editor()are intests/testthat/test-input-code-editor.Rand can be run withdevtools::test(filter = "code-editor").An example Shiny app demonstrating the editor is in
inst/examples-shiny/code-editor/app.Rand can be run with:CSS Customization
The editor uses CSS variables for Bootstrap integration. Key selectors:
bslib-code-editor- Custom element (outer container).code-editor- Inner editor container where prism-code-editor mounts.code-editor-submit-flash- Flash animation on Ctrl+EnterSee
inst/components/scss/input_code_editor.scssfor full styles.