dbuenzli / brr Goto Github PK
View Code? Open in Web Editor NEWBrowser programming toolkit for OCaml
Home Page: http://erratique.ch/software/brr/
License: Other
Browser programming toolkit for OCaml
Home Page: http://erratique.ch/software/brr/
License: Other
Given a Fut.t
whose underlying promise is rejected, Fut.to_promise
creates a promise that never resolves, which then prevents whatever surrounding code from cleaning up and displaying said error.
Example :
let test str to_promise =
ignore (
Jv.Promise.then'
(to_promise
~ok:Fun.id
(Fut.bind
(Fut.return "qwe")
failwith))
(fun x -> Brr.Console.log [str; "ok"; x]; Jv.Promise.resolve Jv.undefined)
(fun x -> Brr.Console.log [str; "error"; x]; Jv.Promise.resolve Jv.undefined))
let () = test "Fut.to_promise" Fut.to_promise
(* Bad. prints neither "ok", nor "error". Instead you get a console warning saying "uncaught in promise: ..." *)
let fut_to_promise
: ok:('a -> Jv.t) -> 'a Fut.or_error -> Jv.Promise.t
= fun ~ok f ->
Jv.Promise.bind (Jv.get (Obj.magic f) "fut")
(function
| Ok v -> Jv.Promise.resolve (ok v)
| Error e -> Jv.Promise.reject e)
let () = test "custom Fut.to_promise" fut_to_promise
(* Good. prints in the "error" branch *)
The implementation of the module says it's an invariant that the promise of a future is never rejected, but the invariant isn't maintained in the bind
function (and map
). Not sure if that's an implementation or documentation bug. I think it'd be more useful to change the implementation as above, rather than changing the documentation.
Sometimes El.prop
is not what you want.
Hey, so I get a kind of weird error. When calling this function
let alert msg =
match Jv.find Jv.global "alert" with
| None -> prerr_endline "no alert"
| Some alert -> ignore @@ Jv.apply alert Jv.[| of_string msg |]
I get an illegal invocation error.
However, this one is fine...almost the same, except for a print function stuck in there. (No idea why the print function helps...)
let alert msg =
match Jv.find Jv.global "alert" with
| None -> prerr_endline "no alert"
| Some alert ->
(* Needs this print to work. *)
prerr_endline "yay!";
ignore @@ Jv.apply alert Jv.[| of_string msg |]
This also works fine.
let alert msg =
match Jv.find Jv.global "window" with
| None -> prerr_endline "no window"
| Some window -> ignore @@ Jv.call window "alert" Jv.[| of_string msg |]
All of them work fine in dev mode, but when in release mode, the error happens.
I'm using js_of_ocaml 4.0.0 and brr 0.0.3.
Have you run in to this sort of thing, or am I just doing something wrong?
There's no reason to annoy people with the note
dependency if they don't want it.
Hi,
I use Brr.El.v to draw some SVG images. However when I try to embed them directly in my HTML they do not show, because the root svg-tag is missing the 'http://www.w3.org/2000/svg' namespace.
Would you accept a change to add namespaces? And what would the best solution be: changing the type At.t from type t = name * Jstr.t
to type t = ns Option.t name * Jstr.t
? Or even
type t =
| Attr name * Jstr.t
| NSAttr ns * name * Jstr.t
Best regards,
--Martin
Replace as much as possible the This is now tracked by #45.external
primitives in Jv
and Jstr
by corresponding calls to Jsoo_runtime.Js
. Not essential, but in general safer and more robust to changes.
Review the callback stuff in brr
we need to use the callback stuff provided in Jsoo_runtime.Js
. This is essential and not done at the moment.
Review and update the cookbook bits on callbacks and OCaml function export to JavaScript.
I'm trying to get ocaml-lsp to work on todomvc.ml
however ocaml-lsp (merlin I guess) is complaining about unbound modules Brr
etc. I guess I have to compile some stuff for ocaml-lsp to start picking things up.
I tried ocamlbuild -use-ocamlfind test/todomvc.byte
-- this command works but that does not help with merlin issues.
The .merlin
has test/**
but it does not get picked up by ocaml-lsp.
I'd like to report what I think is a bug in the URL (percent) encoding of Uri.with_uri
Consider the following minimal example:
open Brr
let () =
let location = Window.location G.window in
let msg =
Jstr.to_string
@@ Uri.to_jstr
@@ Result.get_ok
@@ Uri.with_uri ~path:(Jstr.v "/some/path/that/includes/?(parens)") location in
El.set_children (Document.body G.document) El.[txt' msg]
The output of this is:
file:///some/path/that/includes/%3F(parens)
when I think it should be
file:///some/path/that/includes/%3F%28parens%29
@schutm provided a nice module to generate SVG contents. The only drawback is that one has to use Obj.magic
to get said content inserted into a HTML page. This is because there is currently no way to create values of type El.t
with a namespace specification, as required to create SVG elements.
After seeing #17 and the arguments for not including it, how about adding a El.v_ns
function? That way, it would be possible to have a brr_svg
or a brr_mathml
library that do not force Obj.magic
on their users, with no risk of performance regression.
Greetings, I'm in the process of writing a binding and the webidl has defined a record : typedef record<DOMString, boolean> MyName
- giving, in plain javascript : #{ "a": true, "b": false }
etc - is there any simple way of providing the construct with Jv?
I'm not able to find the window.open
method:
https://developer.mozilla.org/fr/docs/Web/API/Window/open
If it is implemented, would you point me to the right place in the docs? If not, would you like me to submit a pull request for this binding?
Hi,
I've been looking for predefined touch events in Brr.Ev but couldn't find anything in the whole repository apart some constant values.
Do you plan to add them ? If not, would you accept a PR ?
The name of the function target_to_jv
makes me think it would return Jv.t
, but it returns 'a
. I was surprised to find my code compiling without needing to apply my own type coercion to a custom target type. Is this intentional?
I'm getting this error when I hit enter to submit a line for evaluation in the console:
ocaml_console.js:1788 Uncaught TypeError: s.toUtf16 is not a function
at caml_jsstring_of_string (ocaml_console.js:1788)
at caml_js_object (ocaml_console.js:2473)
at set$1 (ocaml_console.js:3826)
at caml_call2 (ocaml_console.js:2791)
at HTMLTextAreaElement.<anonymous> (ocaml_console.js:4151)
I do see the definition of MlBytes.prototype.toUtf16
in ocaml_console.js
.
I have something strange with Jstr.concat:
this code:
let message = Jstr.(concat [ v "A"; v "B"; v "C"]) in
Console.(log [message]);
produce this output in the console : AB,C
. The expected result is ABC
(without coma).
I have taken a look in the code, and except the call to external caml_list_of_js_array
I do not see anything wrong. The coma in the second place seems indicate a wrong parameter in the call, but I cannot see where this definition comes from.
Hello!
I found the FFI example documented here slightly confusing: https://erratique.ch/software/brr/doc/ffi_cookbook.html#fun
To my knowledge, parseInt
won't ever throw, but rather return NaN
:
$ node
Welcome to Node.js v16.20.0.
Type ".help" for more information.
> parseInt("wat")
NaN
See what I've done below:
(* NOTE: matching on the exception variant will never actually occur in this example. Or will it?? *)
let parse_int' = Jv.get Jv.global "parseInt"
let parse_int ?radix s =
let r = Jv.of_option ~none:Jv.undefined Jv.of_int radix in
match Jv.apply parse_int' Jv.[| of_jstr s; r |] with
| exception Jv.Error e -> Error e
| v -> Ok (Jv.to_int v)
let parse_int' ?radix s : (int, Jv.Error.t) result =
let r = Jv.of_option ~none:Jv.undefined Jv.of_int radix in
match Jv.apply (Jv.get Jv.global "parseInt") Jv.[| of_jstr s; r |] with
| v -> (
let v' = Jv.to_float v in
match Float.is_nan v' with
| true -> Error (Jv.Error.v (Jstr.v "Reject NaN!"))
| false -> Ok (int_of_float v'))
;;
Maybe it'd be worth it to emphasize that we can deal with 2 types of functions in JS: ones that fail, and ones that silently do the wrong thing and handle them accordingly?
Here's an example of a builtin function that trows:
> decodeURI('%')
Uncaught URIError: URI malformed
at decodeURI (<anonymous>)
What do you think?
https://erratique.ch/software/brr/doc/web_page_howto.html
The basic instructions result in a working page.
But following the instructions that use Dune results in a blank page with JS that throws:
I didn't do the OCaml Console parts with either.
brr 0.0.1
js_of_ocaml 3.8.0
dune 2.7.1
Safari 14.0.1
At least
let now () =
let d = Jv.new' (Jv.get Jv.global "Date") [||] in
Jv.to_float (Jv.call d "getTime" [||])
I just spent some time banging my head against the wall due to the following behavior:
module B64 = Base64
open Brr
let () =
let encoded = "AAAAAAAAACAAAAAcP9s2eg+QlrxAWRMzMzMzMwAAAAAAAAAAAAAAAA==" in
let dec_js = Jstr.to_string @@ Result.get_ok @@ Base64.decode (Jstr.of_string encoded) in
let dec_caml = B64.decode_exn encoded in
Printf.eprintf "got : %s\n" (String.escaped dec_js);
Printf.eprintf "expected : %s\n" (String.escaped dec_caml);
()
got : \000\000\000\000\000\000\000 \000\000\000\028?\195\1556z\015\194\144\194\150\194\188@Y\01933333\000\000\000\000\000\000\000\000\000\000\000\000
expected: \000\000\000\000\000\000\000 \000\000\000\028?\2196z\015\144\150\188@Y\01933333\000\000\000\000\000\000\000\000\000\000\000\000
where Base64
is the ocaml implementation of Base64 (from the opam library of the same name).
Now, coming back to the documentation of Brr.Base64.decode
, I can see that it says that the string returned is a binary string, which I assume means it is wrong to call Jstr.to_string
on it? But then I couldn't find what to do instead.
Hello, I'm opening the issue because I did not found how to I can declare/access parentNode the property.
The type of the property should be Brr.El.t Brr.El.Prop.t
, but there is no constructor with such signature (only the basic types are provided). Maybe is this causing cyclic references between El
and El.Prop
modules ? Is there an easy way to access to such properties ?
I get the error The external function 'caml_js_meth_call' is not available
when trying to compile a library using brr.
This means going through a FileReader object. data_uri
already does.
since it binds to getContext
.
The image on https://erratique.ch/software/brr/doc/ocaml_console.html is not displaying
This should be "transform"
not "resetTransform"
.
Is there any chance the clip signature could be updated
from val clip : ?fill_rule:Fill_rule.t -> t -> Path.t -> unit
to val clip : ?fill_rule:Fill_rule.t -> ?path:Path.t -> t -> unit
in order to comply with https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
?
-> I have a scenario where I don't know, but wish to retain, the previous clipping region
Hey @dbuenzli
I started using brr and it's great, thanks for the effort. It's not a blocker for my project, but I still wanted to report it just in case there's something broken for Windows users.
I'm using esy but I believe have not much to do here, but when building brr in Windows breaks with the following: https://github.com/davesnx/query-json/runs/5175859022?check_suite_focus=true#step:10:32
Brr version: 0.0.2
esy: 0.6.10
A few things that could be improved in the OCaml console
#use
does a good job at this as per built-in toplevel support)#use
to act over URLs.E.g.
# Uri.of_jstr (Jstr.v "bla");;
Exception: TypeError: Failed to construct 'URL': Invalid URL
As pointed out in #47 (comment), URLs and URL encoding is a zoo, so apologies in advance if I'm misunderstanding something here.
Background: the Brr.Uri
API claims (emphasis added):
Params.t values represent key-value parameters stored in strings as "k0=v0&k1=v1...". They can be constructed from any Jstr.t. In particular it means they can be used with an URI fragment or query.
However, the output of Uri.query
(or Uri.fragment
) has already been percent-decoded. Using Params.of_jstr
then percent-decodes a second time. That results in the following behaviour:
let double_decode x =
Jstr.append (Jstr.v "https://example.com?x=") x
|> Uri.of_jstr |> Result.get_ok
|> Uri.query |> Uri.Params.of_jstr
|> Uri.Params.find (Jstr.v "x") |> Option.get
let _ = [
double_decode (Jstr.v "abc") (* "abc", as expected *) ;
double_decode (Jstr.v "%25") (* "%", as expected *) ;
double_decode (Jstr.v "%2525") (* "%" !!! *) ;
]
For comparison, by using JavaScript directly:
const no_double_decode = x => new URL(`https://example.com?x=${x}`).searchParams.get("x");
[
no_double_decode("abc"), // "abc"
no_double_decode("%25"), // "%"
no_double_decode("%2525") // "%25"
];
Taking a URL, grabbing its query parameter string, and using that to make a Params
is directly encouraged by the API, but the last case here is wrong with Brr
.
I don't see any way to get the correct behaviour either. Some things that don't work:
Params.of_jstr
. This would work, but the API doesn't expose the raw query string.Uri.query
. Uri.query
uses Uri.decode
to percent-decode the result, which means it leaves some %
-escapes in the output. Re-encoding would however escape the %
symbol in those escapes, rather than only percent-encoding the the characters that were percent-decoded (in other words, encode
is not the inverse of decode
).The problem here is not that Params.of_jstr
is wrong, but rather that Brr.Uri
is trying to be overly helpful by returning percent-decoded strings. By https://url.spec.whatwg.org/#concept-urlencoded-parser parsing the query string means splitting on &
, then on =
, then by https://url.spec.whatwg.org/#percent-decode percent-decoding the resulting key/value pairs. But that needs to performed on the raw URL, not the decoded URL.
Realistically, this isn't the only case where the semantics of a URI component can no longer be fully captured after percent-decoding, so if I'm understanding correctly, adding raw (non-decoded) accessors to Uri
is inevitable. That aside, having a Uri.parameters : Uri.t -> Params.t
directly would also be nice.
This is a question and possibly a feature request because I don't know if it's actually possible to do. I tried looking into it myself and got nowhere so I'm posting it here in case someone smarter than me can figure out if the idea's viable or not.
The idea was to create some kind of FFI function creation method for Brr similar to how Ctypes allows you to programmatically define foreign functions entirely within OCaml. To explain, with Ctypes you can define function signatures in a form like string @-> int @-> returning void
and end up with a callable function with the appropriate number of arguments and correct function signature, handling the conversion of OCaml types for each argument as needed.
If possible, something like that could be useful in Brr as a cleaner alternative to writing wrapper functions that do things like Jv.call js_fun [| Jv.of_string "foo"; Jv.of_int 42; Jv.of_string "bar" |]
by hand. Instead, you'd do something like my_fun = fn js_fun string @-> int @-> string
and get a curried function that has the appropriate conversions in place, allowing you to then write my_fun "foo" 42 "bar"
.
This may be impossible to implement; I tried looking into how Ctypes works to see if it could be applied to Brr, but it uses powerful dark magics to do what it does, and I got completely lost trying to make sense of it. I really wanted to make it happen because the way Ctypes works is amazing; Melange and Bucklescript make it easy to do something similar by using external
to bind JS functions, so I thought it'd be nice to give Brr something similar by using a Ctypes-esque solution, but figuring out how Ctypes does its magic is beyond me.
I'm interested in measuring the performance of the todomvc example versus implementations in other languages. My first thought when looking over the code was whether the use of immutable data structures (ocaml's list) would become an issue, especially when removing an element at an arbitrary index. I saw this before when running the todomvc benchmark against a version written using bucklescript-tea. Is it possible for Note to work with mutable data, or maybe the approach would be to use a persistent data structure with better asymptotics?
I am writing a VScode extension using the Brr
library.
I don't have a minimal reproducible example but here is my snippet:
let activate =
Jv.callback ~arity:1 (fun context ->
Console.log "Congratulations, your extension for kalkulu is now active!";
let callback () = Vscode.Window.show "Hello world!" in
let disposable =
Vscode.Command.register ~command:"extension.helloWorld" ~callback
in
Vscode.Extension_context.subscribe context disposable)
in
(* Js.export "activate" @@ activate *)
Jv.set Jv.global "activate" activate
If I uncomment the Js.export
line, it works, but the Jv.set
does not.
I am not sure to understand what is going on. I observe the compiled JS is a little bit different. In particular Js.export
uses "jsoo_exports` value.
I have tried to read the FFI cookbook, but I did not find an explanation. Is this expected? Why?
We don't do it yet, because in Firefox it still seems to be behind flags. In Chrome an error is reported but it's not fatal. Here's a patch.
diff --git a/src/console/manifest.json b/src/console/manifest.json
index 33823fc..456eb29 100644
--- a/src/console/manifest.json
+++ b/src/console/manifest.json
@@ -1,12 +1,13 @@
{
- "manifest_version": 2,
+ "manifest_version": 3,
"name": "OCaml console",
- "version": "0.0.2",
+ "version": "0.0.4",
"author": "The brr programmers",
"description":
"OCaml console developer tools panel. Connects to an OCaml toplevel (REPL) provided by the inspected web page.",
"homepage_url": "https://erratique.ch/software/brr",
"icons": { "128": "ocaml.png" },
- "permissions": ["<all_urls>", "storage", "management"],
+ "permissions": ["storage", "management"],
+ "host_permissions": ["<all_urls>"],
"devtools_page": "devtools.html"
}
Quite a few of the external
definitions in the Jv
and Jstr
modules could be replaced by definitions of the new Jsoo_runtime
module. We should do that, it's safer.
We just need to make sure this doesn't introduce any performance regression since some of those are declared as external
in the .mli
files aswell (I never remember if it has an impact).
I think without aggressive dead code elimination (which is the case with --profile=dev dune builds) there is something that accesses the global document object, and this causes an exception when the code is run in a web worker context.
With --profile=release things seem to be OK.
Maybe worth some comment in the documentation, though it would be really nice if things worked smoothly in dev mode also.
The only problem is that each of the libs uses Callback.register_exception
to register the exception used to represent JavaScript exceptions in OCaml.
The function to create blobs from Jstr.t
seems to be incorrect:
Line 753 in 3d857a6
According to https://w3c.github.io/FileAPI/#blob-section, the constructor for a blob is:
constructor(optional sequence<BlobPart> blobParts,
optional BlobPropertyBag options = {});
i.e the first argument should be a sequence.
When trying to create a blob, I get a browser type error when using the function provided by Brr.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.