From 5bb8f85ee4d1c0e13ef8a965651f8700b47e3a2a Mon Sep 17 00:00:00 2001 From: Koichi Akabe Date: Sun, 14 Jul 2024 16:19:26 +0900 Subject: [PATCH] Support query string in Wasm example (#128) * Support query string Wasm example * Add copy link button * Add cargo settings --- examples/wasm/.cargo/config.toml | 2 + examples/wasm/Cargo.toml | 25 +++++---- examples/wasm/index.html | 25 +++++++++ examples/wasm/src/lib.rs | 41 +++++++------- examples/wasm/src/text_input.rs | 94 +++++++++++++++++++++++++------- vaporetto/src/dict_model.rs | 1 + 6 files changed, 136 insertions(+), 52 deletions(-) create mode 100644 examples/wasm/.cargo/config.toml diff --git a/examples/wasm/.cargo/config.toml b/examples/wasm/.cargo/config.toml new file mode 100644 index 00000000..84671750 --- /dev/null +++ b/examples/wasm/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg=web_sys_unstable_apis"] diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index b4bea139..3156717f 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -4,20 +4,23 @@ version = "0.1.0" edition = "2021" [dependencies] -gloo-worker = "0.2.1" # MIT or Apache-2.0 -i18n-embed = { version = "0.13.8", features = ["fluent-system", "web-sys-requester"]} # MIT -i18n-embed-fl = "0.6.5" # MIT -once_cell = "1.17.0" # MIT or Apache-2.0 -ouroboros = "0.17.2" # MIT or Apache-2.0 -rust-embed = "6.4.2" # MIT -ruzstd = "0.5.0" # MIT +gloo-timers = "0.3.0" # MIT or Apache-2.0 +gloo-worker = "0.5.0" # MIT or Apache-2.0 +i18n-embed = { version = "0.14.1", features = ["fluent-system", "web-sys-requester"]} # MIT +i18n-embed-fl = "0.8.0" # MIT +js-sys = "0.3.69" # MIT or Apache-2.0 +once_cell = "1.19.0" # MIT or Apache-2.0 +ouroboros = "0.18.4" # MIT or Apache-2.0 +rust-embed = "8.5.0" # MIT +ruzstd = "0.7.0" # MIT serde = "1" # MIT or Apache-2.0 -unic-langid = { version = "0.9.1", features = ["macros"] } # MIT or Apache-2.0 +unic-langid = { version = "0.9.5", features = ["macros"] } # MIT or Apache-2.0 vaporetto = { path = "../../vaporetto", default-features = false, features = ["std", "cache-type-score", "fix-weight-length", "tag-prediction"] } # MIT or Apache-2.0 vaporetto_rules = { path = "../../vaporetto_rules" } # MIT or Apache-2.0 -wasm-bindgen = "0.2.83" # MIT or Apache-2.0 -web-sys = { version = "0.3.61", features = ["Event", "EventTarget", "InputEvent"] } # MIT or Apache-2.0 -yew = { version = "0.20", features = ["csr"] } # MIT or Apache-2.0 +wasm-bindgen = "0.2.92" # MIT or Apache-2.0 +wasm-bindgen-futures = "0.4.42" # MIT or Apache-2.0 +web-sys = { version = "0.3.69", features = ["Clipboard", "Event", "EventTarget", "InputEvent"] } # MIT or Apache-2.0 +yew = { version = "0.21", features = ["csr"] } # MIT or Apache-2.0 [profile.release] panic = "abort" diff --git a/examples/wasm/index.html b/examples/wasm/index.html index cf73ed40..9ca034e6 100644 --- a/examples/wasm/index.html +++ b/examples/wasm/index.html @@ -29,6 +29,11 @@ color: #fff; } +html { + max-width: 1000px; + margin: 0 auto; +} + body { width: calc(100% - 20px); margin: 10px; @@ -39,6 +44,26 @@ text-align: right; } +.input-bar { + display: flex; + flex-direction: row; +} + +.input-bar button { + font-size: 14pt; + align-self: center; + background-color: #ffffff; + border: 1px solid #000000; +} + +.input-bar button:hover { + background-color: #dddddd; +} + +.input-bar button.copied { + background-color: #008800; +} + a { color: #000; } diff --git a/examples/wasm/src/lib.rs b/examples/wasm/src/lib.rs index 7cece264..be6fbef7 100644 --- a/examples/wasm/src/lib.rs +++ b/examples/wasm/src/lib.rs @@ -14,6 +14,7 @@ use vaporetto_rules::{ string_filters::KyteaFullwidthFilter, SentenceFilter, StringFilter, }; +use web_sys::UrlSearchParams; use yew::{html, Component, Context, Html}; use crate::text_input::TextInput; @@ -38,10 +39,10 @@ pub struct VaporettoWorker { #[borrows(predictor)] #[covariant] - sentence_orig: Sentence<'static, 'this>, + sentence_filtered: Sentence<'static, 'this>, #[borrows(predictor)] #[covariant] - sentence_filtered: Sentence<'static, 'this>, + sentence_orig: Sentence<'static, 'this>, } impl Worker for VaporettoWorker { @@ -150,13 +151,19 @@ impl Component for App { }) .spawn("./vaporetto_worker.js"); - // Sends a dummy message. - // The first response indicates that the worker is ready. - bridge.send(String::new()); + let text = web_sys::window() + .unwrap() + .location() + .search() + .ok() + .and_then(|s| UrlSearchParams::new_with_str(&s).ok()) + .and_then(|q| q.get("text")) + .unwrap_or_else(String::new); + bridge.send(text.clone()); Self { bridge, - text: String::new().into(), + text: text.into(), tokens: None, n_tags: 0, } @@ -186,22 +193,14 @@ impl Component for App {

-
- { - if self.tokens.is_some() { - html! { - - } - } else { - html!{ - - } - } + { + html! { + } -
+ } { if let Some(tokens) = &self.tokens { html! { diff --git a/examples/wasm/src/text_input.rs b/examples/wasm/src/text_input.rs index abbf546a..37f324a5 100644 --- a/examples/wasm/src/text_input.rs +++ b/examples/wasm/src/text_input.rs @@ -1,19 +1,21 @@ use std::rc::Rc; -use wasm_bindgen::{JsCast, UnwrapThrowExt}; -use web_sys::{Event, HtmlInputElement, InputEvent}; -use yew::{html, Callback, Component, Context, Html, NodeRef, Properties}; +use gloo_timers::callback::Timeout; +use wasm_bindgen_futures::JsFuture; +use web_sys::HtmlInputElement; +use yew::{html, platform::spawn_local, Callback, Component, Context, Html, NodeRef, Properties}; use crate::fl; #[derive(Clone, PartialEq, Properties)] pub struct Props { - pub value: Rc, + pub value: Option>, pub callback: Callback, } pub struct TextInput { - node_ref: NodeRef, + input_ref: NodeRef, + button_ref: NodeRef, } impl Component for TextInput { @@ -22,7 +24,8 @@ impl Component for TextInput { fn create(_ctx: &Context) -> Self { Self { - node_ref: NodeRef::default(), + input_ref: NodeRef::default(), + button_ref: NodeRef::default(), } } @@ -30,21 +33,72 @@ impl Component for TextInput { let Props { value, callback } = ctx.props(); let callback = callback.clone(); - let oninput = Callback::from(move |e: InputEvent| { - let event: Event = e.dyn_into().unwrap_throw(); - let event_target = event.target().unwrap_throw(); - let target: HtmlInputElement = event_target.dyn_into().unwrap_throw(); - callback.emit(target.value()); + let input_ref = self.input_ref.clone(); + let oninput = Callback::from(move |_| { + let input = input_ref.cast::().unwrap(); + callback.emit(input.value()); }); - html! { - + let input_ref = self.input_ref.clone(); + let button_ref = self.button_ref.clone(); + let clipboard_click = Callback::from(move |_| { + let input = input_ref.cast::().unwrap(); + if let Some(clipboard) = web_sys::window().unwrap().navigator().clipboard() { + let loc = web_sys::window().unwrap().location(); + let content = format!( + "{}{}?text={}", + loc.origin().unwrap(), + loc.pathname().unwrap(), + js_sys::encode_uri_component(&input.value()) + ); + let promise = clipboard.write_text(&content); + let button_ref = button_ref.clone(); + spawn_local(async move { + JsFuture::from(promise).await.unwrap(); + let button = button_ref.cast::().unwrap(); + button.set_class_name("copied"); + let button_ref = button_ref.clone(); + let timeout = Timeout::new(1000, move || { + let button = button_ref.cast::().unwrap(); + button.set_class_name(""); + }); + timeout.forget(); + }); + } + }); + if let Some(value) = value.as_ref() { + html! { +
+ + +
+ } + } else { + html! { +
+ + +
+ } } } @@ -54,7 +108,7 @@ impl Component for TextInput { fn rendered(&mut self, _ctx: &Context, first_render: bool) { if first_render { - if let Some(input) = self.node_ref.cast::() { + if let Some(input) = self.input_ref.cast::() { input.focus().unwrap(); } } diff --git a/vaporetto/src/dict_model.rs b/vaporetto/src/dict_model.rs index 23b43a21..9c81be03 100644 --- a/vaporetto/src/dict_model.rs +++ b/vaporetto/src/dict_model.rs @@ -5,6 +5,7 @@ use bincode::{Decode, Encode}; use crate::errors::{Result, VaporettoError}; +#[cfg(feature = "kytea")] #[derive(Clone, Copy, Default)] pub struct DictWeight { pub right: i32,