-
-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a docs/ site for Blink! #168
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
850fb3b
Initial docs commit: create skeleton docs dir
NHDaly 6ef09fd
Starting to populate the docs page!
NHDaly 0932a52
Add Communication page to docs/
NHDaly 92d45df
Added Usage Guide page to docs.
NHDaly aa0b4f4
Add (currently disabled) deploydocs function
NHDaly dc03297
Remove *.toml files, which i guess aren't actually needed
NHDaly 00c3fad
Fix Communication section regarding multiple args to Blink.msg
NHDaly c22e998
Update Overview description in Index.md
NHDaly File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using Documenter, Blink | ||
|
||
makedocs( | ||
modules = [Blink], | ||
format = :html, | ||
sitename = "Blink", | ||
pages = [ | ||
"index.md", | ||
"guide.md", | ||
"Communication" => "communication.md", | ||
"api.md", | ||
#"Subsection" => [ | ||
# ... | ||
#] | ||
], | ||
) | ||
|
||
#deploydocs( | ||
# repo = "github.com/NHDaly/Blink.jl.git", | ||
# julia = "1.0" | ||
#) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# API | ||
|
||
## Window | ||
|
||
```@docs | ||
Window | ||
``` | ||
|
||
```@docs | ||
title | ||
progress | ||
flashframe | ||
``` | ||
|
||
### Misc | ||
|
||
```@docs | ||
opentools | ||
closetools | ||
tools | ||
``` | ||
|
||
## RPC | ||
|
||
```@docs | ||
@js | ||
@js_ | ||
js | ||
Blink.JSString | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
# [Communication between Julia and Javascript](@id Communication) | ||
|
||
After creating a Window and loading HTML and JS, you may want to interact with | ||
julia code (e.g. by clicking a button in HTML, or displaying a plot from julia). | ||
|
||
This section covers this two-way communication. | ||
|
||
## Julia to Javascript | ||
```@setup Blink-win | ||
using Blink | ||
win = Window(Dict(:show=>false), async=false) | ||
``` | ||
|
||
The easiest way to communicate to javascript from julia is with the [`@js`](@ref) and | ||
[`@js_`](@ref) macros. These macros allow you to execute arbitrary javascript code in a | ||
given Window. | ||
|
||
```@repl Blink-win | ||
@js win x = 5; | ||
@js win x | ||
``` | ||
|
||
The `@js_` macro executes its code asynchronously, but doesn't return its | ||
result: | ||
```@repl Blink-win | ||
@time @js win begin # Blocks until finished; `i` is returned | ||
for i in 0:1000000 end # waste time | ||
i # return i | ||
end | ||
|
||
@time @js_ win begin # Returns immediately, but `i` is not returned. | ||
for i in 0:1000000 end # waste time | ||
i # This is ignored | ||
end | ||
``` | ||
|
||
If your javascript expression is complex, or you want to copy-paste existing | ||
javascript, it can be easier to represent it as a pure javascript string. | ||
For that, you can call the [`js`](@ref) function with a [`JSString`](@ref Blink.JSString): | ||
```@repl Blink-win | ||
body!(win, """<div id="box" style="color:red;"></div>""", async=false); | ||
div_id = "box"; | ||
js(win, Blink.JSString("""document.getElementById("$div_id").style.color""")) | ||
``` | ||
|
||
Note that the code passed to these macros runs in its own scope, so any | ||
javascript variables you create with `var` (or the `@var` equivalent for `@js`) | ||
will be inaccessible after returning: | ||
```@repl Blink-win | ||
@js win (@var x_var = 5; x_var) # x_var is only accessible within this scope. | ||
@js win x_var | ||
``` | ||
|
||
## Javascript to Julia | ||
Communication from javascript to julia currently works via a message passing | ||
interface. | ||
|
||
To invoke julia code from javascript, you specify a julia callback via `handle`: | ||
```julia-repl | ||
julia> handle(w, "press") do args | ||
@show args | ||
end | ||
``` | ||
This callback can then be triggered from javscript via `Blink.msg()`: | ||
```@setup handler | ||
using Blink | ||
w = Window(Dict(:show=>false), async=false) | ||
handle(w, "press") do args | ||
@show args | ||
end | ||
``` | ||
```@repl handler | ||
@js w Blink.msg("press", "Hello from JS"); | ||
``` | ||
Note that the javascript function `Blink.msg` takes _exactly_ 1 argument. To | ||
pass more or fewer arguments, pass your arguments as an array: | ||
```@repl handler | ||
handle(w, "event") do count, values, message | ||
# ... | ||
end | ||
@js w Blink.msg("event", [1, ['a','b'], "Hi"]); | ||
``` | ||
|
||
Finally, here is an example that uses a button to call back to julia: | ||
```@setup Blink-w | ||
using Blink | ||
w = Window(Dict(:show=>false), async=false) | ||
``` | ||
```@repl Blink-w | ||
handle(w, "press") do arg | ||
println(arg) | ||
end | ||
body!(w, """<button onclick='Blink.msg("press", "HELLO")'>go</button>""", async=false); | ||
``` | ||
Now, clicking the button will print `HELLO` to julia's STDOUT. | ||
|
||
|
||
## Back-and-forth | ||
|
||
Note that you cannot make a synchronous call to javascript from _within_ a julia | ||
callback, or you'll cause julia to hang: | ||
|
||
**BAD**: | ||
```julia-repl | ||
julia> @js w x = 5 | ||
|
||
julia> handle(w, "press") do args... | ||
# Increment x and get its new value | ||
x = @js w (x += 1; x) # ERROR: Cannot make synchronous calls within a callback. | ||
println("New value: $x") | ||
end | ||
#9 (generic function with 1 method) | ||
|
||
julia> @js w Blink.msg("press", []) | ||
|
||
# JULIA HANGS UNTIL CTRL-C, WHICH KILLS YOUR BLINK WINDOW. | ||
``` | ||
|
||
**GOOD**: Instead, if you need to access the value of `x`, you should simply | ||
provide it when invoking the `press` handler: | ||
```@repl Blink-w | ||
@js w x = 5 | ||
|
||
handle(w, "press") do args... | ||
x = args[1] | ||
# Increment x | ||
@js_ w (x = $x + 1) # Note the _asynchronous_ call. | ||
println("New value: $x") | ||
end | ||
|
||
@js w Blink.msg("press", x) | ||
# JULIA HANGS UNTIL CTRL-C, WHICH KILLS YOUR BLINK WINDOW. | ||
``` | ||
|
||
|
||
## Tasks | ||
|
||
The julia webserver is implemented via Julia | ||
[Tasks](https://docs.julialang.org/en/v1/manual/control-flow/#man-tasks-1). This | ||
means that julia code invoked from javascript will run _sort of_ in parallel to | ||
your main julia code. | ||
|
||
In particular: | ||
- Tasks are _coroutines, not threads_, so they aren't truly running in parallel. | ||
- Instead, execution can switch between your code and the coroutine's code whenever a piece of computation is _interruptible_. | ||
|
||
So, if your Blink callback handler performs uninterruptible work, it will fully | ||
occupy your CPU, preventing any other computation from occuring, and can | ||
potentially hang your computation. | ||
|
||
### Examples: | ||
|
||
**BAD**: If your callback runs a long loop, it won't be uninterruptible while | ||
it's running: | ||
```julia-repl | ||
julia> handle(w, "press") do args... | ||
println("Start") | ||
while true end # infinite loop | ||
println("End") | ||
end | ||
#40 (generic function with 1 method) | ||
|
||
julia> body!(w, """<button onclick='Blink.msg("press", 1)'>go</button>""", async=false); | ||
|
||
julia> # CLICK THE go BUTTON, AND YOUR PROCESS WILL FREEZE | ||
Start | ||
``` | ||
|
||
**BAD**: The same is true if your _main_ julia computation is hogging the CPU, then | ||
your callback can't run: | ||
```julia-repl | ||
julia> handle(w, "press") do args... | ||
println("Start") | ||
sleep(5) # This will happily yield to any other computation. | ||
println("End") | ||
end | ||
#41 (generic function with 1 method) | ||
|
||
julia> body!(w, """<button onclick='Blink.msg("press", 1)'>go</button>""", async=false); | ||
|
||
julia> while true end # Infinite loop | ||
|
||
# NOW, CLICK THE go BUTTON, AND NOTHING HAPPENS, SINCE THE CPU IS BEING HOGGED! | ||
``` | ||
|
||
**GOOD**: So to allow for happy communication, all your computations should be interruptible, which you can achieve with calls such as `yield`, or `sleep`: | ||
```julia-repl | ||
julia> handle(w, "press") do args... | ||
println("Start") | ||
sleep(5) # This will happily yield to any other computation. | ||
println("End") | ||
end | ||
#39 (generic function with 1 method) | ||
|
||
julia> body!(w, """<button onclick='Blink.msg("press", 1)'>go</button>""", async=false); | ||
|
||
julia> while true # Still an infinite loop, but a _fair_ one. | ||
yield() # This will yield to any other computation, allowing the callback to run. | ||
end | ||
|
||
# NOW, CLICKING THE go BUTTON WILL WORK CORRECTLY ✅ | ||
Start | ||
End | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Usage Guide | ||
|
||
Using Blink to build a local web app has two basic steps: | ||
1. Create a window and load all your HTML and JS. | ||
2. Handle interaction between julia and your window. | ||
|
||
## 1. Setting up a new Blink Window | ||
|
||
Create a new window via [`Window`](@ref), and load some html via [`body!`](@ref). | ||
|
||
```julia-repl | ||
julia> using Blink | ||
|
||
julia> w = Window(async=false) # Open a new window | ||
Blink.AtomShell.Window(...) | ||
|
||
julia> body!(w, "Hello World", async=false) # Set the body content | ||
``` | ||
|
||
The main functions for setting content on a window are [`content!(w, | ||
querySelector, html)`](@ref) and [`body!(w, html)`](@ref). `body!` is just | ||
shorthand for `content!(w, "body", html)`. | ||
|
||
You can also load an external url via `loadurl`, which will replace the current | ||
content of the window: | ||
```julia | ||
loadurl(w, "http://julialang.org") # Load a web page | ||
``` | ||
|
||
Note the use of `async=false` in the examples above. By default, these functions | ||
return immediately, but setting `async=false` will block until the function has | ||
completed. This is important if you are executing multiple statements in a row | ||
that depend on the previous statement having completed. | ||
|
||
|
||
### Loading stadalone HTML, CSS & JS files | ||
|
||
You can load complete standalone files via the [`load!`](@ref) function. Blink | ||
will handle the file correctly based on its file type suffix: | ||
```julia | ||
load!(w, "ui/app.css") | ||
load!(w, "ui/frameworks/jquery-3.3.1.js") | ||
``` | ||
|
||
You can also call the corresponding `importhtml!`, `loadcss!`, and `loadjs!` directly. | ||
|
||
## 2. Setting up interaction between Julia and JS | ||
|
||
```@setup Blink-w | ||
using Blink | ||
w = Window(Dict(:show=>false), async=false) | ||
``` | ||
|
||
This topic is covered in more detail in the [Communication](@ref) page. | ||
|
||
Just as you can directly write to the DOM via `content!`, you can | ||
directly execute javscript via the [`@js`](@ref) macro. | ||
|
||
```@repl Blink-w | ||
@js w Math.log(10) | ||
``` | ||
|
||
To invoke julia code from javascript, you can pass a "message" to julia: | ||
|
||
```julia | ||
# Set up julia to handle the "press" message: | ||
handle(w, "press") do args | ||
@show args | ||
end | ||
# Invoke the "press" message from javascript whenever this button is pressed: | ||
body!(w, """<button onclick='Blink.msg("press", "HELLO")'>go</button>"""); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Blink.jl Documentation | ||
|
||
## Overview | ||
|
||
Blink.jl is the Julia wrapper around [Electron](https://electronjs.org/). It | ||
can serve HTML content in a local window, and allows for communication between | ||
Julia and the web page. In this way, therefore, Blink can be used as a GUI | ||
toolkit for building HTML-based applications for the desktop. | ||
|
||
## Installation | ||
To install Blink, run: | ||
|
||
```julia-repl | ||
julia> Pkg.add("Blink") | ||
julia> Blink.AtomShell.install() | ||
``` | ||
|
||
This will install the package, and its dependencies: namely, `Electron`. | ||
|
||
## Documentation Outline | ||
|
||
```@contents | ||
``` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel a bit weird about having repeated myself here, but i think it's good to have simple examples here and a full discussion of these complicated mechanisms there. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me.