
Research
/Security News
Critical Vulnerability in NestJS Devtools: Localhost RCE via Sandbox Escape
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
A Streamlit custom component for viewing JSON with interactive tooltips and tags
A Streamlit custom component for viewing JSON data with interactive tooltips and tags for individual fields.
uv add streamlit-json-tip
Or for a one-off script:
uv run --with streamlit-json-tip streamlit run your_app.py
pip install streamlit-json-tip
With uv:
uv add --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ streamlit-json-tip
With pip:
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ streamlit-json-tip
brew install go-task/tap/go-task
curl -LsSf https://astral.sh/uv/install.sh | sh
Clone this repository:
git clone https://github.com/isaac/streamlit-json-tip.git
cd streamlit-json-tip
Set up development environment with uv:
# Create virtual environment and install all dependencies (including dev dependencies)
uv sync
Run the example app:
uv run streamlit run example_app.py
import streamlit as st
from streamlit_json_tip import json_viewer
# Your JSON data
data = {
"user": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
}
# Help text for specific fields
help_text = {
"user.id": "Unique user identifier",
"user.name": "Full display name",
"user.email": "Primary contact email"
}
# Tags for categorizing fields
tags = {
"user.id": "ID",
"user.name": "PII",
"user.email": "PII"
}
# Display the JSON viewer
selected = json_viewer(
data=data,
help_text=help_text,
tags=tags,
height=400
)
# Handle field selection
if selected:
st.write(f"Selected field: {selected['path']}")
st.write(f"Value: {selected['value']}")
if selected.get('help_text'):
st.write(f"Help: {selected['help_text']}")
Dynamic tooltips allow you to generate contextual help text programmatically based on field paths, values, and the complete data structure:
import streamlit as st
from streamlit_json_tip import json_viewer
# Sample data with various types
data = {
"user": {
"name": "Alice Johnson",
"score": 95,
"email": "alice@company.com",
"role": "admin"
},
"metrics": {
"cpu_usage": 0.78,
"memory_usage": 0.65,
"disk_usage": 0.92
},
"items": [
{"id": 1, "status": "active"},
{"id": 2, "status": "pending"}
]
}
def dynamic_tooltip(path, value, data):
"""Generate contextual tooltips based on field path and value."""
# Name fields
if path.endswith(".name"):
return f"👤 Full name: {len(value)} characters"
# Score fields with conditional icons
elif path.endswith(".score"):
icon = "🟢" if value >= 90 else "🟡" if value >= 70 else "🔴"
return {
"text": f"Performance score: {value}/100",
"icon": icon
}
# Email fields
elif path.endswith(".email"):
domain = value.split("@")[1] if "@" in value else "unknown"
return {
"text": f"📧 Email domain: {domain}",
"icon": "📧"
}
# Usage metrics with warnings
elif "usage" in path:
percentage = f"{value * 100:.1f}%"
if value > 0.9:
return {
"text": f"⚠️ High usage: {percentage}",
"icon": "⚠️"
}
elif value > 0.7:
return f"🟡 Moderate usage: {percentage}"
else:
return f"🟢 Normal usage: {percentage}"
# Status fields
elif path.endswith(".status"):
status_info = {
"active": {"icon": "✅", "desc": "Currently active"},
"pending": {"icon": "⏳", "desc": "Awaiting approval"},
"inactive": {"icon": "❌", "desc": "Not active"}
}
info = status_info.get(value, {"icon": "❓", "desc": "Unknown status"})
return {
"text": f"{info['desc']}: {value}",
"icon": info["icon"]
}
# Role-based tooltips
elif path.endswith(".role"):
role_descriptions = {
"admin": "👑 Full system access",
"user": "👤 Standard user access",
"guest": "👁️ Read-only access"
}
return role_descriptions.get(value, f"Role: {value}")
return None
# Display with dynamic tooltips
json_viewer(
data=data,
dynamic_tooltips=dynamic_tooltip,
height=500
)
Configure Tippy.js tooltip behavior and appearance:
import streamlit as st
from streamlit_json_tip import json_viewer
data = {
"api": {
"endpoint": "https://api.example.com",
"version": "v2.1",
"rate_limit": 1000
},
"database": {
"host": "db.example.com",
"port": 5432,
"ssl": True
}
}
help_text = {
"api.endpoint": "The base URL for API requests",
"api.version": "Current API version - breaking changes in major versions",
"api.rate_limit": "Maximum requests per hour",
"database.host": "Database server hostname",
"database.port": "Database connection port",
"database.ssl": "SSL encryption enabled for secure connections"
}
# Custom tooltip configuration
tooltip_config = {
"placement": "right", # Position: top, bottom, left, right, auto
"animation": "scale", # Animation: fade, shift-away, shift-toward, scale, perspective
"delay": [500, 100], # [show_delay, hide_delay] in milliseconds
"duration": [200, 150], # [show_duration, hide_duration] in milliseconds
"interactive": True, # Allow hovering over tooltip content
"maxWidth": 300, # Maximum width in pixels
"trigger": "mouseenter focus", # Events: mouseenter, focus, click, etc.
"hideOnClick": False, # Keep tooltip open when clicking
"sticky": True, # Tooltip follows cursor movement
"arrow": True, # Show pointing arrow
"theme": "light" # Theme: light, dark, or custom
}
json_viewer(
data=data,
help_text=help_text,
tooltip_config=tooltip_config,
tooltip_icon="💡", # Custom global icon
height=400
)
Handle complex nested structures with comprehensive tooltips:
import streamlit as st
from streamlit_json_tip import json_viewer
# Complex nested data structure
data = {
"users": [
{
"id": 1,
"profile": {
"name": "John Doe",
"email": "john@company.com",
"ssn": "***-**-1234",
"department": "Engineering"
},
"permissions": ["read", "write", "admin"],
"last_login": "2024-01-15T10:30:00Z",
"settings": {
"theme": "dark",
"notifications": True,
"api_key": "sk-abc123...xyz789"
}
}
],
"system": {
"version": "2.1.0",
"environment": "production",
"uptime": 99.9,
"memory_usage": 0.78
}
}
# Static help text for specific fields
help_text = {
"users[0].id": "Unique user identifier in the system",
"system.version": "Current application version following semantic versioning",
"system.environment": "Deployment environment (dev/staging/production)"
}
# Field categorization with tags
tags = {
"users[0].profile.email": "PII",
"users[0].profile.ssn": "SENSITIVE",
"users[0].profile.name": "PII",
"users[0].settings.api_key": "SECRET",
"system.environment": "CONFIG",
"system.version": "INFO"
}
# Advanced dynamic tooltips with context awareness
def advanced_tooltips(path, value, data):
# Get user context for personalized tips
if "users[0]" in path:
user_name = data["users"][0]["profile"]["name"]
if path.endswith(".permissions"):
perm_count = len(value)
return {
"text": f"🔐 {user_name} has {perm_count} permission(s): {', '.join(value)}",
"icon": "🔐"
}
elif path.endswith(".last_login"):
return {
"text": f"🕒 {user_name}'s last activity: {value}",
"icon": "🕒"
}
elif path.endswith(".department"):
dept_info = {
"Engineering": "👨💻 Technical development team",
"Sales": "💼 Revenue generation team",
"Marketing": "📢 Brand and promotion team"
}
return dept_info.get(value, f"Department: {value}")
# System metrics with thresholds
elif path.startswith("system."):
if path.endswith(".uptime"):
if value >= 99.9:
return {"text": f"🟢 Excellent uptime: {value}%", "icon": "🟢"}
elif value >= 99.0:
return {"text": f"🟡 Good uptime: {value}%", "icon": "🟡"}
else:
return {"text": f"🔴 Poor uptime: {value}%", "icon": "🔴"}
elif path.endswith(".memory_usage"):
percentage = f"{value * 100:.1f}%"
if value > 0.9:
return {"text": f"⚠️ Critical memory usage: {percentage}", "icon": "⚠️"}
elif value > 0.7:
return {"text": f"🟡 High memory usage: {percentage}", "icon": "🟡"}
else:
return {"text": f"🟢 Normal memory usage: {percentage}", "icon": "🟢"}
# Sensitive data warnings
if any(keyword in path for keyword in ["ssn", "api_key", "password"]):
return {
"text": "🚨 Sensitive data - handle with care",
"icon": "🚨"
}
return None
# Advanced tooltip configuration for better UX
advanced_config = {
"placement": "auto", # Auto-position based on available space
"animation": "fade", # Smooth fade animation
"delay": [300, 100], # Quick show, delayed hide
"interactive": True, # Allow interaction with tooltip content
"maxWidth": 400, # Wider tooltips for more content
"hideOnClick": False, # Keep tooltips persistent
"appendTo": "parent" # Better positioning within container
}
selected = json_viewer(
data=data,
help_text=help_text,
tags=tags,
dynamic_tooltips=advanced_tooltips,
tooltip_config=advanced_config,
tooltip_icon="ℹ️",
height=600
)
# Handle field selection with detailed information
if selected:
st.sidebar.header("Field Details")
st.sidebar.json({
"Path": selected['path'],
"Value": selected['value'],
"Type": type(selected['value']).__name__,
"Help": selected.get('help_text', 'No help available')
})
The tooltip_config
parameter accepts any valid Tippy.js options:
Option | Type | Default | Description |
---|---|---|---|
placement | str | "top" | Tooltip position: "top", "bottom", "left", "right", "auto" |
animation | str | "fade" | Animation type: "fade", "shift-away", "shift-toward", "scale", "perspective" |
delay | int/list | 0 | Show delay in ms, or [show_delay, hide_delay] |
duration | int/list | [300, 250] | Animation duration in ms, or [show_duration, hide_duration] |
interactive | bool | False | Allow hovering over tooltip content |
maxWidth | int | 350 | Maximum width in pixels |
trigger | str | "mouseenter focus" | Events that trigger tooltip |
hideOnClick | bool | True | Hide tooltip when clicking |
sticky | bool | False | Tooltip follows cursor movement |
arrow | bool | True | Show pointing arrow |
theme | str | "light" | Tooltip theme |
Field paths use dot notation for objects and bracket notation for arrays:
"user.name"
- Object field"items[0].title"
- Array item field"settings.preferences.theme"
- Nested object fieldSet up the development environment (see Development Setup above)
Navigate to the frontend directory:
cd streamlit_json_tip/frontend
Install frontend dependencies:
npm install
Start development server:
npm start
In your Python code, set _RELEASE = False
in __init__.py
Run the example app in another terminal:
uv run streamlit run example_app.py
# Run all Python unit tests with coverage
uv run pytest
# Run tests with verbose output
uv run pytest -v
# Generate HTML coverage report
uv run pytest --cov-report=html
cd streamlit_json_tip/frontend
# Run Jest tests once
npm test -- --ci --watchAll=false
# Run tests with coverage
npm test -- --coverage --ci --watchAll=false
# Run tests in watch mode (for development)
npm test
Both Python and frontend tests run automatically in GitHub Actions on every push and pull request.
Build the frontend:
cd streamlit_json_tip/frontend
npm run build
Set _RELEASE = True
in __init__.py
Build the Python package:
uv run python -m build
Upload to PyPI:
uv run python -m twine upload dist/*
The project includes convenient uv scripts for common development tasks:
task build-frontend
uv run clean # Clean build artifacts
uv run build # Clean + build Python package
uv run build-check # Build + validate package with twine
task release-test # Build + upload to TestPyPI
task release # Build + upload to PyPI
This will build the frontend, package the Python distribution, validate it, and upload to PyPI.
This project uses automated GitHub Actions for releases. Follow these steps to release a new version:
Update the version in pyproject.toml
:
version = "0.2.5" # Increment according to semver
Add changelog entry in CHANGELOG.md
:
## [0.2.5] - 2025-01-26
### ✨ Added
- New feature description
### 🔧 Fixed
- Bug fix description
Commit your changes:
git add pyproject.toml CHANGELOG.md
git commit -m "Bump version to 0.2.5"
git push origin main
git tag v0.2.5
git push origin v0.2.5
Once you push the tag, GitHub Actions will automatically:
To use automated releases, you need:
PYPI_API_TOKEN
to your repository secrets
PYPI_API_TOKEN
If you prefer manual releases or need to troubleshoot:
# Build everything
task build
# Upload to PyPI manually
export TWINE_PASSWORD=your_pypi_token_here
python -m twine upload --username __token__ dist/*
MIT License
FAQs
A Streamlit custom component for viewing JSON with interactive tooltips and tags
We found that streamlit-json-tip demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.