mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-29 19:41:39 +03:00
Merge branch 'v0.10.0' into dr/kv-overhaul
This commit is contained in:
commit
c75eac0527
4
kinode/packages/app-store/Cargo.lock
generated
4
kinode/packages/app-store/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -1890,7 +1890,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
4
kinode/packages/chess/Cargo.lock
generated
4
kinode/packages/chess/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -1813,7 +1813,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
4
kinode/packages/contacts/Cargo.lock
generated
4
kinode/packages/contacts/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -1774,7 +1774,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
4
kinode/packages/homepage/Cargo.lock
generated
4
kinode/packages/homepage/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -1763,7 +1763,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
34
kinode/packages/kns-indexer/Cargo.lock
generated
34
kinode/packages/kns-indexer/Cargo.lock
generated
@ -1313,7 +1313,7 @@ dependencies = [
|
||||
name = "get_block"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=ea8490a)",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -1761,29 +1761,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
"alloy-sol-macro",
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"http",
|
||||
"mime_guess",
|
||||
"rand",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
"url",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
@ -1810,7 +1788,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"hex",
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=ea8490a)",
|
||||
"kinode_process_lib",
|
||||
"process_macros",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
@ -1943,7 +1921,7 @@ dependencies = [
|
||||
name = "node_info"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=ea8490a)",
|
||||
"kinode_process_lib",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -2420,7 +2398,7 @@ dependencies = [
|
||||
name = "reset"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=ea8490a)",
|
||||
"kinode_process_lib",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -2824,7 +2802,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
name = "state"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=d97e012)",
|
||||
"kinode_process_lib",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -37,8 +37,6 @@ const KIMAP_FIRST_BLOCK: u64 = kimap::KIMAP_FIRST_BLOCK; // optimism
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
const KIMAP_FIRST_BLOCK: u64 = 1; // local
|
||||
|
||||
const CURRENT_VERSION: u32 = 1;
|
||||
|
||||
const MAX_PENDING_ATTEMPTS: u8 = 3;
|
||||
const SUBSCRIPTION_TIMEOUT: u64 = 60;
|
||||
const DELAY_MS: u64 = 1_000; // 1s
|
||||
@ -65,7 +63,7 @@ impl State {
|
||||
contract_address: eth::Address::from_str(KIMAP_ADDRESS).unwrap(),
|
||||
names: HashMap::new(),
|
||||
nodes: HashMap::new(),
|
||||
last_checkpoint_block: 0,
|
||||
last_checkpoint_block: KIMAP_FIRST_BLOCK,
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,15 +163,20 @@ enum KnsError {
|
||||
|
||||
call_init!(init);
|
||||
fn init(our: Address) {
|
||||
// state is checkpointed regularly (default every 5 minutes if new events are found)
|
||||
let state = State::load();
|
||||
println!("started");
|
||||
|
||||
if let Err(e) = main(our, state) {
|
||||
println!("fatal error: {e}");
|
||||
// state is checkpointed regularly (default every 5 minutes if new events are found)
|
||||
let mut state = State::load();
|
||||
|
||||
loop {
|
||||
if let Err(e) = main(&our, &mut state) {
|
||||
println!("fatal error: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main(our: Address, mut state: State) -> anyhow::Result<()> {
|
||||
fn main(our: &Address, state: &mut State) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
add_temp_hardcoded_tlzs(&mut state);
|
||||
|
||||
@ -214,7 +217,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
|
||||
// if they do time out, we try them again
|
||||
let eth_provider: eth::Provider = eth::Provider::new(state.chain_id, SUBSCRIPTION_TIMEOUT);
|
||||
|
||||
// subscribe to logs first, so no logs are m issed
|
||||
// subscribe to logs first, so no logs are missed
|
||||
eth_provider.subscribe_loop(1, mints_filter.clone(), 2, 0);
|
||||
eth_provider.subscribe_loop(2, notes_filter.clone(), 2, 0);
|
||||
|
||||
@ -230,14 +233,14 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
|
||||
print_to_terminal(2, &format!("syncing old logs from block: {}", last_block));
|
||||
fetch_and_process_logs(
|
||||
ð_provider,
|
||||
&mut state,
|
||||
state,
|
||||
mints_filter.clone(),
|
||||
&mut pending_notes,
|
||||
&mut last_block,
|
||||
);
|
||||
fetch_and_process_logs(
|
||||
ð_provider,
|
||||
&mut state,
|
||||
state,
|
||||
notes_filter.clone(),
|
||||
&mut pending_notes,
|
||||
&mut last_block,
|
||||
@ -266,12 +269,13 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
|
||||
source,
|
||||
body,
|
||||
capabilities,
|
||||
expects_response,
|
||||
..
|
||||
} = message
|
||||
else {
|
||||
if tick {
|
||||
handle_eth_message(
|
||||
&mut state,
|
||||
state,
|
||||
ð_provider,
|
||||
tick,
|
||||
checkpoint,
|
||||
@ -287,7 +291,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
|
||||
|
||||
if source.node() == our.node() && source.process == "eth:distro:sys" {
|
||||
handle_eth_message(
|
||||
&mut state,
|
||||
state,
|
||||
ð_provider,
|
||||
tick,
|
||||
checkpoint,
|
||||
@ -298,48 +302,41 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
|
||||
&mut last_block,
|
||||
)?;
|
||||
} else {
|
||||
match serde_json::from_slice(&body)? {
|
||||
let response_body = match serde_json::from_slice(&body)? {
|
||||
IndexerRequest::NamehashToName(NamehashToNameRequest { ref hash, .. }) => {
|
||||
// TODO: make sure we've seen the whole block, while actually
|
||||
// sending a response to the proper place.
|
||||
Response::new()
|
||||
.body(IndexerResponse::Name(state.names.get(hash).cloned()))
|
||||
.send()?;
|
||||
IndexerResponse::Name(state.names.get(hash).cloned())
|
||||
}
|
||||
IndexerRequest::NodeInfo(NodeInfoRequest { ref name, .. }) => {
|
||||
Response::new()
|
||||
.body(IndexerResponse::NodeInfo(
|
||||
state.nodes.get(name).map(|n| n.clone().into()),
|
||||
))
|
||||
.send()?;
|
||||
IndexerResponse::NodeInfo(state.nodes.get(name).map(|n| n.clone().into()))
|
||||
}
|
||||
IndexerRequest::Reset => {
|
||||
// check for root capability
|
||||
let root_cap = Capability {
|
||||
issuer: our.clone(),
|
||||
params: "{\"root\":true}".to_string(),
|
||||
};
|
||||
if source.package_id() != our.package_id() {
|
||||
if !capabilities.contains(&root_cap) {
|
||||
Response::new()
|
||||
.body(IndexerResponse::Reset(ResetResult::Err(
|
||||
ResetError::NoRootCap,
|
||||
)))
|
||||
.send()?;
|
||||
continue;
|
||||
}
|
||||
let root_cap = Capability::new(our.clone(), "{\"root\":true}");
|
||||
if source.package_id() != our.package_id() && !capabilities.contains(&root_cap)
|
||||
{
|
||||
IndexerResponse::Reset(ResetResult::Err(ResetError::NoRootCap))
|
||||
} else {
|
||||
// reload state fresh - this will create new db
|
||||
state.reset();
|
||||
IndexerResponse::Reset(ResetResult::Success)
|
||||
}
|
||||
// reload state fresh - this will create new db
|
||||
state.reset();
|
||||
}
|
||||
IndexerRequest::GetState(_) => IndexerResponse::GetState(state.clone().into()),
|
||||
};
|
||||
|
||||
if let IndexerResponse::Reset(ResetResult::Success) = response_body {
|
||||
println!("resetting state");
|
||||
if expects_response.is_some() {
|
||||
Response::new()
|
||||
.body(IndexerResponse::Reset(ResetResult::Success))
|
||||
.send()?;
|
||||
panic!("resetting state, restarting!");
|
||||
}
|
||||
IndexerRequest::GetState(_) => {
|
||||
Response::new()
|
||||
.body(IndexerResponse::GetState(state.clone().into()))
|
||||
.send()?;
|
||||
return Ok(());
|
||||
} else {
|
||||
if expects_response.is_some() {
|
||||
Response::new().body(response_body).send()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
kinode/packages/settings/Cargo.lock
generated
4
kinode/packages/settings/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -1751,7 +1751,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
87
kinode/packages/settings/api/settings:sys-v0.wit
Normal file
87
kinode/packages/settings/api/settings:sys-v0.wit
Normal file
@ -0,0 +1,87 @@
|
||||
interface settings {
|
||||
variant request {
|
||||
/// lazy-load-blob: none.
|
||||
hi(hi-request),
|
||||
/// lazy-load-blob: none.
|
||||
peer-id(string),
|
||||
/// lazy-load-blob: none.
|
||||
eth-config(eth-config-request),
|
||||
/// lazy-load-blob: none.
|
||||
shutdown,
|
||||
/// lazy-load-blob: none.
|
||||
reset,
|
||||
/// lazy-load-blob: none.
|
||||
kill-process(string),
|
||||
/// lazy-load-blob: none.
|
||||
set-stylesheet(string),
|
||||
}
|
||||
|
||||
type response = result<option<settings-data>, settings-error>;
|
||||
|
||||
record hi-request {
|
||||
node: string,
|
||||
content: string,
|
||||
timeout: u64,
|
||||
}
|
||||
|
||||
/// A subset of the actions that can be taken on the `eth`
|
||||
/// runtime module. These are mostly used by the settings frontend.
|
||||
variant eth-config-request {
|
||||
add-provider(provider-config),
|
||||
remove-provider(tuple<u64, string>),
|
||||
set-public,
|
||||
set-private,
|
||||
allow-node(string),
|
||||
unallow-node(string),
|
||||
deny-node(string),
|
||||
undeny-node(string),
|
||||
}
|
||||
|
||||
/// This will be converted to the ProviderConfig type used in `eth`.
|
||||
/// `trusted` in ProviderConfig will always be true.
|
||||
/// Rather than provide full NodeOrRpcUrl, the settings
|
||||
/// process will fetch KNS update for node-providers
|
||||
/// and convert this to the type used in `eth`.
|
||||
record provider-config {
|
||||
chain-id: u64,
|
||||
node-or-rpc-url: node-or-rpc-url,
|
||||
}
|
||||
|
||||
variant node-or-rpc-url {
|
||||
node(string),
|
||||
rpc-url(string),
|
||||
}
|
||||
|
||||
variant settings-data {
|
||||
peer-id(identity),
|
||||
}
|
||||
|
||||
record identity {
|
||||
name: string,
|
||||
networking-key: string,
|
||||
routing: node-routing,
|
||||
}
|
||||
|
||||
variant node-routing {
|
||||
routers(list<string>),
|
||||
direct(direct),
|
||||
}
|
||||
|
||||
record direct {
|
||||
ip: string,
|
||||
ports: list<tuple<string, u16>>,
|
||||
}
|
||||
|
||||
variant settings-error {
|
||||
hi-timeout,
|
||||
hi-offline,
|
||||
kernel-nonresponsive,
|
||||
malformed-request,
|
||||
state-fetch-failed,
|
||||
}
|
||||
}
|
||||
|
||||
world settings-sys-v0 {
|
||||
import settings;
|
||||
include process-v1;
|
||||
}
|
@ -165,7 +165,7 @@
|
||||
margin-left: 6px;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="/settings:settings:sys/assets/index-CwCaX2Ut.js"></script>
|
||||
<script type="module" crossorigin src="/settings:settings:sys/assets/index-CepIUSQF.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/settings:settings:sys/assets/index-iGirBDd0.css">
|
||||
</head>
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
use crate::kinode::process::settings::{
|
||||
Direct, EthConfigRequest as SettingsEthConfigAction, HiRequest, Identity as SettingsIdentity,
|
||||
NodeOrRpcUrl as SettingsNodeOrRpcUrl, NodeRouting as SettingsNodeRouting,
|
||||
Request as SettingsRequest, Response as SettingsResponse, SettingsData, SettingsError,
|
||||
};
|
||||
use kinode_process_lib::{
|
||||
await_message, call_init, eth, get_blob, get_capability, homepage, http, kernel_types, kimap,
|
||||
net, println, Address, Capability, LazyLoadBlob, Message, NodeId, ProcessId, Request, Response,
|
||||
@ -8,36 +13,12 @@ use std::{collections::HashMap, vec};
|
||||
|
||||
const ICON: &str = include_str!("icon");
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum SettingsRequest {
|
||||
Hi {
|
||||
node: NodeId,
|
||||
content: String,
|
||||
timeout: u64,
|
||||
},
|
||||
PeerId(NodeId),
|
||||
EthConfig(eth::EthConfigAction),
|
||||
Shutdown,
|
||||
Reset,
|
||||
KillProcess(ProcessId),
|
||||
SetStylesheet(String),
|
||||
}
|
||||
|
||||
type SettingsResponse = Result<Option<SettingsData>, SettingsError>;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum SettingsData {
|
||||
PeerId(net::Identity),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum SettingsError {
|
||||
HiTimeout,
|
||||
HiOffline,
|
||||
KernelNonresponsive,
|
||||
MalformedRequest,
|
||||
StateFetchFailed,
|
||||
}
|
||||
wit_bindgen::generate!({
|
||||
path: "target/wit",
|
||||
world: "settings-sys-v0",
|
||||
generate_unused_types: true,
|
||||
additional_derives: [serde::Deserialize, serde::Serialize],
|
||||
});
|
||||
|
||||
/// never gets persisted
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -214,11 +195,6 @@ impl SettingsState {
|
||||
}
|
||||
}
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "target/wit",
|
||||
world: "process-v1",
|
||||
});
|
||||
|
||||
call_init!(initialize);
|
||||
fn initialize(our: Address) {
|
||||
// Grab our state, then enter the main event loop.
|
||||
@ -387,11 +363,11 @@ fn handle_settings_request(
|
||||
request: SettingsRequest,
|
||||
) -> SettingsResponse {
|
||||
match request {
|
||||
SettingsRequest::Hi {
|
||||
SettingsRequest::Hi(HiRequest {
|
||||
node,
|
||||
content,
|
||||
timeout,
|
||||
} => {
|
||||
}) => {
|
||||
if let Err(SendError { kind, .. }) = Request::to((&node, "net", "distro", "sys"))
|
||||
.body(content.into_bytes())
|
||||
.send_and_await_response(timeout)
|
||||
@ -421,7 +397,23 @@ fn handle_settings_request(
|
||||
Ok(msg) => match rmp_serde::from_slice::<net::NetResponse>(msg.body()) {
|
||||
Ok(net::NetResponse::Peer(Some(peer))) => {
|
||||
println!("got peer info: {peer:?}");
|
||||
return Ok(Some(SettingsData::PeerId(peer)));
|
||||
// convert Identity to SettingsIdentity
|
||||
let settings_identity = SettingsIdentity {
|
||||
name: peer.name,
|
||||
networking_key: peer.networking_key,
|
||||
routing: match peer.routing {
|
||||
net::NodeRouting::Direct { ip, ports } => {
|
||||
SettingsNodeRouting::Direct(Direct {
|
||||
ip,
|
||||
ports: ports.into_iter().map(|(p, q)| (p, q)).collect(),
|
||||
})
|
||||
}
|
||||
net::NodeRouting::Routers(routers) => {
|
||||
SettingsNodeRouting::Routers(routers)
|
||||
}
|
||||
},
|
||||
};
|
||||
return Ok(Some(SettingsData::PeerId(settings_identity)));
|
||||
}
|
||||
Ok(net::NetResponse::Peer(None)) => {
|
||||
println!("peer not found");
|
||||
@ -436,7 +428,9 @@ fn handle_settings_request(
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsRequest::EthConfig(action) => {
|
||||
SettingsRequest::EthConfig(settings_eth_config_request) => {
|
||||
// convert SettingsEthConfigRequest to EthConfigRequest
|
||||
let action = eth_config_convert(settings_eth_config_request)?;
|
||||
match Request::to(("our", "eth", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&action).unwrap())
|
||||
.send_and_await_response(30)
|
||||
@ -478,8 +472,11 @@ fn handle_settings_request(
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
SettingsRequest::KillProcess(pid) => {
|
||||
SettingsRequest::KillProcess(pid_str) => {
|
||||
// kill a process
|
||||
let Ok(pid) = pid_str.parse::<ProcessId>() else {
|
||||
return SettingsResponse::Err(SettingsError::MalformedRequest);
|
||||
};
|
||||
if let Err(_) = Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kernel_types::KernelCommand::KillProcess(pid)).unwrap())
|
||||
.send_and_await_response(30)
|
||||
@ -517,6 +514,45 @@ fn handle_settings_request(
|
||||
SettingsResponse::Ok(None)
|
||||
}
|
||||
|
||||
fn eth_config_convert(
|
||||
settings_eth_config_request: SettingsEthConfigAction,
|
||||
) -> Result<eth::EthConfigAction, SettingsError> {
|
||||
match settings_eth_config_request {
|
||||
SettingsEthConfigAction::AddProvider(settings_provider_config) => {
|
||||
Ok(eth::EthConfigAction::AddProvider(eth::ProviderConfig {
|
||||
chain_id: settings_provider_config.chain_id,
|
||||
provider: match settings_provider_config.node_or_rpc_url {
|
||||
SettingsNodeOrRpcUrl::Node(node_str) => {
|
||||
// the eth module does not actually need the full routing info
|
||||
// so we can just use the name as the kns update
|
||||
eth::NodeOrRpcUrl::Node {
|
||||
kns_update: net::KnsUpdate {
|
||||
name: node_str,
|
||||
public_key: "".to_string(),
|
||||
ips: vec![],
|
||||
ports: std::collections::BTreeMap::new(),
|
||||
routers: vec![],
|
||||
},
|
||||
use_as_provider: true,
|
||||
}
|
||||
}
|
||||
SettingsNodeOrRpcUrl::RpcUrl(url) => eth::NodeOrRpcUrl::RpcUrl(url),
|
||||
},
|
||||
trusted: true,
|
||||
}))
|
||||
}
|
||||
SettingsEthConfigAction::RemoveProvider((chain_id, provider_str)) => Ok(
|
||||
eth::EthConfigAction::RemoveProvider((chain_id, provider_str)),
|
||||
),
|
||||
SettingsEthConfigAction::SetPublic => Ok(eth::EthConfigAction::SetPublic),
|
||||
SettingsEthConfigAction::SetPrivate => Ok(eth::EthConfigAction::SetPrivate),
|
||||
SettingsEthConfigAction::AllowNode(node) => Ok(eth::EthConfigAction::AllowNode(node)),
|
||||
SettingsEthConfigAction::UnallowNode(node) => Ok(eth::EthConfigAction::UnallowNode(node)),
|
||||
SettingsEthConfigAction::DenyNode(node) => Ok(eth::EthConfigAction::DenyNode(node)),
|
||||
SettingsEthConfigAction::UndenyNode(node) => Ok(eth::EthConfigAction::UndenyNode(node)),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_widget(state: &SettingsState) -> String {
|
||||
let owner_string = state.our_owner.to_string();
|
||||
let tba_string = state.our_tba.to_string();
|
||||
|
@ -68,7 +68,7 @@ function App() {
|
||||
}, []);
|
||||
|
||||
const apiCall = async (body: any) => {
|
||||
await fetch(APP_PATH, {
|
||||
return await fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
@ -106,6 +106,7 @@ function App() {
|
||||
const handlePeerPing = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const form = e.currentTarget;
|
||||
const response = await fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@ -117,14 +118,58 @@ function App() {
|
||||
}
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data === null) {
|
||||
e.currentTarget.reset();
|
||||
form.reset();
|
||||
try {
|
||||
const data = await response.json();
|
||||
if (data === null) {
|
||||
setPeerPingResponse("ping successful!");
|
||||
} else if (data === "HiTimeout") {
|
||||
setPeerPingResponse("node timed out");
|
||||
} else if (data === "HiOffline") {
|
||||
setPeerPingResponse("node is offline");
|
||||
}
|
||||
} catch (err) {
|
||||
setPeerPingResponse("ping successful!");
|
||||
} else if (data === "HiTimeout") {
|
||||
setPeerPingResponse("node timed out");
|
||||
} else if (data === "HiOffline") {
|
||||
setPeerPingResponse("node is offline");
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddEthProvider = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const form = e.currentTarget;
|
||||
const response = await apiCall({
|
||||
"EthConfig": {
|
||||
"AddProvider": {
|
||||
chain_id: Number(formData.get('chain-id')),
|
||||
node_or_rpc_url: { "RpcUrl": formData.get('rpc-url') as string }
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
} catch (err) {
|
||||
form.reset();
|
||||
// this is actually a success
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleRemoveEthProvider = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const form = e.currentTarget;
|
||||
const response = await apiCall({
|
||||
"EthConfig": {
|
||||
"RemoveProvider": [Number(formData.get('chain-id')), formData.get('rpc-url') as string]
|
||||
}
|
||||
});
|
||||
try {
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
} catch (err) {
|
||||
form.reset();
|
||||
// this is actually a success
|
||||
}
|
||||
};
|
||||
|
||||
@ -149,13 +194,14 @@ function App() {
|
||||
<div className="mt-16 flex flex-col justify-start">
|
||||
<button
|
||||
onClick={handleShutdown}
|
||||
className="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded w-full mb-8"
|
||||
id="shutdown"
|
||||
>
|
||||
Shutdown Node
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="bg-yellow-500 hover:bg-yellow-600 text-white font-bold py-2 px-4 rounded w-full"
|
||||
>
|
||||
Reset KNS State
|
||||
</button>
|
||||
@ -182,12 +228,12 @@ function App() {
|
||||
<article id="eth-rpc-providers">
|
||||
<h2>ETH RPC providers</h2>
|
||||
<article id="provider-edits">
|
||||
<form id="add-eth-provider">
|
||||
<form id="add-eth-provider" onSubmit={handleAddEthProvider}>
|
||||
<input type="number" name="chain-id" placeholder="1" />
|
||||
<input type="text" name="rpc-url" placeholder="wss://rpc-url.com" />
|
||||
<button type="submit">add provider</button>
|
||||
</form>
|
||||
<form id="remove-eth-provider">
|
||||
<form id="remove-eth-provider" onSubmit={handleRemoveEthProvider}>
|
||||
<input type="number" name="chain-id" placeholder="1" />
|
||||
<input type="text" name="rpc-url" placeholder="wss://rpc-url.com" />
|
||||
<button type="submit">remove provider</button>
|
||||
|
2
kinode/packages/terminal/Cargo.lock
generated
2
kinode/packages/terminal/Cargo.lock
generated
@ -1919,7 +1919,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
4
kinode/packages/tester/Cargo.lock
generated
4
kinode/packages/tester/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -1751,7 +1751,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ea8490a#ea8490aba4837d04243d84b4f5b76fbefb498007"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=ef78f0e#ef78f0eb18d00826874258131a1bf471e55796f0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -412,7 +412,7 @@ async fn handle_remote_request(
|
||||
}
|
||||
_ => {
|
||||
// if we can't parse this to a NetAction, treat it as a hello and print it,
|
||||
// and respond with a simple "delivered" response
|
||||
// and respond with a simple "ack" response
|
||||
utils::parse_hello_message(
|
||||
&ext.our,
|
||||
&km,
|
||||
|
@ -415,7 +415,7 @@ pub async fn parse_hello_message(
|
||||
.message(Message::Response((
|
||||
Response {
|
||||
inherit: false,
|
||||
body: "delivered".as_bytes().to_vec(),
|
||||
body: b"ack".to_vec(),
|
||||
metadata: None,
|
||||
capabilities: vec![],
|
||||
},
|
||||
|
@ -11,6 +11,7 @@ import KinodeHome from "./pages/KinodeHome"
|
||||
import ImportKeyfile from "./pages/ImportKeyfile";
|
||||
import { UnencryptedIdentity } from "./lib/types";
|
||||
import Header from "./components/Header";
|
||||
import ProgressBar from "./components/ProgressBar";
|
||||
|
||||
function App() {
|
||||
const params = useParams()
|
||||
@ -104,13 +105,33 @@ function App() {
|
||||
? <Navigate to="/login" replace />
|
||||
: <KinodeHome {...props} />
|
||||
} />
|
||||
<Route path="/commit-os-name" element={<CommitDotOsName {...props} />} />
|
||||
<Route path="/mint-os-name" element={<MintDotOsName {...props} />} />
|
||||
<Route path="/set-password" element={<SetPassword {...props} />} />
|
||||
<Route path="/commit-os-name" element={
|
||||
<>
|
||||
<ProgressBar knsName={knsName} />
|
||||
<CommitDotOsName {...props} />
|
||||
</>
|
||||
} />
|
||||
<Route path="/mint-os-name" element={
|
||||
<>
|
||||
<ProgressBar knsName={knsName} />
|
||||
<MintDotOsName {...props} />
|
||||
</>
|
||||
} />
|
||||
<Route path="/set-password" element={
|
||||
<>
|
||||
<ProgressBar knsName={knsName} />
|
||||
<SetPassword {...props} />
|
||||
</>
|
||||
} />
|
||||
<Route path="/reset" element={<ResetName {...props} />} />
|
||||
<Route path="/import-keyfile" element={<ImportKeyfile {...props} />} />
|
||||
<Route path="/login" element={<Login {...props} />} />
|
||||
<Route path="/custom-register" element={<MintCustom {...props} />} />
|
||||
<Route path="/custom-register" element={
|
||||
<>
|
||||
<ProgressBar knsName={knsName} />
|
||||
<MintCustom {...props} />
|
||||
</>
|
||||
} />
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
|
68
kinode/src/register-ui/src/components/ProgressBar.tsx
Normal file
68
kinode/src/register-ui/src/components/ProgressBar.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
|
||||
const steps = [
|
||||
{ path: '/', label: 'Home' },
|
||||
{ path: '/commit-os-name', label: 'Choose Name' },
|
||||
{ path: '/mint-os-name', label: 'Mint Name' },
|
||||
{ path: '/set-password', label: 'Set Password' },
|
||||
];
|
||||
|
||||
interface ProgressBarProps {
|
||||
knsName: string;
|
||||
}
|
||||
|
||||
const ProgressBar = ({ knsName }: ProgressBarProps) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const currentStepIndex = steps.findIndex(step => step.path === location.pathname);
|
||||
|
||||
const isStepAccessible = (index: number) => {
|
||||
// Home is always accessible
|
||||
if (index === 0) return true;
|
||||
|
||||
if (knsName && index <= 2) return true;
|
||||
|
||||
// Otherwise only allow going back
|
||||
return index <= currentStepIndex;
|
||||
};
|
||||
|
||||
const handleStepClick = (path: string, index: number) => {
|
||||
if (isStepAccessible(index)) {
|
||||
navigate(path);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="progress-container">
|
||||
<div className="progress-bar">
|
||||
{steps.map((step, index) => {
|
||||
const accessible = isStepAccessible(index);
|
||||
return (
|
||||
<div key={step.path} className="step-wrapper">
|
||||
<div
|
||||
className={`step ${index <= currentStepIndex ? 'active' : ''} ${
|
||||
index < currentStepIndex ? 'completed' : ''
|
||||
} ${accessible ? 'clickable' : 'disabled'}`}
|
||||
onClick={() => handleStepClick(step.path, index)}
|
||||
>
|
||||
<div className="step-number">{index}</div>
|
||||
<div className="step-label">{step.label}</div>
|
||||
</div>
|
||||
{index < steps.length - 1 && (
|
||||
<div className={`connector ${index < currentStepIndex ? 'active' : ''}`} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{knsName && (
|
||||
<div className="selected-name">
|
||||
Selected name: <span>{knsName}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressBar;
|
@ -1,253 +1,437 @@
|
||||
/* forms */
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
padding: 0.75rem;
|
||||
border: 2px solid var(--orange);
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid var(--orange);
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--dark-orange);
|
||||
box-shadow: 0 0 0 3px rgba(255, 79, 0, 0.2);
|
||||
outline: none;
|
||||
border-color: var(--dark-orange);
|
||||
box-shadow: 0 0 0 3px rgba(255, 79, 0, 0.2);
|
||||
}
|
||||
|
||||
/* tooltips */
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tooltip-text {
|
||||
font-size: 0.8em;
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: #555;
|
||||
color: var(--off-white);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
font-size: 0.8em;
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: #555;
|
||||
color: var(--off-white);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.tooltip-top .tooltip-text {
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
}
|
||||
|
||||
.tooltip-bottom .tooltip-text {
|
||||
top: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
top: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltip-text {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: light-dark(var(--off-white), var(--tasteful-dark));
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
background-color: light-dark(var(--off-white), var(--tasteful-dark));
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 1rem;
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 1rem;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.connect-wallet {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 4rem;
|
||||
/* Add some top padding to account for the fixed header */
|
||||
padding-top: 4rem;
|
||||
/* Add some top padding to account for the fixed header */
|
||||
}
|
||||
|
||||
.enter-kns-name {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kns-input {
|
||||
flex-grow: 1;
|
||||
padding: 0.5rem;
|
||||
font-size: 1.2em;
|
||||
border: 1px solid var(--gray);
|
||||
border-radius: 4px 0 0 4px;
|
||||
flex-grow: 1;
|
||||
padding: 0.5rem;
|
||||
font-size: 1.2em;
|
||||
border: 1px solid var(--gray);
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.kns-suffix {
|
||||
padding: 0.5rem;
|
||||
background-color: var(--blue);
|
||||
border: 1px solid var(--tasteful-dark);
|
||||
border-left: none;
|
||||
border-radius: 0 4px 4px 0;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--blue);
|
||||
border: 1px solid var(--tasteful-dark);
|
||||
border-left: none;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--ansi-red);
|
||||
margin-top: 0.5rem;
|
||||
overflow-wrap: break-word;
|
||||
color: var(--ansi-red);
|
||||
margin-top: 0.5rem;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.direct-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox-container input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
border: 2px solid var(--orange);
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
border: 2px solid var(--orange);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.checkbox-container:hover input~.checkmark {
|
||||
background-color: var(--gray);
|
||||
background-color: var(--gray);
|
||||
}
|
||||
|
||||
.checkbox-container input:checked~.checkmark {
|
||||
background-color: var(--orange);
|
||||
background-color: var(--orange);
|
||||
}
|
||||
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.checkbox-container input:checked~.checkmark:after {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.checkbox-container .checkmark:after {
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid var(--off-white);
|
||||
border-width: 0 3px 3px 0;
|
||||
transform: rotate(45deg);
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid var(--off-white);
|
||||
border-width: 0 3px 3px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
margin-left: 10px;
|
||||
font-size: 0.9em;
|
||||
margin-left: 10px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.file-input-label {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-input-label .button {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0.3rem 0.8rem;
|
||||
font-size: 0.9em;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0.3rem 0.8rem;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.file-input-label:hover .button {
|
||||
background-color: var(--dark-orange);
|
||||
color: var(--off-white);
|
||||
background-color: var(--dark-orange);
|
||||
color: var(--off-white);
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button.back {
|
||||
float: left;
|
||||
width: auto !important;
|
||||
padding: 5px !important;
|
||||
border: none !important;
|
||||
float: left;
|
||||
width: auto !important;
|
||||
padding: 5px !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Progress Bar Styles */
|
||||
.progress-container {
|
||||
font-family: var(--font-family-main);
|
||||
margin-top: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
max-width: 600px;
|
||||
background: linear-gradient(145deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 79, 0, 0.2),
|
||||
rgba(255, 79, 0, 0.2),
|
||||
transparent);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.step-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 2;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.step.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.step.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.step.disabled:hover .step-number {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.step.clickable:not(.active):hover .step-number {
|
||||
background: rgba(255, 79, 0, 0.1);
|
||||
}
|
||||
|
||||
.step.clickable.completed:hover .step-number {
|
||||
background: var(--dark-orange);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.step:hover .step-number {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 12px rgba(255, 79, 0, 0.3);
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 50%;
|
||||
background: var(--off-white);
|
||||
border: 2px solid var(--orange);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
color: var(--orange);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-number::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -2px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step.active .step-number {
|
||||
background: var(--orange);
|
||||
color: var(--off-white);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.step.active .step-number::after {
|
||||
border-color: rgba(255, 79, 0, 0.3);
|
||||
inset: -4px;
|
||||
}
|
||||
|
||||
.step.completed .step-number {
|
||||
background: var(--dark-orange);
|
||||
border-color: var(--dark-orange);
|
||||
color: var(--off-white);
|
||||
}
|
||||
|
||||
.step-label {
|
||||
margin-top: 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--orange);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
opacity: 0.85;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.step.active .step-label {
|
||||
opacity: 1;
|
||||
font-weight: 600;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.connector {
|
||||
flex: 1;
|
||||
height: 2px;
|
||||
background: rgba(255, 79, 0, 0.15);
|
||||
position: relative;
|
||||
top: -8px;
|
||||
transform-origin: left center;
|
||||
margin: 0 0.25rem;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.connector.active {
|
||||
background: linear-gradient(90deg, var(--orange), var(--dark-orange));
|
||||
animation: fillLine 0.4s ease-out forwards;
|
||||
}
|
||||
|
||||
.selected-name {
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text);
|
||||
opacity: 0.9;
|
||||
padding: 0.75rem;
|
||||
background: rgba(255, 79, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.selected-name span {
|
||||
font-weight: 600;
|
||||
color: var(--orange);
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
|
||||
@keyframes fillLine {
|
||||
from {
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user