```fsharp
#r "nuget: fsharp.control.asyncseq, 3.2.1"
#r "nuget: system.reactive, 6.0.1-preview.1"
#r "nuget: system.reactive.linq, 6.0.1-preview.1"
#r "nuget: argu, 6.2.4"
#r "nuget: fsharp.json, 0.4.1"
```

`#!import ../../../polyglot/lib/fsharp/Notebooks.livemd`

<!-- livebook:{"break_markdown":true} -->

`#!import ../../../polyglot/lib/fsharp/Common.fs`

<!-- livebook:{"break_markdown":true} -->

`#!import ../../../polyglot/lib/fsharp/CommonFSharp.fs`

<!-- livebook:{"break_markdown":true} -->

`#!import ../../../polyglot/lib/fsharp/Async.fs`

<!-- livebook:{"break_markdown":true} -->

`#!import ../../../polyglot/lib/fsharp/AsyncSeq.fs`

<!-- livebook:{"break_markdown":true} -->

`#!import ../../../polyglot/lib/fsharp/Runtime.fs`

<!-- livebook:{"break_markdown":true} -->

`#!import ../../../polyglot/lib/fsharp/FileSystem.fs`

```fsharp
#if !INTERACTIVE
open Lib
#endif
```

```fsharp
open Common
open SpiralFileSystem.Operators
```

```fsharp
let workspaceRoot = SpiralFileSystem.get_workspace_root ()
let tmpSpiralPath = workspaceRoot </> "target/plot"
let linePlotsDataPath = tmpSpiralPath </> "line_data"
let linePlotsSvgPath = tmpSpiralPath </> "line_svg"

[ tmpSpiralPath; linePlotsDataPath; linePlotsSvgPath ]
|> List.iter (fun dir -> if Directory.Exists dir |> not then Directory.CreateDirectory dir |> ignore)

Formatter.Register<struct (string * string * string * struct (string * float array * float array) array)> (
    (fun struct (caption, x_desc, y_desc, ys) ->
        let json = (caption, x_desc, y_desc, ys) |> FSharp.Json.Json.serialize
        async {
            let hashHex = json |> SpiralCrypto.hash_text
            let svgPath = linePlotsSvgPath </> $"{hashHex}.svg"

            if System.IO.File.Exists svgPath |> not then
                do! json |> SpiralFileSystem.write_all_text_async $"{linePlotsDataPath </> hashHex}.json"

                // no .NET Interactive kernel since the Kino port (dotnet fsi): no invocation to cancel
                let ct = System.Threading.CancellationToken.None

                let oldLevel = get_trace_level ()
                SpiralTrace.TraceLevel.US0_2
                |> set_trace_level
                use _ = (new_disposable (fun () ->
                    oldLevel |> set_trace_level
                ))

                let plotExePath = workspaceRoot </> $"workspace/target/release/plot{SpiralPlatform.get_executable_suffix ()}"
                let! exitCode, result =
                    SpiralRuntime.execution_options (fun x ->
                        { x with
                            l0 = plotExePath
                            l1 = Some ct
                            l6 = Some workspaceRoot
                        }
                    )
                    |> SpiralRuntime.execute_with_options_async
                if exitCode <> 0 then
                    return failwith $"plot failed with exit code {exitCode} and output {result}"
            return! svgPath |> SpiralFileSystem.read_all_text_async
        }
        |> Async.runWithTimeout 10000
        |> Option.defaultValue json
    ),
    "text/html"
)
```
