
Research
SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains
An emerging npm supply chain attack that infects repos, steals CI secrets, and targets developer AI toolchains for further compromise.
@curvenote/any-widget
Advanced tools
Extension interface and NodeRenderer following the `any-widget` interfaces
Extension interface and NodeRenderer following the any-widget interfaces for embedding interactive widgets in MyST Markdown documents.
The any:widget directive (also available as any:bundle) allows you to embed interactive widgets directly in your MyST markdown files. These widgets are ES modules that can be loaded from a URL and initialized with JSON data.
# myst.yml
project:
plugins:
- any-widget.mjs
If you are using the Curvenote CLI then you can omit the step above as the directive is already included.
To include your widget, provide a reachable URL to the JS module, and optional css file.
:::{any:widget} https://example.com/widget.mjs
:css: https://example.com/widget-styles.css
:class: border rounded p-4
{
"value": 42,
"name": "My Widget"
}
:::
Or do the same using the any:bundle alias.
class: Tailwind CSS classes to apply to the container elementcss (or styles, legacy): URL to a CSS stylesheet to load for the widgetstatic: URL, File path, folder path, or glob pattern for static files to make available to the moduleThis example demonstrates how to create and embed an interactive counter widget in your MyST markdown, based on the anywidget counter example.
Create a file counter.mjs that exports a widget module, in this case we are only exporting a render() function:
// counter.mjs
function render({ model, el }) {
let count = () => model.get("value");
let btn = document.createElement("button");
btn.classList.add("counter-button");
btn.innerHTML = `count is ${count()}`;
btn.addEventListener("click", () => {
model.set("value", count() + 1);
model.save_changes();
});
model.on("change:value", () => {
btn.innerHTML = `count is ${count()}`;
});
el.appendChild(btn);
}
export default { render };
Create a file counter.css with styles for the widget:
/* counter.css */
.counter-button {
background-image: linear-gradient(to right, #a1c4fd, #c2e9fb);
border: 0;
border-radius: 10px;
padding: 10px 50px;
color: white;
font-size: 16px;
cursor: pointer;
transition: transform 0.1s;
}
.counter-button:hover {
transform: scale(1.05);
}
.counter-button:active {
transform: scale(0.95);
}
In your MyST markdown file, use the any:widget directive:
:css: https://example.com/counter.css
:class: my-4
:::{any:widget} https://example.com/counter.mjs
{
"value": 0
}
:::
The widget will automatically:
{"value": 0})The widget uses the model object as the source of truth:
model.get("value") retrieves the current valuemodel.set("value", newValue) updates the valuemodel.on("change:value", callback) listens for value changesmodel.save_changes() syncs changes back to the modelThis ensures the widget state stays synchronized whether updates come from user interactions or programmatic changes.
This package provides two main entry points for different use cases:
any:widget and any:bundle directives in markdownnpm install @curvenote/any-widget
If you're building a CLI tool or custom build pipeline that processes MyST markdown, you'll want to add the directive to your processing pipeline.
Import the directive:
import { anyWidget } from '@curvenote/any-widget';
import type { MystPlugin } from 'myst-common';
const plugin: MystPlugin = {
name: 'My Plugin',
directives: [anyWidget],
// ... other plugin configuration
};
Or use the bundled plugin:
If you prefer a pre-bundled version, you can use the plugin bundle:
import anyWidgetPlugin from '@curvenote/any-widget/dist/plugin/any-widget.mjs';
const plugin: MystPlugin = {
name: 'My Plugin',
directives: [anyWidgetPlugin.anyWidget],
// ... other plugin configuration
};
The directive will parse any:widget and any:bundle directives in your markdown and convert them into AST nodes that can be rendered.
If you're building a theme or React application that renders MyST documents, you'll need to add the React renderers to display the widgets.
Import the renderers:
import { ANY_RENDERERS } from '@curvenote/any-widget/react';
import { Document } from '@myst-theme/providers';
function MyDocument({ ast }) {
return (
<Document
ast={ast}
renderers={{
...defaultRenderers,
...ANY_RENDERERS
}}
/>
);
}
The renderers will automatically:
The package exports TypeScript types for type safety:
import type { AnyWidgetDirective } from '@curvenote/any-widget';
// or
import type { AnyWidgetDirective } from '@curvenote/any-widget/react';
The package provides the following exports:
@curvenote/any-widget): The directive for Node/CLI usage@curvenote/any-widget/react): The React renderers for theme usage@curvenote/any-widget/dist/plugin/any-widget.mjs): Pre-bundled directive pluginnpm install
npm run build
This will:
dist directory (for library exports)dist/plugin/any-widget.mjsnpm run compile
npm test
Or in watch mode:
npm run test:watch
src/
├── index.ts # Main entry point (directive exports)
├── directive/ # Directive implementation
├── renderer/ # React renderer implementation
└── types.ts # TypeScript type definitions
This package is part of the Curvenote monorepo. See the main repository for contribution guidelines.
MIT
FAQs
Extension interface and NodeRenderer following the `any-widget` interfaces
We found that @curvenote/any-widget demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers 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
An emerging npm supply chain attack that infects repos, steals CI secrets, and targets developer AI toolchains for further compromise.

Company News
Socket is proud to join the OpenJS Foundation as a Silver Member, deepening our commitment to the long-term health and security of the JavaScript ecosystem.

Security News
npm now links to Socket's security analysis on every package page. Here's what you'll find when you click through.