#!meta {"kernelInfo":{"defaultKernelName":"spiral","items":[{"aliases":[],"name":"spiral"}]}} #!markdown # DirTreeHtml (Polyglot) #!fsharp #r @"../../../../../../../.nuget/packages/fsharp.control.asyncseq/3.2.1/lib/netstandard2.1/FSharp.Control.AsyncSeq.dll" #r @"../../../../../../../.nuget/packages/system.reactive/6.0.1-preview.1/lib/net6.0/System.Reactive.dll" #r @"../../../../../../../.nuget/packages/system.reactive.linq/6.0.1-preview.1/lib/netstandard2.0/System.Reactive.Linq.dll" #r @"../../../../../../../.nuget/packages/argu/6.2.4/lib/netstandard2.0/Argu.dll" #r @"../../../../../../../.nuget/packages/falco.markup/1.1.1/lib/netstandard2.0/Falco.Markup.dll" #!fsharp #!import ../../lib/fsharp/Notebooks.dib #!import ../../lib/fsharp/Testing.dib #!fsharp #!import ../../lib/fsharp/Common.fs #!import ../../lib/fsharp/CommonFSharp.fs #!import ../../lib/fsharp/Async.fs #!import ../../lib/fsharp/AsyncSeq.fs #!import ../../lib/fsharp/Runtime.fs #!import ../../lib/fsharp/FileSystem.fs #!fsharp #if !INTERACTIVE open Lib #endif #!fsharp open SpiralFileSystem.Operators open Falco.Markup #!fsharp type FileSystemNode = | File of string * string * int64 | Folder of string * string * FileSystemNode list | Root of FileSystemNode list let rec scanDirectory isRoot (basePath : string) (path : string) = let relativePath = path |> SpiralSm.replace basePath "" |> SpiralSm.replace "\\" "/" |> SpiralSm.replace "//" "/" |> SpiralSm.trim_start [| '/' |] let directories = path |> System.IO.Directory.GetDirectories |> Array.toList |> List.sort |> List.map (scanDirectory false basePath) let files = path |> System.IO.Directory.GetFiles |> Array.toList |> List.sort |> List.map (fun f -> File (System.IO.Path.GetFileName f, relativePath, System.IO.FileInfo(f).Length)) let children = directories @ files if isRoot then Root children else Folder (path |> System.IO.Path.GetFileName, relativePath, children) let rec generateHtml fsNode = let sizeLabel size = match float size with | size when size > 1024.0 * 1024.0 -> $"%.2f{size / 1024.0 / 1024.0} MB" | size when size > 1024.0 -> $"%.2f{size / 1024.0} KB" | size -> $"%.2f{size} B" match fsNode with | File (fileName, relativePath, size) -> Elem.div [] [ Text.raw "📄 " Elem.a [ Attr.href $"""{relativePath}{if relativePath = "" then "" else "/"}{fileName}""" ] [ Text.raw fileName ] Elem.span [] [ Text.raw $" ({size |> sizeLabel})" ] ] | Folder (folderName, relativePath, children) -> let size = let rec loop children = children |> List.sumBy (function | File (_, _, size) -> size | Folder (_, _, children) | Root children -> loop children ) loop children Elem.details [ Attr.open' "true" ] [ Elem.summary [] [ Text.raw "📂 " Elem.a [ Attr.href relativePath ] [ Text.raw folderName ] Elem.span [] [ Text.raw $" ({size |> sizeLabel})" ] ] Elem.div [] [ yield! children |> List.map generateHtml ] ] | Root children -> Elem.div [] [ yield! children |> List.map generateHtml ] let generateHtmlForFileSystem root = $"""
{root |> generateHtml |> renderNode} """ #!fsharp //// test let expected = """