13 Commits

5 changed files with 1908 additions and 1148 deletions

1
.console Normal file
View File

@ -0,0 +1 @@
it consoles you

4
dune
View File

@ -15,7 +15,11 @@
zed zed
lambda-term lambda-term
irmin-unix irmin-unix
nottui
nottui-pretty nottui-pretty
uuseg.string
uutf
uucp
ocaml-compiler-libs.common ocaml-compiler-libs.common
ocaml-compiler-libs.bytecomp ocaml-compiler-libs.bytecomp
ocaml-compiler-libs.toplevel)) ocaml-compiler-libs.toplevel))

2004
human.ml

File diff suppressed because it is too large Load Diff

604
irc.ml
View File

@ -14,249 +14,481 @@ open Lwt_react
module F = Fmt module F = Fmt
module Communicator = struct module Communicator = struct
module Message = struct let base_path = "communicator"
type t = {content: string; time: string; mutable seen: bool} let topch = "top"
let make ?(time = "<ts>") content = {content; time; seen= false} module Istore = struct
include Human.Store
let from_storeview (sv : storeview) = sv.store
include Human.Store.Istore
end
module Message = struct
type t = {time: string list; content: string}
let make ?(time = Unix.gettimeofday ()) content =
let tm = Unix.localtime time in
{ time=
List.map string_of_int
[tm.tm_year + 1900; tm.tm_mon + 1; tm.tm_mday; tm.tm_hour]
@ [ string_of_float
( float_of_int (tm.tm_min * tm.tm_sec)
+. fst (modf time) ) ]
; content }
end end
module Channel = struct module Channel = struct
type t = {name: string; content: Message.t list Lwd.var} (* a channels step key may not be blank (i.e. "") *)
type t = {store: Istore.t; path: Istore.key}
let add_msg (c : t) (msg : Message.t) = let make (store : Istore.t) ~path ~(name : string) =
F.epr "Channel.add_msg msg.content=\"%s\"@." msg.content ; Lwt.return {store; path= path @ ["#" ^ name]}
let cn = Lwd.peek c.content in
Lwd.set c.content (msg :: cn)
let make name = {name; content= Lwd.var []} let add_msg {store; path} (msg : Message.t) : unit Lwt.t =
F.epr "add_msg path=[" ;
F.list ~sep:F.semi F.string F.stderr (path @ msg.time) ;
F.epr "] content=%s @." msg.content ;
Istore.set_exn store ~info:Irmin.Info.none (path @ msg.time)
msg.content
end end
module Tree = struct module Tree = struct
open Channel
open Message open Message
type t = type selection = Istore.Key.t
{ channel: Channel.t type t = {store: Istore.t; view: Istore.key}
; subs: t Lwd_table.t
; focus: Nottui.Focus.handle }
type protocol = Irc | Email | Rss | Mublog | ActivityPub let contents {store; view} (s : selection) :
Istore.Contents.t option Lwt.t =
Istore.find store (view @ s)
let add (comm : t) ch : unit = let make_top ?(view = [base_path]) gitpath branchname : t Lwt.t =
let c' = Istore.Repo.v (Irmin_git.config gitpath)
{ channel= ch >>= fun repo ->
; subs= Lwd_table.make () Istore.of_branch repo branchname
; focus= Nottui.Focus.make () } in >>= fun store ->
Lwd_table.append' comm.subs c' let t = {store; view} in
Channel.make store ~path:view ~name:topch
>>= fun ch_top ->
Channel.add_msg ch_top
(Message.make "Communicator restarting...")
>>= fun () ->
Channel.add_msg ch_top
(Message.make "Currently only IRC is implemented")
>>= fun () -> Lwt.return t
let make_top () = let add {store; view} ~(name : string list) ~(config : Istore.tree)
let channel = Channel.make "communicator-top" in : t Lwt.t =
add_msg channel (Message.make "Welcome to the Communicator") ; Istore.get_tree store name
add_msg channel >>= fun tree ->
(Message.make "Currently only IRC is implemented") ; Istore.Tree.remove tree ["_config"]
{channel; subs= Lwd_table.make (); focus= Nottui.Focus.make ()} >>= fun tree ->
Istore.Tree.add_tree tree [] config
>>= fun tree ->
Istore.set_tree_exn ~info:Irmin.Info.none store name tree
>>= fun () -> Lwt.return {store; view}
end
module Protocol = struct
type t = Irc | Email | Rss | Mublog | ActivityPub
let to_string = function
| Irc -> ("IRC", "Internet Relay Chat")
| Email -> ("E-mail", "Electronic Mail")
| Rss -> ("RSS", "Really Simple Subscriptions???")
| Mublog -> ("uBlog", "Microblogging (Twitter)")
| ActivityPub -> ("ActivityPub", "Mastodon, etc.")
let id t = fst (to_string t)
let desc t = snd (to_string t)
end end
module Irc = struct module Irc = struct
module C = Irc_client_tls module C = Irc_client_tls
module M = Irc_message module M = Irc_message
let connection (c : Tree.t) server port nick module Config = struct
(channels : string list) : Channel.t = type t = Istore.tree
let channel =
Channel.make ("IRC: " ^ server ^ ":" ^ string_of_int port) open Lwt.Infix
in
let _c' = Tree.add c channel in let path = "_config"
let add_msg str = Channel.add_msg channel (Message.make str) in
let channel_assoc = ref [] in let make_connection Tree.{store; view} server port nick =
let make_ch name = let name = F.str "%s@%s:%d" nick server port in
let ch = Channel.make name in Istore.Tree.add Istore.Tree.empty ["server"] server
Tree.add c ch ; >>= fun t' ->
channel_assoc := (name, ch) :: !channel_assoc ; Istore.Tree.add t' ["port"] (string_of_int port)
ch in >>= fun t' ->
Lwt.async Istore.Tree.add t' ["nick"] nick
(C.reconnect_loop ~after:30 >>= fun t' ->
~connect:(fun () -> Istore.Tree.add t' ["protocol"] (Protocol.id Irc)
add_msg "Connecting..." ; >>= fun t' ->
C.connect_by_name ~server ~port ~nick () F.epr "Creating connection config /%s/%s/@." name path ;
Istore.set_tree_exn ~info:Irmin.Info.none store
(view @ [name; path])
t'
>>= fun _ -> Lwt.return_unit
let server t : string Lwt.t = Istore.Tree.get t [path; "server"]
let port t : int Lwt.t =
Istore.Tree.get t [path; "port"] >|= fun p -> int_of_string p
let nick t : string Lwt.t = Istore.Tree.get t [path; "nick"]
let protocol t : string option Lwt.t =
Istore.Tree.find t [path; "protocol"]
end
let get_channels ~store ~path =
Istore.list store path
>>= fun c -> >>= fun c ->
Lwt_io.printl "connect_by_name returned" let rec iter l =
>>= fun () -> Lwt.return c ) Lwt_list.filter_map_p
(fun (s, _) ->
if String.length s > 1 && String.get s 0 = '#' then
Lwt.return (Some s)
else Lwt.return None )
l in
iter c
let connect ?(path = [base_path]) ({store; _} : Tree.t) :
unit Lwt.t =
(* search for all connections and start them *)
(* also need ot figure out how to preserve custom ordering of items like servers and channels
maybe just a _order file that has the ordering of files listed and hten gets updated etc. *)
Channel.make store ~path ~name:topch
>>= fun top_channel ->
let _top_msg str =
Channel.add_msg top_channel (Message.make str) in
let channel_assoc = ref [] in
let make_channel store path (name : string) =
Channel.make store ~path ~name
>>= fun ch ->
channel_assoc := (name, ch) :: !channel_assoc ;
Channel.add_msg ch
(Message.make (F.str "channel %s created" name))
>>= fun () -> Lwt.return ch in
Istore.list store path
>>= fun servers ->
Lwt_list.filter_p
(fun (_, tree) ->
Config.protocol tree
>|= function Some p -> p = Protocol.id Irc | None -> false
)
servers
(* filter out non-irc protocols, TODO currently relying on this to filter out non-server folders too *)
>>= fun servers ->
F.epr "protocols filtered for irc@." ;
Lwt_list.iter_p
(fun (name, tree) ->
F.epr "Irc.connect server=%s @." name ;
Config.nick tree
>>= fun nick ->
Config.server tree
>>= fun server ->
Config.port tree
>>= fun port ->
Channel.make store ~path:(path @ [name]) ~name:topch
>>= fun server_channel ->
let add_msg s =
Channel.add_msg server_channel (Message.make s) in
C.reconnect_loop ~after:30
~connect:(fun () ->
add_msg "Connecting..."
>>= fun () ->
C.connect_by_name ~server ~port ~nick ()
>>= fun c -> Lwt.return c )
~f:(fun connection -> ~f:(fun connection ->
add_msg "Connected" ; F.epr "Irc.connect C.reconnect_loop ~f:(Connected...)@." ;
add_msg "Connected"
>>= fun () ->
get_channels ~store ~path:[name]
>>= fun chs ->
Lwt_list.iter_p Lwt_list.iter_p
(fun chname -> (fun chname ->
C.send_join ~connection ~channel:chname C.send_join ~connection ~channel:chname
>>= fun () -> >>= fun () ->
ignore (make_ch chname) ; ignore (make_channel store [name] chname) ;
Lwt.return_unit ) Lwt.return_unit )
channels ) chs )
~callback:(fun _connection result -> ~callback:(fun _connection result ->
match result with match result with
| Result.Ok ({M.command= M.Other _; _} as msg) -> | Result.Ok ({M.command= M.Other _; _} as msg) ->
add_msg (M.to_string msg) ; add_msg (M.to_string msg)
Lwt.return_unit
| Result.Ok | Result.Ok
{M.command= M.PRIVMSG (target, data); prefix= user} {M.command= M.PRIVMSG (target, data); prefix= user}
-> -> (
let user = let user =
match user with match user with
| Some u -> List.hd (String.split_on_char '!' u) | Some u -> List.hd (String.split_on_char '!' u)
| None -> "unknown" in | None -> "unknown" in
( match List.assoc_opt target !channel_assoc with match List.assoc_opt target !channel_assoc with
| Some ch -> Channel.add_msg ch | Some ch ->
| None -> Channel.add_msg (make_ch target) ) Channel.add_msg ch
(Message.make (F.str "<%s> %s" user data)) ; (Message.make (F.str "<%s> %s" user data))
Lwt.return_unit | None ->
make_channel store [server] target
>>= fun ch ->
Channel.add_msg ch
(Message.make (F.str "<%s> %s" user data)) )
| Result.Ok msg -> | Result.Ok msg ->
add_msg (M.to_string msg) ; add_msg (M.to_string msg)
Lwt.return_unit >>= fun () -> Lwt.return_unit
| Result.Error e -> Lwt_io.printl e ) ) ; | Result.Error e -> Lwt_io.printl e )
channel () )
servers
end end
module Panel = struct module Panel = struct
open Nottui open Panel
module P = Nottui_pretty open Panel.Ui
let string ?attr text = P.ui (Nottui_widgets.string ?attr text) type viewer =
let ( ^^ ) = P.( ^^ ) { step: string
let ( ^/^ ) a b = P.(a ^^ break 1 ^^ b) ; var: view Lwd.var
; mutable parent: view
; mutable node: viewer list }
let messagelist (ch : Channel.t) : P.t Lwd.t = and view = [`Empty | `View of viewer]
Lwd.map (Lwd.get ch.content) ~f:(fun (msgs : Message.t list) ->
List.fold_left
(fun doc (msg : Message.t) ->
F.epr "Communicator.Panel.messagelist ch.content=%s@."
msg.content ;
doc
^^ P.group
( string msg.time ^/^ string " | "
^/^ string msg.content )
^^ P.hardline )
P.empty msgs )
open Nottui_widgets let add v node =
( match v with
| `View v ->
v.node <- node :: v.node ;
Lwd.set v.var (`View v)
| `Empty -> () ) ;
node.parent <- v ;
Lwd.set node.var (`View node) ;
`View node
(*type focustree = let make step parent node =
{channel: Channel.t; subs: focustree list; focus: Focus.handle} let v = {step; var= Lwd.var `Empty; parent; node} in
( match parent with
| `View parent ->
parent.node <- v :: parent.node ;
Lwd.set parent.var (`View parent)
| `Empty -> () ) ;
let rec iter = function
| [] -> ()
| x :: xs ->
x.parent <- `View v ;
Lwd.set x.var (`View x) ;
iter xs in
iter node ;
Lwd.set v.var (`View v) ;
`View v
let channeltree (tree : Tree.t) : focustree Lwd.t = let rec last = function
let rec fold (tree : Tree.t) : focustree list Lwd.t = | [] -> None
Lwd_table.map_reduce | [x] -> Some x
(fun _row (tree : Tree.t) -> | _ :: xs -> last xs
Lwd.map (fold tree) ~f:(fun (subs : focustree list) ->
{ channel= tree.channel
; subs
; focus= Focus.make () } ))
([], fun a b -> List.append a b)
tree.subs in
let {channel= tree.channel; subs= fold tree; focus= Focus.make ()} *)
let channelview (tree : Tree.t) : 'a Lwd.t * Channel.t Lwd.var = let rec last_def = function
let channel = Lwd.var tree.channel in | [] -> "[]"
let rec fold ?(indent = 0) ?superfocus (tree : Tree.t) : | [x] -> x
'a Lwd.t = | _ :: xs -> last_def xs
let subfocus = Focus.make () in
let find_node ~step ~view =
match view with
| `Empty -> None
| `View v -> List.find_opt (fun v' -> v'.step = step) v.node
let string_of_path path =
"[" ^ F.str "%a" (F.list ~sep:F.semi F.string) path ^ "]"
let remove (v : viewer) =
Lwd.set v.var `Empty ;
`Empty
let storeview store path =
Istore.get_tree store path
>>= fun tree ->
let update d key (view : view) : view option Lwt.t =
F.epr "fold ~pre:update key=%s @." (string_of_path key) ;
Lwt.return
( match
( List.rev key
, find_node
~step:(Option.value (last key) ~default:"[]")
~view
, d )
with
| [], None, `Added | [], None, `Updated ->
Some (make "[]" view [])
| [], Some v, _ -> Some (`View v)
| [], None, `Removed -> None
| _ :: k :: _, _, _ when k.[0] == '#' -> None
| k :: _, None, `Added | k :: _, None, `Updated ->
Some (make k view [])
| _ :: _, None, `Removed -> None
| _ :: _, Some v, _ -> Some (`View v) ) in
(* if pre returns None, the children of that node are skipped. *)
let rec map ?(key = []) ~node tree (acc : view) : view Lwt.t =
let acc =
match acc with
| `Empty -> make (last_def key) acc []
| v -> v in
Istore.Tree.list tree []
>>= fun tree ->
Lwt_list.iter_s
(fun (s, t) ->
let k = key @ [s] in
node k acc
>|= function
| Some a ->
F.epr "storeview Fold step=%s @." s ;
ignore (map ~key:k ~node t a)
| None -> F.epr "storeview None step=%s @." s )
tree
>|= fun () -> acc in
map ~node:(update `Added) tree `Empty
>>= fun t ->
let root = Lwd.var t in
Istore.watch_key store path (fun diff ->
let d, tree =
match diff with
| `Added (_, tree) -> (`Added, tree)
| `Removed (_, tree) -> (`Removed, tree)
| `Updated (_, (_, tree)) -> (`Updated, tree) in
map ~node:(update d) tree t
>>= fun t' -> Lwd.set root t' ; Lwt.return_unit )
>>= fun watch -> Lwt.return (watch, root)
let channelview (store, path) =
storeview store path
>>= fun (_watch, root) ->
let ui =
Lwd.join Lwd.join
(Lwd_table.map_reduce (Lwd.map (Lwd.get root) ~f:(function
(fun row (tree : Tree.t) -> | `Empty ->
let focus = failwith "channelview says root Lwd.var is `Empty"
match superfocus with | `View v ->
| Some sf -> let rec iter ?(indent = 0) (v : viewer) =
Lwd.map2 (Focus.status sf) Lwd.bind (Lwd.get v.var) ~f:(function
(Focus.status tree.focus) | `Empty -> Lwd.return Ui.empty
~f:(fun superfocus' focus' -> | `View v' ->
if Focus.has_focus superfocus' then let sub =
F.epr Lwd_utils.pack Ui.pack_y
"Focus.has_focus superfocus' = true@." ; (List.map
Focus.release sf ; (iter ~indent:(indent + 1))
Focus.request tree.focus ; v'.node ) in
focus' ) Lwd.map sub ~f:(fun sub ->
| None -> Focus.status tree.focus in Ui.join_y
Lwd.map2 (Ui.string
(Lwd.map focus ~f:(fun focus -> ( String.make indent '>' ^ " "
if Focus.has_focus focus then ^ v'.step ) )
Lwd.set channel tree.channel ; sub ) ) in
Ui.keyboard_area ~focus iter v ) ) in
(fun key -> let chs, chs_push = Lwt_stream.create () in
Channel.make store ~path:[base_path] ~name:topch
>>= fun ch ->
chs_push (Some ch) ;
Lwt.return (chs, ui)
let messagelist ({store; path} : Channel.t) mlist :
Istore.watch Lwt.t =
let mlist' () =
Istore.get_tree store path
>>= fun tree ->
Istore.Tree.fold ~depth:(`Eq 5)
~contents:(fun key contents view ->
match key with match key with
| `ASCII 'w', [] -> ( | [y; m; d; h; s] ->
match Lwd_table.prev row with Lwt.return (((y, m, d, h, s), contents) :: view)
| Some r -> ( | _ ->
match Lwd_table.get r with F.epr
| Some r -> "ERROR: messagelist (fold ~depth:(`Eq 5)) got \
Focus.release tree.focus ; wrong number of steps@." ;
Focus.request r.focus ; Lwt.return view )
`Handled ~node:(fun _key _node view ->
| None -> `Unhandled ) F.epr
| None -> `Unhandled ) "ERROR: messagelist (fold ~depth:(`Eq 5)) found a \
| `ASCII 'a', [] -> ( node@." ;
match superfocus with Lwt.return view )
| Some f -> tree [] in
Focus.release tree.focus ; mlist' ()
Focus.request f ; >>= fun ml ->
`Handled Lwd.set mlist ml ;
| None -> `Unhandled ) Istore.watch_key store path (fun _ ->
| `ASCII 's', [] -> ( mlist' ()
match Lwd_table.next row with >>= fun mlist' -> Lwt.return (Lwd.set mlist mlist') )
| Some r -> (
match Lwd_table.get r with
| Some r ->
Focus.release tree.focus ;
Focus.request r.focus ;
`Handled
| None -> `Unhandled )
| None -> `Unhandled )
| `ASCII 'd', [] ->
Focus.release tree.focus ;
Focus.request subfocus ;
`Handled
| _ -> `Unhandled )
(Ui.join_x
(Ui.join_x
( if Focus.has_focus focus then
string "+"
else string "" )
(string (String.make indent '-')) )
(string Tree.(tree.channel.name)) ) ) )
(fold ~indent:(indent + 1) ~superfocus:subfocus tree)
~f:(fun parent subs -> Ui.join_y parent subs) )
(Lwd_utils.lift_monoid Ui.pack_y)
tree.subs ) in
(fold tree, channel)
let messageview (ch : Channel.t Lwd.var) = let messageview ch =
Panel.Nottui.scroll_area let mlist = Lwd.var [(("", "", "", "", ""), "")] in
(Lwd.map let rec update_messagelist watch () =
(Lwd.bind (Lwd.get ch) ~f:messagelist) Lwt_stream.last_new ch
~f:(P.pretty 200) ) >>= fun ch ->
( match watch with
| None -> Lwt.return_unit
| Some w -> Istore.unwatch w )
>>= fun () ->
messagelist ch mlist
>>= fun watch -> update_messagelist (Some watch) () in
Lwt.async (update_messagelist None) ;
Lwt.return
(Lwd.map (Lwd.get mlist) ~f:(fun mlist ->
scroll
(List.fold_left
(fun doc ((year, month, day, hour, sec), content) ->
F.epr
"Communicator.Panel.messagelist ch.content=%s@."
content ;
doc
^/^ Ui.string
(F.str "%s.%s.%s.%s.%s" year month day hour
sec )
^^ Ui.string " | " ^^ string content )
Ui.empty mlist ) ) )
let commview c = let commview (store, path) =
let cv, ch = channelview c in channelview (store, List.rev (List.tl (List.rev path)))
Nottui_widgets.h_pane >>= fun (ch, cv) ->
(Panel.Nottui.scroll_area cv) messageview ch
(messageview ch) >>= fun mv ->
Lwt.return (Lwd.map2 cv mv ~f:(fun c m -> join_x c m))
type view = Channel of (Channel.t * view list) | Cursor of view let panel ({store; view} : Tree.t) : (Event.t -> atom Lwt.t) Lwt.t
=
let panel (comm : Tree.t) = commview (store, view) >>= fun cv -> Panel.Ui.panel cv
let base = Lwd.var Nottui_widgets.empty_lwd in
Lwd.set base (commview comm) ;
Panel.Nottui.panel (Lwd.join (Lwd.get base)) ()
end end
end end
let _ =
let comm = Communicator.Tree.make_top () in
let _irc =
Communicator.Irc.connection comm "irc.hackint.org" 6697 "cqcaml"
["#CQC"] in
root_actor := std_actor (Communicator.Panel.panel comm)
(** (**
program starts... program starts...
- spawn connections to servers - spawn connections to servers
- these connections will populate the Channel.t in a Channel.tree - these connections will populate the Channel.t in a Channel.tree
**) **)
let _ =
Lwt.async (fun () ->
Communicator.Tree.make_top "commstore" "current"
>>= fun comm ->
Communicator.Irc.Config.make_connection comm "irc.hackint.org"
6697 "cqcaml"
>>= fun () ->
Lwt.async (fun () -> Communicator.Irc.connect comm) ;
F.epr
"root_actor := std_actor (Communicator.Panel.panel comm)@." ;
Communicator.Panel.panel comm
>|= fun f ->
root_actor :=
std_actor
(Lwt.return
Panel.
{ act=
(fun _ events ->
Lwt_list.fold_left_s
(fun _ ev ->
f ev
>>= fun i ->
Lwt.return (fun s ->
( s
, ( Gg.Box2.of_pts Gg.V2.zero (snd i)
, fst i ) ) ) )
Display.pane_empty events )
; subpanels= []
; tag= "irc" } ) )

361
opam-switch Normal file
View File

@ -0,0 +1,361 @@
opam-version: "2.0"
compiler: ["ocaml-system.4.13.1"]
roots: [
"bogue.20210917"
"findlib_top.v0.11.0"
"glfw-ocaml.3.3.1-1"
"huffman.0.1.2"
"inuit.0.4.1"
"irc-client.0.7.0"
"irc-client-lwt.0.7.0"
"irc-client-tls.0.7.0"
"irc-client-unix.0.7.0"
"irmin.2.9.0"
"irmin-unix.2.9.0"
"lambda-term.3.1.0"
"lwd.0.1"
"lwt_ppx.2.0.3"
"merlin.4.4-413"
"note.0.0.1"
"nottui.0.1"
"nottui-lwt.0.1"
"nottui-pretty.0.1"
"ocaml-manual.4.13.0"
"ocaml-system.4.13.1"
"ocamlformat.0.20.1"
"odig.0.0.7"
"odoc.2.0.2"
"pp.1.1.2"
"pprint.20211129"
"tgls.0.8.5"
"tsdl.0.9.8"
"user-setup.0.7"
"wall.0.4.1"
"zed.3.1.0"
]
installed: [
"angstrom.0.15.0"
"arp.3.0.0"
"asn1-combinators.0.2.6"
"astring.0.8.5"
"awa.0.0.4"
"awa-mirage.0.0.4"
"b0.0.0.3"
"base.v0.14.2"
"base-bigarray.base"
"base-bytes.base"
"base-threads.base"
"base-unix.base"
"base64.3.5.0"
"bheap.2.0.0"
"bigarray-compat.1.0.0"
"bigstringaf.0.8.0"
"biniou.1.2.1"
"bogue.20210917"
"bos.0.2.0"
"ca-certs.0.2.2"
"ca-certs-nss.3.71"
"camomile.1.0.2"
"carton.0.4.3"
"carton-git.0.4.3"
"carton-lwt.0.4.3"
"cf.0.4"
"cf-lwt.0.4"
"charInfo_width.1.1.0"
"checkseum.0.3.2"
"cmdliner.1.0.4"
"cohttp.4.0.0"
"cohttp-lwt.4.0.0"
"cohttp-lwt-unix.4.0.0"
"conduit.4.0.2"
"conduit-lwt.4.0.2"
"conduit-lwt-unix.4.0.2"
"conf-cairo.1"
"conf-gles2.1"
"conf-glfw3.2"
"conf-gmp.3"
"conf-gmp-powm-sec.3"
"conf-libffi.2.0.0"
"conf-libX11.1"
"conf-m4.1"
"conf-pkg-config.2"
"conf-sdl2.1"
"conf-sdl2-image.1"
"conf-sdl2-ttf.1"
"cppo.1.6.8"
"crunch.3.2.0"
"csexp.1.5.1"
"cstruct.6.0.1"
"cstruct-lwt.6.0.1"
"cstruct-sexp.6.0.1"
"cstruct-unix.6.0.1"
"ctypes.0.20.0"
"ctypes-foreign.0.18.0"
"decompress.1.4.2"
"digestif.1.1.0"
"dispatch.0.5.0"
"domain-name.0.3.1"
"dot-merlin-reader.4.1"
"duff.0.4"
"dune.2.9.1"
"dune-build-info.2.9.1"
"dune-configurator.2.9.1"
"duration.0.2.0"
"easy-format.1.3.2"
"either.1.0.0"
"emile.1.1"
"encore.0.8"
"eqaf.0.8"
"ethernet.3.0.0"
"findlib_top.v0.11.0"
"fix.20211125"
"fmt.0.9.0"
"fpath.0.7.3"
"fsevents.0.3.0"
"fsevents-lwt.0.3.0"
"gg.0.9.3"
"git.3.6.0"
"git-cohttp.3.6.0"
"git-cohttp-unix.3.6.0"
"git-unix.3.6.0"
"glfw-ocaml.3.3.1-1"
"gmap.0.3.0"
"graphql.0.13.0"
"graphql-cohttp.0.13.0"
"graphql-lwt.0.13.0"
"graphql_parser.0.13.0"
"graphv_core.0.1.1"
"graphv_core_lib.0.1.1"
"graphv_font.0.1.1"
"graphv_font_js.0.1.1"
"graphv_gles2.0.1.1"
"graphv_gles2_native_impl.0.1.1"
"graphv_webgl.0.1.1"
"graphv_webgl_impl.0.1.1"
"grenier.0.13"
"hex.1.4.0"
"hkdf.1.0.4"
"huffman.0.1.2"
"hxd.0.3.1"
"index.1.5.0"
"inotify.2.3"
"integers.0.5.1"
"inuit.0.4.1"
"ipaddr.5.2.0"
"ipaddr-sexp.5.2.0"
"irc-client.0.7.0"
"irc-client-lwt.0.7.0"
"irc-client-tls.0.7.0"
"irc-client-unix.0.7.0"
"irmin.2.9.0"
"irmin-fs.2.9.0"
"irmin-git.2.9.0"
"irmin-graphql.2.9.0"
"irmin-http.2.9.0"
"irmin-layers.2.9.0"
"irmin-pack.2.9.0"
"irmin-unix.2.9.0"
"irmin-watcher.0.5.0"
"jbuilder.1.0+beta20.2"
"js_of_ocaml.3.11.0"
"js_of_ocaml-compiler.3.11.0"
"js_of_ocaml-ppx.3.11.0"
"jsonm.1.0.1"
"ke.0.4"
"lambda-term.3.1.0"
"logs.0.7.0"
"lru.0.3.0"
"lwd.0.1"
"lwt.5.5.0"
"lwt-dllist.1.0.1"
"lwt_log.1.1.1"
"lwt_ppx.2.0.3"
"lwt_react.1.1.5"
"macaddr.5.2.0"
"macaddr-cstruct.5.2.0"
"magic-mime.1.2.0"
"menhir.20211128"
"menhirLib.20211128"
"menhirSdk.20211128"
"merlin.4.4-413"
"metrics.0.3.0"
"mew.0.1.0"
"mew_vi.0.5.0"
"mimic.0.0.4"
"mirage-clock.4.0.0"
"mirage-clock-unix.4.0.0"
"mirage-crypto.0.10.5"
"mirage-crypto-ec.0.10.5"
"mirage-crypto-pk.0.10.5"
"mirage-crypto-rng.0.10.5"
"mirage-device.2.0.0"
"mirage-flow.3.0.0"
"mirage-kv.4.0.0"
"mirage-net.4.0.0"
"mirage-no-solo5.1"
"mirage-no-xen.1"
"mirage-profile.0.9.1"
"mirage-protocols.8.0.0"
"mirage-random.3.0.0"
"mirage-stack.4.0.0"
"mirage-time.3.0.0"
"mmap.1.1.0"
"mtime.1.3.0"
"note.0.0.1"
"nottui.0.1"
"nottui-lwt.0.1"
"nottui-pretty.0.1"
"notty.0.2.2"
"num.1.4"
"oasis.0.4.11"
"ocaml.4.13.1"
"ocaml-compiler-libs.v0.12.4"
"ocaml-config.2"
"ocaml-manual.4.13.0"
"ocaml-migrate-parsetree.2.3.0"
"ocaml-options-vanilla.1"
"ocaml-syntax-shims.1.0.0"
"ocaml-system.4.13.1"
"ocaml-version.3.4.0"
"ocamlbuild.0.14.0"
"ocamlfind.1.9.1"
"ocamlformat.0.20.1"
"ocamlgraph.2.0.0"
"ocamlify.0.0.1"
"ocamlmod.0.0.9"
"ocb-stubblr.0.1.1-1"
"ocp-indent.1.8.1"
"ocplib-endian.1.2"
"odig.0.0.7"
"odoc.2.0.2"
"odoc-parser.1.0.0"
"optint.0.1.0"
"parsexp.v0.14.1"
"pbkdf.1.2.0"
"pecu.0.6"
"pp.1.1.2"
"pprint.20211129"
"ppx_cstruct.6.0.1"
"ppx_derivers.1.2.1"
"ppx_deriving.5.2.1"
"ppx_irmin.2.9.0"
"ppx_repr.0.5.0"
"ppx_sexp_conv.v0.14.3"
"ppxlib.0.24.0"
"progress.0.2.1"
"psq.0.2.0"
"ptime.0.8.6"
"randomconv.0.1.3"
"re.1.10.3"
"react.1.2.1"
"repr.0.5.0"
"result.1.5"
"rresult.0.6.0"
"semaphore-compat.1.0.1"
"seq.base"
"sexplib.v0.14.0"
"sexplib0.v0.14.0"
"stb_image.0.5"
"stb_truetype.0.6"
"stdio.v0.14.0"
"stdlib-shims.0.3.0"
"stringext.1.6.0"
"tcpip.7.0.0"
"terminal.0.2.1"
"terminal_size.0.1.4"
"tgls.0.8.5"
"tls.0.14.1"
"tls-mirage.0.14.1"
"topkg.1.0.4"
"trie.1.0.0"
"tsdl.0.9.8"
"tsdl-image.0.3.2"
"tsdl-ttf.0.3.2"
"tyxml.4.5.0"
"uchar.0.0.2"
"uri.4.2.0"
"uri-sexp.4.2.0"
"user-setup.0.7"
"uucp.14.0.0"
"uuseg.14.0.0"
"uutf.1.0.2"
"vector.1.0.0"
"wall.0.4.1"
"webmachine.0.7.0"
"x509.0.14.1"
"yaml.3.0.0"
"yojson.1.7.0"
"zarith.1.12"
"zed.3.1.0"
]
pinned: ["lwd.0.1" "nottui.0.1"]
package "lwd" {
opam-version: "2.0"
version: "0.1"
synopsis: "Lightweight reactive documents"
maintainer: "fred@tarides.com"
authors: "Frédéric Bour"
license: "MIT"
homepage: "https://github.com/let-def/lwd"
doc: "https://let-def.github.io/lwd/doc"
bug-reports: "https://github.com/let-def/lwd/issues"
depends: [
"dune" {>= "2.0"}
"seq"
"ocaml" {>= "4.03"}
"qtest" {with-test}
"qcheck" {with-test}
]
build: [
["dune" "subst"] {pinned}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
]
dev-repo: "git+https://github.com/let-def/lwd.git"
url {
src: "git+file:///home/cqc/p/console/ref/lwd#master"
}
}
package "nottui" {
opam-version: "2.0"
version: "0.1"
synopsis: "UI toolkit for the terminal built on top of Notty and Lwd"
maintainer: "fred@tarides.com"
authors: "Frédéric Bour"
license: "MIT"
homepage: "https://github.com/let-def/lwd"
doc: "https://let-def.github.io/lwd/doc"
bug-reports: "https://github.com/let-def/lwd/issues"
depends: [
"dune" {>= "2.0"}
"lwd"
"notty"
]
build: [
["dune" "subst"] {pinned}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
]
dev-repo: "git+https://github.com/let-def/lwd.git"
url {
src: "git+file:///home/cqc/p/console/ref/lwd#master"
}
}