[@@@module "%/../oar-code.ml"]
let icon__file =
{|<!-- @license lucide-static v1.17.0 - ISC -->
<svg
class="lucide lucide-file"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
</svg>|}
let icon__folder =
{|<!-- @license lucide-static v1.17.0 - ISC -->
<svg
class="lucide lucide-folder"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
</svg>|}
let style = {|
html {
--light-5: #9a9996;
--light-4: #c0bfbc;
--light-3: #deddda;
--light-2: #f6f5f4;
--light-1: #ffffff;
--dark-1: #77767b;
--dark-2: #5e5c64;
--dark-3: #3d3846;
--spacing: 12px;
--font-size: 14pt;
--icon-size: calc(var(--font-size) * 1.15);
--font-row-height: calc(var(--font-size) * 1.25);
color: var(--light-5);
font-size: var(--font-size);
font-family: sans-serif;
}
pre {
color: var(--dark-3);
}
body {
min-height: 100vh;
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
margin-right: 0;
}
nav:has(> ul.list-of-files) {
overflow-x: auto;
border: solid gainsboro 1px;
border-radius: 8px;
}
ul {
margin-block-start: 0px;
margin-block-end: 0px;
}
ul.list-of-files {
margin-block-start: 0px;
padding-inline-start: 0px;
display: grid;
grid-template-columns: var(--font-row-height) 1fr;
grid-auto-rows: var(--font-row-height);
padding-bottom: 8px;
overflow-y: hidden;
width: max-content;
height: 100%;
}
ul.list-of-files > li {
list-style-type: none;
grid-column: 1 / -1;
display: grid;
grid-template-columns: subgrid;
}
ul.list-of-files > li > :nth-child(2) {
display: flex;
align-items: center;
}
ul.list-of-files > .header {
font-size: 0.8em;
font-weight: 600;
align-content: center;
}
ul.list-of-files > .header > div {
padding-top: 8px;
padding-left: 4px;
padding-bottom: 4px;
-webkit-user-select: none;
}
ul.list-of-files > .header > div:not(:empty):hover {
background-color: #e6e6e6;
color: var(--dark-2);
}
ul.list-of-files > .header > div:not(:empty):active {
background-color: #c2c2c2;
color: var(--dark-3);
}
ul.list-of-files > li:not:(:first-child) {
}
ul.list-of-files > li > div:first-child {
display: flex;
align-items: center;
justify-content: center;
}
body.fsbrowse > main {
display: flex;
padding-left: var(--spacing);
flex-direction: column;
}
body.fsbrowse > main pre {
overflow-x: auto;
}
body.fsbrowse.root {
display: grid;
grid-template-rows: min-content min-content 1fr min-content;
grid-template-columns: minmax(0,1fr) minmax(0, 700px) minmax(0,1fr);
}
body.fsbrowse.root > header.first {
grid-row: 1 / 2;
grid-column: 2 / 3;
}
body.fsbrowse.root > header.second {
display: grid;
grid-template-columns: minmax(240px,1fr) minmax(0,340px);
grid-row: 2 / 3;
grid-column: 2 / 3;
}
body.fsbrowse.root > header.second > nav {
grid-column: 1 / 2;
margin-top: var(--spacing);
margin-left: var(--spacing);
margin-right: var(--spacing);
margin-bottom: var(--spacing);
}
body.fsbrowse.root > header.second > nav > ul.list-of-files {
}
body.fsbrowse.root > header.second > aside {
grid-column: 2 / 3;
padding-right: var(--spacing);
padding-top: var(--spacing);
padding-bottom: var(--spacing);
}
body.fsbrowse.root > main {
grid-row: 3 / 4;
grid-column: 2 / 3;
}
.render h1,
.render h2,
.render h3,
.render h4 {
margin-block-start: 0;
margin-block-end: 0.2em;
font-size: 1em;
color: black;
}
.render p {
margin-block-start: 0;
margin-block-end: 0.2em;
}
.render ul,
.render ol {
margin-block-start: 0;
margin-block-end: 0.2em;
}
body.fsbrowse.root > footer {
grid-row: 4 / 5;
grid-column: 2 / 3;
}
body.fsbrowse:not(.root) {
display: grid;
grid-template-columns: 300px minmax(0,1fr);
grid-template-rows: min-content 1fr min-content;
}
body.fsbrowse:not(.root).sidebar-collapsed {
grid-template-columns: 0px minmax(0,1fr);
}
body.fsbrowse:not(.root).sidebar-collapsed > aside {
z-index: 0;
}
body.fsbrowse:not(.root).sidebar-collapsed > aside > nav {
width: 0px;
visibility: hidden;
}
body.fsbrowse:not(.root).sidebar-collapsed > aside > hgroup > *:not(:first-child) {
display: none;
}
body.fsbrowse:not(.root).sidebar-collapsed > aside > hgroup > :first-child:not(:hover) {
opacity: 30%;
}
body.fsbrowse:not(.root) > header {
grid-column: 1 / -1;
grid-row: 1 / 2;
}
body.fsbrowse:not(.root) > aside {
grid-column: 1 / 2;
grid-row: 2 / 3;
padding-top: var(--spacing);
padding-left: var(--spacing);
}
body.fsbrowse:not(.root) > aside > nav {
margin-top: var(--spacing);
}
body.fsbrowse:not(.root) > main {
grid-column: 2 / 3;
grid-row: 2 / 3;
}
body.fsbrowse:not(.root) > main.render {
padding-top: var(--spacing);
}
body.fsbrowse:not(.root) > main.raw {
flex-direction: row;
}
body.fsbrowse:not(.root) > main > * {
}
body.fsbrowse:not(.root) > footer {
grid-column: 1 / -1;
grid-row: 3 / 4;
}
aside h2 {
display: inline-block;
font-weight: 600;
margin-top: 0;
margin-bottom: 0;
font-size: 1em;
color: black;
}
footer {
padding: var(--spacing);
color: var(--light-4);
text-align: center;
}
footer * {
color: var(--light-4);
}
pre {
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
margin-right: 0;
}
a {
word-wrap: anywhere;
}
.file::before {
mask-image: url("icon.file.svg");
mask-size: contain;
background-color: var(--light-3);
background-size: var(--icon-size) var(--icon-size);
width: var(--icon-size);
height: var(--icon-size);
display: inline-block;
content: "";
}
.folder::before {
mask-image: url("icon.folder.svg");
mask-size: contain;
background-color: var(--light-3);
background-size: var(--icon-size) var(--icon-size);
width: var(--icon-size);
height: var(--icon-size);
display: inline-block;
content: "";
}
|}
let pre_escape_1 =
let pattern = Re.(char '<' |> compile) in
Re.replace ~all:true ~f:(fun _ -> "<‍") pattern
let pre_escape s = pre_escape_1 s
let head_meta = {|
<meta name=\"src-lang\" content=\"ocaml :dsl oar\">
<meta name=\"ssg\" content=\"oar.fsbrowse\">
<meta name="viewport" content="width=device-width, initial-scale=1" >
|}
module Typst =
struct
open Eio
let html_compile ~fs ~process_mgr path =
let outpath = Files.make_tempfile1 ~fs ~suffix:".html" () in
Process.run process_mgr ("typst" :: "compile" :: "--features" :: "html" :: Path.(native_exn path) :: Path.(native_exn outpath) :: []);
outpath
end
module Option =
struct include Option
let value ~default t =
match t with
| None -> default ()
| Some x -> x
end
module Typst__codewrap =
struct
open Eio
let code_escape =
Re.replace ~all:true ~f:(fun _ -> "\\`") @@ Re.compile @@ Re.char '`'
let code_enter =
Re.replace ~all:true ~f:(fun _ -> "`") @@ Re.compile @@ Re.str "\\`"
let html_compile ~fs ~process_mgr ?language:lang_override path =
(* traceln "oar-web: typst-codewrap: trying to compile '%s'" @@ Path.native_exn path; *)
let language = Option.value ~default:(fun () ->
try Oar_code.Language_infer.of_file path
with Failure s -> Eio.traceln "oar: oar-web: an error occurred during language inference: '%s'. Defaulting to plain text" s; "plain") lang_override in
let midpath = Files.make_tempfile1 ~fs ~suffix:".typ" ()
and outpath = Files.make_tempfile1 ~fs ~suffix:".html" () in
assert begin
let content = Path.load path in
Path.save ~create:(`Exclusive 0o700) midpath ( {|```|} ^ language ^ "\n" ^ code_escape content ^ {|```|} );
true
end;
(* traceln "oar-web: typst-codewrap: outpath is '%s'" @@ Path.native_exn outpath; *)
(* traceln "oar-web: typst-codewrap: processing end"; *)
Process.run process_mgr ("typst" :: "compile" :: "--features" :: "html" :: Path.(native_exn midpath) :: Path.(native_exn outpath) :: []);
outpath
end