Plugins live under the plugins/ directory in your problem package ZIP. Each plugin is a named subdirectory containing at minimum an index.html file.
my-problem.zip
├── problem_statement/
│ └── problem.en.tex
└── plugins/
└── my-visualizer/
├── index.html ← Required entry point
├── script.js ← Your plugin logic
├── style.css ← Your plugin styles
└── assets/
└── icon.svg ← Any additional assets
The plugin name is the directory name (my-visualizer in the example above). This name must match exactly what you pass to \plugin{my-visualizer} in the problem statement.
After the problem package is imported, all plugin files are stored and served via the platform API:
GET /api/v1/problems/{problemId}/plugin/{pluginName}/{...filePath}
For example, script.js inside my-visualizer is served at:
/api/v1/problems/42/plugin/my-visualizer/script.js
The entry point index.html is always loaded first by the iframe.
Inside index.html, always use relative paths to reference other files in the same plugin directory. The platform resolves them correctly regardless of the problem ID or environment.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- Relative paths work correctly -->
<script src="./monkeyCodeInput.js"></script>
<script src="./script.js"></script>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<img src="./assets/icon.svg" alt="icon">
</body>
</html>
/script.js) or paths that include the problem ID. These will break when the problem is viewed. Always use ./filename relative paths.The platform automatically injects monkeyCodeInput.js into every plugin's storage directory at import time. You do not need to include it in your ZIP — it will always be available at ./monkeyCodeInput.js relative to your index.html.
plugins/
my-visualizer/
index.html
script.js
monkeyCodeInput.js ← Injected automatically, not from your ZIP
You can organise a plugin with as many files as needed. Subdirectories are supported:
plugins/
my-visualizer/
index.html
script.js
style.css
lib/
d3.min.js ← Bundled third-party library
helpers.js
assets/
background.png
font.woff2
Reference subdirectory files with relative paths from index.html:
<script src="./lib/d3.min.js"></script>
<script src="./lib/helpers.js"></script>
<link rel="stylesheet" href="./style.css">
A problem can contain multiple plugins, each in its own subdirectory:
plugins/
visualizer/
index.html
script.js
debug-view/
index.html
style.css
Each plugin is completely independent — they have separate storage paths, separate iframes, and do not share files or state. Reference them individually in the problem statement:
\plugin{visualizer}
\plugin{debug-view}
After uploading the problem package, the platform stores plugin files at:
{problemId}/
plugins/
{pluginName}/
index.html
script.js
style.css
monkeyCodeInput.js ← Auto-injected
This maps directly to the API route used by the iframe src, so the directory structure you define in the ZIP is preserved exactly as-is in storage.