A fork of mtelver's day10 project
1# js_top_worker Demo
2
3This directory contains a comprehensive demo showcasing all features of js_top_worker - an OCaml toplevel that runs in a browser WebWorker.
4
5## Features Demonstrated
6
7- **Basic Execution**: Run OCaml code and see results
8- **Multiple Environments**: Create isolated execution contexts
9- **MIME Output**: Rich output (HTML, SVG, images) via `Mime_printer`
10- **Autocomplete**: Code completion suggestions
11- **Type Information**: Hover-style type queries
12- **Error Reporting**: Static analysis and error detection
13- **Directives**: `#show`, `#install_printer`, `#help`, etc.
14- **Custom Printers**: Install custom value formatters
15- **Library Loading**: Dynamic `#require` for findlib packages
16- **Toplevel Scripts**: Execute multi-phrase scripts
17
18## Prerequisites
19
20You need:
21- OCaml 5.2+ with opam
22- The following opam packages installed:
23 - js_of_ocaml, js_of_ocaml-ppx
24 - dune (3.0+)
25 - All js_top_worker dependencies
26
27## Quick Start
28
29### 1. Build the Project
30
31From the repository root:
32
33```bash
34# Install dependencies (if not already done)
35opam install . --deps-only
36
37# Build everything
38dune build
39```
40
41### 2. Prepare the Example Directory
42
43The build generates the `_opam` directory with compiled libraries:
44
45```bash
46# This is done automatically by dune, but you can also run manually:
47dune build @example/default
48```
49
50This creates `_build/default/example/_opam/` containing:
51- `worker.js` - The WebWorker toplevel
52- `lib/` - Compiled CMI files and JavaScript-compiled CMA files
53- `findlib_index.json` - Index of available packages
54
55### 3. Start the Web Server
56
57```bash
58cd _build/default/example
59python3 server.py 8000
60```
61
62Or use any HTTP server with CORS support. The `server.py` script adds necessary headers.
63
64### 4. Open the Demo
65
66Navigate to: **http://localhost:8000/demo.html**
67
68## File Structure
69
70```
71example/
72├── demo.html # Main demo page (feature showcase)
73├── demo.js # JavaScript client for the demo
74├── worker.ml # WebWorker entry point (1 line!)
75├── server.py # Development server with CORS
76├── dune # Build configuration
77├── _opam/ # Generated: compiled packages
78│ ├── worker.js # The compiled WebWorker
79│ ├── lib/ # CMI files and .cma.js files
80│ └── findlib_index.json # Package index
81└── *.html, *.ml # Other example files
82```
83
84## Adding More Libraries
85
86To add additional OCaml libraries to the demo, edit the `dune` file:
87
88```dune
89(rule
90 (targets
91 (dir _opam))
92 (action
93 (run jtw opam -o _opam str stringext core YOUR_LIBRARY)))
94```
95
96Then rebuild:
97
98```bash
99dune build @example/default
100```
101
102The `jtw opam` command:
1031. Finds all transitive dependencies
1042. Copies CMI files for type information
1053. Compiles CMA files to JavaScript with `js_of_ocaml`
1064. Generates the findlib index and dynamic_cmis.json files
107
108## How It Works
109
110### Architecture
111
112```
113┌─────────────────┐ postMessage/JSON-RPC ┌─────────────────┐
114│ Browser Tab │ ◄──────────────────────────► │ WebWorker │
115│ (demo.js) │ │ (worker.js) │
116│ │ │ │
117│ - UI rendering │ │ - OCaml toplevel│
118│ - RPC client │ │ - Merlin engine │
119│ - MIME display │ │ - #require │
120└─────────────────┘ └─────────────────┘
121```
122
123### RPC Methods
124
125| Method | Description |
126|--------|-------------|
127| `init` | Initialize toplevel with config |
128| `setup` | Setup an environment (start toplevel) |
129| `exec` | Execute a phrase |
130| `exec_toplevel` | Execute a toplevel script |
131| `create_env` | Create isolated environment |
132| `destroy_env` | Destroy an environment |
133| `list_envs` | List all environments |
134| `complete_prefix` | Get autocomplete suggestions |
135| `type_enclosing` | Get type at position |
136| `query_errors` | Get errors/warnings for code |
137
138### MIME Output
139
140User code can produce rich output using the `Mime_printer` module:
141
142```ocaml
143(* SVG output *)
144Mime_printer.push "image/svg" "<svg>...</svg>";;
145
146(* HTML output *)
147Mime_printer.push "text/html" "<table>...</table>";;
148
149(* Base64-encoded image *)
150Mime_printer.push ~encoding:Base64 "image/png" "iVBORw0KGgo...";;
151```
152
153The demo page renders these appropriately in the UI.
154
155## Troubleshooting
156
157### "Worker error" on startup
158
159- Check browser console for details
160- Ensure `_opam/worker.js` exists
161- Verify the server is running with CORS headers
162
163### "Failed to fetch" errors
164
165- The worker loads files via HTTP; check network tab
166- Ensure `lib/ocaml/` directory has CMI files
167- Check `findlib_index.json` file exists
168
169### Library not found with #require
170
171- Add the library to the `jtw opam` command in dune
172- Rebuild with `dune build @example/default`
173- Check `_opam/lib/PACKAGE/META` exists
174
175### Autocomplete/type info not working
176
177- Merlin needs CMI files; ensure they're in `_opam/lib/`
178- The `dynamic_cmis.json` file must be present for each library
179
180## Development
181
182To modify the worker libraries:
183
1841. Edit files in `lib/` (core implementation)
1852. Edit files in `lib-web/` (browser-specific code)
1863. Rebuild: `dune build`
1874. Refresh the browser (worker is cached, may need hard refresh)
188
189The worker entry point is minimal:
190
191```ocaml
192(* worker.ml *)
193let _ = Js_top_worker_web.Worker.run ()
194```
195
196All the complexity is in the `js_top_worker-web` library.