Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<!-- <script src="./main.mjs" type="module"></script> -->
<div class="app"></div>
<script
src="./target/js/debug/build/main/main.js"
src="./target/js/debug/build/app.js"
type="module"
defer
></script>
Expand Down
6 changes: 4 additions & 2 deletions moon.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"deps": {
"tiye/respo": "0.2.3",
"tiye/dom-ffi": "0.2.3",
"tiye/respo_css": "0.1.4"
"tiye/respo_css": "0.1.4",
"moonbitlang/async": "0.15.0",
"tiye/respo-markdown": "0.1.2"
},
"readme": "README.md",
"repository": "",
Expand All @@ -13,4 +15,4 @@
"description": "",
"source": "src",
"preferred-target": "js"
}
}
30 changes: 27 additions & 3 deletions src/main/main.mbt → src/main.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using @respo_node {div, span, button}
using @css {respo_style}

///|
let app_store_key : String = "mbt-workflow"
let app_store_key : String = "moonverse-portal"

///|
fn view(
Expand All @@ -16,7 +16,11 @@ fn view(
}
// @dom_ffi.log("Store to render: " + store.to_json().stringify(indent=2))
div(class_name=@respo.ui_global, style=respo_style(padding=Px(12)), [
comp_counter(store.states.pick("counter"), store.counted),
comp_query(
store.states.pick("query"),
store.query_result,
store.query_error,
),
])
}

Expand All @@ -25,8 +29,10 @@ fn main {
let window = @dom_ffi.window()
if window.document().query_selector(".app") is Some(mount_target) {
let mount_target = mount_target.reinterpret_as_node()
let mut store_0 : Store = @respo.try_load_storage(app_store_key)
store_0 = { ..store_0, states: @respo.RespoStatesTree::default() }
let app : @respo.RespoApp[Store] = {
store: Ref::new(@respo.try_load_storage(app_store_key)),
store: Ref::new(store_0),
mount_target,
storage_key: app_store_key,
}
Expand All @@ -37,6 +43,24 @@ fn main {
@dom_ffi.log("Action: \{op}")
}
app.store.val = app.store.val.update(op)
match op {
StartQuery(prompt) => {
let _ = @js_async.Promise::from_async(async fn() {
try {
let result = run_query(prompt)
app.store.val = app.store.val.update(QuerySuccess(result))
@respo.mark_need_rerender()
} catch {
Failure(msg) => {
app.store.val = app.store.val.update(QueryError(msg))
@respo.mark_need_rerender()
}
}
})
()
}
_ => ()
}
})
let dev_mode = @dom_ffi.new_url_search_params(window.location().search()).get(
"mode",
Expand Down
88 changes: 0 additions & 88 deletions src/main/counter.mbt

This file was deleted.

4 changes: 3 additions & 1 deletion src/main/moon.pkg.json → src/moon.pkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"alias": "respo_node"
},
{ "path": "tiye/dom-ffi", "alias": "dom_ffi" },
{ "path": "tiye/respo_css", "alias": "css" }
{ "path": "tiye/respo_css", "alias": "css" },
{ "path": "tiye/respo-markdown", "alias": "markdown" },
{ "path": "moonbitlang/async/js_async", "alias": "js_async" }
]
}
214 changes: 214 additions & 0 deletions src/query_comp.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
///|
struct QueryState {
input_val : String
loading : Bool
} derive(Default, ToJson, @json.FromJson)

///|
fn comp_query(
states : @respo.RespoStatesTree,
result : String,
error : String,
) -> @respo_node.RespoNode[ActionOp] {
Comment thread
tiye marked this conversation as resolved.
Outdated
let cursor = states.path()
let state = (states.cast_branch() : QueryState)

///|
fn start_query_request(
prompt : String,
dispatch : @respo_node.DispatchFn[ActionOp],
) -> Unit {
println("start_query_request: prompt = \{prompt}")
// 设置 loading = true
dispatch.set_state(cursor, QueryState::{ input_val: prompt, loading: true }) catch {
_ => ()
}
// 使用纯 JS 实现的异步请求
Comment thread
tiye marked this conversation as resolved.
Outdated
run_query_js(
prompt,
fn(result) {
println("Query success, result length: \{result.length()}")
dispatch.set_state(cursor, QueryState::{
input_val: prompt,
loading: false,
}) catch {
_ => ()
}
dispatch.run(QuerySuccess(result)) catch {
_ => ()
}
@respo.mark_need_rerender()
},
fn(err) {
println("Query error: \{err}")
dispatch.set_state(cursor, QueryState::{
input_val: prompt,
loading: false,
}) catch {
_ => ()
}
dispatch.run(QueryError(err)) catch {
_ => ()
}
@respo.mark_need_rerender()
},
)
println("start_query_request: after run_query_js")
}

@respo_node.div(class_name=style_query_container, [
// 标题
@respo_node.div(class_name=style_title, [
@respo_node.text_node("MoonBit Query"),
]),
// 输入区域
Comment thread
tiye marked this conversation as resolved.
Outdated
@respo_node.div(class_name=style_input_row, [
@respo_node.input(
value=state.input_val,
placeholder="Enter your question about MoonBit...",
class_name=style_input,
on_input=fn(e, dispatch) {
match e {
Input(value~, ..) =>
dispatch.set_state(cursor, { ..state, input_val: value })
_ => ()
}
},
),
@respo_node.button(
inner_text=if state.loading { "Loading..." } else { "Submit" },
class_name=style_button,
on_click=fn(_e, dispatch) {
if not(state.loading) && not(state.input_val.is_empty()) {
let prompt = state.input_val
println("Submit clicked, prompt: \{prompt}")
dispatch.run(StartQuery(prompt))
Comment thread
tiye marked this conversation as resolved.
Outdated
start_query_request(prompt, dispatch)
}
},
),
]),
// 错误信息
if not(error.is_empty()) {
@respo_node.div(class_name=style_error, [@respo_node.text_node(error)])
} else {
@respo_node.span([])
},
Comment thread
tiye marked this conversation as resolved.
Outdated
// 结果显示
if not(result.is_empty()) {
@respo_node.div(class_name=style_result, [@markdown.comp_md_block(result)])
} else if state.loading {
@respo_node.div(class_name=style_loading, [
@respo_node.text_node("Thinking..."),
])
} else {
@respo_node.span([])
},
Comment thread
tiye marked this conversation as resolved.
Outdated
])
}

// Static styles

///|
let style_query_container : String = @respo_node.static_style([
(
"&",
@css.respo_style(
padding=Px(20),
max_width=Px(800),
margin=Auto,
font_family="system-ui, -apple-system, sans-serif",
),
),
])

///|
let style_title : String = @respo_node.static_style([
(
"&",
@css.respo_style(
font_size=24,
font_weight="bold",
margin_bottom=Px(16),
color=Hsl(220, 13, 18),
),
),
])

///|
let style_input_row : String = @respo_node.static_style([
("&", @css.respo_style(display=Flex, gap=Px(8), margin_bottom=Px(16))),
])

///|
let style_input : String = @respo_node.static_style([
(
"&",
@css.respo_style(
flex=1.0,
padding=Px(12),
font_size=16,
border=@css.CssBorder::new(width=1.0, style=Solid, color=Hsl(220, 13, 80)),
border_radius=6.0,
outline=@css.CssOutline::None,
),
),
])

///|
let style_button : String = @respo_node.static_style([
(
"&",
@css.respo_style(
padding_left=Px(24),
padding_right=Px(24),
padding_top=Px(12),
padding_bottom=Px(12),
font_size=16,
font_weight="600",
background_color=Hsl(220, 90, 56),
color=White,
border_width=Px(0),
border_radius=6.0,
cursor=Pointer,
),
),
])

///|
let style_error : String = @respo_node.static_style([
(
"&",
@css.respo_style(
padding=Px(12),
margin_bottom=Px(16),
background_color=Hsl(0, 80, 95),
color=Hsl(0, 70, 40),
border_radius=6.0,
border=@css.CssBorder::new(width=1.0, style=Solid, color=Hsl(0, 70, 80)),
),
),
])

///|
let style_result : String = @respo_node.static_style([
(
"&",
@css.respo_style(
padding=Px(16),
background_color=Hsl(220, 14, 96),
border_radius=8.0,
white_space="pre-wrap",
line_height=Em(1.6),
font_size=15,
),
),
])

///|
let style_loading : String = @respo_node.static_style([
(
"&",
@css.respo_style(padding=Px(16), color=Hsl(220, 13, 50), font_style=Italic),
),
])
Loading