Плагины Stipple
StippleDownloads
StippleDownloads — это плагин для Stipple, позволяющий скачивать динамически создаваемые файлы. Обработчики событий гарантируют, что копию файла получит только запрашивающий клиент.
Поддерживаются текстовые и двоичные файлы, а имена файлов можно выбирать произвольным образом.
Примеры
Скачивание содержимого переменной в виде необработанных данных или BSON
using Stipple, Stipple.ReactiveTools
using StippleUI, StippleDownloads
using BSON
@app begin
@in data = randn(1000)
@event download_raw begin
download_binary(__model__, data, "raw")
end
@event download_bson begin
# При вызове @save для `data` выдается ошибка, так как это реактивная переменная. Необходимо создать копию.
data_var = data
io = IOBuffer()
BSON.@save io data_var
seekstart(io)
download_binary(__model__, take!(io), "data.bson")
end
end
function ui()
row([
cell(btn(class="q-ml-lg", "Raw", icon="download", @on(:click, :download_raw, :addclient), color="primary", nocaps=true))
cell(btn(class="q-ml-lg", "BSON", icon="download", @on(:click, :download_bson, :addclient), color="secondary", nocaps=true))
])
end
@page("/", ui)
Скачивание DataFrame в виде листа Excel
using Stipple, Stipple.ReactiveTools
using StippleUI
using StippleDownloads
using DataFrames
using XLSX
import Stipple.opts
import StippleUI.Tables.table
function df_to_xlsx(df)
io = IOBuffer()
XLSX.writetable(io, df)
take!(io)
end
@app begin
@out table = DataTable(DataFrame(:a => rand(1:10, 5), :b => rand(1:10, 5)))
@in text = "The quick brown fox jumped over the ..."
@event download_text begin
download_text(__model__, :text)
end
@event download_df begin
try
download_binary(__model__, df_to_xlsx(table.data), "file.xlsx"; client = event["_client"])
catch ex
println(ex)
end
end
end
function ui()
row(cell(class = "st-module", [
row([
cell(textfield(class = "q-pr-md", "Download text", :text, placeholder = "no output yet ...", :outlined, :filled, type = "textarea"))
cell(table(class = "q-pl-md", :table))
])
row([
cell(col = 1, "Without client info")
cell(btn("Text File", icon = "download", @on(:click, :download_text), color = "primary", nocaps = true))
cell(col = 1, "With client info")
cell(btn(class = "q-ml-lg", "Excel File", icon = "download", @on(:click, :download_df, :addclient), color = "primary", nocaps = true))
])
]))
end
@page("/", ui)
up(open_browser = true)
Чтобы увидеть разницу между вызовами с информацией о клиенте и без нее, продублируйте вкладку приложения и нажмите кнопку Download Text (Скачать текст).
Будут скачаны два идентичных файла, так как при дублировании вкладки создается синхронизированная копия приложения. Чтобы файл получал только запрашивающий клиент, следует ограничить загрузку с помощью event["_client"]
и добавить для активирующей кнопки дополнительное свойство :addclient
, в которое будет включаться эта информация.
StippleMarkdown
Отображение текста Markdown в приложениях Genie
Этот пакет содержит два новых компонента Stipple: markdowntext
и markdowncard
. В качестве аргумента можно передать либо строку текста Markdown, либо символ, который ссылается на переменную с текстом Markdown.
using GenieFramework
@genietools
using StippleMarkdown
@app begin
@out txt = "**hello** world"
end
@deps StippleMarkdown
ui() = [ markdowntext(:txt), markdowntext("## Hello World!"), markdowncard(:txt), markdowncard("## Hello World!\n This is a Markdown card")]
@page("/", ui)
StippleLatex
StippleLatex использует Vue-Katex для реализации форматирования LaTeX в Stipple. Есть три возможных варианта добавления содержимого LaTeX на веб-страницу:
-
Элемент span LaTeX:
latex(<LaTeX formula>, <formatting options>)
-
HTML-элемент с содержимым LaTeX через строковый макрос с необязательным модификатором, который может принимать значение
auto
илиdisplay
:span(latex"<LaTeX formula>")
span(latex"<LaTeX formula>"display)
-
HTML-элемент с содержимым LaTeX через макрос
@latex
с дополнительными параметрами:span(@latex(raw"<LaTeX formula>")
span(@latex(raw"<LaTeX formula>", display = true)
Все аргументы также поддерживают символы для привязки к полям модели. Вот приложение, демонстрирующее возможные варианты использования.
using Stipple, Stipple.ReactiveTools
using StippleUI
using StippleLatex
## определяем небольшой генератор формул
function nestlist(f, a; init = nothing)
T = eltype(a)
list = T[]
el = init
for (i, x) in enumerate(a)
el = i == 1 && init === nothing ? x : f(el, x)
push!(list, el)
end
list
end
formula = nestlist(*, ["", raw"\sin", "^2", " x", " +", raw" \sqrt{", "a", "^2", " +", " b", "^2"])
formula[contains.(formula, "sqrt")] .*= "}"
## настраиваем приложение
@app begin
@in x = 0
@in formula_1 = raw"\int_{a}^{b} f(x) \, dx = F(x)\Biggr|^b_a"
@in formula_2 = raw""
@private p = @task 1 + 1
@onchange isready begin
if !istaskstarted(p) || istaskdone(p)
p = @task begin
println("Task started")
while x <= 100
sleep(1)
x += 1
pos = x < 6 ? 1 : (x - 5) % (length(formula) + 5) + 1
formula_2 = formula[min(pos, length(formula))]
end
end
schedule(p)
end
end
end
function ui()
[
row(cell(class = "st-module", [
cell(h1(latex("\\LaTeX") * "-Demo"))
cell(h2(latex"a^2 + b^2 = c^2"))
]))
row(cell(class = "st-module", [
textfield("Enter your LaTeX-Forumla", :formula_1,)
cell(class = "q-pa-md", latex":formula_1"display)
row([
cell(class = "q-pa-md bg-red-1", raw"""cell(latex"\cos^2x"display)""")
cell(class = "q-pa-md bg-green-1", latex"\cos^2x"display)
])
row([
cell(class = "q-pa-md bg-red-1", raw"""cell(latex"This is auto mode with a formula \(\cos^2x\)"auto)""")
cell(class = "q-pa-md bg-green-1", latex"This is auto mode with a formula \(\cos^2x\)"auto)
])
row([
cell(class = "q-pa-md bg-red-1", raw"""latex(class = "q-pa-md", raw"\tan^2x", display = true)""")
cell(class = "bg-green-1 q-pa-md", latex(class = "q-pa-md", raw"\tan^2x", display = true))
])
bignumber("Wait for 5", :x, color = R"x >= 5 ? 'negative' : 'positive'", icon = "calculate")
row([
cell(class = "q-pa-md bg-red-1", raw"""cell(class = "q-pa-md", @latex(raw"\tanh^2 y", display = R"x >= 5"))""")
cell(class = "q-pa-md bg-green-1", @latex(raw"\tanh^2 y", display = R"x >= 5"))
])
row([
cell(class = "q-pa-md bg-red-1", raw"""@latex(raw"This is auto mode with an inline formula \(\cos^2x\) and a display formula $$\sin^2x$$""")
cell(class = "q-pa-md bg-green-1", @latex(raw"This is auto mode with an inline formula \(\cos^2x\) and a display formula $$\sin^2x$$", auto = true))
])
]))
row(cell(class = "st-module", [
textfield(class = "q-pa-lg", "LaTeX", :formula_2)
cell(class = "q-pa-md", "Result:")
cell(class = "q-pa-md", latex":formula_2"display)
]))
]
end
route("/") do
page(@init(), ui()) |> html
end
up()

StippleMathjs
Плагин StippleMathjs добавляет mathjs в проект Stipple или GenieFramwork.
Кроме того, он добавляет автоматическое преобразование всех типов чисел Complex
между сервером и клиентом.
Пример приложения
using Stipple, Stipple.ReactiveTools
using StippleUI
using StippleMathjs
x0 = 1.0
y0 = 2.0
@app begin
@in x = x0
@in y = y0
@in z = x0 + y0 * im
@in z2::ComplexF64 = x0 + y0 * im
@onchange x, y begin
# обновляем z, не инициируя `@onchange z`
z[!] = x + y * im
# обновляем x и y в клиенте
@push z
end
@onchange z begin
# обновляем x и y, не инициируя `@onchange x, y`
x[!] = z.re
y[!] = z.im
# обновляем x и y в клиенте
@push x
@push y
end
end
@deps StippleMathjs
function ui()
[
card(class = "q-pa-md", [
numberfield(class = "q-ma-md", "x", :x)
numberfield(class = "q-ma-md", "y", :y)
])
card(class = "q-pa-md q-my-md", [
row([cell(col = 2, "z"), cell("{{ z }}")])
row([cell(col = 2, "z.mul(z)"), cell("{{ z.mul(z) }}")])
row([cell(col = 2, "z.abs()"), cell("{{ z.abs() }}")])
btn(class = "q-my-md", "square(z)", color = "primary", @click("z = z.mul(z)"))
])
]
end
@page("/", ui, debounce = 10)
up()
Пример приложения с правильным содержимым файла Manifest.toml доступен в StippleDemos.jl.
Примечание
-
Из-за имеющейся в настоящее время в Stipple ошибки
x0
иy0
необходимо определять вне цикла, чтобы у z был правильный тип. В качестве альтернативы можно объявитьz
явно, например, так:z::ComplexF64
. -
Этот пакет может служить хорошим примером внедрения исходного кода JavaScript в собственный проект.
StippleKeplerGL.jl

Пакет Julia для интеграции карт KeplerGL в приложения Genie/Stipple.
Установка
Для установки пакета выполните в командной строке Julia следующую команду:
] add StippleKeplerGL
Пример
using Stipple, Stipple.ReactiveTools
using StippleUI
using StippleKeplerGL
using DataFrames
using CSV
using Colors
using ColorBrewer
keplergl_path = Base.pkgdir(isdefined(@__MODULE__, :KeplerGLBase) ? KeplerGLBase : KeplerGL)
df = CSV.read(joinpath(keplergl_path, "assets", "example_data", "data.csv"), DataFrame)
## token = "токен, пожалуйста"
m1 = KeplerGL.KeplerGLMap(token, center_map=false)
KeplerGL.add_point_layer!(m1, df, :Latitude, :Longitude,
color = colorant"rgb(23,184,190)", color_field = :Magnitude, color_scale = "quantize",
color_range = ColorBrewer.palette("PRGn", 6),
radius_field = :Magnitude, radius_scale = "sqrt", radius_range = [4.2, 96.2], radius_fixed = false,
filled = true, opacity = 0.39, outline = false
)
m1.config[:config][:mapState][:latitude] = 38.32068477880718
m1.config[:config][:mapState][:longitude]= -120.42806781055732
m1.config[:config][:mapState][:zoom] = 4.886825331541375
m1.window[:map_legend_show] = m1.window[:map_legend_active] = m1.window[:visible_layers_show] = m1.window[:visible_layers_active] = false
m2 = KeplerGL.KeplerGLMap(token, center_map=false)
KeplerGL.add_point_layer!(m2, df, :Latitude, :Longitude,
color = colorant"rgb(23,184,190)", color_field = :Magnitude, color_scale = "quantize",
color_range = ColorBrewer.palette("RdYlGn", 6),
radius_field = :Magnitude, radius_scale = "sqrt", radius_range = [4.2, 96.2], radius_fixed = false,
filled = true, opacity = 0.39, outline = false
)
m2.config[:config][:mapState][:latitude] = 38.32068477880718
m2.config[:config][:mapState][:longitude]= -122.42806781055732
m2.config[:config][:mapState][:zoom] = 4.886825331541375
m2.window[:map_legend_show] = m2.window[:map_legend_active] = m2.window[:visible_layers_show] = m2.window[:visible_layers_active] = false
d1, d2 = m1.datasets, m2.datasets
@app begin
@out map1 = m1
@out map2 = m2
@in clear_data = false
@in restore_data = false
@in show_legend = false
@in go_west = false
@in go_east = false
@onbutton clear_data begin
__model__["map1.datasets"] = []
__model__["map2.datasets"] = []
end
@onbutton restore_data begin
__model__["map1.datasets"] = d1
__model__["map2.datasets"] = d2
end
@onbutton go_west begin
map1.config[:config][:mapState][:longitude] -= 1
__model__["map1.config.config.mapState.longitude"] = map1.config[:config][:mapState][:longitude]
map2.config[:config][:mapState][:longitude] -= 1
__model__["map2.config.config.mapState.longitude"] = map2.config[:config][:mapState][:longitude]
end
@onbutton go_east begin
map1.config[:config][:mapState][:longitude] += 1
__model__["map1.config.config.mapState.longitude"] = map1.config[:config][:mapState][:longitude]
map2.config[:config][:mapState][:longitude] += 1
__model__["map2.config.config.mapState.longitude"] = map2.config[:config][:mapState][:longitude]
end
@onchange show_legend begin
__model__["map1.window.map_legend_show"] = show_legend
__model__["map2.window.map_legend_show"] = show_legend
# в качестве альтернативы можно использовать следующие строки для отображения условных обозначений через бэкенд,
# но при этом во фронтенд будут переданы все данные карты
# map1.window[:map_legend_show] = show_legend
# notify(map1)
end
end
@deps StippleKeplerGL
isdefined(Stipple, :register_global_components) && Stipple.register_global_components("VueKeplerGl", legacy = true)
ui() = [
column(class = "full-height", [
row(col = :auto, class = "items-center", [
h5(class = "col-auto q-pl-lg q-py-md", "KeplerGL Demo")
cell()
btn(col = :auto, "", icon = "west", @click(:go_west), class = "q-mr-md", [tooltip("go west")])
btn(col = :auto, "", icon = "east", @click(:go_east), class = "q-mr-md", [tooltip("go east")])
btn(col = :auto, "", icon = "delete", @click(:clear_data), class = "q-mr-md", [tooltip("clear data")])
btn(col = :auto, "", icon = "restore_from_trash", @click(:restore_data), class = "q-mr-md", [tooltip("restore data")])
toggle(col = :auto, "legend", :show_legend, class = "q-mr-md")
])
cell(keplergl(:map1, ref = "map1", id = "map1"))
cell(keplergl(:map2, ref = "map2"))
])
]
route("/") do
# раскомментируйте следующую строку для тестирования или отладки
global model
model = @init
page(class = "fixed-full", model, ui) |> html
end
up(open_browser = true)
StippleTypedArrays
StippleTypedArrays — это плагин Stipple для интеграции массивов TypedArrays из JavaScript.
Типичным вариантом их использования являются буферы, например для скачивания файлов или обработки и отправки информации по двоичным каналам.
В StippleTypedArrays введена векторная оболочка TypedArray
, которую можно использовать в объявлениях типов для переменных приложения.
Обратите внимание:
на стороне бэкенда все обработчики работают обычным образом. Однако на стороне клиента JavaScript не может отслеживать типизированные массивы, поэтому любые изменения буфера на стороне клиента не будут автоматически синхронизированы.
Чтобы синхронизировать данные с сервером, после изменения значения необходимо вызвать this.push('data')
.
Демонстрационное приложение
using Stipple, Stipple.ReactiveTools
using StippleUI
using StippleTypedArrays
using StippleDownloads
@app begin
@in data = TypedArray(UInt8[])a
@in data64 = TypedArray(UInt64[])
@in add_data = false
@in clear_data = false
@onbutton add_data begin
x = rand(0:255)
push!(data, x)
notify(data)
push!(data64, x + 1000)
notify(data64)
end
@onbutton clear_data begin
data = data64 = []
end
end
@deps StippleTypedArrays
function ui()
row(cell(class = "st-module q-ma-md", [
row(class = "q-pa-md bg-green-2", "Data: [{{ data }}]")
row(class = "q-pa-md q-my-lg bg-green-4", "Data64: [{{ data64 }}]")
row([
btn("Add data", icon = "add", @click(:add_data), color = "primary", nocaps = true)
btn(class = "q-ml-lg", "Clear data", icon = "delete_forever", @click(:clear_data), color = "primary", nocaps = true)
])
]))
end
@page("/", ui)
up(open_browser = true)
