initial commit: rewrote in (embarassingly bad) wasm
This commit is contained in:
commit
772c15bcc1
|
@ -0,0 +1,2 @@
|
|||
/dist/
|
||||
/target/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "bits-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[dependencies]
|
||||
log = "0.4.26"
|
||||
seq-macro = "0.3.6"
|
||||
wasm-logger = "0.2.0"
|
||||
web-sys = { version = "0.3", features = [
|
||||
"HtmlTableCellElement",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
] }
|
||||
yew = { version = "0.21", features = ["csr"] }
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>bits n bits</title>
|
||||
<link data-trunk rel="sass" href="index.scss" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,142 @@
|
|||
body {
|
||||
background: linear-gradient(to bottom right, bisque, hsl(24, 57%, 70%));
|
||||
height: 100vw;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
#bytes_in {
|
||||
text-align: center;
|
||||
border: 0px;
|
||||
font-size: 56px;
|
||||
font-family: 'Cute Font';
|
||||
filter: sepia(100%);
|
||||
border-radius: 10px;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
#input_box {
|
||||
filter:
|
||||
// drop-shadow(5px 5px red)
|
||||
sepia(100%) // drop-shadow(-5px -5px hsl(24, 27%, 40%))
|
||||
drop-shadow(10px 10px 10px 10px hsl(24, 27%, 40%));
|
||||
height: 150px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bit {
|
||||
color: blue;
|
||||
font-weight: 200;
|
||||
font-size: 80px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: bisque;
|
||||
}
|
||||
|
||||
th:nth-child(odd) {
|
||||
background-color: burlywood;
|
||||
}
|
||||
|
||||
|
||||
#bit_wrap table:nth-of-type(even) {
|
||||
margin-left: 5px;
|
||||
margin-bottom: 5px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
#bit_wrap table:nth-of-type(odd) {
|
||||
margin-left: 0px;
|
||||
margin-bottom: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
#activated_ext {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-basis: content;
|
||||
justify-content: center;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-o-user-select: none;
|
||||
text-align: center;
|
||||
border: 0px;
|
||||
font-size: 36px;
|
||||
font-family: 'Cute Font';
|
||||
|
||||
background: -webkit-linear-gradient(hsl(24, 27%, 40%), hsl(24, 27%, 50%));
|
||||
background-clip: border-box;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
#activated_wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-basis: content;
|
||||
justify-content: center;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-o-user-select: none;
|
||||
text-align: center;
|
||||
border: 0px;
|
||||
font-size: 36px;
|
||||
font-family: 'Cute Font';
|
||||
|
||||
background: -webkit-linear-gradient(hsl(24, 27%, 40%), hsl(24, 27%, 50%));
|
||||
background-clip: border-box;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
#bit_wrap {
|
||||
filter: drop-shadow(3px 3px red) sepia(100%) drop-shadow(-3px -3px hsl(24, 27%, 40%));
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-basis: content;
|
||||
justify-content: center;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-o-user-select: none;
|
||||
}
|
||||
|
||||
#activated {
|
||||
text-wrap: balance;
|
||||
font-weight: bold;
|
||||
font-size: 64px;
|
||||
color: grey;
|
||||
word-wrap: break-word;
|
||||
|
||||
text-align: center;
|
||||
/* width: 90vw; */
|
||||
}
|
||||
|
||||
#activated_text {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.bit:hover {
|
||||
filter: brightness(85%) saturate(150%);
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: x-large;
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
#![allow(clippy::expect_fun_call)]
|
||||
use core::ops::Range;
|
||||
|
||||
use web_sys::{HtmlInputElement, HtmlTableCellElement, wasm_bindgen::JsCast};
|
||||
use yew::prelude::*;
|
||||
|
||||
struct BitsTable {
|
||||
bits: Html,
|
||||
value_box: Html,
|
||||
}
|
||||
|
||||
const HEX: &[char] = &[
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C',
|
||||
'D', 'E', 'F', 'x',
|
||||
];
|
||||
|
||||
const ALLOWED_CHARS: &[char] = &[
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C',
|
||||
'D', 'E', 'F', 'x', '_',
|
||||
];
|
||||
|
||||
fn value_to_u32(value: &str) -> u32 {
|
||||
let value = value.trim();
|
||||
if let Some(value) = value.strip_prefix("0x") {
|
||||
u32::from_str_radix(value.trim(), 16)
|
||||
} else if let Some(value) = value.strip_prefix("0b") {
|
||||
u32::from_str_radix(value.trim(), 2)
|
||||
} else if let Some(value) = value.strip_prefix("0o") {
|
||||
u32::from_str_radix(value.trim(), 8)
|
||||
} else {
|
||||
value.parse()
|
||||
}
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
impl Component for BitsTable {
|
||||
type Message = ();
|
||||
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
let bit_click = |bit: u8| {
|
||||
move |ev: MouseEvent| {
|
||||
let input = web_sys::window()
|
||||
.unwrap()
|
||||
.document()
|
||||
.unwrap()
|
||||
.get_element_by_id("bytes_in")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap();
|
||||
let mut value = value_to_u32(&input.value());
|
||||
if let Some(target) = ev
|
||||
.target()
|
||||
.and_then(|t| t.dyn_into::<HtmlTableCellElement>().ok())
|
||||
{
|
||||
match target.inner_text().as_str() {
|
||||
"0" => {
|
||||
target.set_inner_text("1");
|
||||
value |= 1 << bit;
|
||||
}
|
||||
_ => {
|
||||
target.set_inner_text("0");
|
||||
value &= !(1 << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input.set_value(format!("{value:#X}").as_str());
|
||||
|
||||
input
|
||||
.dispatch_event(&InputEvent::new("input").unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
let bits = (0..4usize)
|
||||
.rev()
|
||||
.map(|idx| {
|
||||
let headers = (idx * 8..idx * 8 + 8)
|
||||
.rev()
|
||||
.map(|idx| {
|
||||
html! {
|
||||
<th>{idx}</th>
|
||||
}
|
||||
})
|
||||
.collect::<Html>();
|
||||
let values = (idx * 8..idx * 8 + 8)
|
||||
.rev()
|
||||
.map(|idx| html! {
|
||||
<th class="bit" id={format!("bit{idx}")} onclick={bit_click(idx as _)}>{0}</th>
|
||||
})
|
||||
.collect::<Html>();
|
||||
|
||||
html! {
|
||||
<table>
|
||||
<tr class="header">
|
||||
{headers}
|
||||
</tr>
|
||||
<tr>
|
||||
{values}
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
})
|
||||
.collect::<Html>();
|
||||
|
||||
let on_input = move |ev: InputEvent| {
|
||||
let input_element = ev
|
||||
.target()
|
||||
.and_then(|t| t.dyn_into::<HtmlInputElement>().ok())
|
||||
.expect("on_input should have a target");
|
||||
let text = input_element.value().matches(HEX).collect::<String>();
|
||||
input_element.set_value(
|
||||
&input_element
|
||||
.value()
|
||||
.matches(ALLOWED_CHARS)
|
||||
.collect::<String>(),
|
||||
);
|
||||
let mut num: u32 = match text.strip_prefix("0x") {
|
||||
Some(hex) => u32::from_str_radix(hex, 16).unwrap_or_default(),
|
||||
None => text.parse().unwrap_or_default(),
|
||||
};
|
||||
let doc = web_sys::window().unwrap().document().unwrap();
|
||||
if num == 0 {
|
||||
doc.get_element_by_id("activated_wrap")
|
||||
.unwrap()
|
||||
.set_attribute("style", "visibility: hidden;")
|
||||
.unwrap();
|
||||
} else {
|
||||
doc.get_element_by_id("activated_wrap")
|
||||
.unwrap()
|
||||
.set_attribute("style", "")
|
||||
.unwrap();
|
||||
}
|
||||
let mut act: Vec<Range<u8>> = vec![];
|
||||
for idx in 0..32u8 {
|
||||
let cell = doc
|
||||
.get_element_by_id(format!("bit{idx}").as_str())
|
||||
.expect("missing bit")
|
||||
.dyn_into::<HtmlTableCellElement>()
|
||||
.expect(format!("bit{idx} should be a table cell").as_str());
|
||||
let set = match num & 1 {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if set {
|
||||
match act.iter_mut().last() {
|
||||
Some(last) => {
|
||||
if last.end + 1 == idx {
|
||||
last.end += 1;
|
||||
} else {
|
||||
act.push(idx..idx);
|
||||
}
|
||||
}
|
||||
None => act.push(idx..idx),
|
||||
}
|
||||
}
|
||||
cell.set_inner_text(match set {
|
||||
false => "0",
|
||||
true => "1",
|
||||
});
|
||||
num >>= 1;
|
||||
}
|
||||
if act.is_empty() {
|
||||
doc.get_element_by_id("activated")
|
||||
.unwrap()
|
||||
.set_text_content(None);
|
||||
return;
|
||||
}
|
||||
|
||||
let act_str = act
|
||||
.into_iter()
|
||||
.map(|act| {
|
||||
if act.is_empty() {
|
||||
act.start.to_string()
|
||||
} else {
|
||||
format!("{act:?}")
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
doc.get_element_by_id("activated")
|
||||
.unwrap()
|
||||
.set_text_content(Some(&act_str));
|
||||
};
|
||||
|
||||
let value_box = html! {
|
||||
<div id="input_box">
|
||||
<input id="bytes_in" oninput={on_input} />
|
||||
</div>
|
||||
};
|
||||
Self { bits, value_box }
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
let bits = self.bits.clone();
|
||||
let value_box = self.value_box.clone();
|
||||
|
||||
html! {
|
||||
<>
|
||||
{value_box}
|
||||
<br />
|
||||
<div id="bit_wrap">
|
||||
{bits}
|
||||
</div>
|
||||
<div id="activated_ext">
|
||||
<h2>{"activated"}</h2>
|
||||
</div>
|
||||
<div id="activated_wrap">
|
||||
<p id="activated"></p>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::default());
|
||||
|
||||
yew::Renderer::<BitsTable>::new().render();
|
||||
}
|
Loading…
Reference in New Issue