From 241dc9c99bed4dddbc748aad54cee5bf7d77ab92 Mon Sep 17 00:00:00 2001 From: polwex Date: Sun, 15 Jun 2025 02:29:59 +0700 Subject: lfg --- bs5/server/dune | 3 + bs5/server/pages/Index.re | 216 ++++++++++++++++++++++++++++++++++++++++++--- bs5/server/pages/dune | 2 + bs5/server/rsc/Date.ml | 15 ++++ bs5/server/rsc/Markdown.ml | 202 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 426 insertions(+), 12 deletions(-) create mode 100644 bs5/server/rsc/Date.ml create mode 100644 bs5/server/rsc/Markdown.ml diff --git a/bs5/server/dune b/bs5/server/dune index 9565f90..7a838ba 100644 --- a/bs5/server/dune +++ b/bs5/server/dune @@ -13,6 +13,9 @@ rsc pages ; + ; sister packages + shared + ; dream server-reason-react.belt server-reason-react.js diff --git a/bs5/server/pages/Index.re b/bs5/server/pages/Index.re index d5af822..685069a 100644 --- a/bs5/server/pages/Index.re +++ b/bs5/server/pages/Index.re @@ -1,16 +1,208 @@ +open Rsc; +module Section = { + [@react.component] + let make = (~title, ~children, ~description=?) => { + +

+ {React.string(title)} +

+ {switch (description) { + | Some(description) => + description + | None => React.null + }} +
+ children + ; + }; +}; + +module ExpandedContent = { + [@react.component] + let make = (~id, ~content: string, ~updatedAt: float, ~title: string) => { + let lastUpdatedAt = + if (Date.is_today(updatedAt)) { + Date.format_time(updatedAt); + } else { + Date.format_date(updatedAt); + }; + + let summary = + content |> Markdown.extract_text |> Markdown.summarize(~words=20); + + + {switch (String.trim(summary)) { + | "" => {React.string("(No content)")} + | s => s + }} + +
+ }> +
+ title + lastUpdatedAt +
+ ; + }; +}; + module Page = { [@react.async.component] let make = () => { - // let promiseIn2 = - // Lwt.bind(Lwt_unix.sleep(2.0), _ => - // Lwt.return("Solusionao in 2 seconds!") - // ); - // let promiseIn4 = - // Lwt.bind(Lwt_unix.sleep(4.0), _ => - // Lwt.return("Solusionao in 4 seconds!") - // ); + let promiseIn2 = + Lwt.bind(Lwt_unix.sleep(2.0), _ => + Lwt.return("Solusionao in 2 seconds!") + ); + let promiseIn4 = + Lwt.bind(Lwt_unix.sleep(4.0), _ => + Lwt.return("Solusionao in 4 seconds!") + ); + Lwt.return( -
{React.string("Well hi")}
, + + +

+ {React.string( + "Server side rendering server components and client components", + )} +

+ + "React server components. Lazy loading of client components. Client props encodings, such as promises, React elements, and primitive types." + +
+
+
+ +
+
+
+ {React.string("H E A D E R")} )} + string_list=["Item 1", "Item 2"] + promise=promiseIn2> +
+ {React.string( + "This footer is a React.element as a server component into client prop, yay!", + )} +
+
+
+
+
+ {React.string("H E A D E R")} )} + string_list=["Item 1", "Item 2"] + promise=promiseIn2> +
+ {React.string( + "This footer is a React.element as a server component into client prop, yay!", + )} +
+
+
+
+
+ +
+
+
+ +
+
+

+ {React.string("Server functions")} +

+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
, ); }; }; @@ -25,15 +217,15 @@ module App = {
- // -
+ + ; }; }; let handler = request => - Rsc.DreamRSC.create_from_request( + DreamRSC.create_from_request( ~bootstrap_modules=["/static/demo/SinglePageRSC.re.js"], , request, diff --git a/bs5/server/pages/dune b/bs5/server/pages/dune index bc63199..47c4f2b 100644 --- a/bs5/server/pages/dune +++ b/bs5/server/pages/dune @@ -2,6 +2,8 @@ (name pages) (libraries rsc + shared + ; dream lwt.unix server-reason-react.belt diff --git a/bs5/server/rsc/Date.ml b/bs5/server/rsc/Date.ml new file mode 100644 index 0000000..5abf9c3 --- /dev/null +++ b/bs5/server/rsc/Date.ml @@ -0,0 +1,15 @@ +let is_today date = + let now = Unix.localtime (Unix.time ()) in + let d = Unix.localtime date in + now.tm_year = d.tm_year && now.tm_mon = d.tm_mon && now.tm_mday = d.tm_mday + +let format_time date = + let t = Unix.localtime date in + let hour = t.tm_hour mod 12 in + let hour = if hour = 0 then 12 else hour in + let ampm = if t.tm_hour >= 12 then "pm" else "am" in + Printf.sprintf "%d:%02d %s" hour t.tm_min ampm + +let format_date date = + let t = Unix.localtime date in + Printf.sprintf "%d/%d/%02d" (t.tm_mon + 1) t.tm_mday (t.tm_year mod 100) diff --git a/bs5/server/rsc/Markdown.ml b/bs5/server/rsc/Markdown.ml new file mode 100644 index 0000000..e8c8942 --- /dev/null +++ b/bs5/server/rsc/Markdown.ml @@ -0,0 +1,202 @@ +module List = struct + include List + let rec take lst n = + match (lst, n) with + | ([], _) -> [] + | (_, 0) -> [] + | (x :: xs, n) -> x :: take xs (n - 1) +end + +let convert_headings text = + text + |> Str.global_replace (Str.regexp "^#### \\(.*\\)$") "

\\1

" + |> Str.global_replace (Str.regexp "^### \\(.*\\)$") "

\\1

" + |> Str.global_replace (Str.regexp "^## \\(.*\\)$") "

\\1

" + |> Str.global_replace (Str.regexp "^# \\(.*\\)$") "

\\1

" + +let convert_emphasis text = + text + |> Str.global_replace + (Str.regexp "\\*\\*\\([^*]*\\)\\*\\*") + "\\1" + |> Str.global_replace + (Str.regexp "__\\([^_]*\\)__") + "\\1" + |> Str.global_replace (Str.regexp "\\*\\([^*]*\\)\\*") "\\1" + |> Str.global_replace (Str.regexp "_\\([^_]*\\)_") "\\1" + +let convert_code text = + text + |> Str.global_replace + (Str.regexp "```\\([^`]*\\)```") + "
\\1
" + |> Str.global_replace (Str.regexp "`\\([^`]*\\)`") "\\1" + +let convert_links text = + text + |> Str.global_replace + (Str.regexp "\\[\\([^]]*\\)\\](\\([^)]*\\))") + "\\1" + +let convert_lists text = + let lines = String.split_on_char '\n' text in + + let process_line line = + match line with + | line when Str.string_match (Str.regexp "^-\\s*\\(.*\\)$") line 0 -> + "
  • " ^ Str.matched_group 1 line ^ "
  • " + | line when Str.string_match (Str.regexp "^\\+\\s*\\(.*\\)$") line 0 -> + "
  • " ^ Str.matched_group 1 line ^ "
  • " + | line when Str.string_match (Str.regexp "^\\*\\s*\\(.*\\)$") line 0 -> + "
  • " ^ Str.matched_group 1 line ^ "
  • " + | line + when Str.string_match (Str.regexp "^\\d+\\.\\s*\\(.*\\)$") line 0 -> + "
  • " ^ Str.matched_group 1 line ^ "
  • " + | _ -> line + in + + let wrap_consecutive_items lines = + let rec aux acc current_list lines = + match (current_list, lines) with + | ([], []) -> List.rev acc + | (hd :: tl, []) -> + List.rev [ + "
      " ^ String.concat "\n" (List.rev (hd :: tl)) ^ "
    "; + ] @ acc + | ([], line :: rest) -> + if Str.string_match (Str.regexp "^
  • ") line 0 then + aux acc [line] rest + else + aux (line :: acc) [] rest + | (items, line :: rest) -> + if Str.string_match (Str.regexp "^
  • ") line 0 then + aux acc (line :: current_list) rest + else + aux + (line :: ("
      " ^ String.concat "\n" (List.rev items) ^ "
    ") :: acc) + [] + rest + in + aux [] [] lines + in + + lines + |> List.map process_line + |> wrap_consecutive_items + |> String.concat "\n" + +let wrap_lists text = + text + |> Str.global_replace + (Str.regexp "
  • .*
  • \\(\n
  • .*
  • \\)*") + "
      \\0
    " + +let convert_blockquotes text = + let lines = String.split_on_char '\n' text in + + let rec process_lines acc in_quote lines = + match lines with + | [] when in_quote -> List.rev ("" :: acc) + | [] -> List.rev acc + | line :: rest -> + let trimmed = String.trim line in + if Str.string_match (Str.regexp "^>\\s*\\(.*\\)$") trimmed 0 then + let content = Str.matched_group 1 trimmed in + if in_quote then + process_lines (content :: acc) true rest + else + process_lines (content :: "
    " :: acc) true rest + else if trimmed = "" then + if in_quote then + process_lines ("
    " :: acc) false rest + else + process_lines (line :: acc) false rest + else if in_quote then + process_lines (line :: acc) true rest + else + process_lines (line :: acc) false rest + in + + lines |> process_lines [] false |> String.concat "\n" + +let convert_paragraphs text = + let lines = String.split_on_char '\n' text in + + let is_block_element line = + Str.string_match + (Str.regexp "^<\\(h[1-6]\\|ul\\|ol\\|blockquote\\|pre\\)>") + line + 0 + in + + let wrap_paragraphs lines = + let rec aux acc current_p lines = + match lines with + | [] when current_p <> "" -> + List.rev (("

    " ^ current_p ^ "

    ") :: acc) + | [] -> List.rev acc + | line :: rest when is_block_element line -> + if current_p <> "" then + aux (line :: ("

    " ^ current_p ^ "

    ") :: acc) "" rest + else + aux (line :: acc) "" rest + | line :: rest when String.trim line = "" -> + if current_p <> "" then + aux (("

    " ^ current_p ^ "

    ") :: acc) "" rest + else + aux acc "" rest + | line :: rest -> + let sep = + if current_p = "" then + "" + else + " " + in + aux acc (current_p ^ sep ^ String.trim line) rest + in + aux [] "" lines + in + + lines |> wrap_paragraphs |> String.concat "\n" + +let to_html markdown = + markdown + |> convert_headings + |> convert_emphasis + |> convert_code + |> convert_links + |> convert_lists + |> wrap_lists + |> convert_blockquotes + |> convert_paragraphs + |> String.trim + +let extract_text markdown = + markdown + |> Str.global_replace (Str.regexp "\\[([^]]*)\\]\\([^)]*\\)") "\\1" + |> Str.global_replace (Str.regexp "\\*\\*\\([^*]*\\)\\*\\*") "\\1" + |> Str.global_replace (Str.regexp "\\*\\([^*]*\\)\\*") "\\1" + |> Str.global_replace (Str.regexp "__\\([^_]*\\)__") "\\1" + |> Str.global_replace (Str.regexp "_\\([^_]*\\)_") "\\1" + |> Str.global_replace (Str.regexp "~~\\([^~]*\\)~~") "\\1" + |> Str.global_replace (Str.regexp "`\\([^`]*\\)`") "\\1" + |> Str.global_replace (Str.regexp "```[^`]*```") "" + |> Str.global_replace (Str.regexp "^#+ .*$") "\n" + |> Str.global_replace (Str.regexp "^#* .*$") "\n" + |> Str.global_replace (Str.regexp "> \\|>") "" + |> Str.global_replace (Str.regexp "\\[\\|\\]\\|\\(\\|\\)") "" + |> Str.global_replace (Str.regexp "-\\|\\+\\|\\*\\s+") "" + |> Str.global_replace (Str.regexp "^\\d+\\.\\s+") "" + |> Str.global_replace (Str.regexp "\\\\") "" + |> String.trim + +let summarize text ~words:n = + let words = Str.split (Str.regexp "[ \n\r\t]+") text in + let truncated = List.take words n in + let dots = + if List.length words > n then + "..." + else + "" + in + String.concat " " truncated ^ dots \ No newline at end of file -- cgit v1.2.3