mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-31 13:52:29 +00:00
feat(web): add AES-CBC page with file encryption and UI improvements
This commit is contained in:
parent
651651780f
commit
721c712ba3
@ -1,4 +1,3 @@
|
|||||||
use crate::components::radio_button::RadioButton;
|
|
||||||
use cipher_factory::prelude::*;
|
use cipher_factory::prelude::*;
|
||||||
use leptos::{prelude::*, tachys::dom::event_target_value};
|
use leptos::{prelude::*, tachys::dom::event_target_value};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -11,7 +10,7 @@ pub fn ConfigurationSection(
|
|||||||
set_mode: WriteSignal<OperationMode>,
|
set_mode: WriteSignal<OperationMode>,
|
||||||
output_fmt: ReadSignal<OutputFormat>,
|
output_fmt: ReadSignal<OutputFormat>,
|
||||||
update_output: impl Fn(OutputFormat) + Copy + Send + 'static,
|
update_output: impl Fn(OutputFormat) + Copy + Send + 'static,
|
||||||
) -> AnyView {
|
) -> impl IntoView {
|
||||||
let handle_format_change = move |ev| {
|
let handle_format_change = move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
let fmt = OutputFormat::from_str(&val).unwrap_or_default();
|
let fmt = OutputFormat::from_str(&val).unwrap_or_default();
|
||||||
@ -39,39 +38,49 @@ pub fn ConfigurationSection(
|
|||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>"Configuration"</label>
|
<label>"Mode"</label>
|
||||||
<div class="controls-row">
|
<div class="controls-row">
|
||||||
<div class="radio-group">
|
<div class="mode-toggle">
|
||||||
<RadioButton value=OperationMode::Encrypt current=mode set_current=set_mode />
|
<button
|
||||||
<RadioButton value=OperationMode::Decrypt current=mode set_current=set_mode />
|
type="button"
|
||||||
|
class=move || if mode.get() == OperationMode::Encrypt { "mode-btn active" } else { "mode-btn" }
|
||||||
|
on:click=move |_| set_mode(OperationMode::Encrypt)
|
||||||
|
>
|
||||||
|
"Encrypt"
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class=move || if mode.get() == OperationMode::Decrypt { "mode-btn active" } else { "mode-btn" }
|
||||||
|
on:click=move |_| set_mode(OperationMode::Decrypt)
|
||||||
|
>
|
||||||
|
"Decrypt"
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{move || {
|
{move || {
|
||||||
if mode.get() != OperationMode::Decrypt {
|
if mode.get() != OperationMode::Decrypt {
|
||||||
return view! { <span></span> }.into_any();
|
return view! { <span></span> }.into_any();
|
||||||
}
|
}
|
||||||
view! {
|
view! {
|
||||||
<div class="format-controls-box">
|
<div class="format-select">
|
||||||
<div class="format-controls">
|
<label>"Output:"</label>
|
||||||
<label>"Output format:"</label>
|
<select
|
||||||
<select
|
on:wheel=handle_format_wheel
|
||||||
on:wheel=handle_format_wheel
|
on:change=handle_format_change
|
||||||
on:change=handle_format_change
|
prop:value=move || output_fmt.get().to_string()
|
||||||
prop:value=move || output_fmt.get().to_string()
|
>
|
||||||
>
|
{OutputFormat::iter()
|
||||||
{OutputFormat::iter()
|
.map(|fmt| {
|
||||||
.map(|fmt| {
|
view! {
|
||||||
view! {
|
<option value=fmt.to_string()>{fmt.to_string()}</option>
|
||||||
<option value=fmt.to_string()>{fmt.to_string()}</option>
|
}
|
||||||
}
|
})
|
||||||
})
|
.collect_view()}
|
||||||
.collect_view()}
|
</select>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
.into_any()
|
.into_any()
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}.into_any()
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,5 +6,4 @@ pub mod file_input;
|
|||||||
pub mod iv_input;
|
pub mod iv_input;
|
||||||
pub mod key_input;
|
pub mod key_input;
|
||||||
pub mod output_box;
|
pub mod output_box;
|
||||||
pub mod radio_button;
|
|
||||||
pub mod text_input;
|
pub mod text_input;
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
use cipher_factory::prelude::OperationMode;
|
|
||||||
use leptos::prelude::*;
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn RadioButton(
|
|
||||||
value: OperationMode,
|
|
||||||
current: ReadSignal<OperationMode>,
|
|
||||||
set_current: WriteSignal<OperationMode>,
|
|
||||||
) -> AnyView {
|
|
||||||
view! {
|
|
||||||
<div class="radio-button">
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="crypto-mode"
|
|
||||||
value=value.to_string()
|
|
||||||
prop:checked=move || current.get() == value
|
|
||||||
on:change=move |_| set_current.set(value)
|
|
||||||
/>
|
|
||||||
{value.to_string()}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
@ -701,11 +701,13 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input mode toggle (Text/File switcher)
|
// Mode toggle buttons (shared style for encrypt/decrypt and text/file)
|
||||||
|
.mode-toggle,
|
||||||
.input-mode-toggle {
|
.input-mode-toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
background: var(--bg-input);
|
background: var(--bg-input);
|
||||||
|
border: 1px solid var(--border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|
||||||
@ -713,10 +715,10 @@ main {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
padding: 4px 12px;
|
padding: 6px 14px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.8rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
@ -731,6 +733,41 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format selector (appears next to mode toggle in decrypt mode)
|
||||||
|
.format-select {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--bg-input);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
animation: fadeIn 0.2s ease-in-out;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
background: var(--bg-body);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--text-main);
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Textarea wrapper
|
// Textarea wrapper
|
||||||
.textarea-wrapper {
|
.textarea-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user