diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c64c3dbab..149947205 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -8,6 +8,8 @@ ## 22.0.0-alpha.2 - 2026-03-13 ### Added +* Add `--root` option to `fsdocs watch` to override the root URL for generated pages. Useful for serving docs via GitHub Codespaces, reverse proxies, or other remote hosting where `localhost` URLs are inaccessible. E.g. `fsdocs watch --root /` or `fsdocs watch --root https://example.com/docs/`. When not set, defaults to `http://localhost:/` as before. [#924](https://github.com/fsprojects/FSharp.Formatting/issues/924) +* Fix `fsdocs watch` hot-reload WebSocket to connect using the page's actual host (`window.location.host`) instead of a hardcoded `localhost:`, so hot-reload works correctly in GitHub Codespaces, behind reverse proxies, and over HTTPS. [#924](https://github.com/fsprojects/FSharp.Formatting/issues/924) * Search dialog now auto-focuses the search input when opened, clears on close, and can be triggered with `Ctrl+K` / `Cmd+K` in addition to `/`. * Add `dotnet fsdocs convert` command to convert a single `.md`, `.fsx`, or `.ipynb` file to HTML (or another output format) without building a full documentation site. [#811](https://github.com/fsprojects/FSharp.Formatting/issues/811) * `fsdocs convert` now accepts the input file as a positional argument (e.g. `fsdocs convert notebook.ipynb -o notebook.html`). [#1019](https://github.com/fsprojects/FSharp.Formatting/pull/1019) diff --git a/src/fsdocs-tool/BuildCommand.fs b/src/fsdocs-tool/BuildCommand.fs index 8df394de5..3e7981bb2 100644 --- a/src/fsdocs-tool/BuildCommand.fs +++ b/src/fsdocs-tool/BuildCommand.fs @@ -819,11 +819,11 @@ module Serve = let refreshEvent = FSharp.Control.Event() /// generate the script to inject into html to enable hot reload during development - let generateWatchScript (port: int) = - let tag = - """ + let generateWatchScript () = + """ """ - tag.Replace("{{PORT}}", string port) - let connectedClients = ConcurrentDictionary() let socketHandler (webSocket: WebSocket) (context: HttpContext) = @@ -1560,9 +1558,15 @@ type CoreBuildOptions(watch) = // Adjust the user substitutions for 'watch' mode root let userRoot, userParameters = if watch then - let userRoot = sprintf "http://localhost:%d/" this.port_option - - if userParametersDict.ContainsKey(ParamKeys.root) then + let userRoot = + match this.root_override_option with + | Some r -> r + | None -> sprintf "http://localhost:%d/" this.port_option + + if + userParametersDict.ContainsKey(ParamKeys.root) + && this.root_override_option.IsNone + then printfn "ignoring user-specified root since in watch mode, root = %s" userRoot let userParameters = @@ -1873,7 +1877,7 @@ type CoreBuildOptions(watch) = let getLatestWatchScript () = if watch then // if running in watch mode, inject hot reload script - [ ParamKeys.``fsdocs-watch-script``, Serve.generateWatchScript this.port_option ] + [ ParamKeys.``fsdocs-watch-script``, Serve.generateWatchScript () ] else // otherwise, inject empty replacement string [ ParamKeys.``fsdocs-watch-script``, "" ] @@ -2325,6 +2329,9 @@ type CoreBuildOptions(watch) = abstract port_option: int default x.port_option = 0 + abstract root_override_option: string option + default x.root_override_option = None + [] @@ -2504,3 +2511,12 @@ type WatchCommand() = [] member val port = 8901 with get, set + + override x.root_override_option = if x.root = "" then None else Some x.root + + [/.")>] + member val root = "" with get, set