My aggregated monorepo of OCaml code, automaintained
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add design doc for 5 backlog tasks

Covers: preloaded list symtable check, scrollycode theming via
html-generate --config, mermaid/SPA inline script fix, oxcaml
investigation, and jtw opam META/universe fixes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+147
+147
docs/plans/2026-03-02-tasks-design.md
··· 1 + # Tasks Design — 2026-03-02 2 + 3 + ## Overview 4 + 5 + Five tasks from the backlog, to be developed on two feature branches: 6 + 7 + - **Branch A (`js_top_worker-fixes`)**: Tasks 1 + 5 — changes confined to `js_top_worker` and adjacent 8 + - **Branch B (`scrollycode-and-spa`)**: Tasks 2 + 3 — changes to `odoc`, `odoc-docsite`, `odoc-scrollycode-extension` 9 + - **Task 4** (oxcaml investigation): read-only, no branch needed 10 + 11 + --- 12 + 13 + ## Task 1 — Preloaded list runtime check 14 + 15 + **Problem**: `js_top_worker/lib/findlibish.ml` contains a manually maintained static list of ~50 OCaml package names (`preloaded`) representing packages compiled into the worker binary. This list goes out of date whenever the binary's dependencies change. 16 + 17 + **Approach**: Replace the static list with a runtime check using OCaml's `Symtable` module. 18 + 19 + **Design**: 20 + 21 + - Add a function `is_module_available : string -> bool` that calls 22 + `Symtable.is_global_defined (Ident.create_persistent module_name)` to test 23 + whether a given capitalised module name is already linked into the binary. 24 + - Add `is_package_preloaded : Impl.dynamic_cmis -> bool` that calls 25 + `is_module_available` on each name in `dcs.dcs_toplevel_modules` (the 26 + capitalised module list provided by `dynamic_cmis.json`). 27 + - In the `require` function, replace: 28 + ```ocaml 29 + let should_load = (not (List.mem lib.name preloaded)) && not cmi_only 30 + ``` 31 + with: 32 + ```ocaml 33 + let should_load = (not (is_package_preloaded dcs)) && not cmi_only 34 + ``` 35 + - Delete the `preloaded` list entirely. 36 + 37 + **Edge cases**: 38 + - If `dcs_toplevel_modules` is empty, `is_package_preloaded` returns `false` → package treated as not preloaded (safe: worst case we attempt to load it). 39 + - Exact `Symtable` API to be confirmed against the project's OCaml version during implementation. 40 + 41 + **Files changed**: `js_top_worker/lib/findlibish.ml` 42 + 43 + --- 44 + 45 + ## Task 2 — Scrollycode theming via `odoc html-generate --config` 46 + 47 + **Problem**: Three scrollycode demo pages are meant to have distinct visual themes but theming is never applied. Per-page theme selection (via tag suffixes like `@scrolly.warm`) is the wrong abstraction — theming is a site-level concern, not a page-level concern. 48 + 49 + **Approach**: Add a generic `--config key=value` argument to `odoc html-generate`, plumbed into `Html.Config.t`. The odoc-docsite shell reads the config to include the appropriate theme CSS globally. Individual `.mld` files have no control over theming. 50 + 51 + **Design**: 52 + 53 + ### odoc CLI (`odoc html-generate`) 54 + - Add `--config key=value` as a repeatable argument. 55 + - Accumulate pairs into `Html.Config.t` as `config : (string * string) list` (or equivalent map). 56 + 57 + ### odoc-docsite shell 58 + - Read `scrollycode.theme` from the config at shell-generation time. 59 + - If set (e.g. `"warm"`), emit `<link rel="stylesheet" href="extensions/scrollycode-{theme}.css">` in the global shell HTML so every page in the site receives the theme. 60 + 61 + ### odoc-scrollycode-extension 62 + - `to_document` includes only the structural `scrollycode.css` — no theme CSS. 63 + - Remove any `@scrolly.warm` / `@scrolly.dark` / `@scrolly.notebook` tag-variant handling (never implement it). 64 + 65 + ### Demo `.mld` files 66 + - `warm_parser.mld`, `dark_repl.mld`, `notebook_testing.mld`: use plain `@scrolly` with no suffix. 67 + 68 + ### Demo dune rule (`odoc-interactive-extension/doc/dune` or equivalent) 69 + - Pass `--config scrollycode.theme=warm` (or chosen theme) to `odoc html-generate`. 70 + 71 + **Files changed**: 72 + - `odoc/` — CLI argument parsing, `Html.Config.t` 73 + - `odoc-docsite/` — shell generation reads config 74 + - `odoc-scrollycode-extension/src/scrollycode_extension.ml` 75 + - `odoc-scrollycode-extension/test/warm_parser.mld`, `dark_repl.mld`, `notebook_testing.mld` 76 + - Demo site dune rule 77 + 78 + --- 79 + 80 + ## Task 3 — Mermaid extension / SPA inline script handling 81 + 82 + **Problem**: The SPA shell's `navigateTo` function (in `odoc_docsite_js.ml`) fetches a new page's HTML and injects `link[rel="stylesheet"]` and `script[src]` elements from its `<head>`, but ignores inline `<script>` elements. The Mermaid extension (and several others: msc, dot, scrollycode meta-tags) uses `Js_inline` for initialisation scripts, which are never re-executed on SPA navigation. 83 + 84 + **Approach (Approach A)**: Fix the SPA shell to honour all resource types the extension API can generate, specifically by executing inline scripts after external scripts have loaded. 85 + 86 + **Design**: 87 + 88 + In `navigateTo` in `odoc_docsite_js.ml`: 89 + 90 + 1. Collect both external (`script[src]`) and inline (`script:not([src])`) elements from the fetched page's `<head>`. 91 + 2. Inject external scripts as before, but track load completion. 92 + 3. After all external scripts have loaded, execute inline scripts in document order by creating new `<script>` elements with the same `textContent` and appending them to `document.head`. 93 + 94 + The inline scripts come from extension `Js_inline` resources and are not present in the base shell, so collecting all `head script:not([src])` from the fetched page is safe and correct. 95 + 96 + This single change fixes mermaid, msc, dot, and scrollycode meta-tag initialisation simultaneously. 97 + 98 + **Files changed**: `odoc-docsite/src/odoc_docsite_js.ml` 99 + 100 + --- 101 + 102 + ## Task 4 — Oxcaml modes/layouts investigation 103 + 104 + **Problem**: odoc produces different mode/layout annotations than utop for some OxCaml types. Example — `Base.Uniform_array.compare__local`: 105 + 106 + - odoc: `('a : value_or_null) @ local -> ...` 107 + - utop: `'a @ local -> ...` 108 + 109 + **Approach**: Read-only investigation. 110 + 111 + 1. Examine `Base.Uniform_array` in the oxmono at `/cache/jons-agent/oxmono`. 112 + 2. Trace how odoc processes the type compared to how utop resolves it. 113 + 3. Determine whether the discrepancy is a rendering/pretty-printing difference, a semantics difference, or an odoc bug. 114 + 4. Write a finding report. 115 + 116 + **No code changes expected** (investigation only). 117 + 118 + --- 119 + 120 + ## Task 5 — jtw opam fixes 121 + 122 + **Problem A**: `jtw opam` crashes with a `None` exception when libraries are installed under `_build/install` because META file paths are outside the findlib tree, and `Fpath.relativize ~root:findlib_dir |> Option.get` fails for such paths. 123 + 124 + **Problem B**: The universe dune rule in `odoc-interactive-extension/doc/dune` only lists `yojson`, missing `js_top_worker-widget-leaflet` which is required by the map demo. 125 + 126 + **Design**: 127 + 128 + ### Fix A — META relativization (`js_top_worker/bin/jtw.ml`) 129 + - In the `opam` function, replace the direct `Fpath.relativize ~root:findlib_dir meta_file |> Option.get |> Fpath.parent` with `relativize_or_fallback ~findlib_dir meta_file |> Fpath.parent`. 130 + - `relativize_or_fallback` already exists and handles paths outside the findlib tree correctly (extracts path components after the `lib` directory); it is already used for CMI files. 131 + 132 + ### Fix B — Universe dune rule (`odoc-interactive-extension/doc/dune`) 133 + - Add `js_top_worker-widget-leaflet` to the `jtw opam` invocation so the leaflet widget is available in the universe for the map demo. 134 + 135 + **Files changed**: 136 + - `js_top_worker/bin/jtw.ml` 137 + - `odoc-interactive-extension/doc/dune` 138 + 139 + --- 140 + 141 + ## Branch structure 142 + 143 + | Branch | Tasks | Repos touched | 144 + |--------|-------|---------------| 145 + | `js_top_worker-fixes` | 1, 5 | `js_top_worker`, `odoc-interactive-extension` | 146 + | `scrollycode-and-spa` | 2, 3 | `odoc`, `odoc-docsite`, `odoc-scrollycode-extension` | 147 + | *(none)* | 4 | read-only investigation |