Remove WASM (#3148)

liberal party took over, more cuts
This commit is contained in:
Firstyear 2024-10-26 17:19:13 +10:00 committed by GitHub
parent 5c9eb87a75
commit 2e6d940691
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
168 changed files with 267 additions and 26027 deletions

View file

@ -1,56 +0,0 @@
---
name: WASM Testing
# Trigger the workflow on push or pull request
"on": [push, pull_request]
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
wasm_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# - name: Check arch
# run: |
# uname -a
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.5
with:
version: "v0.4.2"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install wasm-pack
run: cargo install wasm-pack
- name: Install dependencies
run: |
scripts/install_ubuntu_dependencies.sh
# https://github.com/browser-actions/setup-chrome
- name: Install Chrome Headless
uses: browser-actions/setup-chrome@latest
with:
chrome-version: latest
# https://github.com/marketplace/actions/setup-chromedriver
- uses: nanasess/setup-chromedriver@v2
# with:
# Optional: do not specify to match Chrome's version
# chromedriver-version: '88.0.4324.96'
# - run: make webui
- name: "Run wasm-pack test"
# https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/browsers.html
run: |
rustup target add wasm32-unknown-unknown
make webui/test
- name: "Run webdriver tests"
run: |
chromedriver &
cargo test -p kanidmd_testkit --features webdriver test_webdriver
env:
DISPLAY: ":99"

1
.gitignore vendored
View file

@ -19,7 +19,6 @@ tools/orca/example_profiles/small/orca-edited.toml
*.d.ts
server/web_ui/*/pkg/*.js
server/web_ui/*/pkg/*.wasm
# kanidm simple packaging
deployment-config/

851
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -23,10 +23,6 @@ members = [
"unix_integration/nss_kanidm",
"unix_integration/pam_kanidm",
"unix_integration/resolver",
"server/web_ui/admin",
"server/web_ui/login_flows",
"server/web_ui/user",
"server/web_ui/shared",
"server/daemon",
"server/lib",
"server/lib-macros",
@ -169,7 +165,6 @@ compact_jwt = { version = "^0.4.2", default-features = false }
concread = "^0.5.3"
cron = "0.12.1"
crossbeam = "0.8.4"
criterion = "^0.5.1"
csv = "1.3.0"
dialoguer = "0.10.4"
dhat = "0.3.3"
@ -178,11 +173,8 @@ fernet = "^0.2.1"
filetime = "^0.2.24"
fs4 = "^0.8.3"
futures = "^0.3.31"
futures-concurrency = "^3.1.0"
futures-util = { version = "^0.3.30", features = ["sink"] }
gix = { version = "0.64.0", default-features = false }
gloo = "^0.8.1"
gloo-utils = "0.2.0"
hashbrown = { version = "0.14.3", features = ["serde", "inline-more", "ahash"] }
hex = "^0.4.3"
http = "1.1.0"
@ -198,7 +190,6 @@ image = { version = "0.24.9", default-features = false, features = [
] }
itertools = "0.13.0"
enum-iterator = "2.1.0"
js-sys = "^0.3.70"
kanidmd_web_ui_shared = { path = "./server/web_ui/shared" }
# REMOVE this
lazy_static = "^1.5.0"
@ -247,7 +238,6 @@ reqwest = { version = "0.12.8", default-features = false, features = [
"gzip",
"rustls-tls-native-roots",
] }
rpassword = "^7.3.1"
rusqlite = { version = "^0.28.0", features = ["array", "bundled"] }
rustls = { version = "0.23.13", default-features = false, features = [
"aws_lc_rs",
@ -259,7 +249,6 @@ serde = "^1.0.210"
serde_cbor = { version = "0.12.0-dev", package = "serde_cbor_2" }
serde_json = "^1.0.132"
serde_urlencoded = "^0.7.1"
serde-wasm-bindgen = "0.5"
serde_with = "3.11.0"
sha-crypt = "0.5.0"
sha2 = "0.10.8"
@ -283,7 +272,6 @@ tracing = { version = "^0.1.40", features = [
"max_level_trace",
"release_max_level_debug",
] }
# tracing = { version = "^0.1.37" }
tracing-subscriber = { version = "^0.3.18", features = ["env-filter"] }
tracing-forest = "^0.1.6"
@ -293,10 +281,6 @@ utoipa = { version = "4.2.0", features = ["url", "uuid"] }
utoipa-swagger-ui = "6.0.0"
uuid = "^1.11.0"
wasm-bindgen = "^0.2.95"
wasm-bindgen-futures = "^0.4.45"
wasm-bindgen-test = "0.3.45"
webauthn-authenticator-rs = { version = "0.5.0", features = [
"softpasskey",
"softtoken",
@ -306,14 +290,12 @@ webauthn-rs = { version = "0.5.0", features = ["preview-features"] }
webauthn-rs-core = "0.5.0"
webauthn-rs-proto = "0.5.0"
web-sys = "^0.3.72"
whoami = "^1.5.2"
walkdir = "2"
x509-cert = "0.2.5"
yew = "^0.20.0"
yew-router = "^0.17.0"
zxcvbn = "^2.2.2"
nonempty = "0.8.1"

View file

@ -316,14 +316,6 @@ cert/clean:
rm -f /tmp/kanidm/ca.txt*
rm -f /tmp/kanidm/ca.{cnf,srl,srl.old}
.PHONY: webui
webui: ## Build the WASM web frontend
cd server/web_ui && ./build_wasm.sh
.PHONY: webui/test
webui/test: ## Run wasm-pack test
cd server/web_ui/shared/ && wasm-pack test --headless --chrome --mode no-install
.PHONY: rust/coverage
coverage/test: ## Run coverage tests
coverage/test:

View file

@ -114,21 +114,3 @@ When a service like sudo, sshd, su, etc. wants to authenticate someone, it opens
that service, then performs authentication according to the modules defined in the pam.d config. For
example, if you run `ls -al /etc/pam.d /usr/etc/pam.d` in SUSE, you can see the services and their
respective pam.d config.
## Troubleshooting builds
### WASM Build failures due to "Error: Not able to find or install a local wasm-bindgen."
This seems to relate to a version mismatch error in `wasm-pack` as seen in
[this thread in the wasm-pack repository](https://github.com/rustwasm/wasm-pack/issues/1138).
Try reinstalling `wasm-bindgen-cli` by running the following (the `--force` is important):
```shell
cargo install --force wasm-bindgen-cli
```
Or reinstalling `wasm-pack` similarly.
If that doesn't work, try running the build with the `RUST_LOG=debug` environment variable to
investigate further.

View file

@ -98,9 +98,6 @@ git and compiler tools. You should install this first.
You will need [rustup](https://rustup.rs/) to install a Rust toolchain.
To build the Web UI you'll need [wasm-pack](https://rustwasm.github.io/wasm-pack/)
(`cargo install wasm-pack`).
### SUSE / OpenSUSE
You will need to install rustup and our build dependencies with:
@ -326,31 +323,6 @@ For example, this will set the CPU flags to "none" and the location for the Web
KANIDM_BUILD_PROFILE=release_linux cargo build --release --bin kanidmd
```
### Building the Web UI
> [!NOTE:]
>
> There is a pre-packaged version of the Web UI at `/server/web_ui/pkg/`, which can be used
> directly. This means you don't need to build the Web UI yourself.
The Web UI uses Rust WebAssembly rather than Javascript. To build this you need to set up the
environment:
```bash
cargo install wasm-pack
```
Then you are able to build the UI:
```bash
cd server/web_ui/
./build_wasm_dev.sh
```
To build for release, run `build_wasm.sh`, or `make webui` from the project root.
The "developer" profile for kanidmd will automatically use the pkg output in this folder.
### Development Server for Interactive Testing
Especially if you wish to develop the WebUI then the ability to run the server from the source tree

View file

@ -6,7 +6,6 @@
cargo install cargo-audit
cargo install cargo-outdated
cargo install cargo-udeps
cargo install wasm-bindgen-cli
```
## Pre Release Check List
@ -34,7 +33,6 @@ cargo install wasm-bindgen-cli
### Administration
- [ ] build wasm components with release profile
- [ ] Update `RELEASE_NOTES.md`
- [ ] Update `README.md`
- [ ] cargo test
@ -70,7 +68,7 @@ cargo install wasm-bindgen-cli
## Final Release Check List
### Git Management
### Git Management Part Deux
- [ ] git checkout 1.1.x
- [ ] git pull origin 1.1.x

View file

@ -41,5 +41,4 @@ tokio = { workspace = true, features = [
toml = { workspace = true }
uuid = { workspace = true, features = ["serde", "v4"] }
url = { workspace = true, features = ["serde"] }
webauthn-rs-proto = { workspace = true, features = ["wasm"] }
# hyper = { workspace = true }
webauthn-rs-proto = { workspace = true }

View file

@ -1,4 +1,3 @@
web_ui_pkg_path = "/pkg"
htmx_ui_pkg_path = "/hpkg"
# Don't set the cpu_flags to autodetect for this platform
# cpu_flags = "none"

View file

@ -1,4 +1,3 @@
web_ui_pkg_path = "../web_ui/pkg"
htmx_ui_pkg_path = "../core/static"
# Set to native for developer machines.
cpu_flags = "native"

View file

@ -1,4 +1,3 @@
web_ui_pkg_path = "/usr/share/kanidm/ui/pkg"
htmx_ui_pkg_path = "/usr/share/kanidm/ui/hpkg"
# Don't set the value for autodetect
# cpu_flags = "none"

View file

@ -54,7 +54,6 @@ impl std::fmt::Display for CpuOptLevel {
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct ProfileConfig {
web_ui_pkg_path: String,
htmx_ui_pkg_path: String,
#[serde(default)]
cpu_flags: CpuOptLevel,
@ -127,10 +126,6 @@ pub fn apply_profile() {
}
println!("cargo:rustc-env=KANIDM_PROFILE_NAME={}", profile);
println!("cargo:rustc-env=KANIDM_CPU_FLAGS={}", profile_cfg.cpu_flags);
println!(
"cargo:rustc-env=KANIDM_WEB_UI_PKG_PATH={}",
profile_cfg.web_ui_pkg_path
);
println!(
"cargo:rustc-env=KANIDM_HTMX_UI_PKG_PATH={}",
profile_cfg.htmx_ui_pkg_path

View file

@ -28,7 +28,6 @@ opentelemetry-otlp = { workspace = true, default-features = false, features = [
"grpc-tonic",
] }
opentelemetry_sdk = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
tracing = { workspace = true, features = ["attributes"] }
tracing-forest = { workspace = true, features = [
@ -39,4 +38,3 @@ tracing-forest = { workspace = true, features = [
] }
tracing-opentelemetry = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
uuid = { workspace = true, features = ["v4"] }

View file

@ -16,10 +16,8 @@ test = true
doctest = true
[features]
# default = ["dev-oauth2-device-flow"]
wasm = ["webauthn-rs-proto/wasm"]
default = []
test = []
dev-oauth2-device-flow = []
[dependencies]
@ -33,7 +31,6 @@ serde_json = { workspace = true }
serde_with = { workspace = true, features = ["time_0_3", "base64", "hex"] }
smartstring = { workspace = true, features = ["serde"] }
time = { workspace = true, features = ["serde", "std"] }
tracing = { workspace = true }
url = { workspace = true, features = ["serde"] }
urlencoding = { workspace = true }
utoipa = { workspace = true }

View file

@ -9,11 +9,6 @@ if [ -z "$(which cargo)" ]; then
ERROR=1
fi
if [ -z "$(which wasm-pack)" ]; then
echo "You don't have wasm-pack installed! Installing it now..."
cargo install wasm-pack
fi
if [ $ERROR -eq 1 ]; then
exit 1
fi

View file

@ -9,7 +9,7 @@ else
fi
${SUDOCMD} apt-get update &&
${SUDOCMD} apt-get install -y \
${SUDOCMD} apt-get install -y \
libpam0g-dev \
libudev-dev \
libssl-dev \
@ -31,9 +31,10 @@ if [ "${PACKAGING}" -eq 1 ]; then
# This works in Debian, but not in Ubuntu because they do multiarch weird.
# It would be too invasive to config a daily driver Ubuntu install for multiarch,
# so instead we don't, and just warn.
# shellcheck disable=SC1091
source /etc/os-release
if [[ "$ID" == "ubuntu" ]]; then
2>&1 echo "You're running Ubuntu, so we're skipping enabling multiarch for you because it would be too invasive. You won't be able to build valid debs for other than your native architecture."
echo 2>&1 "You're running Ubuntu, so we're skipping enabling multiarch for you because it would be too invasive. You won't be able to build valid debs for other than your native architecture."
${SUDOCMD} apt-get install -y \
libpam0g \
libssl3
@ -71,14 +72,6 @@ if [ -z "$(which cargo)" ]; then
ERROR=1
fi
if [ $ERROR -eq 0 ] && [ -z "$(which wasm-pack)" ]; then
echo "You don't have wasm-pack installed! Installing it now..."
cargo install wasm-pack
fi
if [ $ERROR -eq 0 ] && [ -z "$(which wasm-bindgen)" ]; then
echo "You don't have wasm-bindgen installed! Installing it now..."
cargo install -f wasm-bindgen-cli
fi
if [ $ERROR -eq 0 ] && [ -z "$(which cross)" ]; then
echo "You don't have cross installed! Installing it now..."
cargo install -f cross
@ -88,7 +81,6 @@ if [ $ERROR -eq 0 ] && [ -z "$(which cargo-deb)" ]; then
cargo install -f cargo-deb
fi
if [ $ERROR -eq 1 ]; then
exit 1
fi

View file

@ -38,13 +38,6 @@ COPY . /usr/src/kanidm
# ======================
# WORKDIR /usr/src/kanidm/server/web_ui
# # This can't be used in the wasm build for now.
# # ENV RUSTFLAGS="-Clinker=clang"
# RUN ./build_wasm.sh
# ======================
WORKDIR /usr/src/kanidm/kanidmd/daemon
# Exports don't persist through RUN statements.
@ -72,7 +65,6 @@ RUN \
pam
COPY --from=builder /usr/src/kanidm/target/release/kanidmd /sbin/
COPY --from=builder /usr/src/kanidm/server/web_ui/pkg /pkg
COPY --from=builder /usr/src/kanidm/server/core/static /hpkg
RUN chmod +x /sbin/kanidmd

View file

@ -16,19 +16,17 @@ test = true
doctest = false
[features]
default = ["ui_htmx"]
ui_htmx = []
default = []
dev-oauth2-device-flow = []
[dependencies]
async-trait = { workspace = true }
askama = { workspace = true, features = ["with-axum"] }
askama_axum = { workspace = true }
axum = { workspace = true }
axum-htmx = { workspace = true }
axum-extra = { version = "0.9.4", features = ["cookie"] }
axum-macros = "0.4.1"
axum-server = { version = "0.7.1", features = ["tls-openssl"] }
axum-server = { version = "0.7.1", default-features = false }
bytes = { workspace = true }
chrono = { workspace = true }
compact_jwt = { workspace = true }
@ -36,7 +34,6 @@ cron = { workspace = true }
filetime = { workspace = true }
futures = { workspace = true }
futures-util = { workspace = true }
hashbrown = { workspace = true }
hyper = { workspace = true }
hyper-util = { workspace = true }
kanidm_proto = { workspace = true }
@ -50,7 +47,6 @@ openssl = { workspace = true }
opentelemetry = { workspace = true, features = ["logs"] }
# opentelemetry_api = { workspace = true, features = ["logs"] }
qrcode = { workspace = true, features = ["svg"] }
rand = { workspace = true }
regex = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
@ -71,8 +67,6 @@ tower-http = { version = "0.6.1", features = [
"uuid",
] }
tracing = { workspace = true, features = ["attributes"] }
tracing-subscriber = { workspace = true, features = ["time", "json"] }
urlencoding = { workspace = true }
url = { workspace = true, features = ["serde"] }
uuid = { workspace = true, features = ["serde", "v4"] }
utoipa = { workspace = true, features = [

View file

@ -12,7 +12,6 @@ use time::OffsetDateTime;
use tracing::{info, instrument, trace};
use uuid::Uuid;
use kanidmd_lib::{
event::{CreateEvent, DeleteEvent, ModifyEvent, ReviveRecycledEvent},
filter::{Filter, FilterInvalid},

View file

@ -266,7 +266,6 @@ impl ServerConfig {
"KANIDM_PKG_VERSION",
"KANIDM_PRE_RELEASE",
"KANIDM_PROFILE_NAME",
"KANIDM_WEB_UI_PKG_PATH",
];
if ignorable_build_fields.contains(&key.as_str()) {

View file

@ -2,28 +2,22 @@ use std::path::PathBuf;
/// Generates the integrity hash for a file based on a filename
pub fn generate_integrity_hash(filename: String) -> Result<String, String> {
let wasm_filepath = PathBuf::from(filename);
match wasm_filepath.exists() {
false => Err(format!(
"Can't find {:?} to generate file hash",
&wasm_filepath
)),
let filepath = PathBuf::from(filename);
match filepath.exists() {
false => Err(format!("Can't find {:?} to generate file hash", &filepath)),
true => {
let filecontents = match std::fs::read(&wasm_filepath) {
let filecontents = match std::fs::read(&filepath) {
Ok(value) => value,
Err(error) => {
return Err(format!(
"Failed to read {:?}, skipping: {:?}",
wasm_filepath, error
filepath, error
));
}
};
let shasum = openssl::hash::hash(openssl::hash::MessageDigest::sha384(), &filecontents)
.map_err(|_| {
format!(
"Failed to generate SHA384 hash for WASM at {:?}",
wasm_filepath
)
format!("Failed to generate SHA384 hash for file at {:?}", filepath)
})?;
Ok(openssl::base64::encode_block(&shasum))
}
@ -32,35 +26,6 @@ pub fn generate_integrity_hash(filename: String) -> Result<String, String> {
#[derive(Clone)]
pub struct JavaScriptFile {
// Relative to the pkg/ dir
pub filepath: &'static str,
// Dynamic
pub dynamic: bool,
// SHA384 hash of the file
pub hash: String,
// if it's a module add the "type"
pub filetype: Option<String>,
}
impl JavaScriptFile {
/// returns a `<script>` or `<meta>` HTML tag, includes the hash as a query value as a cache-busting mechanism
pub fn as_tag(&self) -> String {
let filetype = match &self.filetype {
Some(val) => {
format!(" type=\"{}\"", val.as_str())
}
_ => String::from(""),
};
if self.dynamic {
format!(
r#"<meta async src="/pkg/{}?hash={}" integrity="sha384-{}"{} />"#,
self.filepath, &self.hash, &self.hash, &filetype,
)
} else {
format!(
r#"<script async src="/pkg/{}?hash={}" integrity="sha384-{}"{}></script>"#,
self.filepath, &self.hash, &self.hash, &filetype,
)
}
}
}

View file

@ -7,9 +7,7 @@ mod javascript;
mod manifest;
pub(crate) mod middleware;
mod oauth2;
mod tests;
pub(crate) mod trace;
mod ui;
mod v1;
mod v1_domain;
mod v1_oauth2;
@ -35,7 +33,6 @@ use axum::{
use axum_extra::extract::cookie::CookieJar;
use compact_jwt::{JwsCompact, JwsHs256Signer, JwsVerifier};
use futures::pin_mut;
use hashbrown::HashMap;
use hyper::body::Incoming;
use hyper_util::rt::{TokioExecutor, TokioIo};
use kanidm_proto::{constants::KSESSIONID, internal::COOKIE_AUTH_SESSION_ID};
@ -70,8 +67,6 @@ pub struct ServerState {
pub qe_r_ref: &'static QueryServerReadV1,
// Store the token management parts.
pub jws_signer: JwsHs256Signer,
// The SHA384 hashes of javascript files we're going to serve to users
pub js_files: JavaScriptFiles,
pub(crate) trust_x_forward_for: bool,
pub csp_header: HeaderValue,
pub domain: String,
@ -117,74 +112,28 @@ impl ServerState {
}
}
#[derive(Clone)]
pub struct JavaScriptFiles {
all_pages: Vec<JavaScriptFile>,
selected: HashMap<String, JavaScriptFile>,
}
pub fn get_js_files(role: ServerRole) -> Result<JavaScriptFiles, ()> {
pub(crate) fn get_js_files(role: ServerRole) -> Result<Vec<JavaScriptFile>, ()> {
let mut all_pages: Vec<JavaScriptFile> = Vec::new();
let mut selected: HashMap<String, JavaScriptFile> = HashMap::new();
if !matches!(role, ServerRole::WriteReplicaNoUI) {
// let's set up the list of js module hashes
let pkg_path = if cfg!(feature = "ui_htmx") {
env!("KANIDM_HTMX_UI_PKG_PATH").to_owned()
} else {
env!("KANIDM_WEB_UI_PKG_PATH").to_owned()
};
let pkg_path = env!("KANIDM_HTMX_UI_PKG_PATH").to_owned();
let filelist = if cfg!(feature = "ui_htmx") {
vec![
("external/bootstrap.bundle.min.js", None, false, false),
("external/htmx.min.1.9.12.js", None, false, false),
("external/confetti.js", None, false, false),
("external/base64.js", None, false, false),
("modules/cred_update.mjs", None, false, false),
("pkhtml.js", None, false, false),
]
} else {
vec![
(
"wasmloader_admin.js",
Some("module".to_string()),
false,
true,
),
(
"wasmloader_login_flows.js",
Some("module".to_string()),
false,
true,
),
(
"wasmloader_user.js",
Some("module".to_string()),
false,
true,
),
("shared.js", Some("module".to_string()), false, false),
("external/bootstrap.bundle.min.js", None, false, false),
("external/viz.js", None, true, false),
]
};
let filelist = [
"external/bootstrap.bundle.min.js",
"external/htmx.min.1.9.12.js",
"external/confetti.js",
"external/base64.js",
"modules/cred_update.mjs",
"pkhtml.js",
];
for (filepath, filetype, dynamic, select) in filelist {
for filepath in filelist {
match generate_integrity_hash(format!("{}/{}", pkg_path, filepath,)) {
Ok(hash) => {
let js = JavaScriptFile {
filepath,
dynamic,
hash,
filetype,
};
if select {
selected.insert(filepath.to_string(), js);
} else {
let js = JavaScriptFile { hash };
all_pages.push(js)
}
}
Err(err) => {
admin_error!(
?err,
@ -196,10 +145,7 @@ pub fn get_js_files(role: ServerRole) -> Result<JavaScriptFiles, ()> {
}
}
}
Ok(JavaScriptFiles {
all_pages,
selected,
})
Ok(all_pages)
}
pub async fn create_https_server(
@ -214,16 +160,11 @@ pub async fn create_https_server(
) -> Result<task::JoinHandle<()>, ()> {
let rx = server_message_tx.subscribe();
let js_files = get_js_files(config.role)?;
let all_js_files = get_js_files(config.role)?;
// set up the CSP headers
// script-src 'self'
// 'sha384-Zao7ExRXVZOJobzS/uMp0P1jtJz3TTqJU4nYXkdmsjpiVD+/wcwCyX7FGqRIqvIz'
// 'sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM'
// 'unsafe-eval';
let mut all_js_files = js_files.all_pages.clone();
for (_, jsfile) in js_files.selected.clone() {
all_js_files.push(jsfile);
}
// 'sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM';
let js_directives = all_js_files
.into_iter()
@ -245,7 +186,7 @@ pub async fn create_https_server(
"frame-ancestors 'none'; ",
"img-src 'self' data:; ",
"worker-src 'none'; ",
"script-src 'self' 'unsafe-eval'{};",
"script-src 'self'{};",
),
js_checksums
);
@ -261,7 +202,6 @@ pub async fn create_https_server(
qe_w_ref,
qe_r_ref,
jws_signer,
js_files,
trust_x_forward_for,
csp_header,
domain: config.domain.clone(),
@ -270,36 +210,17 @@ pub async fn create_https_server(
let static_routes = match config.role {
ServerRole::WriteReplica | ServerRole::ReadOnlyReplica => {
// Create a spa router that captures everything at ui without key extraction.
if cfg!(feature = "ui_htmx") {
Router::new()
.route("/ui/images/oauth2/:rs_name", get(oauth2::oauth2_image_get))
.route("/ui/images/domain", get(v1_domain::image_get))
.route("/manifest.webmanifest", get(manifest::manifest)) // skip_route_check
// Layers only apply to routes that are *already* added, not the ones
// added after.
.layer(middleware::compression::new())
.layer(from_fn(middleware::caching::cache_me_short))
.route("/", get(|| async { Redirect::to("/ui") }))
.route("/manifest.webmanifest", get(manifest::manifest)) // skip_route_check
.nest("/ui", views::view_router())
} else {
Router::new()
.route("/ui/images/oauth2/:rs_name", get(oauth2::oauth2_image_get))
.layer(middleware::compression::new())
// Direct users to the base app page. If a login is required,
// then views will take care of redirection.
.route("/", get(|| async { Redirect::temporary("/ui") }))
.route("/manifest.webmanifest", get(manifest::manifest)) // skip_route_check
// user UI app is the catch-all
.nest("/ui", ui::spa_router_user_ui())
// login flows app
.nest("/ui/login", ui::spa_router_login_flows())
.nest("/ui/reauth", ui::spa_router_login_flows())
.nest("/ui/oauth2", ui::spa_router_login_flows())
// admin app
.nest("/ui/admin", ui::spa_router_admin())
// skip_route_check
}
// Can't compress on anything that changes
}
ServerRole::WriteReplicaNoUI => Router::new(),
};
@ -312,7 +233,6 @@ pub async fn create_https_server(
let app = match config.role {
ServerRole::WriteReplicaNoUI => app,
ServerRole::WriteReplica | ServerRole::ReadOnlyReplica => {
let pkg_router = if cfg!(feature = "ui_htmx") {
let pkg_path = PathBuf::from(env!("KANIDM_HTMX_UI_PKG_PATH"));
if !pkg_path.exists() {
eprintln!(
@ -321,22 +241,9 @@ pub async fn create_https_server(
);
std::process::exit(1);
}
Router::new().nest_service("/pkg", ServeDir::new(pkg_path))
let pkg_router = Router::new()
.nest_service("/pkg", ServeDir::new(pkg_path))
// TODO: Add in the br precompress
} else {
let pkg_path = PathBuf::from(env!("KANIDM_WEB_UI_PKG_PATH"));
if !pkg_path.exists() {
eprintln!(
"Couldn't find Web UI package path: ({}), quitting.",
env!("KANIDM_WEB_UI_PKG_PATH")
);
std::process::exit(1);
}
Router::new()
.nest_service("/pkg", ServeDir::new(pkg_path).precompressed_br())
.layer(middleware::compression::new())
}
.layer(from_fn(middleware::caching::cache_me_short));
app.merge(pkg_router)

View file

@ -1,25 +0,0 @@
#[test]
fn test_javscriptfile() {
// make sure it outputs what we think it does
use crate::https::JavaScriptFile;
let jsf = JavaScriptFile {
filepath: "wasmloader_admin.js",
dynamic: false,
hash: "1234567890".to_string(),
filetype: Some("module".to_string()),
};
assert_eq!(
jsf.as_tag(),
r#"<script async src="/pkg/wasmloader_admin.js?hash=1234567890" integrity="sha384-1234567890" type="module"></script>"#
);
let jsf = JavaScriptFile {
filepath: "wasmloader_admin.js",
dynamic: false,
hash: "1234567890".to_string(),
filetype: None,
};
assert_eq!(
jsf.as_tag(),
r#"<script async src="/pkg/wasmloader_admin.js?hash=1234567890" integrity="sha384-1234567890"></script>"#
);
}

View file

@ -1,89 +0,0 @@
use axum::extract::State;
use axum::http::header::CONTENT_TYPE;
use axum::http::HeaderValue;
use axum::response::Response;
use axum::routing::get;
use axum::Router;
use super::ServerState;
use crate::https::extractors::{DomainInfo, DomainInfoRead};
pub const CSS_NAVBAR_NAV: &str = "navbar navbar-expand-md navbar-dark bg-dark mb-4";
pub const CSS_NAVBAR_BRAND: &str = "navbar-brand d-flex align-items-center";
pub const CSS_NAVBAR_LINKS_UL: &str = "navbar-nav";
pub(crate) fn spa_router_user_ui() -> Router<ServerState> {
Router::new()
.route("/", get(ui_handler_user_ui))
.fallback(ui_handler_user_ui)
}
/// This handles /ui/admin and all sub-paths
pub(crate) fn spa_router_admin() -> Router<ServerState> {
Router::new()
.route("/", get(ui_handler_admin))
.fallback(ui_handler_admin)
}
/// This handles the following base paths:
/// - /ui/login
/// - /ui/reauth
/// - /ui/oauth2
pub(crate) fn spa_router_login_flows() -> Router<ServerState> {
Router::new()
.route("/", get(ui_handler_login_flows))
.fallback(ui_handler_login_flows)
}
pub(crate) async fn ui_handler_user_ui(
State(state): State<ServerState>,
DomainInfo(domain_info): DomainInfo,
) -> Response<String> {
ui_handler_generic(state, "wasmloader_user.js", domain_info).await
}
pub(crate) async fn ui_handler_admin(
State(state): State<ServerState>,
DomainInfo(domain_info): DomainInfo,
) -> Response<String> {
ui_handler_generic(state, "wasmloader_admin.js", domain_info).await
}
pub(crate) async fn ui_handler_login_flows(
State(state): State<ServerState>,
DomainInfo(domain_info): DomainInfo,
) -> Response<String> {
ui_handler_generic(state, "wasmloader_login_flows.js", domain_info).await
}
pub(crate) async fn ui_handler_generic(
state: ServerState,
wasmloader: &str,
domain_info: DomainInfoRead,
) -> Response<String> {
// let's get the tags we want to load the javascript files
let mut jsfiles: Vec<String> = state
.js_files
.all_pages
.into_iter()
.map(|j| j.as_tag())
.collect();
if let Some(jsfile) = state.js_files.selected.get(wasmloader) {
jsfiles.push(jsfile.clone().as_tag())
};
let body: String = format!(
include_str!("ui_html.html"),
jstags = jsfiles.join("\n"),
cache_buster_key = crate::https::cache_buster::get_cache_buster_key(),
display_name = domain_info.display_name()
);
let mut res = Response::new(body);
res.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("text/html;charset=utf-8"),
);
res
}

View file

@ -1,46 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="theme-color" content="white" />
<meta name="viewport" content="width=device-width" />
<title>{display_name}</title>
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon"
href="/pkg/img/favicon.png?v={cache_buster_key}" />
<link rel="apple-touch-icon"
href="/pkg/img/logo-256.png?v={cache_buster_key}" />
<link rel="apple-touch-icon" sizes="180x180"
href="/pkg/img/logo-180.png?v={cache_buster_key}" />
<link rel="apple-touch-icon" sizes="192x192"
href="/pkg/img/logo-192.png?v={cache_buster_key}" />
<link rel="apple-touch-icon" sizes="512x512"
href="/pkg/img/logo-square.svg?v={cache_buster_key}" />
<link rel="stylesheet" href="/pkg/external/bootstrap.min.css"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" />
<link rel="stylesheet"
href="/pkg/style.css?v={cache_buster_key}" />
{jstags}
</head>
<body class="flex-column d-flex h-100">
<main class="flex-shrink-0 form-signin m-auto">
<center>
<img
src="/pkg/img/logo-square.svg?v={cache_buster_key}"
alt="Kanidm"
class="kanidm_logo" />
<h3>Kanidm is loading, please wait... </h3>
</center>
</main>
<footer class="footer mt-auto py-3 bg-light text-end">
<div class="container">
<span class="text-muted">Powered by <a
href="https://kanidm.com">Kanidm</a></span>
</div>
</footer>
</body>
</html>

View file

@ -14,9 +14,14 @@
(% block body %)
<main class="flex-shrink-0 container form-signin m-auto" id="cred-reset-form">
<center>
(% if domain_info.image().is_some() %)
<img src="/ui/images/domain"
alt="(( domain_info.display_name() ))" class="kanidm_logo" />
(% else %)
<img
src="/pkg/img/logo-square.svg?v=((crate::https::cache_buster::get_cache_buster_key()))"
alt="Kanidm" class="kanidm_logo" />
alt="(( domain_info.display_name() ))" class="kanidm_logo" />
(% endif %)
<h2>Credential Reset</h2>
<h3>(( domain_info.display_name() ))</h3>
</center>

View file

@ -9,11 +9,11 @@
<main id="main" class="form-signin m-auto align-items-center d-flex flex-column">
(% if display_ctx.domain_info.image().is_some() %)
<img src="/ui/images/domain"
alt="Kanidm" class="kanidm_logo" />
alt="(( display_ctx.domain_info.display_name() ))" class="kanidm_logo" />
(% else %)
<img
src="/pkg/img/logo-square.svg?v=((crate::https::cache_buster::get_cache_buster_key()))"
alt="Kanidm" class="kanidm_logo" />
alt="(( display_ctx.domain_info.display_name() ))" class="kanidm_logo" />
(% endif %)
<h3>Kanidm</h3>
(% if let Some(reauth) = display_ctx.reauth %)

View file

@ -1,15 +1,15 @@
<nav class="(( crate::https::ui::CSS_NAVBAR_NAV ))">
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
<div class="container-lg">
<a class="(( crate::https::ui::CSS_NAVBAR_BRAND ))" href="/ui/apps">
<a class="navbar-brand d-flex align-items-center" href="/ui/apps">
(% if navbar_ctx.domain_info.image().is_some() %)
<img src="/ui/images/domain"
alt="Kanidm" width="auto" height="40" class="navbar-toggler-img" />
alt="(( navbar_ctx.domain_info.display_name() ))" width="auto" height="40" class="navbar-toggler-img" />
(% else %)
<img
src="/pkg/img/logo.svg?v=((crate::https::cache_buster::get_cache_buster_key()))"
alt="Kanidm" width="auto" height="40" class="navbar-toggler-img" />
alt="(( navbar_ctx.domain_info.display_name() ))" width="auto" height="40" class="navbar-toggler-img" />
(% endif %)
<div class="ps-2">Kanidm</div>
<div class="ps-2">(( navbar_ctx.domain_info.display_name() ))</div>
</a>
<!-- this shows a button on mobile devices to open the menu-->
@ -21,7 +21,7 @@
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="(( crate::https::ui::CSS_NAVBAR_LINKS_UL ))">
<ul class="navbar-nav me-auto mb-2 mb-md-0">
<li>
<a class="nav-link" href=((Urls::Apps))>
<span data-feather="file"></span>Apps</a>
@ -31,7 +31,7 @@
<span data-feather="file"></span>Profile</a>
</li>
</ul>
<ul class="(( crate::https::ui::CSS_NAVBAR_LINKS_UL )) ms-md-auto">
<ul class="navbar-nav me-auto mb-2 mb-md-0 ms-md-auto">
<li>
<a class="nav-link" href="#" data-bs-toggle="modal"
data-bs-target="#signoutModal">Sign out</a>

View file

@ -34,10 +34,8 @@ futures = { workspace = true }
dhat = { workspace = true, optional = true }
clap = { workspace = true, features = ["env"] }
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] }
tokio-util = { workspace = true, features = ["codec"] }
toml = { workspace = true }
tracing = { workspace = true }
serde_json.workspace = true

View file

@ -17,14 +17,6 @@ path = "src/lib.rs"
test = true
doctest = false
[[bench]]
name = "scaling_10k"
harness = false
[[bench]]
name = "image_benches"
harness = false
[features]
default = []
dhat-heap = ["dep:dhat"]
@ -48,8 +40,7 @@ kanidm_proto = { workspace = true }
kanidm_lib_crypto = { workspace = true }
lazy_static = { workspace = true }
ldap3_proto = { workspace = true }
libc = { workspace = true }
libsqlite3-sys = { workspace = true }
# libsqlite3-sys = { workspace = true }
num_enum = { workspace = true }
# We need to explicitly ask for openssl-sys so that we get the version propagated
# into the build.rs for legacy feature checks.
@ -69,19 +60,15 @@ serde = { workspace = true, features = ["derive"] }
serde_cbor = { workspace = true }
serde_json = { workspace = true }
sketching = { workspace = true }
smartstring = { workspace = true, features = ["serde"] }
smolset = { workspace = true }
sshkey-attest = { workspace = true }
time = { workspace = true, features = ["serde", "std"] }
tokio = { workspace = true, features = ["net", "sync", "time", "rt"] }
tokio-util = { workspace = true, features = ["codec"] }
toml = { workspace = true }
nonempty = { workspace = true, features = ["serialize"] }
tracing = { workspace = true, features = ["attributes"] }
url = { workspace = true, features = ["serde"] }
urlencoding = { workspace = true }
uuid = { workspace = true, features = ["serde", "v4"] }
webauthn-rs = { workspace = true, features = [
"resident-key-support",
@ -110,7 +97,6 @@ compact_jwt = { workspace = true, features = [
"hsm-crypto",
"unsafe_release_without_verify",
] }
criterion = { workspace = true, features = ["html_reports"] }
futures = { workspace = true }
kanidmd_lib_macros = { workspace = true }
# This is needed so that we can use a test feature across the crate boundary to proto

View file

@ -1,134 +1,135 @@
/// This file contains benchmarks for the image module so we can work out the best order to run things in
use std::time::Duration;
// /// This file contains benchmarks for the image module so we can work out the best order to run things in
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use kanidmd_lib::valueset::image::jpg;
use kanidmd_lib::valueset::image::png;
// use std::time::Duration;
pub fn bench_png_lodepng_validate(c: &mut Criterion) {
let mut group = c.benchmark_group("png_lodepng_validate");
group.bench_function("png_lodepng_validate_oversize", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.png",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(filename).unwrap());
b.iter(|| png::png_lodepng_validate(&contents, black_box("oversize_dimensions.png")))
});
group.bench_function("png_lodepng_validate_ok", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.png",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(filename).unwrap());
b.iter(|| png::png_lodepng_validate(&contents, black_box("oversize_dimensions.png")))
});
group.finish();
}
// use criterion::{black_box, criterion_group, criterion_main, Criterion};
// use kanidmd_lib::valueset::image::jpg;
// use kanidmd_lib::valueset::image::png;
pub fn bench_png_has_trailer(c: &mut Criterion) {
let mut group = c.benchmark_group("png_has_trailer");
group.bench_function("png_has_trailer_oversize", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.png",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(filename).unwrap());
b.iter(|| png::png_has_trailer(&contents));
});
group.bench_function("png_has_trailer_ok", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/ok.png",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(filename).unwrap());
b.iter(|| png::png_has_trailer(&contents));
});
group.finish();
}
// pub fn bench_png_lodepng_validate(c: &mut Criterion) {
// let mut group = c.benchmark_group("png_lodepng_validate");
// group.bench_function("png_lodepng_validate_oversize", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.png",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(filename).unwrap());
// b.iter(|| png::png_lodepng_validate(&contents, black_box("oversize_dimensions.png")))
// });
// group.bench_function("png_lodepng_validate_ok", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.png",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(filename).unwrap());
// b.iter(|| png::png_lodepng_validate(&contents, black_box("oversize_dimensions.png")))
// });
// group.finish();
// }
pub fn bench_jpg(c: &mut Criterion) {
let mut group = c.benchmark_group("jpg");
group.bench_function("check_jpg_header", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.jpg",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(filename).unwrap());
b.iter(|| jpg::check_jpg_header(&contents));
});
group.bench_function("has_trailer", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.jpg",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(filename).unwrap());
b.iter(|| jpg::has_trailer(&contents));
});
group.bench_function("use_decoder", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.jpg",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(&filename).unwrap());
b.iter(|| jpg::validate_decoding(&filename, &contents, image::io::Limits::default()));
});
group.finish();
}
// pub fn bench_png_has_trailer(c: &mut Criterion) {
// let mut group = c.benchmark_group("png_has_trailer");
// group.bench_function("png_has_trailer_oversize", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.png",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(filename).unwrap());
// b.iter(|| png::png_has_trailer(&contents));
// });
// group.bench_function("png_has_trailer_ok", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/ok.png",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(filename).unwrap());
// b.iter(|| png::png_has_trailer(&contents));
// });
// group.finish();
// }
pub fn compare_jpg(c: &mut Criterion) {
let mut group = c.benchmark_group("compare_jpg");
group.bench_function("header, trailer, decoder", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.jpg",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(&filename).unwrap());
b.iter(|| {
assert!(jpg::check_jpg_header(&contents).is_ok());
assert!(jpg::has_trailer(&contents).is_ok());
assert!(
jpg::validate_decoding(&filename, &contents, image::io::Limits::default()).is_ok()
);
});
});
group.bench_function("trailer, header, decoder", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.jpg",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(&filename).unwrap());
b.iter(|| {
assert!(jpg::has_trailer(&contents).is_ok());
assert!(jpg::check_jpg_header(&contents).is_ok());
assert!(
jpg::validate_decoding(&filename, &contents, image::io::Limits::default()).is_ok()
);
});
});
group.bench_function("decoder, trailer, header", |b| {
let filename = black_box(format!(
"{}/src/valueset/image/test_images/oversize_dimensions.jpg",
env!("CARGO_MANIFEST_DIR")
));
let contents = black_box(std::fs::read(&filename).unwrap());
b.iter(|| {
assert!(
jpg::validate_decoding(&filename, &contents, image::io::Limits::default()).is_ok()
);
assert!(jpg::has_trailer(&contents).is_ok());
assert!(jpg::check_jpg_header(&contents).is_ok());
});
});
// pub fn bench_jpg(c: &mut Criterion) {
// let mut group = c.benchmark_group("jpg");
// group.bench_function("check_jpg_header", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.jpg",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(filename).unwrap());
// b.iter(|| jpg::check_jpg_header(&contents));
// });
// group.bench_function("has_trailer", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.jpg",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(filename).unwrap());
// b.iter(|| jpg::has_trailer(&contents));
// });
// group.bench_function("use_decoder", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.jpg",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(&filename).unwrap());
// b.iter(|| jpg::validate_decoding(&filename, &contents, image::io::Limits::default()));
// });
// group.finish();
// }
group.finish();
}
// pub fn compare_jpg(c: &mut Criterion) {
// let mut group = c.benchmark_group("compare_jpg");
// group.bench_function("header, trailer, decoder", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.jpg",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(&filename).unwrap());
// b.iter(|| {
// assert!(jpg::check_jpg_header(&contents).is_ok());
// assert!(jpg::has_trailer(&contents).is_ok());
// assert!(
// jpg::validate_decoding(&filename, &contents, image::io::Limits::default()).is_ok()
// );
// });
// });
// group.bench_function("trailer, header, decoder", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.jpg",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(&filename).unwrap());
// b.iter(|| {
// assert!(jpg::has_trailer(&contents).is_ok());
// assert!(jpg::check_jpg_header(&contents).is_ok());
// assert!(
// jpg::validate_decoding(&filename, &contents, image::io::Limits::default()).is_ok()
// );
// });
// });
// group.bench_function("decoder, trailer, header", |b| {
// let filename = black_box(format!(
// "{}/src/valueset/image/test_images/oversize_dimensions.jpg",
// env!("CARGO_MANIFEST_DIR")
// ));
// let contents = black_box(std::fs::read(&filename).unwrap());
// b.iter(|| {
// assert!(
// jpg::validate_decoding(&filename, &contents, image::io::Limits::default()).is_ok()
// );
// assert!(jpg::has_trailer(&contents).is_ok());
// assert!(jpg::check_jpg_header(&contents).is_ok());
// });
// });
criterion_group!(
name = png_tests;
config = Criterion::default()
.measurement_time(Duration::from_secs(15))
.with_plots();
targets = bench_png_lodepng_validate, bench_png_has_trailer, bench_jpg, compare_jpg
);
criterion_main!(png_tests);
// group.finish();
// }
// criterion_group!(
// name = png_tests;
// config = Criterion::default()
// .measurement_time(Duration::from_secs(15))
// .with_plots();
// targets = bench_png_lodepng_validate, bench_png_has_trailer, bench_jpg, compare_jpg
// );
// criterion_main!(png_tests);

View file

@ -1,138 +0,0 @@
use std::time::{Duration, Instant, SystemTime};
use criterion::{
criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput,
};
use kanidmd_lib::entry::{Entry, EntryInit, EntryNew};
use kanidmd_lib::entry_init;
use kanidmd_lib::prelude::{Attribute, EntryClass};
use kanidmd_lib::testkit::{setup_idm_test, TestConfiguration};
use kanidmd_lib::value::Value;
pub fn duration_from_epoch_now() -> Duration {
#[allow(clippy::expect_used)]
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("invalid duration from epoch now")
}
pub fn scaling_user_create_single(c: &mut Criterion) {
let mut group = c.benchmark_group("user_create_single");
group.sample_size(10);
group.sampling_mode(SamplingMode::Flat);
group.warm_up_time(Duration::from_secs(5));
group.measurement_time(Duration::from_secs(120));
for size in &[100, 250, 500, 1000, 1500, 2000, 5000, 10000] {
group.throughput(Throughput::Elements(*size));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
b.iter_custom(|iters| {
let mut elapsed = Duration::from_secs(0);
println!("iters, size -> {iters:?}, {size:?}");
for _i in 0..iters {
let mut rt = tokio::runtime::Builder::new_current_thread();
elapsed = rt
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(async {
let (idms, _idms_delayed, _idms_audit) =
setup_idm_test(TestConfiguration::default()).await;
let ct = duration_from_epoch_now();
let start = Instant::now();
for counter in 0..size {
let mut idms_prox_write =
idms.proxy_write(ct).await.expect("Failed to get write txn");
let name = format!("testperson_{counter}");
let e1 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Name, Value::new_iname(&name)),
(Attribute::Description, Value::new_utf8s("criterion")),
(Attribute::DisplayName, Value::new_utf8s(&name))
);
let cr = idms_prox_write.qs_write.internal_create(vec![e1]);
assert!(cr.is_ok());
idms_prox_write.commit().expect("Must not fail");
}
elapsed.checked_add(start.elapsed()).unwrap()
});
}
elapsed
});
});
}
group.finish();
}
pub fn scaling_user_create_batched(c: &mut Criterion) {
let mut group = c.benchmark_group("user_create_batched");
group.sample_size(10);
group.sampling_mode(SamplingMode::Flat);
group.warm_up_time(Duration::from_secs(5));
group.measurement_time(Duration::from_secs(120));
for size in &[100, 250, 500, 1000, 1500, 2000, 5000, 10000] {
group.throughput(Throughput::Elements(*size));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
b.iter_custom(|iters| {
let mut elapsed = Duration::from_secs(0);
println!("iters, size -> {iters:?}, {size:?}");
let data: Vec<_> = (0..size)
.map(|i| {
let name = format!("testperson_{i}");
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Name, Value::new_iname(&name)),
(Attribute::Description, Value::new_utf8s("criterion")),
(Attribute::DisplayName, Value::new_utf8s(&name))
)
})
.collect();
for _i in 0..iters {
let mut rt = tokio::runtime::Builder::new_current_thread();
elapsed = rt
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(async {
let (idms, _idms_delayed, _idms_audit) =
setup_idm_test(TestConfiguration::default()).await;
let ct = duration_from_epoch_now();
let start = Instant::now();
let mut idms_prox_write =
idms.proxy_write(ct).await.expect("Failed to get write txn");
let cr = idms_prox_write.qs_write.internal_create(data.clone());
assert!(cr.is_ok());
idms_prox_write.commit().expect("Must not fail");
elapsed.checked_add(start.elapsed()).unwrap()
});
}
elapsed
});
});
}
group.finish();
}
criterion_group!(
name = scaling_basic;
config = Criterion::default()
.measurement_time(Duration::from_secs(15))
.with_plots();
targets = scaling_user_create_single, scaling_user_create_batched
);
criterion_main!(scaling_basic);

View file

@ -30,9 +30,7 @@ kanidm_client = { workspace = true }
kanidm_proto = { workspace = true }
kanidmd_core = { workspace = true }
kanidmd_lib = { workspace = true }
lazy_static = { workspace = true }
openssl = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true, default-features = false, features = ["cookies"] }
serde = { workspace = true }
sketching = { workspace = true }

View file

@ -1,70 +0,0 @@
[package]
name = "kanidmd_web_ui_admin"
description = "Kanidm Server Web UI - Admin Interface"
documentation = "https://docs.rs/kanidm/latest/kanidm/"
version = { workspace = true }
authors = [
"William Brown <william@blackhats.net.au>",
"James Hodgkinson <james@terminaloutcomes.com>",
]
rust-version = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
kanidm_proto = { workspace = true, features = ["wasm"] }
kanidmd_web_ui_shared = { workspace = true }
gloo = { workspace = true }
js-sys = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde-wasm-bindgen = { workspace = true }
time = { workspace = true }
uuid = { workspace = true }
wasm-bindgen = { workspace = true }
wasm-bindgen-futures = { workspace = true }
yew = { workspace = true, features = ["csr"] }
yew-router = { workspace = true }
gloo-utils = { workspace = true }
enum-iterator = { workspace = true}
[dependencies.web-sys]
workspace = true
features = [
# "AuthenticationExtensionsClientOutputs",
# "AuthenticatorResponse",
# "CredentialCreationOptions",
# "CredentialRequestOptions",
# "CredentialsContainer",
"DomTokenList",
"Element",
"Event",
"FocusEvent",
"FormData",
"Headers",
"HtmlButtonElement",
"HtmlDocument",
"HtmlFormElement",
"HtmlSelectElement",
"HtmlInputElement",
"Navigator",
# "PublicKeyCredential",
# "PublicKeyCredentialCreationOptions",
# "PublicKeyCredentialRpEntity",
# "PublicKeyCredentialUserEntity",
"Request",
# "RequestCredentials",
# "RequestInit",
# "RequestMode",
# "RequestRedirect",
"Response",
"Window",
]
[dev-dependencies]
wasm-bindgen-test = { workspace = true }

View file

@ -1,584 +0,0 @@
use std::collections::BTreeMap;
use gloo::console;
use wasm_bindgen::JsValue;
use yew::{html, Component, Context, Html, Properties};
use yew_router::prelude::Link;
use super::prelude::*;
use crate::components::admin_menu::{Entity, EntityType, GetError};
use crate::router::AdminRoute;
use kanidmd_web_ui_shared::constants::{CSS_CELL, CSS_DT, CSS_TABLE};
impl From<GetError> for AdminListAccountsMsg {
fn from(ge: GetError) -> Self {
AdminListAccountsMsg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
pub struct AdminListAccounts {
state: ViewState,
}
// callback messaging for this confused pile of crab-bait
pub enum AdminListAccountsMsg {
/// When the server responds and we need to update the page
Responded {
response: BTreeMap<String, Entity>,
},
Failed {
emsg: String,
kopid: Option<String>,
},
}
enum ViewState {
/// waiting for the page to load
Loading,
/// server has responded
Responded { response: BTreeMap<String, Entity> },
/// failed to pull the details
#[allow(dead_code)]
Failed {
// TODO: use this
emsg: String,
kopid: Option<String>,
},
#[allow(dead_code)]
/// Not authorized to pull the details
NotAuthorized {}, // TODO: use this
}
enum ViewAccountState {
/// waiting for the page to load
Loading,
/// server has responded
Responded { response: Entity },
/// failed to pull the details
#[allow(dead_code)]
Failed {
// TODO: use this
emsg: String,
kopid: Option<String>,
},
#[allow(dead_code)]
/// Not authorized to pull the details
NotAuthorized {}, // TODO: use this
}
#[derive(PartialEq, Properties, Eq)]
pub struct AdminListAccountsProps {
// for filtering and pagination
// #[allow(dead_code)]
// search: Option<String>,
// #[allow(dead_code)]
// page: Option<u32>,
}
/// Pulls all accounts (service or person-class) from the backend and returns a HashMap
/// with the "name" field being the keys, for easy human-facing sortability.
pub async fn get_accounts() -> Result<AdminListAccountsMsg, GetError> {
// TODO: the actual pulling and turning into a BTreeMap in this and get_groups could *probably* be rolled up into one function? The result object differs but all the widgets are the same.
let mut all_accounts = BTreeMap::new();
// we iterate over these endpoints
let endpoints = [
("/v1/service_account", EntityType::ServiceAccount),
("/v1/person", EntityType::Person),
];
for (endpoint, object_type) in endpoints {
let (_, _, value, _) = match do_request(endpoint, RequestMethod::GET, None::<JsValue>).await
{
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
})
}
};
// TODO: this kind of thing comes back when you're logged out: SerdeError(Error("invalid type: string \"sessionexpired\", expected a sequence", line: 1, column: 16))', server/web_ui/src/components/admin_accounts.rs:107:27
let data: Vec<Entity> = match serde_wasm_bindgen::from_value(value) {
Ok(value) => value,
Err(error) => {
return Err(GetError {
err: format!("Failed to grab the account data into JSON: {:?}", error),
});
}
};
for entity in data.iter() {
let mut new_entity = entity.to_owned();
new_entity.object_type = object_type.clone();
// first we try the short name and, if that isn't there then just use the SPN...
#[allow(clippy::expect_used)]
let entity_id = match entity.attrs.name.first() {
Some(value) => value.to_string(),
None => entity
.attrs
.spn
.first()
.expect("Failed to grab the SPN for an account.")
.to_string(),
};
all_accounts.insert(entity_id.to_string(), new_entity);
}
}
Ok(AdminListAccountsMsg::Responded {
response: all_accounts,
})
}
impl Component for AdminListAccounts {
type Message = AdminListAccountsMsg;
type Properties = AdminListAccountsProps;
fn create(ctx: &Context<Self>) -> Self {
// TODO: work out the querystring thing so we can just show x number of elements
// console::log!("query: {:?}", location().query);
// start pulling the account data on startup
ctx.link().send_future(async move {
match get_accounts().await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminListAccounts {
state: ViewState::Loading,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminListAccountsMsg::Responded { response } => {
// TODO: do we paginate here?
/*
// Seems broken
#[cfg(debug_assertions)]
for key in response.keys() {
#[allow(clippy::unwrap_used)]
console::log!(
"response: {:?}",
serde_json::to_string(response.get(key).unwrap()).unwrap()
);
}
*/
self.state = ViewState::Responded { response };
return true;
}
AdminListAccountsMsg::Failed { emsg, kopid } => {
console::log!("emsg: {:?}", emsg);
console::log!("kopid: {:?}", kopid);
}
}
false
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<>
{do_page_header("Account Administration")}
{ alpha_warning_banner() }
<div id={"accountlist"}>
{match &self.state {
ViewState::Loading => {
html! {"Waiting on the accounts list to load..."}
}
ViewState::Responded { response } => {
let scope_col = "col";
html!(
<table class={CSS_TABLE}>
<thead>
<tr>
<th scope={scope_col}></th>
<th scope={scope_col}>{"Display Name"}</th>
<th scope={scope_col}>{"Username"}</th>
<th scope={scope_col}>{"Description"}</th>
</tr>
</thead>
{
response.keys().map(|name| {
#[allow(clippy::expect_used)]
let account: &Entity = response.get(name).expect("Couldn't get account key when it was just in the iter...");
let display_name: String = match account.attrs.displayname.first() {
Some(value) => value.to_string(),
None => String::from(""),
};
let description: String = match account.attrs.description.first() {
Some(value) => value.to_string(),
None => String::from(""),
};
let account_type: Html = match account.object_type {
EntityType::ServiceAccount => html!{<img src={"/pkg/img/icon-robot.svg"} alt={"Service Account"} class={"p-0"} />},
EntityType::Person => html!{<img src={"/pkg/img/icon-person.svg"} alt={"Person"} class={"p-0"} />},
_ => html!("x"),
};
let uuid: String = match account.attrs.uuid.first() {
Some(value) => value.to_string(),
None => {
console::error!("Account without a UUID?", format!("{:?}", account).to_string());
String::from("Unknown UUID!")
}
};
let object_link = match account.object_type {
EntityType::Person => AdminRoute::ViewPerson{id_or_name:uuid.clone()},
EntityType::ServiceAccount => AdminRoute::ViewServiceAccount{id_or_name:uuid.clone()},
// because matching is hard
_ => AdminRoute::ViewPerson{id_or_name:uuid.clone()},
};
html!{
<tr key={uuid}>
<td class={CSS_CELL}>{account_type}</td>
<th scope={scope_col} class={CSS_CELL}>
<Link<AdminRoute> to={object_link} >
{display_name}
</Link<AdminRoute>>
</th>
<td class={CSS_CELL}>{name}</td>
<td class={CSS_CELL}>{description}</td>
</tr>
}
}).collect::<Html>()
}
</table>
)
}
ViewState::Failed { emsg, kopid } => {
console::error!("Failed to pull details", format!("{:?}", kopid));
html!(
<>
{do_alert_error("Failed to Query Accounts", Some(emsg), false)}
</>
)
}
ViewState::NotAuthorized {} => {
do_alert_error("You're not authorized to see this page!", None, false)
}
}}
</div>
</>
}
}
}
// impl AdminListAccounts {
// /// output the information based on what's in the current state
// fn view_state(&self, _ctx: &Context<Self>) -> Html {
// }
// }
impl From<GetError> for AdminViewPersonMsg {
fn from(ge: GetError) -> Self {
AdminViewPersonMsg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
pub struct AdminViewPerson {
#[allow(dead_code)]
state: ViewAccountState,
}
#[derive(Properties, PartialEq, Eq, Clone)]
/// Properties for accounts, either Person or Service Account
pub struct AdminViewAccountProps {
pub id_or_name: String,
}
// callback messaging for this confused pile of crab-bait
pub enum AdminViewPersonMsg {
/// When the server responds and we need to update the page
Responded { response: Entity },
#[allow(dead_code)]
Failed { emsg: String, kopid: Option<String> },
}
impl Component for AdminViewPerson {
type Message = AdminViewPersonMsg;
type Properties = AdminViewAccountProps;
fn create(ctx: &Context<Self>) -> Self {
let uuid = ctx.props().id_or_name.clone();
ctx.link().send_future(async move {
match get_person(&uuid).await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminViewPerson {
state: ViewAccountState::Loading,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminViewPersonMsg::Responded { response } => {
self.state = ViewAccountState::Responded { response }
}
AdminViewPersonMsg::Failed { emsg, kopid } => {
self.state = ViewAccountState::Failed { emsg, kopid }
}
}
true
}
fn view(&self, _ctx: &Context<Self>) -> Html {
match &self.state {
ViewAccountState::Loading => html! {{"Loading..."}},
ViewAccountState::Failed { emsg, kopid } => do_alert_error(
emsg.clone().as_str(),
Some(&format!("Operation ID: {:?}", kopid)),
false,
),
// TODO: the not authorized page needs to be better
ViewAccountState::NotAuthorized {} => {
html! {{"You are not authorized to view this page!"}}
}
ViewAccountState::Responded { response } => {
// TODO: This is pretty lacking in detail, even logged in as the idm_admin user, so will have to work out how to pull all the details
let username = match response.attrs.name.first() {
Some(value) => value.to_owned(),
None => String::from("Unable to query username"),
};
let display_name = match response.attrs.displayname.first() {
Some(value) => value.to_string(),
None => String::from("Display Name Unset"),
};
// let mail_primary = match userinfo.uat.mail_primary.as_ref() {
// Some(email_address) => {
// html! {
// <a href={ format!("mailto:{}", &email_address)}>
// {email_address}
// </a>
// }
// }
// None => html! { {"<primary email is unset>"}},
// };
html! {
<>
{do_page_header(display_name.as_str())}
{alpha_warning_banner()}
// <dt class={CSS_DT}>{ "Display Name" }</dt>
// <dl class="row">
// <dd class="col">{ }</dd>
// <dt class={CSS_DT}>{ "Primary Email" }</dt>
// <dd class="col">{mail_primary}</dd>
// <dt class={CSS_DT}>{ "Group Memberships" }</dt>
// <dd class="col">
// <ul class="list-group">
// {
// match user_groups {
// Some(grouplist) => html!{
// {
// for grouplist.iter()
// .map(|group|
// {
// html!{ <li>{
// #[allow(clippy::unwrap_used)]
// group.split('@').next().unwrap().to_string()
// }</li> }
// })
// }
// },
// None => html!{
// <li>{"Not a member of any groups"}</li>
// }
// }
// }
// </ul>
// </dd>
// <dt class={CSS_DT}>
// { "User's SPN" }
// </dt>
// <dd class="col">
// { username.to_string() }{"@"}{ domain }
// </dd>
<dt class={CSS_DT}>{ "Username" }</dt>
<dd class="col">{ username }</dd>
<dt class={CSS_DT}>{ "User's UUID" }</dt>
<dd class="col">{ response.attrs.uuid.clone().first() }</dd>
// </dl>
</>
}
}
}
}
}
impl From<GetError> for AdminViewServiceAccountMsg {
fn from(ge: GetError) -> Self {
AdminViewServiceAccountMsg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
pub struct AdminViewServiceAccount {
#[allow(dead_code)]
state: ViewAccountState,
}
// callback messaging for this confused pile of crab-bait
pub enum AdminViewServiceAccountMsg {
/// When the server responds and we need to update the page
Responded { response: Entity },
#[allow(dead_code)]
Failed { emsg: String, kopid: Option<String> },
}
impl Component for AdminViewServiceAccount {
type Message = AdminViewServiceAccountMsg;
type Properties = AdminViewAccountProps;
fn create(ctx: &Context<Self>) -> Self {
let id_or_name = ctx.props().id_or_name.clone();
ctx.link().send_future(async move {
match get_service_account(&id_or_name).await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminViewServiceAccount {
state: ViewAccountState::Loading,
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
match &self.state {
ViewAccountState::Loading => html! {{"Loading..."}},
ViewAccountState::Responded { response } => {
let account = &response.attrs;
let username = match account.name.first() {
Some(value) => value.as_str(),
None => "Unable to pull username",
};
let displayname = match account.displayname.first() {
Some(value) => value.as_str(),
None => "Unable to pull displayname",
};
let description = match account.description.first() {
Some(value) => html! {<p>{"Description: "}{value.as_str()}</p>},
None => html! {},
};
html! {
<>
{do_page_header(&format!("Service Account: {}", username))}
{alpha_warning_banner()}
<p>{"Display Name: "}{displayname}</p>
{description}
</>
}
}
ViewAccountState::Failed { emsg, kopid } => html! {
do_alert_error(emsg.as_str(), Some(&format!("Operation ID: {:?}", kopid)), false)
},
// TODO: this error needs fixing
ViewAccountState::NotAuthorized {} => {
html! {{"You're not authorized to view this page!"}}
}
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminViewServiceAccountMsg::Responded { response } => {
self.state = ViewAccountState::Responded { response }
}
AdminViewServiceAccountMsg::Failed { emsg, kopid } => {
self.state = ViewAccountState::Failed { emsg, kopid }
}
}
true
}
}
/// pull the details for a single person by UUID
pub async fn get_person(uuid: &str) -> Result<AdminViewPersonMsg, GetError> {
let (_, _, value, _) = match do_request(
format!("/v1/person/{}", uuid).as_str(),
RequestMethod::GET,
None::<JsValue>,
)
.await
{
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
})
}
};
let data: Entity = match serde_wasm_bindgen::from_value(value) {
Ok(value) => value,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
});
}
};
Ok(AdminViewPersonMsg::Responded { response: data })
}
/// pull the details for a single service_account by UUID
pub async fn get_service_account(uuid: &str) -> Result<AdminViewServiceAccountMsg, GetError> {
let (_, _, value, _) = match do_request(
format!("/v1/service_account/{}", uuid).as_str(),
RequestMethod::GET,
None::<JsValue>,
)
.await
{
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!(
"Failed to grab the service_account data into JSON: {:?}",
error
),
})
}
};
let data: Entity = match serde_wasm_bindgen::from_value(value) {
Ok(value) => value,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
});
}
};
Ok(AdminViewServiceAccountMsg::Responded { response: data })
}

View file

@ -1,436 +0,0 @@
use std::collections::BTreeMap;
use gloo::console::{self};
use kanidmd_web_ui_shared::utils::{do_alert_error, do_page_header};
use wasm_bindgen::JsValue;
use yew::{html, Component, Context, Html, Properties};
use yew_router::prelude::Link;
use crate::components::admin_menu::{Entity, EntityType, GetError};
use crate::router::AdminRoute;
use kanidmd_web_ui_shared::constants::{CSS_CELL, CSS_TABLE};
use kanidmd_web_ui_shared::{alpha_warning_banner, do_request, RequestMethod};
#[cfg(debug_assertions)]
use serde::Serialize;
impl From<GetError> for AdminListGroupsMsg {
fn from(ge: GetError) -> Self {
AdminListGroupsMsg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
pub struct AdminListGroups {
state: GroupsViewState,
}
// callback messaging for this confused pile of crab-bait
pub enum AdminListGroupsMsg {
/// When the server responds and we need to update the page
Responded {
response: BTreeMap<String, Entity>,
},
Failed {
emsg: String,
kopid: Option<String>,
},
}
enum GroupsViewState {
/// waiting for the page to load
Loading,
/// server has responded
Responded { response: BTreeMap<String, Entity> },
/// failed to pull the details
#[allow(dead_code)]
Failed {
// TODO: use this
emsg: String,
kopid: Option<String>,
},
#[allow(dead_code)]
/// Not authorized to pull the details
NotAuthorized {}, // TODO: use this
}
#[derive(PartialEq, Properties, Eq)]
pub struct AdminListGroupsProps {
// for filtering and pagination
// #[allow(dead_code)]
// search: Option<String>,
// #[allow(dead_code)]
// page: Option<u32>,
}
/// Pulls all accounts (service or person-class) from the backend and returns a HashMap
/// with the "name" field being the keys, for easy human-facing sortability.
pub async fn get_groups() -> Result<AdminListGroupsMsg, GetError> {
let mut all_groups = BTreeMap::new();
// we iterate over these endpoints
let endpoints = [("/v1/group", EntityType::Group)];
for (endpoint, object_type) in endpoints {
let (_, _, value, _) = match do_request(endpoint, RequestMethod::GET, None::<JsValue>).await
{
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!("Failed to grab the group data into JSON: {:?}", error),
})
}
};
let data: Vec<Entity> = match serde_wasm_bindgen::from_value(value) {
Ok(value) => value,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
});
}
};
for entity in data.iter() {
let mut new_entity = entity.to_owned();
new_entity.object_type = object_type.clone();
// first we try the short name and, if that isn't there then just use the SPN...
#[allow(clippy::expect_used)]
let entity_id = match entity.attrs.name.first() {
Some(value) => value.to_string(),
None => entity
.attrs
.spn
.first()
.expect("Failed to grab the SPN for a group.")
.to_string(),
};
all_groups.insert(entity_id.to_string(), new_entity);
}
}
Ok(AdminListGroupsMsg::Responded {
response: all_groups,
})
}
impl Component for AdminListGroups {
type Message = AdminListGroupsMsg;
type Properties = AdminListGroupsProps;
fn create(ctx: &Context<Self>) -> Self {
// TODO: work out the querystring thing so we can just show x number of elements
// console::log!("query: {:?}", location().query);
// start pulling the account data on startup
ctx.link().send_future(async move {
match get_groups().await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminListGroups {
state: GroupsViewState::Loading,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminListGroupsMsg::Responded { response } => {
// TODO: do we paginate here?
#[cfg(debug_assertions)]
for key in response.keys() {
let j = response
.get(key)
.and_then(|k| {
k.serialize(&serde_wasm_bindgen::Serializer::json_compatible())
.ok()
})
.and_then(|jsv| js_sys::JSON::stringify(&jsv).ok().map(|s| s.into()))
.unwrap_or_else(|| "Failed to dump response key".to_string());
console::log!("response: {}", j);
}
self.state = GroupsViewState::Responded { response };
return true;
}
AdminListGroupsMsg::Failed { emsg, kopid } => {
// TODO: make this push a view state
console::log!("emsg: {:?}", emsg);
console::log!("kopid: {:?}", kopid);
}
}
false
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<>
{do_page_header("Group Administration")}
{ alpha_warning_banner() }
<div id={"grouplist"}>
{self.view_state(ctx)}
</div>
</>
}
}
}
impl AdminListGroups {
/// output the information based on what's in the current state
fn view_state(&self, _ctx: &Context<Self>) -> Html {
match &self.state {
GroupsViewState::Loading => {
html! {"Waiting on the groups list to load..."}
}
GroupsViewState::Responded { response } => {
let scope_col = "col";
html!(
<table class={CSS_TABLE}>
<thead>
<tr>
<th scope={scope_col}>{"Name"}</th>
<th scope={scope_col}>{"Description"}</th>
</tr>
</thead>
{
response.keys().map(|name| {
#[allow(clippy::expect_used)]
let group: &Entity = response.get(name).expect("Couldn't get group key when it was just in the iter...");
let description: String = match group.attrs.description.first() {
Some(value) => value.to_string(),
None => String::from(""),
};
let uuid: String = match group.attrs.uuid.first() {
Some(value) => value.to_string(),
None => {
console::error!("Group without a UUID?", format!("{:?}", group).to_string());
String::from("GROUP WITHOUT A UUID!")
}
};
html!{
<tr key={uuid.clone()}>
<td class={CSS_CELL} scope={scope_col}>
<Link<AdminRoute> to={AdminRoute::ViewGroup{id_or_name:{uuid.clone()}}} >{name}</Link<AdminRoute>></td>
<td class={CSS_CELL}>{description}</td>
</tr>
}
}).collect::<Html>()
}
</table>
)
}
GroupsViewState::Failed { emsg, kopid } => {
console::error!("Failed to pull details", format!("{:?}", kopid));
html!(
<>
{do_alert_error("Failed to Query Groups", Some(emsg), false)}
</>
)
}
GroupsViewState::NotAuthorized {} => {
do_alert_error("You're not authorized to see this page!", None, false)
}
}
}
}
#[derive(Properties, PartialEq, Eq, Clone)]
pub struct AdminViewGroupProps {
pub id_or_name: String,
}
// callback messaging for group detail view
pub enum AdminViewGroupMsg {
/// When the server responds and we need to update the page
Responded { response: Entity },
#[allow(dead_code)]
Failed { emsg: String, kopid: Option<String> },
#[allow(dead_code)]
NotAuthorized {},
}
impl From<GetError> for AdminViewGroupMsg {
fn from(ge: GetError) -> Self {
AdminViewGroupMsg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
enum GroupViewState {
/// waiting for the page to load
Loading,
/// server has responded
Responded { response: Entity },
/// failed to pull the details
#[allow(dead_code)]
Failed {
// TODO: use this
emsg: String,
kopid: Option<String>,
},
#[allow(dead_code)]
/// Not authorized to pull the details
NotAuthorized {}, // TODO: use this
}
pub struct AdminViewGroup {
state: GroupViewState,
}
impl Component for AdminViewGroup {
type Message = AdminViewGroupMsg;
type Properties = AdminViewGroupProps;
fn create(ctx: &Context<Self>) -> Self {
let id_or_name = ctx.props().id_or_name.clone();
ctx.link().send_future(async move {
match get_group(&id_or_name).await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminViewGroup {
state: GroupViewState::Loading,
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
match &self.state {
GroupViewState::Loading => html! {"Loading..."},
GroupViewState::Responded { response } => {
let group_name = match response.attrs.name.first() {
Some(value) => value.as_str(),
None => {
// TODO: this should throw an error
"No Group Name?"
}
};
let page_title = format!("Group: {}", group_name);
let group_uuid = match response.attrs.uuid.first() {
Some(value) => value.clone(),
None => String::from("Error querying UUID!"),
};
html! {
<>
{do_page_header(&page_title)}
<p>{"UUID: "}{group_uuid}</p>
// membership
// TODO: need to pull the member details so we can identify what they are
{
if !response.attrs.member.is_empty() {
html!{
<>
<h3>{"Members"}</h3>
<ul>
{
response.attrs.member.iter().map(|group| {
html!{
<li>{group}</li>
}
}).collect::<Html>()
}
</ul>
</>
}
} else {
html!{<></>}
}
}
// this group is a member of the below
// TODO: need to pull the names so we can identify what they are
{
if !response.attrs.memberof.is_empty() {
html!{
<>
<h3>{"Group is a member of the following"}</h3>
<ul>
{
response.attrs.memberof.iter().map(|group| {
html!{
<li>
{group}
</li>
}
}).collect::<Html>()
}
</ul>
</>
}
} else {
html!{<></>}
}
}
</>
}
}
GroupViewState::Failed { emsg, kopid } => do_alert_error(
emsg,
Some(
kopid
.as_ref()
.unwrap_or(&String::from("unknown operation ID")),
),
false,
),
GroupViewState::NotAuthorized {} => {
do_alert_error("You are not authorized to view this page!", None, false)
}
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminViewGroupMsg::Responded { response } => {
self.state = GroupViewState::Responded { response };
true
}
AdminViewGroupMsg::Failed { emsg, kopid } => {
self.state = GroupViewState::Failed { emsg, kopid };
true
}
AdminViewGroupMsg::NotAuthorized {} => {
self.state = GroupViewState::NotAuthorized {};
true
}
}
}
}
/// pull the details for a single group by UUID
pub async fn get_group(groupid: &str) -> Result<AdminViewGroupMsg, GetError> {
let endpoint = format!("/v1/group/{}", groupid);
let (_, _, value, _) = match do_request(&endpoint, RequestMethod::GET, None::<JsValue>).await {
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
})
}
};
let data: Entity = match serde_wasm_bindgen::from_value(value) {
Ok(value) => value,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
});
}
};
Ok(AdminViewGroupMsg::Responded { response: data })
}

View file

@ -1,146 +0,0 @@
use kanidmd_web_ui_shared::alpha_warning_banner;
use serde::{Deserialize, Serialize};
use yew::{html, Component, Context, Html, Properties};
use yew_router::prelude::Link;
use kanidmd_web_ui_shared::constants::{
CSS_CARD, CSS_CARD_BODY, CSS_LINK_DARK_STRETCHED, CSS_PAGE_HEADER,
};
// use crate::error::FetchError;
use crate::router::AdminRoute;
#[derive(Eq, PartialEq, Properties)]
pub struct Props;
// #[derive(PartialEq, Properties)]
// pub struct ListProps {
// #[allow(dead_code)]
// search: Option<String>,
// #[allow(dead_code)]
// page: Option<u32>,
// }
pub struct AdminMenu;
impl Component for AdminMenu {
type Message = ();
type Properties = Props;
fn create(_ctx: &Context<Self>) -> Self {
AdminMenu
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<>
<div class={CSS_PAGE_HEADER}>
<h2>{ "System Administration" }</h2>
</div>
{ alpha_warning_banner() }
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
<div class="col">
<div class={CSS_CARD}>
<Link<AdminRoute> classes={CSS_LINK_DARK_STRETCHED} to={AdminRoute::AdminListAccounts}>
<img src={"/pkg/img/icon-accounts.svg"} />
</Link<AdminRoute>>
<div class={CSS_CARD_BODY}>
<h3>
<Link<AdminRoute> classes={CSS_LINK_DARK_STRETCHED} to={AdminRoute::AdminListAccounts}>
{ "Accounts" }
</Link<AdminRoute>>
</h3>
</div>
</div>
</div>
// card for groups
<div class="col">
<div class={CSS_CARD}>
<Link<AdminRoute> classes={CSS_LINK_DARK_STRETCHED} to={AdminRoute::AdminListGroups}>
<img src={"/pkg/img/icon-groups.svg"} />
</Link<AdminRoute>>
<div class={CSS_CARD_BODY}>
<h3>
<Link<AdminRoute> classes={CSS_LINK_DARK_STRETCHED} to={AdminRoute::AdminListGroups}>
{ "Groups" }
</Link<AdminRoute>>
</h3>
</div>
</div>
</div>
// card for oauth
<div class="col">
<div class={CSS_CARD}>
<Link<AdminRoute> classes={CSS_LINK_DARK_STRETCHED} to={AdminRoute::AdminListOAuth2}>
<img src={"/pkg/img/icon-oauth2.svg"} />
</Link<AdminRoute>>
<div class={CSS_CARD_BODY}>
<h3>
<Link<AdminRoute> classes={CSS_LINK_DARK_STRETCHED} to={AdminRoute::AdminListOAuth2}>
{ "OAuth Configurations" }
</Link<AdminRoute>>
</h3>
</div>
</div>
</div>
</div>
</>
}
}
}
// TODO: can this come from somewhere else more simply? Probably not, because reasons.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Attributes {
pub class: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub description: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub displayname: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub name: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub spn: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub uuid: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub oauth2_rs_name: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub oauth2_rs_origin: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub member: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub memberof: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum EntityType {
Person,
ServiceAccount,
Group,
OAuth2RP,
Unknown,
}
impl Default for EntityType {
fn default() -> Self {
Self::Unknown
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Entity {
pub attrs: Attributes,
#[serde(default)]
pub object_type: EntityType,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GetError {
pub err: String,
}

View file

@ -1,435 +0,0 @@
use std::collections::BTreeMap;
use gloo::console;
use wasm_bindgen::JsValue;
use yew::{html, Component, Context, Html, Properties};
use yew_router::prelude::Link;
use super::prelude::*;
use crate::components::admin_menu::{Entity, EntityType, GetError};
use crate::router::AdminRoute;
use kanidmd_web_ui_shared::constants::{CSS_CELL, CSS_TABLE};
#[cfg(debug_assertions)]
use serde::Serialize;
impl From<GetError> for AdminListOAuth2Msg {
fn from(ge: GetError) -> Self {
AdminListOAuth2Msg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
pub struct AdminListOAuth2 {
state: ListViewState,
}
// callback messaging for this confused pile of crab-bait
pub enum AdminListOAuth2Msg {
/// When the server responds and we need to update the page
Responded {
response: BTreeMap<String, Entity>,
},
Failed {
emsg: String,
kopid: Option<String>,
},
}
enum ListViewState {
/// waiting for the page to load
Loading,
/// server has responded
Responded { response: BTreeMap<String, Entity> },
/// failed to pull the details
#[allow(dead_code)]
Failed {
// TODO: use this
emsg: String,
kopid: Option<String>,
},
#[allow(dead_code)]
/// Not authorized to pull the details
NotAuthorized {}, // TODO: use this
}
#[derive(PartialEq, Properties, Eq)]
pub struct AdminListOAuth2Props {
// for filtering and pagination
// #[allow(dead_code)]
// search: Option<String>,
// #[allow(dead_code)]
// page: Option<u32>,
}
/// Pulls all OAuth2 RPs from the backend and returns a HashMap
/// with the "name" field being the keys, for easy human-facing sortability.
pub async fn get_entities() -> Result<AdminListOAuth2Msg, GetError> {
// TODO: the actual pulling and turning into a BTreeMap across the admin systems could *probably* be rolled up into one function? The result object differs but all the query bits are the same.
let mut oauth2_objects = BTreeMap::new();
// we iterate over these endpoints
let endpoints = [("/v1/oauth2", EntityType::OAuth2RP)];
for (endpoint, object_type) in endpoints {
let (_, _, value, _) = match do_request(endpoint, RequestMethod::GET, None::<JsValue>).await
{
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
})
}
};
let data: Vec<Entity> = match serde_wasm_bindgen::from_value(value) {
Ok(value) => value,
Err(error) => {
return Err(GetError {
err: format!("Failed to grab the oauth2 data into JSON: {:?}", error),
});
}
};
for entity in data.into_iter() {
let mut new_entity = entity.to_owned();
new_entity.object_type = object_type.clone();
// first we try the uuid and if that isn't there oh no.
#[allow(clippy::expect_used)]
let entity_id = entity
.attrs
.uuid
.first()
.map(|e| e.to_owned())
.unwrap_or(String::from("Unknown entity name!"));
oauth2_objects.insert(entity_id, new_entity);
}
}
Ok(AdminListOAuth2Msg::Responded {
response: oauth2_objects,
})
}
impl Component for AdminListOAuth2 {
type Message = AdminListOAuth2Msg;
type Properties = AdminListOAuth2Props;
fn create(ctx: &Context<Self>) -> Self {
// TODO: work out the querystring thing so we can just show x number of elements
// start pulling the data on startup
ctx.link().send_future(async move {
match get_entities().await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminListOAuth2 {
state: ListViewState::Loading,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminListOAuth2Msg::Responded { response } => {
// TODO: do we paginate here?
#[cfg(debug_assertions)]
for key in response.keys() {
let j = response
.get(key)
.and_then(|k| {
k.serialize(&serde_wasm_bindgen::Serializer::json_compatible())
.ok()
})
.and_then(|jsv| js_sys::JSON::stringify(&jsv).ok().map(|s| s.into()))
.unwrap_or_else(|| "Failed to dump response key".to_string());
console::log!("response: {}", j);
}
self.state = ListViewState::Responded { response };
return true;
}
AdminListOAuth2Msg::Failed { emsg, kopid } => {
console::log!("emsg: {:?}", emsg);
console::log!("kopid: {:?}", kopid);
}
}
false
}
fn view(&self, _ctx: &Context<Self>) -> Html {
match &self.state {
ListViewState::Loading => {
html! {"Waiting on the OAuth2 data to load..."}
}
ListViewState::Responded { response } => {
let scope_col = "col";
html! {
<>
{do_page_header("OAuth2")}
{ alpha_warning_banner() }
<div id={"accountlist"}>
<table class={CSS_TABLE}>
<thead>
<tr>
<th scope={scope_col}>{"Display Name"}</th>
<th scope={scope_col}>{"Username"}</th>
<th scope={scope_col}>{"Description"}</th>
</tr>
</thead>
{
response.keys().map(|uuid| {
#[allow(clippy::expect_used)]
let oauth2_object: &Entity = response.get(uuid).expect("Couldn't get oauth2 key when it was just in the iter...");
let display_name: String = match oauth2_object.attrs.displayname.first() {
Some(value) => value.to_string(),
None => String::from(""),
};
let description: String = match oauth2_object.attrs.description.first() {
Some(value) => value.to_string(),
None => String::from(""),
};
let uuid: String = match oauth2_object.attrs.uuid.first() {
Some(value) => value.to_string(),
None => {
console::error!("Config without a UUID?", format!("{:?}", oauth2_object).to_string());
String::from("Unknown UUID!")
}
};
let rs_link = match oauth2_object.attrs.oauth2_rs_name.first() {
Some(id_or_name) => {
html!{
<Link<AdminRoute> to={AdminRoute::ViewOAuth2RP{id_or_name: id_or_name.clone()}}>
{display_name}
</Link<AdminRoute>>
}
},
None => {
html!{{display_name}}
}
};
html!{
<tr key={uuid.clone()}>
<th scope={scope_col} class={CSS_CELL}>
{rs_link}
</th>
<td class={CSS_CELL}>{uuid}</td>
<td class={CSS_CELL}>{description}</td>
</tr>
}
}).collect::<Html>()
}
</table>
</div>
</>
}
}
ListViewState::Failed { emsg, kopid } => {
console::error!("Failed to pull details", format!("{:?}", kopid));
html!(
<>
{do_alert_error("Failed to Query OAuth2", Some(emsg), false)}
</>
)
}
ListViewState::NotAuthorized {} => {
do_alert_error("You're not authorized to see this page!", None, false)
}
}
}
}
impl From<GetError> for AdminViewOAuth2Msg {
fn from(ge: GetError) -> Self {
AdminViewOAuth2Msg::Failed {
emsg: ge.err,
kopid: None,
}
}
}
// callback messaging for this confused pile of crab-bait
pub enum AdminViewOAuth2Msg {
/// When the server responds and we need to update the page
Responded {
response: Entity,
},
Failed {
emsg: String,
kopid: Option<String>,
},
}
#[derive(PartialEq, Eq, Properties)]
pub struct AdminViewOAuth2Props {
pub id_or_name: String,
}
enum ViewState {
/// waiting for the page to load
Loading,
/// server has responded
Responded { response: Entity },
/// failed to pull the details
#[allow(dead_code)]
Failed {
// TODO: use this
emsg: String,
kopid: Option<String>,
},
#[allow(dead_code)]
/// Not authorized to pull the details
NotAuthorized {}, // TODO: use this
}
pub struct AdminViewOAuth2 {
state: ViewState,
}
impl Component for AdminViewOAuth2 {
type Message = AdminViewOAuth2Msg;
type Properties = AdminViewOAuth2Props;
fn create(ctx: &Context<Self>) -> Self {
let rs_name = ctx.props().id_or_name.clone();
// start pulling the data on startup
ctx.link().send_future(async move {
match get_oauth2_rp(&rs_name).await {
Ok(v) => v,
Err(v) => v.into(),
}
});
AdminViewOAuth2 {
state: ViewState::Loading,
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
match &self.state {
ViewState::Loading => {
html! {"Waiting on the OAuth2 data to load..."}
}
ViewState::Responded { response } => {
let oauth2_object: &Entity = response;
let display_name: String = match oauth2_object.attrs.displayname.first() {
Some(value) => value.to_string(),
None => String::from("!error getting display name!"),
};
let description: String = match oauth2_object.attrs.description.first() {
Some(value) => value.to_string(),
None => String::from(""),
};
let oauth2_rs_name: String = match oauth2_object.attrs.oauth2_rs_name.first() {
Some(value) => value.to_string(),
None => String::from("!error getting oauth2_rs_name!"),
};
let oauth2_rs_origin: String = match oauth2_object.attrs.oauth2_rs_origin.first() {
Some(value) => value.to_string(),
None => String::from("!error getting oauth2_rs_origin!"),
};
let uuid: String = match oauth2_object.attrs.uuid.first() {
Some(value) => value.to_string(),
None => {
console::error!("Config without a UUID?", format!("{:?}", oauth2_object));
String::from("Unknown UUID!")
}
};
html! {
<>
{do_page_header(display_name.as_str())}
{alpha_warning_banner()}
<p>{"UUID: "}{uuid}</p>
<p>{description}</p>
<p>{"RS Name: "}{oauth2_rs_name}</p>
<p>{"Origin: "}{oauth2_rs_origin}</p>
</>
}
}
ViewState::Failed { emsg, kopid } => {
console::error!("Failed to pull details", format!("{:?}", kopid));
html!(
<>
{do_alert_error("Failed to Query OAuth2", Some(emsg), false)}
</>
)
}
ViewState::NotAuthorized {} => {
do_alert_error("You're not authorized to see this page!", None, false)
}
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
AdminViewOAuth2Msg::Responded { response } => {
// TODO: do we paginate here?
/*
// Seems broken
#[cfg(debug_assertions)]
for key in response.keys() {
console::log!(
"response: {:?}",
serde_json::to_string(response.get(key).unwrap()).unwrap()
);
}
*/
self.state = ViewState::Responded { response };
}
AdminViewOAuth2Msg::Failed { emsg, kopid } => {
console::log!("emsg: {:?}", &emsg);
console::log!("kopid: {:?}", kopid.to_owned());
self.state = ViewState::Failed { emsg, kopid };
}
}
true
}
}
pub async fn get_oauth2_rp(rs_name: &str) -> Result<AdminViewOAuth2Msg, GetError> {
let endpoint = format!("/v1/oauth2/{}", rs_name);
let (_, _, value, _) = match do_request(&endpoint, RequestMethod::GET, None::<JsValue>).await {
Ok(val) => val,
Err(error) => {
return Err(GetError {
err: format!("{:?}", error),
})
}
};
let data: Entity = match serde_wasm_bindgen::from_value(value) {
Ok(value) => {
console::log!(format!("{:?}", value));
value
}
Err(error) => {
//TODO: turn this into an error, and handle when we aren't authorized. The server doesn't seem to be sending back anything nice for this, which is.. painful.
console::log!(
"Failed to grab the OAuth2 RP data into JSON:",
format!("{:?}", error)
);
return Err(GetError {
err: format!("Failed to grab the OAuth2 RP data into JSON: {:?}", error),
});
}
};
Ok(AdminViewOAuth2Msg::Responded { response: data })
}

View file

@ -1,340 +0,0 @@
use enum_iterator::{all, Sequence};
#[cfg(debug_assertions)]
use gloo::console;
use serde::Serialize;
use std::fmt::{Display, Formatter};
use yew::prelude::*;
use kanidm_proto::internal::Filter::{Eq, Or};
use kanidm_proto::internal::{SearchRequest, SearchResponse};
use kanidm_proto::v1::Entry;
use kanidmd_web_ui_shared::constants::CSS_PAGE_HEADER;
use kanidmd_web_ui_shared::{do_request, error::FetchError, RequestMethod};
use wasm_bindgen::prelude::*;
use web_sys::HtmlInputElement;
use yew_router::Routable;
use crate::router::AdminRoute;
use kanidmd_web_ui_shared::ui::{error_page, loading_spinner};
use kanidmd_web_ui_shared::utils::{init_graphviz, open_blank};
pub enum Msg {
NewFilters { filters: Vec<ObjectType> },
NewObjects { entries: Vec<Entry> },
Error { emsg: String, kopid: Option<String> },
}
impl From<FetchError> for Msg {
fn from(fe: FetchError) -> Self {
Msg::Error {
emsg: fe.as_string(),
kopid: None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Sequence)]
pub enum ObjectType {
Group,
BuiltinGroup,
ServiceAccount,
Person,
}
impl Display for ObjectType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let str = match self {
ObjectType::Group => "Group",
ObjectType::BuiltinGroup => "Built In Group",
ObjectType::ServiceAccount => "Service Account",
ObjectType::Person => "Person",
};
write!(f, "{str}")
}
}
impl TryFrom<String> for ObjectType {
type Error = ();
fn try_from(value: String) -> Result<Self, Self::Error> {
all::<ObjectType>()
.find(|x| format!("{x}") == value)
.ok_or(())
}
}
pub enum State {
Waiting,
Ready { entries: Vec<Entry> },
Error { emsg: String, kopid: Option<String> },
}
pub struct AdminObjectGraph {
state: State,
filters: Vec<ObjectType>,
}
impl Component for AdminObjectGraph {
type Message = Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
#[cfg(debug_assertions)]
console::debug!("views::objectgraph::create");
ctx.link()
.send_future(async { Self::fetch_objects().await.unwrap_or_else(|v| v.into()) });
let state = State::Waiting;
AdminObjectGraph {
state,
filters: vec![],
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
#[cfg(debug_assertions)]
console::debug!("views::objectgraph::update");
match msg {
Msg::NewObjects { entries } => {
#[cfg(debug_assertions)]
console::debug!("Received new objects");
self.state = State::Ready { entries }
}
Msg::NewFilters { filters } => {
#[cfg(debug_assertions)]
console::debug!("Received new filters");
self.filters = filters;
}
Msg::Error { emsg, kopid } => self.state = State::Error { emsg, kopid },
}
true
}
fn changed(&mut self, _ctx: &Context<Self>, _props: &Self::Properties) -> bool {
#[cfg(debug_assertions)]
console::debug!("views::objectgraph::changed");
false
}
fn view(&self, ctx: &Context<Self>) -> Html {
match &self.state {
State::Waiting => self.view_waiting(),
State::Ready { entries } => self.view_ready(ctx, &self.filters, entries),
State::Error { emsg, kopid } => self.view_error(ctx, emsg, kopid.as_deref()),
}
}
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
#[cfg(debug_assertions)]
console::debug!("views::objectgraph::rendered");
}
}
impl AdminObjectGraph {
fn view_waiting(&self) -> Html {
loading_spinner()
}
fn view_ready(&self, ctx: &Context<Self>, filters: &[ObjectType], entries: &[Entry]) -> Html {
let typed_entries = entries
.iter()
.filter_map(|entry| {
let classes = entry.attrs.get("class")?;
let uuid = entry.attrs.get("uuid")?.first()?;
// Logic to decide the type of each entry
let obj_type = if classes.contains(&"group".to_string()) {
if uuid.starts_with("00000000-0000-0000-0000-") {
ObjectType::BuiltinGroup
} else {
ObjectType::Group
}
} else if classes.contains(&"account".to_string()) {
if classes.contains(&"person".to_string()) {
ObjectType::Person
} else if classes.contains(&"service-account".to_string()) {
ObjectType::ServiceAccount
} else {
return None;
}
} else {
return None;
};
// Filter out the things we want to keep, if the filter is empty we assume we want all.
if !filters.contains(&obj_type) && !filters.is_empty() {
return None;
}
let spn = entry.attrs.get("spn")?.first()?;
Some((spn.clone(), uuid.clone(), obj_type))
})
.collect::<Vec<(String, String, ObjectType)>>();
// Vec<obj, uuid, obj's members>
let members_of = entries
.iter()
.filter_map(|entry| {
let spn = entry.attrs.get("spn")?.first()?.clone();
let uuid = entry.attrs.get("uuid")?.first()?.clone();
let keep = typed_entries
.iter()
.any(|(_, filtered_uuid, _)| &uuid == filtered_uuid);
if keep {
Some((spn, uuid, entry.attrs.get("member")?.clone()))
} else {
None
}
})
.collect::<Vec<_>>();
// Constructing graph source
let mut sb = String::new();
sb.push_str("digraph {\n rankdir=\"RL\"\n");
for (spn, _, members) in members_of {
members
.iter()
.filter(|member| typed_entries.iter().any(|(spn, _, _)| spn == *member))
.for_each(|member| {
sb.push_str(format!(r#" "{spn}" -> "{member}"{}"#, "\n").as_str());
});
}
for (spn, uuid, obj_type) in typed_entries {
let (color, shape, route) = match obj_type {
ObjectType::Group => ("#b86367", "box", AdminRoute::ViewGroup { id_or_name: uuid }),
ObjectType::BuiltinGroup => (
"#8bc1d6",
"component",
AdminRoute::ViewGroup { id_or_name: uuid },
),
ObjectType::ServiceAccount => (
"#77c98d",
"parallelogram",
AdminRoute::ViewServiceAccount { id_or_name: uuid },
),
ObjectType::Person => (
"#af8bd6",
"ellipse",
AdminRoute::ViewPerson { id_or_name: uuid },
),
};
let url = route.to_path();
sb.push_str(
format!(
r#" "{spn}" [color = "{color}", shape = {shape}, URL = "{url}"]{}"#,
"\n"
)
.as_str(),
);
}
sb.push('}');
init_graphviz(sb.as_str());
let node_refs = all::<ObjectType>()
.map(|object_type: ObjectType| (object_type, NodeRef::default()))
.collect::<Vec<_>>();
let on_checkbox_click = {
let scope = ctx.link().clone();
let node_refs = node_refs.clone();
move |_: Event| {
let mut filters = vec![];
for (obj_type, node_ref) in &node_refs {
if let Some(input_el) = node_ref.cast::<HtmlInputElement>() {
let checked = input_el.checked();
if checked {
filters.push(*obj_type);
}
}
}
scope.send_message(Msg::NewFilters { filters });
}
};
let view_graph_source = {
move |_: MouseEvent| {
open_blank(sb.as_str());
}
};
html! {
<>
<div class={CSS_PAGE_HEADER}>
<h2>{ "ObjectGraph view" }</h2>
</div>
if entries.is_empty() {
<div>
<h5>{ "No graph objects for the applied filters." }</h5>
</div>
} else {
<div class="column">
<div class="hstack gap-3">
{
node_refs.iter().map(|(ot, node_ref)| {
let ot_str = format!("{}", ot);
let selected = filters.contains(ot);
html! {
<>
<div class="form-check">
<input class="form-check-input obj-graph-filter-cb" type="checkbox" ref={ node_ref } id={ot_str.clone()} onchange={on_checkbox_click.clone()} checked={selected}/>
<label class="form-check-label" for={ot_str.clone()}>{ot_str.clone()}</label>
</div>
if *ot != ObjectType::last().unwrap() {
<div class="vr"></div>
}
</>
}
}).collect::<Html>()
}
</div>
<button class="btn btn-primary mt-2" onclick={view_graph_source}>{ "View graph source" }</button>
</div>
<div id="graph-container" class="mt-3"></div>
}
</>
}
}
fn view_error(&self, _ctx: &Context<Self>, msg: &str, kopid: Option<&str>) -> Html {
error_page(msg, kopid)
}
async fn fetch_objects() -> Result<Msg, FetchError> {
let req_body = SearchRequest {
filter: Or(vec![
Eq("class".to_string(), "person".to_string()),
Eq("class".to_string(), "service_account".to_string()),
Eq("class".to_string(), "group".to_string()),
]),
};
let req_jsvalue = req_body
.serialize(&serde_wasm_bindgen::Serializer::json_compatible())
.expect("Failed to serialise request");
let req_jsvalue = js_sys::JSON::stringify(&req_jsvalue).expect_throw("failed to stringify");
let (kopid, status, value, _) =
do_request("/v1/raw/search", RequestMethod::POST, Some(req_jsvalue)).await?;
if status == 200 {
let search_resp: SearchResponse = serde_wasm_bindgen::from_value(value)
.expect_throw("Invalid response type - objectgraph::SearchRequest");
Ok(Msg::NewObjects {
entries: search_resp.entries,
})
} else {
let emsg = value.as_string().unwrap_or_default();
Ok(Msg::Error { emsg, kopid })
}
}
}

View file

@ -1,12 +0,0 @@
pub mod admin_accounts;
pub mod admin_groups;
pub mod admin_menu;
pub mod admin_oauth2;
pub mod admin_objectgraph;
mod prelude {
pub use kanidmd_web_ui_shared::alpha_warning_banner;
pub use kanidmd_web_ui_shared::utils::{do_alert_error, do_page_header};
pub use kanidmd_web_ui_shared::{do_request, RequestMethod};
}

View file

@ -1,153 +0,0 @@
mod components;
mod router;
use gloo::console::{self, error};
use kanidmd_web_ui_shared::add_body_form_classes;
use kanidmd_web_ui_shared::constants::{
CSS_NAVBAR_BRAND, CSS_NAVBAR_LINKS_UL, CSS_NAVBAR_NAV, CSS_NAV_LINK, ID_NAVBAR_COLLAPSE,
IMG_LOGO_SQUARE, URL_USER_HOME,
};
use kanidmd_web_ui_shared::ui::{signout_link, signout_modal, ui_logout};
use kanidmd_web_ui_shared::utils::do_footer;
#[allow(unused_imports)] // because it's needed to compile wasm things
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
use yew::{html, Component, Context, Html};
use yew_router::prelude::Link;
use yew_router::{BrowserRouter, Switch};
use crate::router::AdminRoute;
pub struct AdminApp {}
/// This builds the navbar, it's not generic because the link on the logo is different
fn make_navbar(links: Vec<Html>) -> Html {
html! {
<nav class={CSS_NAVBAR_NAV}>
<div class="container-fluid">
<a href={URL_USER_HOME} class={CSS_NAVBAR_BRAND}>
{"Kanidm Administration"}
</a>
// this shows a button on mobile devices to open the menu
<button class="navbar-toggler bg-white" type="button" data-bs-toggle="collapse" data-bs-target={["#", ID_NAVBAR_COLLAPSE].concat()} aria-controls={ID_NAVBAR_COLLAPSE} aria-expanded="false" aria-label="Toggle navigation">
<img src={IMG_LOGO_SQUARE} alt="Toggle navigation" class="navbar-toggler-img" />
</button>
<div class="collapse navbar-collapse" id={ID_NAVBAR_COLLAPSE}>
<ul class={CSS_NAVBAR_LINKS_UL}>
{ links.into_iter().map(|link| {
html!{ <li class="mb-1">
{ link }
</li>
} }).collect::<Html>()
}
</ul>
</div>
</div>
</nav>
}
}
#[derive(Clone, Debug)]
pub enum AdminViewsMsg {
Logout,
LogoutComplete,
}
impl Component for AdminApp {
type Message = AdminViewsMsg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
#[cfg(debug_assertions)]
console::debug!("manager::create");
AdminApp {}
}
fn changed(&mut self, _ctx: &Context<Self>, _props: &Self::Properties) -> bool {
#[cfg(debug_assertions)]
console::debug!("manager::change");
false
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
#[cfg(debug_assertions)]
console::debug!("manager::update");
match msg {
AdminViewsMsg::Logout => {
console::debug!("manager::update -> logout");
ctx.link().send_future(async {
match Self::fetch_logout().await {
Ok(v) => v,
Err(v) => {
error!("... failed to log out? {:?}", v);
AdminViewsMsg::Logout
}
}
});
}
AdminViewsMsg::LogoutComplete => {
let window = gloo_utils::window();
window.location().set_href(URL_USER_HOME).unwrap();
}
}
true
}
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
#[cfg(debug_assertions)]
console::debug!("manager::rendered");
}
fn view(&self, ctx: &Context<Self>) -> Html {
add_body_form_classes!();
let links = vec![
html! {<a href={URL_USER_HOME} class={CSS_NAV_LINK}>{"Home"}</a>},
html! {<Link<AdminRoute> classes={CSS_NAV_LINK} to={AdminRoute::AdminMenu}>{"Admin"}</Link<AdminRoute>>},
html! {<Link<AdminRoute> classes={CSS_NAV_LINK} to={AdminRoute::AdminListAccounts}>{"Accounts"}</Link<AdminRoute>>},
html! {<Link<AdminRoute> classes={CSS_NAV_LINK} to={AdminRoute::AdminListGroups}>{"Groups"}</Link<AdminRoute>>},
html! {<Link<AdminRoute> classes={CSS_NAV_LINK} to={AdminRoute::AdminObjectGraph}>{"ObjectGraph"}</Link<AdminRoute>>},
html! {<Link<AdminRoute> classes={CSS_NAV_LINK} to={AdminRoute::AdminListOAuth2}>{"OAuth2"}</Link<AdminRoute>>},
signout_link(),
];
html! {
<BrowserRouter>
// sign out modal dialogue box
{signout_modal(ctx, AdminViewsMsg::Logout)}
{make_navbar(links)}
<main class="p-3 x-auto">
<Switch<AdminRoute> render={ router::switch } />
</main>
{ do_footer() }
</BrowserRouter>
}
}
}
impl AdminApp {
async fn fetch_logout() -> Result<AdminViewsMsg, String> {
match ui_logout().await {
Ok(_) => Ok(AdminViewsMsg::LogoutComplete),
Err((emsg, _kopid)) => {
error!("failed to process logout request: {}", emsg);
Ok(AdminViewsMsg::Logout)
}
}
}
}
/// This is the entry point of the web front end. This triggers the manager app to load and begin
/// its event loop.
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub fn run_app() -> Result<(), JsValue> {
yew::Renderer::<AdminApp>::new().render();
Ok(())
}

View file

@ -1,72 +0,0 @@
#![allow(clippy::disallowed_types)] // because `Routable` uses a hashmap
use serde::{Deserialize, Serialize};
use yew::{html, Html};
use yew_router::prelude::Redirect;
use yew_router::Routable;
use crate::components;
#[derive(Routable, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub enum AdminRoute {
#[at("/ui/admin")]
AdminMenu,
#[at("/ui/admin/groups")]
AdminListGroups,
#[at("/ui/admin/accounts")]
AdminListAccounts,
#[at("/ui/admin/object-graph")]
AdminObjectGraph,
#[at("/ui/admin/oauth2")]
AdminListOAuth2,
#[at("/ui/admin/group/:id_or_name")]
ViewGroup { id_or_name: String },
#[at("/ui/admin/person/:id_or_name")]
ViewPerson { id_or_name: String },
#[at("/ui/admin/service_account/:id_or_name")]
ViewServiceAccount { id_or_name: String },
#[at("/ui/admin/oauth2/:id_or_name")]
ViewOAuth2RP { id_or_name: String },
#[not_found]
#[at("/ui/admin/404")]
NotFound,
}
pub(crate) fn switch(route: AdminRoute) -> Html {
match route {
AdminRoute::AdminMenu => html! {
<components::admin_menu::AdminMenu />
},
AdminRoute::AdminListAccounts => html!(
<components::admin_accounts::AdminListAccounts />
),
AdminRoute::AdminListGroups => html!(
<components::admin_groups::AdminListGroups />
),
AdminRoute::AdminObjectGraph => html!(
<components::admin_objectgraph::AdminObjectGraph />
),
AdminRoute::AdminListOAuth2 => html!(
<components::admin_oauth2::AdminListOAuth2 />
),
AdminRoute::ViewGroup { id_or_name } => {
html!(<components::admin_groups::AdminViewGroup id_or_name={id_or_name} />)
}
AdminRoute::ViewPerson { id_or_name } => html!(
<components::admin_accounts::AdminViewPerson id_or_name={id_or_name} />
),
AdminRoute::ViewServiceAccount { id_or_name } => html!(
<components::admin_accounts::AdminViewServiceAccount id_or_name={id_or_name} />
// html! {<></>}
),
AdminRoute::ViewOAuth2RP { id_or_name } => html! {
<components::admin_oauth2::AdminViewOAuth2 id_or_name={id_or_name} />
},
AdminRoute::NotFound => html! (
<Redirect<AdminRoute> to={AdminRoute::NotFound}/>
),
}
}

View file

@ -1,7 +0,0 @@
// loads the module which loads the WASM. It's loaders all the way down.
import init, { run_app } from '/pkg/kanidmd_web_ui_admin.js';
async function main() {
await init('/pkg/kanidmd_web_ui_admin_bg.wasm');
run_app();
}
main()

View file

@ -1,72 +0,0 @@
#!/bin/sh
set -e
# This builds the assets for the Web UI, defaulting to a release build.
if [ ! -f build_wasm.sh ]; then
echo "Please run from the crate directory. (server/web_ui)"
exit 1
fi
if [ -z "$(which rsync)" ]; then
echo "Cannot find rsync which is needed to move things around, quitting!"
exit 1
fi
if [ -z "$(which wasm-pack)" ]; then
echo "Cannot find wasm-pack which is needed to build the UI, quitting!"
exit 1
fi
if [ -z "$(which wasm-bindgen)" ]; then
echo "Cannot find wasm-bindgen which is needed to build the UI, quitting!"
exit 1
fi
if [ -z "$(which bc)" ]; then
echo "Cannot find bc which is needed to build the UI, quitting!"
exit 1
fi
if [ -z "${BUILD_FLAGS}" ]; then
export BUILD_FLAGS="--release"
fi
echo "Cleaning up pkg dir"
find pkg/ -type f -delete
find pkg/ -mindepth 1 -type d -delete
touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_IN_BUILDS
if [ -f ./pkg/.gitignore ]; then
rm ./pkg/.gitignore
fi
# copy the shared static things
rsync -av shared/static/* admin/static/* user/static/* login_flows/static/* pkg/
cd admin
echo "building admin"
../individual_build.sh || exit 1
cd ..
echo "done building admin"
cd login_flows
echo "building login_flows"
../individual_build.sh || exit 1
cd ..
echo "done building login_flows"
cd user
echo "building user"
../individual_build.sh || exit 1
cd ..
echo "done building user"
if [ -z "${SKIP_BROTLI}" ]; then
# updates the brotli-compressed files
echo "brotli-compressing compressible files over 16KB in size..."
find ./pkg -size +16k -type f \
-not -name '*.br' \
-not -name '*.png' \
-exec ./find_best_brotli.sh "{}" \; || exit 1
fi

View file

@ -1,2 +0,0 @@
#!/bin/sh
SKIP_BROTLI=1 BUILD_FLAGS="--dev" ./build_wasm.sh

View file

@ -1,64 +0,0 @@
#!/bin/bash
# check brotli's installed
if ! command -v brotli &> /dev/null
then
echo "brotli tool could not be found, please make sure you have it in your path!"
exit 1
fi
# Exit if no argument is provided
if [ $# -eq 0 ]; then
echo "No filename provided"
exit 1
fi
filename=$1
echo "#####################################"
echo "Compressing $1"
# Exit if the file doesn't exist
if [ ! -f "${filename}" ]; then
echo "File ${filename} not found"
exit 1
fi
original_size_kb=$(du -k "${filename}" | cut -f1)
tmpfile=$(mktemp)
for num in {10..24}
do
{
size=$(brotli --lgwin="${num}" -c "${filename}" | wc -c)
echo "${num} ${size}" >> "${tmpfile}"
} &
done
# Wait for all background jobs to finish
wait
# Process results
{
read -r min_num min_size
while read -r num size; do
if (( size < min_size )); then
min_size=$size
min_num=$num
fi
done
echo "Original size was ${original_size_kb}KB"
echo "Smallest compressed size was ${min_size} bytes for NUM=${min_num}"
} < "${tmpfile}"
# Clean up
rm "${tmpfile}"
# Use the smallest NUM value found in the test command
brotli --force --lgwin="${min_num}" "${filename}" -o "${filename}.br"
# find the difference in the file sizes
compressed_size_kb=$(du -k "${filename}.br" | cut -f1)
size_difference=$(bc <<< "${original_size_kb} - ${compressed_size_kb}")
echo "The compressed file is ${size_difference}KB smaller than the original file"

View file

@ -1,45 +0,0 @@
#!/bin/bash
set -e
if [ ! -f ../individual_build.sh ]; then
echo "Please run from the package base directory!"
exit 1
fi
if [ -z "${BUILD_FLAGS}" ]; then
BUILD_FLAGS="--release"
fi
if [ -z "$(which rsync)" ]; then
echo "Cannot find rsync which is needed to move things around, quitting!"
exit 1
fi
if [ -z "$(which wasm-pack)" ]; then
echo "Cannot find wasm-pack which is needed to build the UI, quitting!"
exit 1
fi
mkdir -p ./pkg
echo "Cleaning up WASM files before build..."
find ./pkg/ -name 'kanidmd*' -exec rm "{}" \;
# we can disable this since we want it to expand
# shellcheck disable=SC2086
wasm-pack build ${BUILD_FLAGS} --no-typescript --target web --no-pack
echo "######################"
echo "Moving files around..."
echo "######################"
touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_ADD_TO_SRC && \
rm ./pkg/.gitignore
echo "######################"
echo "Moving files up into the webui pkg dir..."
echo "######################"
rsync -av pkg/* ../pkg/
echo "######################"
echo " Done!"
echo "######################"

View file

@ -1,42 +0,0 @@
[package]
name = "kanidmd_web_ui_login_flows"
description = "Kanidm Server Web UI - Login Flows"
documentation = "https://docs.rs/kanidm/latest/kanidm/"
version = { workspace = true }
authors = [
"William Brown <william@blackhats.net.au>",
"James Hodgkinson <james@terminaloutcomes.com>",
]
rust-version = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
gloo = { workspace = true }
js-sys = { workspace = true }
kanidm_proto = { workspace = true, features = ["wasm"] }
kanidmd_web_ui_shared = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde-wasm-bindgen = { workspace = true }
wasm-bindgen = { workspace = true }
wasm-bindgen-futures = { workspace = true }
uuid = { workspace = true }
yew = { workspace = true, features = ["csr"] }
yew-router = { workspace = true }
time = { workspace = true }
gloo-utils = { workspace = true }
web-sys = { workspace = true, features = [
"CredentialsContainer",
"Location",
"Navigator",
"Window",
] }
[dev-dependencies]
wasm-bindgen-test = { workspace = true }

File diff suppressed because it is too large Load diff

View file

@ -1,73 +0,0 @@
//! This handles the login/auth flows, and is designed to be smol and snappy
//! so it loads fast and gets the user to where they need to go!
//!
//! - /ui/login
//! - /ui/oauth2
//! - /ui/reauth
mod components;
mod oauth2;
pub mod router;
use gloo::console;
use kanidmd_web_ui_shared::constants::URL_LOGIN;
use kanidmd_web_ui_shared::utils::window;
use router::LoginRoute;
#[allow(unused_imports)] // because it's needed to compile wasm things
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsValue, UnwrapThrowExt};
use yew::{html, Html};
use yew_router::{BrowserRouter, Switch};
use crate::components::{LoginApp, LoginWorkflow};
use crate::oauth2::Oauth2App;
// Needed for yew to pass by value
#[allow(clippy::needless_pass_by_value)]
/// Handle routes for the login_flows app
fn switch(route: LoginRoute) -> Html {
#[cfg(debug_assertions)]
console::debug!(format!("UserUiApp::switch -> {:?}", route).as_str());
match route {
LoginRoute::Login => html! {<LoginApp workflow={LoginWorkflow::Login} />},
LoginRoute::Reauth => html! {<LoginApp workflow={LoginWorkflow::Reauth} />},
LoginRoute::Oauth2 => html! {<Oauth2App />},
LoginRoute::NotFound => {
console::error!("Unknown route {}, showing login flow");
window()
.location()
.set_href(URL_LOGIN)
.expect_throw("Failed to redirect user to the login page!");
html! { <a href={URL_LOGIN}>{"Click here to return to the login page..."}</a> }
}
}
}
struct LoginFlowsApp {}
impl yew::Component for LoginFlowsApp {
type Message = ();
type Properties = ();
fn create(_ctx: &yew::Context<Self>) -> Self {
Self {}
}
fn view(&self, _ctx: &yew::Context<Self>) -> Html {
html! {
<BrowserRouter>
<Switch<LoginRoute> render={switch} />
</BrowserRouter>
}
}
}
/// This is the entry point of the web front end.
///
/// This triggers the manager app to load and begin its event loop.
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub fn run_app() -> Result<(), JsValue> {
yew::Renderer::<LoginFlowsApp>::new().render();
Ok(())
}

View file

@ -1,536 +0,0 @@
use gloo::console;
use kanidm_proto::constants::uri::{OAUTH2_AUTHORISE, OAUTH2_AUTHORISE_PERMIT};
use kanidm_proto::constants::{APPLICATION_JSON, KOPID};
pub use kanidm_proto::oauth2::{AuthorisationRequest, AuthorisationResponse};
use kanidmd_web_ui_shared::constants::{CONTENT_TYPE, CSS_ALERT_DANGER, URL_OAUTH2};
use kanidmd_web_ui_shared::utils::{do_alert_error, do_footer, window};
use kanidmd_web_ui_shared::{
add_body_form_classes, fetch_session_valid, logo_img, remove_body_form_classes, SessionStatus,
};
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, RequestRedirect, Response};
use yew::prelude::*;
use yew_router::prelude::*;
use super::router::LoginRoute;
use kanidmd_web_ui_shared::models::{
pop_oauth2_authorisation_request, push_login_hint, push_oauth2_authorisation_request,
push_return_location,
};
use kanidmd_web_ui_shared::{do_request, error::FetchError, utils, RequestMethod};
use serde::Serialize;
use std::collections::BTreeSet;
enum State {
LoginRequired,
// We are in the process of checking the auth token to be sure we can proceed.
TokenCheck,
// Token check done, lets do it.
SubmitAuthReq,
Consent {
client_name: String,
#[allow(dead_code)]
scopes: BTreeSet<String>,
pii_scopes: BTreeSet<String>,
consent_token: String,
},
ConsentGranted(String),
AccessDenied(Option<String>),
ErrInvalidRequest,
}
pub struct Oauth2App {
state: State,
}
#[derive(Debug)]
pub enum Oauth2Msg {
LoginRequired,
LoginProceed,
#[allow(dead_code)]
ConsentGranted(String),
TokenValid,
Consent {
client_name: String,
scopes: BTreeSet<String>,
pii_scopes: BTreeSet<String>,
consent_token: String,
},
Redirect(String),
AccessDenied {
kopid: Option<String>,
},
Error {
emsg: String,
kopid: Option<String>,
},
}
impl From<FetchError> for Oauth2Msg {
fn from(fe: FetchError) -> Self {
Oauth2Msg::Error {
emsg: fe.as_string(),
kopid: None,
}
}
}
impl From<SessionStatus> for Oauth2Msg {
fn from(value: SessionStatus) -> Self {
match value {
SessionStatus::TokenValid => Oauth2Msg::TokenValid,
SessionStatus::LoginRequired => Oauth2Msg::LoginRequired,
SessionStatus::Error { emsg, kopid } => Oauth2Msg::Error { emsg, kopid },
}
}
}
impl Oauth2App {
/// Validate that the current auth token's OK
async fn fetch_session_valid() -> Result<Oauth2Msg, FetchError> {
fetch_session_valid().await.map(|v| v.into())
}
async fn fetch_authreq(authreq: AuthorisationRequest) -> Result<Oauth2Msg, FetchError> {
let req_jsvalue_1 = authreq
.serialize(&serde_wasm_bindgen::Serializer::json_compatible())
.expect("Failed to serialise request");
let req_jsvalue =
js_sys::JSON::stringify(&req_jsvalue_1).expect_throw("failed to stringify");
#[cfg(debug_assertions)]
console::debug!(&format!("fetch_authreq post {}", req_jsvalue));
let (kopid, status, value, headers) =
do_request(OAUTH2_AUTHORISE, RequestMethod::POST, Some(req_jsvalue)).await?;
#[cfg(debug_assertions)]
console::debug!(&format!("fetch_authreq result {}", status));
if status == 200 {
let state: AuthorisationResponse = serde_wasm_bindgen::from_value(value)
.map_err(|e| {
let e_msg = format!("serde error -> {:?}", e);
console::error!(e_msg.as_str());
})
.expect_throw("Invalid response type");
match state {
AuthorisationResponse::ConsentRequested {
client_name,
scopes,
pii_scopes,
consent_token,
} => Ok(Oauth2Msg::Consent {
client_name,
scopes,
pii_scopes,
consent_token,
}),
AuthorisationResponse::Permitted => {
if let Some(loc) = headers.get("location").ok().flatten() {
Ok(Oauth2Msg::Redirect(loc))
} else {
Ok(Oauth2Msg::Error {
emsg: "no location header".to_string(),
kopid,
})
}
}
}
} else if status == 403 {
Ok(Oauth2Msg::AccessDenied { kopid })
} else {
let emsg = value.as_string().unwrap_or_default();
Ok(Oauth2Msg::Error { emsg, kopid })
}
}
async fn fetch_consent_token(consent_token: String) -> Result<Oauth2Msg, FetchError> {
let req_jsvalue = consent_token
.serialize(&serde_wasm_bindgen::Serializer::json_compatible())
.expect("Failed to serialise request");
let req_jsvalue = js_sys::JSON::stringify(&req_jsvalue).expect_throw("failed to stringify");
let opts = RequestInit::new();
opts.set_method("POST");
opts.set_mode(RequestMode::SameOrigin);
opts.set_redirect(RequestRedirect::Manual); // can't replace with do_request because of this
opts.set_body(&req_jsvalue);
let request = Request::new_with_str_and_init(OAUTH2_AUTHORISE_PERMIT, &opts)?;
request
.headers()
.set(CONTENT_TYPE, APPLICATION_JSON)
.expect_throw("failed to set header");
/*
if let Some(bearer_token) = get_bearer_token() {
request
.headers()
.set("authorization", &bearer_token)
.expect_throw("failed to set authorisation header");
}
*/
let window = utils::window();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
let status = resp.status();
let headers = resp.headers();
let kopid = headers.get(KOPID).ok().flatten();
if status == 200 {
if let Some(loc) = headers.get("location").ok().flatten() {
Ok(Oauth2Msg::Redirect(loc))
} else {
Ok(Oauth2Msg::Error {
emsg: "no location header".to_string(),
kopid,
})
}
} else {
let emsg = "Redirect error".to_string();
Ok(Oauth2Msg::Error { emsg, kopid })
}
}
}
impl Component for Oauth2App {
type Message = Oauth2Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
#[cfg(debug_assertions)]
console::debug!("oauth2::create");
// Do we have a query here?
// Did we get sent a valid Oauth2 request?
let location = ctx
.link()
.location()
.expect_throw("Can't access browser current location");
let query: Option<AuthorisationRequest> = location
.query()
.map_err(|e| {
let e_msg = format!(
"failed to decode authorisation request url parameters -> {:?}",
e
);
console::error!(e_msg.as_str());
})
.ok()
.or_else(|| {
console::log!("using previously storage oauth2 authorisation request if possible");
pop_oauth2_authorisation_request()
});
add_body_form_classes!();
// If we have neither we need to say that we can not proceed at all.
let query = match query {
Some(q) => q,
None => {
return Oauth2App {
state: State::ErrInvalidRequest,
};
}
};
console::debug!(format!("{query:?}",));
// In the query, if this is openid there MAY be a hint
// as to the users name.
// See: https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters
// specifically, login_hint
if let Some(login_hint) = query.oidc_ext.login_hint.clone() {
push_login_hint(login_hint)
}
// Push the request down. This covers if we move to LoginRequired so we can restore where
// we were / what we were doing.
push_oauth2_authorisation_request(query);
// Start the fetch req.
// Put the fetch handle into the consent type.
ctx.link().send_future(async {
match Self::fetch_session_valid().await {
Ok(v) => v,
Err(v) => v.into(),
}
});
Oauth2App {
state: State::TokenCheck,
}
}
fn changed(&mut self, _ctx: &Context<Self>, _props: &Self::Properties) -> bool {
#[cfg(debug_assertions)]
console::debug!("oauth2::change");
false
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
#[cfg(debug_assertions)]
console::debug!(&format!("oauth2::update {:?}", msg));
match msg {
Oauth2Msg::LoginRequired => {
self.state = State::LoginRequired;
true
}
Oauth2Msg::LoginProceed => {
let current_loc = window()
.location()
.as_string()
.unwrap_or(URL_OAUTH2.to_string());
push_return_location(&current_loc);
ctx.link()
.navigator()
.expect_throw("failed to read history")
.push(&LoginRoute::Login);
// Don't need to redraw as we are yolo-ing out.
false
}
Oauth2Msg::TokenValid => {
// Okay we can proceed, pop the query.
let ar = pop_oauth2_authorisation_request();
self.state = match (&self.state, ar) {
(State::TokenCheck, Some(ar)) => {
ctx.link().send_future(async {
match Self::fetch_authreq(ar).await {
Ok(v) => v,
Err(v) => v.into(),
}
});
State::SubmitAuthReq
}
_ => {
console::error!("Invalid state transition");
State::ErrInvalidRequest
}
};
true
}
Oauth2Msg::Consent {
client_name,
scopes,
pii_scopes,
consent_token,
} => {
self.state = match &self.state {
State::SubmitAuthReq => State::Consent {
client_name,
scopes,
pii_scopes,
consent_token,
},
_ => {
console::error!("Invalid state transition");
State::ErrInvalidRequest
}
};
true
}
Oauth2Msg::ConsentGranted(_) => {
self.state = match &self.state {
State::Consent {
consent_token,
client_name,
..
} => {
let cr_c = consent_token.clone();
ctx.link().send_future(async {
match Self::fetch_consent_token(cr_c).await {
Ok(v) => v,
Err(v) => v.into(),
}
});
State::ConsentGranted(client_name.to_string())
}
_ => {
console::error!("Invalid state transition");
State::ErrInvalidRequest
}
};
// We need to send off fetch task here.
true
}
Oauth2Msg::AccessDenied { kopid } => {
console::error!(format!("opid - {:?}", kopid).as_str());
self.state = State::AccessDenied(kopid);
true
}
Oauth2Msg::Error { emsg, kopid } => {
self.state = State::ErrInvalidRequest;
console::error!(format!("opid - {:?}, msg - {}", kopid, emsg).as_str());
true
}
Oauth2Msg::Redirect(loc) => {
#[cfg(debug_assertions)]
console::debug!(format!("Redirecting to {}", loc).as_str());
// Send the location here, and then update will trigger the redir via
// https://docs.rs/web-sys/0.3.51/web_sys/struct.Location.html#method.replace
// see https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
let location = utils::window().location();
match location.replace(loc.as_str()) {
// No need to redraw, we are leaving.
Ok(_) => false,
Err(e) => {
// Something went bang, oops.
console::error!(format!("{:?}", e).as_str());
self.state = State::ErrInvalidRequest;
true
}
}
}
}
}
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
#[cfg(debug_assertions)]
console::debug!("oauth2::rendered");
}
fn view(&self, ctx: &Context<Self>) -> Html {
#[cfg(debug_assertions)]
console::debug!("oauth2::view");
let body_content = match &self.state {
State::LoginRequired => {
// <body class="html-body form-body">
html! {
<form
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
console::debug!("oauth2::view -> LoginRequired - prevent_default()");
e.prevent_default();
Oauth2Msg::LoginProceed
} ) }
action="javascript:void(0);"
>
<h1 class="h3 mb-3 fw-normal">
// TODO: include the domain display name here, and the RS display name?
{"Sign in to proceed" }
</h1>
<button autofocus=true class="w-100 btn btn-lg btn-primary" type="submit">
{ "Sign in" }
</button>
</form>
}
}
State::Consent {
client_name,
scopes: _,
pii_scopes,
consent_token: _,
} => {
let client_name = client_name.clone();
let pii_req = if pii_scopes.is_empty() {
html! {
<div>
<p>{ "This site will not have access to your personal information." }</p>
<p>{ "If this site requests personal information in the future we will check with you." }</p>
</div>
}
} else {
html! {
<div>
<p>{ "This site has requested to see the following personal information." }</p>
<ul>
{
pii_scopes.iter().map(|s| html! { <li>{ s }</li> } ).collect::<Html>()
}
</ul>
<p>{ "If this site requests different personal information in the future we will check with you again." }</p>
</div>
}
};
// <body class="html-body form-body">
let app_name = client_name.clone();
html! {
<form
onsubmit={ ctx.link().callback(move |e: SubmitEvent| {
console::debug!("oauth2::view -> Consent - prevent_default()");
e.prevent_default();
Oauth2Msg::ConsentGranted(client_name.to_string())
} ) }
action="javascript:void(0);"
>
<h2 class="h3 mb-3 fw-normal">{"Consent to Proceed to " }{ app_name }</h2>
{ pii_req }
<div class="text-center">
<button autofocus=true class="w-100 btn btn-lg btn-primary" type="submit">{ "Proceed" }</button>
</div>
</form>
}
}
State::ConsentGranted(app_name) => {
html! {
<div class="alert alert-success" role="alert">
<h2 class="text-center">{ "Taking you to " }{app_name}{" ... " }</h2>
</div>
}
}
State::SubmitAuthReq | State::TokenCheck => {
html! {
<div class="alert alert-light" role="alert">
<h2 class="text-center">{ "Processing ... " }</h2>
</div>
}
}
State::AccessDenied(kopid) => {
html! {
<div class={CSS_ALERT_DANGER} role="alert">
<h1>{ "Access Denied" } </h1>
<p>
{ "You do not have access to the requested resources." }
</p>
<p>
{ if let Some(opid) = kopid {
format!("Operation ID: {}", opid)
} else {
"Operation ID: -".to_string()
}
}
</p>
</div>
}
}
State::ErrInvalidRequest => do_alert_error(
"Invalid request",
Some("Please close this window and try again from the beginning."),
false,
),
};
html! {
<>
<main class="form-signin">
<center>
{logo_img()}
</center>
<div class="container">
{ body_content }
</div>
</main>
{ do_footer() }
</>
}
}
fn destroy(&mut self, _ctx: &Context<Self>) {
console::debug!("oauth2::destroy");
remove_body_form_classes!();
}
}

View file

@ -1,18 +0,0 @@
#![allow(clippy::disallowed_types)] // because `Routable` uses a hashmap
use serde::{Deserialize, Serialize};
use yew_router::Routable;
#[derive(Routable, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub enum LoginRoute {
#[at("/ui/login")]
Login,
#[at("/ui/reauth")]
Reauth,
#[at("/ui/oauth2")]
Oauth2,
#[not_found]
#[at("/ui/login/404")]
NotFound,
}

View file

@ -1,7 +0,0 @@
// loads the module which loads the WASM. It's loaders all the way down.
import init, { run_app } from '/pkg/kanidmd_web_ui_login_flows.js';
async function main() {
await init('/pkg/kanidmd_web_ui_login_flows_bg.wasm');
run_app();
}
main()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -1,16 +0,0 @@
var maxParticleCount=150;var particleSpeed=2;var startConfetti;var stopConfetti;var toggleConfetti;var removeConfetti;(function(){startConfetti=startConfettiInner;stopConfetti=stopConfettiInner;toggleConfetti=toggleConfettiInner;removeConfetti=removeConfettiInner;var colors=["DodgerBlue","OliveDrab","Gold","Pink","SlateBlue","LightBlue","Violet","PaleGreen","SteelBlue","SandyBrown","Chocolate","Crimson"]
var streamingConfetti=false;var animationTimer=null;var particles=[];var waveAngle=0;function resetParticle(particle,width,height){particle.color=colors[(Math.random()*colors.length)|0];particle.x=Math.random()*width;particle.y=Math.random()*height-height;particle.diameter=Math.random()*10+5;particle.tilt=Math.random()*10-10;particle.tiltAngleIncrement=Math.random()*0.07+0.05;particle.tiltAngle=0;return particle;}
function startConfettiInner(){var width=window.innerWidth;var height=window.innerHeight;window.requestAnimFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(callback){return window.setTimeout(callback,16.6666667);};})();var canvas=document.getElementById("confetti-canvas");if(canvas===null){canvas=document.createElement("canvas");canvas.setAttribute("id","confetti-canvas");canvas.setAttribute("style","display:block;z-index:999999;pointer-events:none");document.body.appendChild(canvas);canvas.width=width;canvas.height=height;window.addEventListener("resize",function(){canvas.width=window.innerWidth;canvas.height=window.innerHeight;},true);}
var context=canvas.getContext("2d");while(particles.length<maxParticleCount)
particles.push(resetParticle({},width,height));streamingConfetti=true;if(animationTimer===null){(function runAnimation(){context.clearRect(0,0,window.innerWidth,window.innerHeight);if(particles.length===0)
animationTimer=null;else{updateParticles();drawParticles(context);animationTimer=requestAnimFrame(runAnimation);}})();}}
function stopConfettiInner(){streamingConfetti=false;}
function removeConfettiInner(){stopConfetti();particles=[];}
function toggleConfettiInner(){if(streamingConfetti)
stopConfettiInner();else
startConfettiInner();}
function drawParticles(context){var particle;var x;for(var i=0;i<particles.length;i++){particle=particles[i];context.beginPath();context.lineWidth=particle.diameter;context.strokeStyle=particle.color;x=particle.x+particle.tilt;context.moveTo(x+particle.diameter/2,particle.y);context.lineTo(x,particle.y+particle.tilt+particle.diameter/2);context.stroke();}}
function updateParticles(){var width=window.innerWidth;var height=window.innerHeight;var particle;waveAngle+=0.01;for(var i=0;i<particles.length;i++){particle=particles[i];if(!streamingConfetti&&particle.y<-15)
particle.y=height+100;else{particle.tiltAngle+=particle.tiltAngleIncrement;particle.x+=Math.sin(waveAngle);particle.y+=(Math.cos(waveAngle)+particle.diameter+particleSpeed)*0.5;particle.tilt=Math.sin(particle.tiltAngle)*15;}
if(particle.x>width+20||particle.x<-20||particle.y>height){if(streamingConfetti&&particles.length<=maxParticleCount)
resetParticle(particle,width,height);else{particles.splice(i,1);i--;}}}}})();

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,834 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg5800"
version="1.1"
viewBox="0 0 39.6875 39.6875"
height="150"
width="150"
sodipodi:docname="icon-accounts.svg"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
inkscape:export-filename="../94e4957b/kani-yellow-sign.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1391"
inkscape:window-height="819"
id="namedview108"
showgrid="false"
inkscape:zoom="2.2181916"
inkscape:cx="99.179892"
inkscape:cy="83.175864"
inkscape:window-x="49"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer3"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showguides="true">
<sodipodi:guide
position="0,-35.312501"
orientation="0,1"
id="guide932"
inkscape:locked="false"
inkscape:label="MiddleHorizontal"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="75,39.687501"
orientation="-1,0"
id="guide934"
inkscape:locked="false"
inkscape:label="MiddleVertical"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="4.7580686,-10.089218"
orientation="0,-1"
id="guide1652"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs5794">
<radialGradient
id="Gradient_4"
gradientUnits="userSpaceOnUse"
cx="420.20801"
cy="346.897"
r="21.1">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8111" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8113" />
</radialGradient>
<radialGradient
id="Gradient_5"
gradientUnits="userSpaceOnUse"
cx="385.79401"
cy="98.971001"
r="165.23399">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8116" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8118" />
</radialGradient>
<radialGradient
id="Gradient_6"
gradientUnits="userSpaceOnUse"
cx="154.649"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8121" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8123" />
</radialGradient>
<radialGradient
id="Gradient_7"
gradientUnits="userSpaceOnUse"
cx="120.117"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8126" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8128" />
</radialGradient>
<radialGradient
id="Gradient_8"
gradientUnits="userSpaceOnUse"
cx="660.41699"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8131" />
<stop
offset="1"
stop-color="#F60012"
id="stop8133" />
</radialGradient>
<radialGradient
id="Gradient_9"
gradientUnits="userSpaceOnUse"
cx="625.88501"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8136" />
<stop
offset="1"
stop-color="#F60012"
id="stop8138" />
</radialGradient>
<radialGradient
id="Gradient_10"
gradientUnits="userSpaceOnUse"
cx="283.38901"
cy="577.77899"
r="21.1">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8141" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8143" />
</radialGradient>
<radialGradient
id="Gradient_11"
gradientUnits="userSpaceOnUse"
cx="213.711"
cy="337.353"
r="165.23399">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8146" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8148" />
</radialGradient>
<linearGradient
id="Gradient_12"
gradientUnits="userSpaceOnUse"
x1="395.94601"
y1="909.60699"
x2="398.86099"
y2="889.30902">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8151" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8153" />
</linearGradient>
<linearGradient
id="Gradient_13"
gradientUnits="userSpaceOnUse"
x1="364.41199"
y1="920.81097"
x2="381.88199"
y2="898.88202">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8156" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8158" />
</linearGradient>
<linearGradient
id="Gradient_14"
gradientUnits="userSpaceOnUse"
x1="400.103"
y1="884.01398"
x2="414.327"
y2="886.43201">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8161" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8163" />
</linearGradient>
<linearGradient
id="Gradient_15"
gradientUnits="userSpaceOnUse"
x1="401.52899"
y1="-883.02899"
x2="418.35501"
y2="-884.72101"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8166" />
<stop
offset="1"
stop-color="#90282C"
id="stop8168" />
</linearGradient>
<clipPath
id="Clip_1">
<path
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c -2.74,0.34 -5.51,-0.08 -8.21,-0.56 v 0 c 1.75,0.6 3.93,1.12 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8171" />
</clipPath>
<linearGradient
id="Gradient_16"
gradientUnits="userSpaceOnUse"
x1="406.53"
y1="880.51599"
x2="409.40302"
y2="880.80499">
<stop
offset="0"
stop-color="#AF2525"
id="stop8174" />
<stop
offset="1"
stop-color="#90282C"
id="stop8176" />
</linearGradient>
<linearGradient
id="Gradient_17"
gradientUnits="userSpaceOnUse"
x1="452.20001"
y1="905.45599"
x2="444.604"
y2="892.92102">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8179" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8181" />
</linearGradient>
<linearGradient
id="Gradient_18"
gradientUnits="userSpaceOnUse"
x1="462.254"
y1="893.729"
x2="456.45901"
y2="887.92401">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8184" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8186" />
</linearGradient>
<linearGradient
id="Gradient_19"
gradientUnits="userSpaceOnUse"
x1="446.13501"
y1="882.91602"
x2="429.77499"
y2="883.61603">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8189" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8191" />
</linearGradient>
<linearGradient
id="Gradient_20"
gradientUnits="userSpaceOnUse"
x1="443.24799"
y1="-882.92798"
x2="426.452"
y2="-884.64899"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8194" />
<stop
offset="1"
stop-color="#90282C"
id="stop8196" />
</linearGradient>
<clipPath
id="Clip_2">
<path
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -3.99,0.52 -9.46,1.2 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8199" />
</clipPath>
<linearGradient
id="Gradient_21"
gradientUnits="userSpaceOnUse"
x1="438.23401"
y1="880.48499"
x2="435.36099"
y2="880.78003">
<stop
offset="0"
stop-color="#AF2525"
id="stop8202" />
<stop
offset="1"
stop-color="#90282C"
id="stop8204" />
</linearGradient>
<radialGradient
id="Gradient_22"
gradientUnits="userSpaceOnUse"
cx="421.086"
cy="879.30603"
r="5.8280001">
<stop
offset="0"
stop-color="#E61F19"
id="stop8207" />
<stop
offset="0.988"
stop-color="#9E282C"
id="stop8209" />
<stop
offset="1"
stop-color="#9E282C"
id="stop8211" />
</radialGradient>
<radialGradient
id="Gradient_23"
gradientUnits="userSpaceOnUse"
cx="146.832"
cy="-316.11899"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8214" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8216" />
</radialGradient>
<radialGradient
id="Gradient_24"
gradientUnits="userSpaceOnUse"
cx="55.174999"
cy="-176.464"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8219" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8221" />
</radialGradient>
<radialGradient
id="Gradient_25"
gradientUnits="userSpaceOnUse"
cx="565.53101"
cy="-165.11301"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8224" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8226" />
</radialGradient>
<radialGradient
id="Gradient_26"
gradientUnits="userSpaceOnUse"
cx="333.47"
cy="-63.138"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8229" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8231" />
</radialGradient>
<radialGradient
id="Gradient_27"
gradientUnits="userSpaceOnUse"
cx="536.58099"
cy="578.03198"
r="21.1">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8234" />
<stop
offset="1"
stop-color="#229614"
id="stop8236" />
</radialGradient>
<radialGradient
id="Gradient_28"
gradientUnits="userSpaceOnUse"
cx="553.57397"
cy="328.29001"
r="165.23399">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8239" />
<stop
offset="1"
stop-color="#229614"
id="stop8241" />
</radialGradient>
<radialGradient
id="Gradient_29"
gradientUnits="userSpaceOnUse"
cx="488.81201"
cy="-304.62601"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8244" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8246" />
</radialGradient>
<symbol
id="balloons"
viewBox="0 0 825 962">
<g
style="fill:none;stroke:#eccb78;stroke-width:4.069;stroke-linecap:round;stroke-linejoin:round"
id="g8261">
<path
d="M 291.13,584.61 422.41,889.24 540.38,589.26"
id="path8249" />
<path
d="m 426.95,357.75 -4.54,531.49"
id="path8251" />
<path
d="M 158.17,411.35 421.71,889.24 667.1,402.98"
id="path8253" />
<path
d="m 415.21,958.51 c 8.81,-23.5 6.08,-71.63 6.08,-71.63 -3.66,47 -29.02,70.1 -29.02,70.1"
id="path8255" />
<path
d="m 456.29,956.29 c 0,0 -26.52,-21.77 -32.58,-68.52 0,0 -0.26,48.21 9.75,71.23"
id="path8257" />
<path
d="m 404.06,957.77 c 0,0 13.62,-20.66 18.45,-69.2"
id="path8259" />
</g>
<path
style="fill:url(#Gradient_4)"
d="m 423.73,337.05 -21.46,35.03 c -1.86,3.04 0.32,6.93 3.89,6.93 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.83 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.28 -3.95,-2.28 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8263" />
<path
style="fill:url(#Gradient_5)"
d="m 273.05,178.98 c 0,97.08 68.93,175.78 153.96,175.79 v 0 c 85.03,0 153.96,-78.7 153.97,-175.79 v 0 C 580.98,81.9 512.05,3.2 427.01,3.2 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8265" />
<path
style="fill:url(#Gradient_6)"
d="m 158.17,425.62 -21.46,35.02 c -1.86,3.04 0.32,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 L 166,425.71 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8267" />
<path
style="opacity:0.95;fill:url(#Gradient_7)"
d="m 7.37,267.55 c 0,97.08 68.93,175.78 153.97,175.78 v 0 c 85.03,0 153.96,-78.7 153.96,-175.78 v 0 C 315.3,170.47 246.37,91.77 161.34,91.77 v 0 C 76.31,91.77 7.38,170.47 7.37,267.55 Z"
id="path8269" />
<path
style="fill:url(#Gradient_8)"
d="m 663.94,425.62 -21.46,35.02 c -1.86,3.04 0.33,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8271" />
<path
style="opacity:0.95;fill:url(#Gradient_9)"
d="m 513.14,267.55 c 0,97.08 68.93,175.78 153.96,175.78 v 0 c 85.03,0 153.96,-78.7 153.97,-175.78 v 0 C 821.07,170.47 752.14,91.77 667.1,91.77 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8273" />
<path
style="fill:url(#Gradient_10)"
d="m 285.46,567.53 -16.21,37.74 c -1.41,3.27 1.31,6.82 4.83,6.3 v 0 l 41.28,-5.97 c 3.47,-0.5 5.1,-4.58 2.93,-7.33 v 0 l -25.06,-31.76 c -0.93,-1.17 -2.25,-1.74 -3.57,-1.74 v 0 c -1.72,0 -3.42,0.96 -4.2,2.76 z"
id="path8275" />
<path
style="opacity:0.95;fill:url(#Gradient_11)"
d="m 240.79,236.67 c -84.15,12.18 -141.11,99.94 -127.2,196.02 v 0 c 13.9,96.08 93.39,164.1 177.54,151.92 v 0 c 84.15,-12.18 141.1,-99.94 127.21,-196.02 v 0 C 405.52,300 336.93,235.26 260.34,235.26 v 0 c -6.47,0 -12.99,0.46 -19.55,1.41 z"
id="path8277" />
<path
style="fill:url(#Gradient_27)"
d="M 542.05,569.11 513.87,599 c -2.44,2.59 -1.1,6.85 2.38,7.59 v 0 l 40.83,8.54 c 3.43,0.72 6.36,-2.55 5.25,-5.88 v 0 l -12.64,-38.43 c -0.66,-2.01 -2.48,-3.13 -4.35,-3.14 v 0 c -1.18,0 -2.38,0.46 -3.29,1.43 z"
id="path8279" />
<path
style="opacity:0.95;fill:url(#Gradient_28)"
d="m 426.83,383.51 c -19.89,95.02 31.46,186.18 114.68,203.6 v 0 c 83.23,17.42 166.82,-45.49 186.71,-140.52 v 0 C 748.11,351.57 696.76,260.41 613.54,242.99 v 0 c -9.34,-1.96 -18.68,-2.9 -27.94,-2.89 v 0 c -73.25,0 -141.11,59.05 -158.77,143.41 z"
id="path8281" />
<path
style="opacity:0.7;fill:url(#Gradient_23)"
d="m 126.74,409.71 c 0,0 29.93,-94.86 60.85,-110.42 19.92,-10.03 -4.23,-30.39 -27.01,-14.05 -6.7,5.14 -12.35,11.69 -17.06,18.66 -14.36,21.28 -19.56,46.01 -20.09,71.27 v 0.02 c -0.07,11.6 1.32,23.12 3.31,34.52 z"
id="path8283" />
<path
style="opacity:0.7;fill:url(#Gradient_24)"
d="m 30.74,269.01 c 0,0 34.33,-93.36 65.93,-107.45 20.37,-9.09 -2.81,-30.55 -26.32,-15.3 0,0 -51.89,32.72 -39.61,122.75 z"
id="path8285" />
<path
style="opacity:0.7;fill:url(#Gradient_25)"
d="m 537.49,256.64 c 0,0 37.96,-91.94 70.09,-104.8 20.71,-8.29 -1.61,-30.64 -25.71,-16.31 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8287" />
<path
style="opacity:0.7;fill:url(#Gradient_26)"
d="m 305.43,154.66 c 0,0 37.96,-91.94 70.09,-104.79 20.71,-8.28 -1.61,-30.64 -25.71,-16.32 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8289" />
<path
style="opacity:0.7;fill:url(#Gradient_29)"
d="m 454.08,393.82 c 0,0 44.65,-88.88 77.64,-99.32 21.27,-6.73 0.66,-30.67 -24.42,-18.17 0,0 -55.25,26.66 -53.22,117.49 z"
id="path8291" />
<path
style="fill:url(#Gradient_12)"
d="m 375.83,907.75 9.08,1.65 c -1.21,2.8 0.79,11.89 0.79,11.89 v 0 c 8.26,-15.42 34.5,-34.41 33.81,-33.91 v 0 l 0.71,-5.79 c -24.03,1.57 -44.39,26.16 -44.39,26.16 z"
id="path8293" />
<path
style="fill:url(#Gradient_13)"
d="m 380.2,872.6 c 13.6,7.74 40.82,5.99 40.82,5.99 v 0 c -22.8,-5.81 -32.93,-7.21 -37.4,-7.21 v 0 c -4.17,0 -3.42,1.22 -3.42,1.22 z"
id="path8295" />
<path
style="fill:url(#Gradient_14)"
d="m 380.45,890.39 c 7.74,5.62 42.01,-3.08 42.01,-3.09 v 0 l -0.09,-10.01 c -42.04,-1.73 -42.31,-5.05 -42.31,-5.05 v 0 z"
id="path8297" />
<g
clip-path="url(#Clip_1)"
opacity="0.24"
id="g8301">
<path
style="fill:url(#Gradient_15)"
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c 0,0 -3,0.5 -8.21,-0.56 2.01,0.59 4.41,1.58 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8299" />
</g>
<path
style="fill:url(#Gradient_16)"
d="m 409.38,881 c -1.53,-0.45 -2.83,-0.68 -2.83,-0.68 v 0 c 1.01,0.28 1.95,0.5 2.83,0.68 z"
id="path8303" />
<path
style="fill:url(#Gradient_17)"
d="m 425.53,887.37 c 0,0 25.53,18.33 33.88,33.85 v 0 c 0,0 1.98,-9.1 0.76,-11.89 v 0 l 9.08,-1.67 c 0,0 -20.4,-24.55 -44.44,-26.08 v 0 z"
id="path8305" />
<path
style="fill:url(#Gradient_18)"
d="m 423.74,878.59 c 0,0 40.72,9.96 40.43,-5.69 v 0 c -0.01,-0.45 0.33,-1.72 -3.83,-1.73 v 0 c -4.58,0 -14.62,1.54 -36.6,7.42 z"
id="path8307" />
<path
style="fill:url(#Gradient_19)"
d="m 422.39,877.29 -0.07,10.01 c 0,0 34.28,8.63 42.01,3 v 0 l -0.11,-17.86 c 0,0 -0.29,1.43 -41.83,4.85 z"
id="path8309" />
<g
clip-path="url(#Clip_2)"
opacity="0.24"
id="g8313">
<path
style="fill:url(#Gradient_20)"
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -9.75,1.64 -12.88,-1.55 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8311" />
</g>
<path
style="fill:url(#Gradient_21)"
d="m 435.38,880.98 c 0.88,-0.18 1.83,-0.4 2.83,-0.69 v 0 c 0,0 -1.3,0.23 -2.83,0.69 z"
id="path8315" />
<path
style="fill:url(#Gradient_22)"
d="m 417.09,877.31 c 0,0 -1.77,8.19 0.74,11.61 v 0 l 9.25,-0.01 c 0,0 2.64,-7.59 0.88,-11.59 v 0 c 0,0 -4.62,-1 -7.84,-1 v 0 c -1.6,0 -2.86,0.25 -3.03,0.99 z"
id="path8317" />
</symbol>
</defs>
<metadata
id="metadata5797">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Kani"
style="display:inline"
transform="translate(-40.228511,-7.4385631)">
<path
id="path5404"
d="m 47.256748,30.044057 c -1.108637,2.679174 1.907156,5.148881 1.920091,5.188787 -0.550848,-1.615849 -1.133032,-2.920248 -0.0623,-4.049623"
style="fill:#803300;stroke:#000000;stroke-width:0.32966;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-rear-claw-left"
inkscape:export-filename="../1d28c49c/party-kani.png"
inkscape:export-xdpi="160.64"
inkscape:export-ydpi="160.64" />
<path
style="fill:#803300;stroke:#000000;stroke-width:0.32966;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 72.279451,29.867841 c 1.108639,2.679173 -1.907154,5.148881 -1.920091,5.188782 0.550847,-1.615844 1.133031,-2.920243 0.0623,-4.049619"
id="path5408"
inkscape:label="kani-rear-claw-right" />
<path
id="path4529"
d="m 59.687987,15.86156 c -0.431403,-0.0051 -0.84422,0.775615 -1.612258,2.30322 -2.32472,-2.556767 -2.443632,-2.587721 -3.24461,0.873588 -0.03747,0.01568 -0.07486,0.03161 -0.112266,0.0477 -2.928143,-1.755243 -3.079434,-1.768972 -2.857219,1.661998 -0.07111,0.05273 -0.142154,0.10618 -0.212947,0.160477 -3.000566,-1.188615 -3.130304,-1.113719 -2.371021,2.179539 -0.03555,0.03847 -0.0712,0.07676 -0.106646,0.115675 -3.058205,-0.826252 -3.143772,-0.702098 -2.003221,2.524684 -0.04894,0.0702 -0.09751,0.141613 -0.146165,0.212776 -3.446985,-0.368477 -3.517796,-0.301154 -1.765405,2.91037 -0.0085,0.01589 -0.01734,0.03109 -0.02591,0.04702 -1.4122,0.675083 -1.879711,1.497912 -2.548875,2.602365 -0.05195,2.190449 2.292587,4.07824 5.663674,6.223636 -1.158177,-2.146516 -2.969363,-3.234648 -3.31684,-5.897064 0.508886,-0.957788 1.374276,-1.288645 2.117359,-1.71958 8.116214,4.736605 16.686002,4.791513 25.235776,-0.181428 0.745079,0.433512 1.615611,0.763192 2.126555,1.72486 -0.347475,2.662414 -2.158831,3.750377 -3.317007,5.896892 3.371082,-2.145394 5.715783,-4.033017 5.663843,-6.223466 -0.650598,-1.073811 -1.113187,-1.88104 -2.436098,-2.545467 1.652553,-3.038888 1.495242,-3.06749 -1.973239,-2.695722 -0.02938,-0.03798 -0.05866,-0.07643 -0.08808,-0.114138 1.198204,-3.382817 1.123036,-3.471219 -2.107139,-2.594874 0.81753,-3.529305 0.708286,-3.543698 -2.564551,-2.242913 0.246522,-3.72022 0.158214,-3.712493 -2.930134,-1.860122 -0.823543,-3.566516 -0.920717,-3.555903 -3.27698,-0.964219 -0.886279,-1.626423 -1.352579,-2.440599 -1.790618,-2.445808 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:0.32966;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-body" />
<g
id="g5758"
transform="matrix(0.32966003,0,0,0.32966003,25.875957,-8.7851129)"
style="display:inline"
inkscape:label="kani-face">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399"
inkscape:label="kani-smile" />
<g
id="g351"
inkscape:label="kani-eye-left">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417" />
</g>
<g
id="g355"
inkscape:label="kani-eye-right">
<path
id="path5433"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389"
d="m 51.475333,28.920487 c -0.168261,0.0013 -0.280434,0.01794 -0.262006,0.02931 0,0 -3.011135,3.934858 0.102216,6.151064 a 3.2132327,2.7286249 37.793943 0 0 0.473761,3.106624 3.2132327,2.7286249 37.793943 0 0 3.48277,1.307827 l -1.174949,-2.836952 3.031834,0.242419 a 3.2132327,2.7286249 37.793943 0 0 -2.31634,-2.937459 3.2132327,2.7286249 37.793943 0 0 -2.310205,0.003 c -0.613929,-0.449677 -1.525896,-1.623909 -0.01345,-4.112595 0.500447,-0.823475 -0.508826,-0.95705 -1.013621,-0.953144 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.32966;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-down" />
<path
id="path8665"
d="m 83.960168,109.11313 c -0.27485,0.43011 -0.4985,0.69147 -0.49798,0.62578 0,0 -14.965898,1.38701 -15.636208,-10.186101 a 9.7471106,8.2770874 84.335985 0 1 -7.216103,-6.22894 9.7471106,8.2770874 84.335985 0 1 2.259156,-11.0566 l 5.392134,7.59511 4.268531,-8.17939 a 9.7471106,8.2770874 84.335985 0 1 3.80891,10.68931 9.7471106,8.2770874 84.335985 0 1 -3.73468,5.92977 c 0.16465,2.302521 1.70979,6.539611 10.54286,6.669381 2.92274,0.043 1.63761,2.85111 0.81319,4.14154 z"
style="display:none;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-up" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="kani-claw-right-overlay"
style="display:inline"
transform="translate(-40.228511,-7.4385631)">
<path
d="m 74.643953,26.251952 c 1.156883,-1.026462 1.383369,-2.311371 1.047116,-3.752599 l -2.436647,1.263643 z"
style="display:inline;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:0.32966;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path523"
inkscape:label="under-kani-claw" />
<path
id="path5732-74"
d="m 71.753651,21.394807 c -0.719107,0.469439 -1.267535,1.208385 -1.51602,2.042676 -0.326817,1.104585 -0.08447,2.222568 0.632582,2.918124 -1.17999,3.220643 -3.314446,2.161056 -3.359552,2.118688 -0.362682,-0.282284 -1.70696,1.196822 0.175601,1.723889 2.940634,0.823316 4.211648,-1.804817 4.666738,-3.187938 0.808096,0.06183 1.654903,-0.257623 2.324887,-0.877051 1.011643,-0.938034 1.432188,-2.380843 1.04105,-3.571645 l -2.74009,1.385776 0.0019,-0.425981 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.32966;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.32966;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 71.753651,21.394807 c -0.719107,0.46944 -1.267535,1.208385 -1.51602,2.042676 -0.326817,1.104585 -0.08447,2.222568 0.632582,2.918125 -1.17999,3.220639 -3.314446,2.161052 -3.359552,2.118684 -0.362682,-0.282284 -1.70696,1.196821 0.175604,1.723888 2.940631,0.823316 4.211645,-1.804816 4.666735,-3.187934 0.808099,0.06183 1.654907,-0.257623 2.324891,-0.877051 1.011642,-0.938034 1.432189,-2.380843 1.04105,-3.571645 l -2.740091,1.385776 0.0019,-0.425981 z"
id="path5728" />
<path
id="path5732"
d="m 71.753651,21.394807 c -0.719107,0.46944 -1.267535,1.208385 -1.51602,2.042676 -0.326817,1.104585 -0.08447,2.222568 0.632582,2.918125 -1.17999,3.220639 -3.314446,2.161052 -3.359552,2.118684 -0.362682,-0.282284 -1.70696,1.196821 0.175604,1.723888 2.940631,0.823316 4.211645,-1.804816 4.666735,-3.187934 0.808099,0.06183 1.654907,-0.257623 2.324891,-0.877051 1.011642,-0.938034 1.432189,-2.380843 1.04105,-3.571645 l -2.740091,1.385776 0.0019,-0.425981 z"
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.32966;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g7998"
inkscape:label="party-hat"
transform="matrix(1.0107117,0,0,0.94642879,-34.697969,-6.0098395)"
style="display:none">
<path
style="display:inline;fill:#1da1f2;fill-opacity:1;stroke:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path7024"
inkscape:label="party-hat-background"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 90.866459,64.772976 -1.399384,3.650367 c 0,0 6.297977,-0.0079 9.604616,-2.229612 3.306639,-2.221751 6.641769,-4.763148 6.637539,-5.892882 -0.004,-1.129737 -1.2244,-3.398221 -1.2244,-3.398221 0,0 -1.76379,2.983434 -5.089689,4.759036 -3.325897,1.775606 -6.256176,3.317156 -8.528682,3.111312 z"
id="path7711"
inkscape:label="yellow-middle" />
<path
style="fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 95.283815,50.496253 c 0,0 2.017636,-0.742584 2.746046,-1.209859 0.68409,-0.438843 2.540819,-1.493505 2.505569,-1.916931 -0.0353,-0.423423 0.90713,1.638551 0.90713,1.638551 0,0 -0.43327,0.543192 -1.447199,1.610006 -1.01394,1.066813 -1.72817,1.358505 -2.26456,1.622681 -0.53638,0.264174 -2.0866,0.861497 -2.636383,0.793816 -0.549783,-0.06768 0.189397,-2.538264 0.189397,-2.538264 z"
id="path7713"
sodipodi:nodetypes="csccsssc"
inkscape:label="yellow-top" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 109.92717,71.488689 c 0,0 -2.20481,3.007619 -3.4367,4.221512 -1.06044,1.044951 -3.3987,2.32693 -5.45873,3.108264 -2.060039,0.78133 -7.390321,2.031263 -10.319319,2.035505 -2.53475,0.0037 -3.102127,-0.17882 -4.727213,-0.614261 -1.010264,-0.270699 -0.801047,2.045289 -0.801047,2.045289 l 4.858605,2.209687 10.217314,-0.32013 c 0,0 3.08806,-0.85865 3.26326,-0.9226 0.17519,-0.06395 5.61136,-3.775927 5.91906,-4.176562 0.30769,-0.400637 1.97931,-2.854544 1.85876,-3.106917 -0.12055,-0.25237 -1.37399,-4.479787 -1.37399,-4.479787 z"
id="path7715"
sodipodi:nodetypes="csssscccsssc"
inkscape:label="yellow-bottom" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 88.208385,72.747325 c 0,0 6.251797,0.04012 9.907566,-1.876691 3.875559,-2.032049 5.147419,-2.116642 8.954089,-7.814465 -0.0353,-0.423423 1.59724,3.704345 1.59724,3.704345 0,0 -2.99139,5.115034 -6.19574,6.653752 -4.079519,1.958969 -6.324966,2.675602 -8.934752,3.106262 -1.713747,0.282798 -4.8213,0.352969 -6.27215,-0.01279 -0.537127,-0.135411 0.943747,-3.760409 0.943747,-3.760409 z"
id="path7982"
sodipodi:nodetypes="csccssscc"
inkscape:label="white-middle" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 92.993027,57.289539 c 0,0 3.629855,-0.719631 5.482954,-2.090937 0.65332,-0.483462 3.662839,-3.285322 4.163269,-4.079419 -0.0353,-0.423423 0.98376,2.839616 0.98376,2.839616 0,0 -1.55545,1.919882 -2.68768,2.888013 -1.118619,0.956483 -3.276219,2.193755 -4.815637,2.748644 -1.634013,0.588985 -3.388075,0.798009 -3.937858,0.730328 -0.549783,-0.06768 0.811192,-3.036245 0.811192,-3.036245 z"
id="path7979"
sodipodi:nodetypes="csccsssc"
inkscape:label="white-top" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#ff00ff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path508"
inkscape:label="party-hat-outline"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:#8f57a8;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 95.307706,40.797374 8.863844,3.5236 c -4.511069,-1.476791 -5.455019,1.171445 -8.289058,2.243988 0,0 5.051628,-7.95802 4.765018,-7.734177 -1.618949,1.264417 -2.259979,9.16058 -2.259979,9.16058 l -0.26088,-9.396127 3.698779,7.921822 -7.078677,-2.809491 8.818747,-2.23602 z"
id="path3171-3"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="display:inline;fill:#0056ed;fill-opacity:1;stroke:#ae00ff;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 96.516104,39.086314 5.923406,7.476417 c -3.172509,-3.530708 -5.311942,-1.706696 -8.303144,-2.191314 0,0 8.348654,-4.375959 7.988584,-4.324981 -2.03392,0.28796 -6.529397,6.811072 -6.529397,6.811072 l 4.462297,-8.273038 -0.747319,8.710783 -4.732664,-5.966807 8.758203,2.462516 z"
id="path3171"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="fill:#744eaa;fill-opacity:1;stroke:#8f57a8;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 85.749242,80.805507 -1.299196,-0.793626 -0.72012,1.516668 -1.19957,0.02641 1.07959,1.592728 -0.479079,1.665647 1.727851,-0.04555 0.945116,1.102037 1.595689,-0.420439 1.013886,1.119783 1.616715,-0.611034 0.955484,0.869926 1.417278,-0.82042 1.399024,0.893951 1.62274,-0.936247 1.191522,0.880515 1.692439,-0.626057 1.62431,0.594614 1.273839,-0.730245 1.14228,0.675465 0.83977,-0.542118 1.7433,0.433922 1.37853,-0.883036 1.46376,0.789948 0.98369,-0.723702 1.56885,0.265388 0.83917,-1.064009 1.5817,0.538036 1.04635,-1.072595 1.45989,-0.386437 -0.0243,-1.46382 0.73064,-1.311775 -1.08662,-0.620199 -0.54503,-1.441178 -1.29888,0.821314 -1.72095,-0.803735 -0.89109,1.605976 -1.44802,-0.173747 -0.40144,0.954405 -1.25934,-0.74345 -1.14276,1.025692 -2.05145,-0.719347 c 0,0 -1.1538,1.054828 -1.21655,1.018337 -0.0627,-0.03649 -0.72908,-0.632037 -0.72908,-0.632037 l -2.781839,0.990357 -1.01136,-0.982973 -0.76464,0.897745 -1.265471,-0.508519 -1.392952,0.640072 -1.261957,-0.842494 -1.207529,1.021591 -2.137111,-1.168069 -0.83207,0.565255 -0.716719,-0.940683 -1.262123,0.222665 -0.854411,-1.073467 z"
id="path3419"
inkscape:label="purple-frizzy-bit" />
</g>
<g
id="g571"
transform="matrix(-0.37792196,-0.19227008,-0.19227008,0.37792196,160.95541,83.78303)"
inkscape:export-filename="../fe6a9b8b/kani-stabby.png"
inkscape:export-xdpi="370.63721"
inkscape:export-ydpi="370.63721"
style="display:none"
inkscape:label="knife-left">
<path
d="M 327.32922,104.16963 313.23927,90.079688 246.31471,157.00414 c 0,0 -6.09712,4.9021 -4.45506,8.97393 1.6429,4.07209 4.34482,5.77218 11.55969,6.84567 6.14218,0.99798 6.56385,-1.72199 9.06459,-9.07879 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 5.35834,-9.35496 4.43089,-10.42811 12.07206,-10.21327 4.21357,0.0716 4.88879,-0.46249 6.31789,-1.74872 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439"
style="fill:#c52a34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path561" />
<path
d="M 333.98084,110.82126 313.52961,90.369671 c 0,0 66.27001,-67.178418 73.76936,-74.221626 7.49688,-7.0446203 13.081,-13.0810013 23.41845,-16.14946258 -0.68086,6.58883098 -6.21418,20.98428158 -13.48493,31.66357158 -7.2711,10.681759 -36.32483,51.549656 -63.25165,79.159106"
style="fill:#b9b8b7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path563" />
<path
d="m 404.21431,2.595027 c -6.52039,3.3330447 -11.1573,8.142464 -16.91534,13.553018 -7.49935,7.043208 -73.76936,74.221626 -73.76936,74.221626 l 15.67956,15.678859 c 26.92717,-27.608745 55.97949,-68.476642 63.25094,-79.158049 5.17807,-7.605536 9.46362,-17.0825591 11.7542,-24.295454"
style="fill:#999a99;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path565" />
<path
d="m 255.68936,173.17156 c -0.61955,0 -1.31989,-0.0656 -2.11762,-0.19541 -4.67209,-0.69397 -7.69193,-1.90252 -9.11972,-3.20187 l 0.0139,-0.014 c 1.82658,1.5197 4.5489,2.40839 8.9534,3.06351 0.81082,0.13188 1.52216,0.19899 2.15166,0.19899 4.13826,0 4.74239,-2.8925 6.91293,-9.27778 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 4.98299,-8.6995 4.53108,-10.23726 10.58792,-10.23726 0.4565,0 0.94862,0.009 1.48414,0.024 0.24729,0.004 0.48295,0.006 0.70661,0.006 3.58916,0 4.26614,-0.54433 5.61128,-1.75507 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439 v 0 c 0,0 -5.6,5.75451 -7.02875,7.03933 -1.34549,1.21109 -2.01824,1.7593 -5.61058,1.7593 -0.2226,0 -0.45649,-0.002 -0.70238,-0.006 -0.53269,-0.0152 -1.0227,-0.0236 -1.47708,-0.0236 -6.06319,0 -5.60035,1.54375 -10.5851,10.24714 -5.35693,9.35778 -17.62054,20.09429 -26.55122,29.02274 -8.92845,8.92803 -10.31434,4.2562 -12.81342,11.61408 -2.17593,6.40046 -2.71998,9.34932 -6.87133,9.34932 m 71.63986,-69.00193 -8.63636,-8.63635 v 0 l 8.63636,8.63635"
style="fill:#cdccca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path567" />
<path
d="m 255.571,173.02273 c -0.6295,0 -1.34084,-0.0671 -2.15166,-0.19899 -4.4045,-0.65512 -7.12682,-1.54381 -8.9534,-3.06351 l 74.22692,-74.22695 8.63636,8.63635 c 0,0 -5.60459,5.74958 -7.03334,7.03439 -1.34514,1.21074 -2.02212,1.75507 -5.61128,1.75507 -0.22366,0 -0.45932,-0.002 -0.70661,-0.006 -0.53552,-0.0152 -1.02764,-0.024 -1.48414,-0.024 -6.05684,0 -5.60493,1.53776 -10.58792,10.23726 -5.35587,9.35814 -17.63924,20.0762 -26.56932,29.0037 -8.92912,8.92874 -10.3536,4.21845 -12.85268,11.57524 -2.17054,6.38528 -2.77467,9.27778 -6.91293,9.27778"
style="fill:#9b232b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path569" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 39 KiB

View file

@ -1,971 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg5800"
version="1.1"
viewBox="0 0 39.6875 39.687498"
height="150"
width="150"
sodipodi:docname="icon-groups.svg"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
inkscape:export-filename="../94e4957b/kani-yellow-sign.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1391"
inkscape:window-height="847"
id="namedview108"
showgrid="false"
inkscape:zoom="3.3668237"
inkscape:cx="80.342786"
inkscape:cy="69.650216"
inkscape:window-x="49"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer2"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showguides="true">
<sodipodi:guide
position="0,-35.3125"
orientation="0,1"
id="guide932"
inkscape:locked="false"
inkscape:label="MiddleHorizontal"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="74.999998,39.687496"
orientation="-1,0"
id="guide934"
inkscape:locked="false"
inkscape:label="MiddleVertical"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="4.7580685,-10.089218"
orientation="0,-1"
id="guide1652"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs5794">
<radialGradient
id="Gradient_4"
gradientUnits="userSpaceOnUse"
cx="420.20801"
cy="346.897"
r="21.1">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8111" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8113" />
</radialGradient>
<radialGradient
id="Gradient_5"
gradientUnits="userSpaceOnUse"
cx="385.79401"
cy="98.971001"
r="165.23399">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8116" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8118" />
</radialGradient>
<radialGradient
id="Gradient_6"
gradientUnits="userSpaceOnUse"
cx="154.649"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8121" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8123" />
</radialGradient>
<radialGradient
id="Gradient_7"
gradientUnits="userSpaceOnUse"
cx="120.117"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8126" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8128" />
</radialGradient>
<radialGradient
id="Gradient_8"
gradientUnits="userSpaceOnUse"
cx="660.41699"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8131" />
<stop
offset="1"
stop-color="#F60012"
id="stop8133" />
</radialGradient>
<radialGradient
id="Gradient_9"
gradientUnits="userSpaceOnUse"
cx="625.88501"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8136" />
<stop
offset="1"
stop-color="#F60012"
id="stop8138" />
</radialGradient>
<radialGradient
id="Gradient_10"
gradientUnits="userSpaceOnUse"
cx="283.38901"
cy="577.77899"
r="21.1">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8141" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8143" />
</radialGradient>
<radialGradient
id="Gradient_11"
gradientUnits="userSpaceOnUse"
cx="213.711"
cy="337.353"
r="165.23399">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8146" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8148" />
</radialGradient>
<linearGradient
id="Gradient_12"
gradientUnits="userSpaceOnUse"
x1="395.94601"
y1="909.60699"
x2="398.86099"
y2="889.30902">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8151" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8153" />
</linearGradient>
<linearGradient
id="Gradient_13"
gradientUnits="userSpaceOnUse"
x1="364.41199"
y1="920.81097"
x2="381.88199"
y2="898.88202">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8156" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8158" />
</linearGradient>
<linearGradient
id="Gradient_14"
gradientUnits="userSpaceOnUse"
x1="400.103"
y1="884.01398"
x2="414.327"
y2="886.43201">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8161" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8163" />
</linearGradient>
<linearGradient
id="Gradient_15"
gradientUnits="userSpaceOnUse"
x1="401.52899"
y1="-883.02899"
x2="418.35501"
y2="-884.72101"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8166" />
<stop
offset="1"
stop-color="#90282C"
id="stop8168" />
</linearGradient>
<clipPath
id="Clip_1">
<path
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c -2.74,0.34 -5.51,-0.08 -8.21,-0.56 v 0 c 1.75,0.6 3.93,1.12 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8171" />
</clipPath>
<linearGradient
id="Gradient_16"
gradientUnits="userSpaceOnUse"
x1="406.53"
y1="880.51599"
x2="409.40302"
y2="880.80499">
<stop
offset="0"
stop-color="#AF2525"
id="stop8174" />
<stop
offset="1"
stop-color="#90282C"
id="stop8176" />
</linearGradient>
<linearGradient
id="Gradient_17"
gradientUnits="userSpaceOnUse"
x1="452.20001"
y1="905.45599"
x2="444.604"
y2="892.92102">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8179" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8181" />
</linearGradient>
<linearGradient
id="Gradient_18"
gradientUnits="userSpaceOnUse"
x1="462.254"
y1="893.729"
x2="456.45901"
y2="887.92401">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8184" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8186" />
</linearGradient>
<linearGradient
id="Gradient_19"
gradientUnits="userSpaceOnUse"
x1="446.13501"
y1="882.91602"
x2="429.77499"
y2="883.61603">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8189" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8191" />
</linearGradient>
<linearGradient
id="Gradient_20"
gradientUnits="userSpaceOnUse"
x1="443.24799"
y1="-882.92798"
x2="426.452"
y2="-884.64899"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8194" />
<stop
offset="1"
stop-color="#90282C"
id="stop8196" />
</linearGradient>
<clipPath
id="Clip_2">
<path
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -3.99,0.52 -9.46,1.2 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8199" />
</clipPath>
<linearGradient
id="Gradient_21"
gradientUnits="userSpaceOnUse"
x1="438.23401"
y1="880.48499"
x2="435.36099"
y2="880.78003">
<stop
offset="0"
stop-color="#AF2525"
id="stop8202" />
<stop
offset="1"
stop-color="#90282C"
id="stop8204" />
</linearGradient>
<radialGradient
id="Gradient_22"
gradientUnits="userSpaceOnUse"
cx="421.086"
cy="879.30603"
r="5.8280001">
<stop
offset="0"
stop-color="#E61F19"
id="stop8207" />
<stop
offset="0.988"
stop-color="#9E282C"
id="stop8209" />
<stop
offset="1"
stop-color="#9E282C"
id="stop8211" />
</radialGradient>
<radialGradient
id="Gradient_23"
gradientUnits="userSpaceOnUse"
cx="146.832"
cy="-316.11899"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8214" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8216" />
</radialGradient>
<radialGradient
id="Gradient_24"
gradientUnits="userSpaceOnUse"
cx="55.174999"
cy="-176.464"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8219" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8221" />
</radialGradient>
<radialGradient
id="Gradient_25"
gradientUnits="userSpaceOnUse"
cx="565.53101"
cy="-165.11301"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8224" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8226" />
</radialGradient>
<radialGradient
id="Gradient_26"
gradientUnits="userSpaceOnUse"
cx="333.47"
cy="-63.138"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8229" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8231" />
</radialGradient>
<radialGradient
id="Gradient_27"
gradientUnits="userSpaceOnUse"
cx="536.58099"
cy="578.03198"
r="21.1">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8234" />
<stop
offset="1"
stop-color="#229614"
id="stop8236" />
</radialGradient>
<radialGradient
id="Gradient_28"
gradientUnits="userSpaceOnUse"
cx="553.57397"
cy="328.29001"
r="165.23399">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8239" />
<stop
offset="1"
stop-color="#229614"
id="stop8241" />
</radialGradient>
<radialGradient
id="Gradient_29"
gradientUnits="userSpaceOnUse"
cx="488.81201"
cy="-304.62601"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8244" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8246" />
</radialGradient>
<symbol
id="balloons"
viewBox="0 0 825 962">
<g
style="fill:none;stroke:#eccb78;stroke-width:4.069;stroke-linecap:round;stroke-linejoin:round"
id="g8261">
<path
d="M 291.13,584.61 422.41,889.24 540.38,589.26"
id="path8249" />
<path
d="m 426.95,357.75 -4.54,531.49"
id="path8251" />
<path
d="M 158.17,411.35 421.71,889.24 667.1,402.98"
id="path8253" />
<path
d="m 415.21,958.51 c 8.81,-23.5 6.08,-71.63 6.08,-71.63 -3.66,47 -29.02,70.1 -29.02,70.1"
id="path8255" />
<path
d="m 456.29,956.29 c 0,0 -26.52,-21.77 -32.58,-68.52 0,0 -0.26,48.21 9.75,71.23"
id="path8257" />
<path
d="m 404.06,957.77 c 0,0 13.62,-20.66 18.45,-69.2"
id="path8259" />
</g>
<path
style="fill:url(#Gradient_4)"
d="m 423.73,337.05 -21.46,35.03 c -1.86,3.04 0.32,6.93 3.89,6.93 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.83 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.28 -3.95,-2.28 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8263" />
<path
style="fill:url(#Gradient_5)"
d="m 273.05,178.98 c 0,97.08 68.93,175.78 153.96,175.79 v 0 c 85.03,0 153.96,-78.7 153.97,-175.79 v 0 C 580.98,81.9 512.05,3.2 427.01,3.2 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8265" />
<path
style="fill:url(#Gradient_6)"
d="m 158.17,425.62 -21.46,35.02 c -1.86,3.04 0.32,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 L 166,425.71 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8267" />
<path
style="opacity:0.95;fill:url(#Gradient_7)"
d="m 7.37,267.55 c 0,97.08 68.93,175.78 153.97,175.78 v 0 c 85.03,0 153.96,-78.7 153.96,-175.78 v 0 C 315.3,170.47 246.37,91.77 161.34,91.77 v 0 C 76.31,91.77 7.38,170.47 7.37,267.55 Z"
id="path8269" />
<path
style="fill:url(#Gradient_8)"
d="m 663.94,425.62 -21.46,35.02 c -1.86,3.04 0.33,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8271" />
<path
style="opacity:0.95;fill:url(#Gradient_9)"
d="m 513.14,267.55 c 0,97.08 68.93,175.78 153.96,175.78 v 0 c 85.03,0 153.96,-78.7 153.97,-175.78 v 0 C 821.07,170.47 752.14,91.77 667.1,91.77 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8273" />
<path
style="fill:url(#Gradient_10)"
d="m 285.46,567.53 -16.21,37.74 c -1.41,3.27 1.31,6.82 4.83,6.3 v 0 l 41.28,-5.97 c 3.47,-0.5 5.1,-4.58 2.93,-7.33 v 0 l -25.06,-31.76 c -0.93,-1.17 -2.25,-1.74 -3.57,-1.74 v 0 c -1.72,0 -3.42,0.96 -4.2,2.76 z"
id="path8275" />
<path
style="opacity:0.95;fill:url(#Gradient_11)"
d="m 240.79,236.67 c -84.15,12.18 -141.11,99.94 -127.2,196.02 v 0 c 13.9,96.08 93.39,164.1 177.54,151.92 v 0 c 84.15,-12.18 141.1,-99.94 127.21,-196.02 v 0 C 405.52,300 336.93,235.26 260.34,235.26 v 0 c -6.47,0 -12.99,0.46 -19.55,1.41 z"
id="path8277" />
<path
style="fill:url(#Gradient_27)"
d="M 542.05,569.11 513.87,599 c -2.44,2.59 -1.1,6.85 2.38,7.59 v 0 l 40.83,8.54 c 3.43,0.72 6.36,-2.55 5.25,-5.88 v 0 l -12.64,-38.43 c -0.66,-2.01 -2.48,-3.13 -4.35,-3.14 v 0 c -1.18,0 -2.38,0.46 -3.29,1.43 z"
id="path8279" />
<path
style="opacity:0.95;fill:url(#Gradient_28)"
d="m 426.83,383.51 c -19.89,95.02 31.46,186.18 114.68,203.6 v 0 c 83.23,17.42 166.82,-45.49 186.71,-140.52 v 0 C 748.11,351.57 696.76,260.41 613.54,242.99 v 0 c -9.34,-1.96 -18.68,-2.9 -27.94,-2.89 v 0 c -73.25,0 -141.11,59.05 -158.77,143.41 z"
id="path8281" />
<path
style="opacity:0.7;fill:url(#Gradient_23)"
d="m 126.74,409.71 c 0,0 29.93,-94.86 60.85,-110.42 19.92,-10.03 -4.23,-30.39 -27.01,-14.05 -6.7,5.14 -12.35,11.69 -17.06,18.66 -14.36,21.28 -19.56,46.01 -20.09,71.27 v 0.02 c -0.07,11.6 1.32,23.12 3.31,34.52 z"
id="path8283" />
<path
style="opacity:0.7;fill:url(#Gradient_24)"
d="m 30.74,269.01 c 0,0 34.33,-93.36 65.93,-107.45 20.37,-9.09 -2.81,-30.55 -26.32,-15.3 0,0 -51.89,32.72 -39.61,122.75 z"
id="path8285" />
<path
style="opacity:0.7;fill:url(#Gradient_25)"
d="m 537.49,256.64 c 0,0 37.96,-91.94 70.09,-104.8 20.71,-8.29 -1.61,-30.64 -25.71,-16.31 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8287" />
<path
style="opacity:0.7;fill:url(#Gradient_26)"
d="m 305.43,154.66 c 0,0 37.96,-91.94 70.09,-104.79 20.71,-8.28 -1.61,-30.64 -25.71,-16.32 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8289" />
<path
style="opacity:0.7;fill:url(#Gradient_29)"
d="m 454.08,393.82 c 0,0 44.65,-88.88 77.64,-99.32 21.27,-6.73 0.66,-30.67 -24.42,-18.17 0,0 -55.25,26.66 -53.22,117.49 z"
id="path8291" />
<path
style="fill:url(#Gradient_12)"
d="m 375.83,907.75 9.08,1.65 c -1.21,2.8 0.79,11.89 0.79,11.89 v 0 c 8.26,-15.42 34.5,-34.41 33.81,-33.91 v 0 l 0.71,-5.79 c -24.03,1.57 -44.39,26.16 -44.39,26.16 z"
id="path8293" />
<path
style="fill:url(#Gradient_13)"
d="m 380.2,872.6 c 13.6,7.74 40.82,5.99 40.82,5.99 v 0 c -22.8,-5.81 -32.93,-7.21 -37.4,-7.21 v 0 c -4.17,0 -3.42,1.22 -3.42,1.22 z"
id="path8295" />
<path
style="fill:url(#Gradient_14)"
d="m 380.45,890.39 c 7.74,5.62 42.01,-3.08 42.01,-3.09 v 0 l -0.09,-10.01 c -42.04,-1.73 -42.31,-5.05 -42.31,-5.05 v 0 z"
id="path8297" />
<g
clip-path="url(#Clip_1)"
opacity="0.24"
id="g8301">
<path
style="fill:url(#Gradient_15)"
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c 0,0 -3,0.5 -8.21,-0.56 2.01,0.59 4.41,1.58 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8299" />
</g>
<path
style="fill:url(#Gradient_16)"
d="m 409.38,881 c -1.53,-0.45 -2.83,-0.68 -2.83,-0.68 v 0 c 1.01,0.28 1.95,0.5 2.83,0.68 z"
id="path8303" />
<path
style="fill:url(#Gradient_17)"
d="m 425.53,887.37 c 0,0 25.53,18.33 33.88,33.85 v 0 c 0,0 1.98,-9.1 0.76,-11.89 v 0 l 9.08,-1.67 c 0,0 -20.4,-24.55 -44.44,-26.08 v 0 z"
id="path8305" />
<path
style="fill:url(#Gradient_18)"
d="m 423.74,878.59 c 0,0 40.72,9.96 40.43,-5.69 v 0 c -0.01,-0.45 0.33,-1.72 -3.83,-1.73 v 0 c -4.58,0 -14.62,1.54 -36.6,7.42 z"
id="path8307" />
<path
style="fill:url(#Gradient_19)"
d="m 422.39,877.29 -0.07,10.01 c 0,0 34.28,8.63 42.01,3 v 0 l -0.11,-17.86 c 0,0 -0.29,1.43 -41.83,4.85 z"
id="path8309" />
<g
clip-path="url(#Clip_2)"
opacity="0.24"
id="g8313">
<path
style="fill:url(#Gradient_20)"
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -9.75,1.64 -12.88,-1.55 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8311" />
</g>
<path
style="fill:url(#Gradient_21)"
d="m 435.38,880.98 c 0.88,-0.18 1.83,-0.4 2.83,-0.69 v 0 c 0,0 -1.3,0.23 -2.83,0.69 z"
id="path8315" />
<path
style="fill:url(#Gradient_22)"
d="m 417.09,877.31 c 0,0 -1.77,8.19 0.74,11.61 v 0 l 9.25,-0.01 c 0,0 2.64,-7.59 0.88,-11.59 v 0 c 0,0 -4.62,-1 -7.84,-1 v 0 c -1.6,0 -2.86,0.25 -3.03,0.99 z"
id="path8317" />
</symbol>
</defs>
<metadata
id="metadata5797">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Kani"
style="display:inline"
transform="translate(-40.228511,-7.4385631)">
<path
id="path5404"
d="m 58.64796,20.686712 c 0.593984,1.435445 -1.021814,2.758664 -1.028747,2.780043 0.295135,-0.865737 0.607057,-1.564607 0.03337,-2.169704"
style="fill:#803300;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-rear-claw-left"
inkscape:export-filename="../1d28c49c/party-kani.png"
inkscape:export-xdpi="160.64"
inkscape:export-ydpi="160.64" />
<path
style="fill:#803300;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 45.241311,20.592298 c -0.593984,1.435446 1.021817,2.758664 1.028745,2.780043 -0.29513,-0.865737 -0.607052,-1.564607 -0.03336,-2.169703"
id="path5408"
inkscape:label="kani-rear-claw-right" />
<path
id="path4529"
d="m 51.987557,13.088022 c 0.231137,-0.0028 0.452316,0.41556 0.863816,1.234019 1.245537,-1.369863 1.309249,-1.386448 1.738393,0.46805 0.02008,0.0084 0.04012,0.01694 0.06016,0.02556 1.56884,-0.940424 1.649896,-0.947779 1.530839,0.890464 0.03809,0.02825 0.07616,0.05688 0.114094,0.08598 1.607642,-0.636835 1.677152,-0.596707 1.270344,1.167751 0.01907,0.02062 0.03817,0.04112 0.05714,0.06198 1.638526,-0.442689 1.68437,-0.376169 1.073287,1.352673 0.02622,0.03761 0.05225,0.07587 0.07832,0.114001 1.846823,-0.197423 1.884762,-0.161352 0.945868,1.559317 0.0046,0.0085 0.0093,0.01665 0.01388,0.02519 0.756628,0.361696 1.007111,0.802551 1.365635,1.394295 0.02783,1.173596 -1.228321,2.185036 -3.03448,3.334494 0.620528,-1.150059 1.590923,-1.733058 1.777095,-3.159525 -0.272651,-0.513163 -0.736309,-0.690429 -1.134438,-0.921315 -4.348503,2.537775 -8.940015,2.567194 -13.520807,-0.09721 -0.399201,0.232267 -0.865611,0.408903 -1.139365,0.924145 0.186169,1.426465 1.156657,2.009374 1.777184,3.159433 -1.806157,-1.149458 -3.062397,-2.160808 -3.034571,-3.334406 0.348577,-0.575325 0.596425,-1.007821 1.305212,-1.363808 -0.885405,-1.628173 -0.801118,-1.643497 1.05722,-1.444312 0.01575,-0.02035 0.03142,-0.04095 0.04719,-0.06115 -0.641971,-1.812442 -0.601698,-1.859807 1.128965,-1.390279 -0.438017,-1.890928 -0.379489,-1.898641 1.374031,-1.201706 -0.132081,-1.993217 -0.08476,-1.989078 1.569908,-0.996615 0.441235,-1.910866 0.493297,-1.90518 1.755736,-0.516609 0.474851,-0.871405 0.724682,-1.307623 0.959377,-1.310414 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-body" />
<g
id="g5758"
transform="matrix(-0.17662502,0,0,0.17662502,70.103346,-0.11715463)"
style="display:inline"
inkscape:label="kani-face">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399"
inkscape:label="kani-smile" />
<g
id="g351"
inkscape:label="kani-eye-left">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417" />
</g>
<g
id="g355"
inkscape:label="kani-eye-right">
<path
id="path5433"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389"
d="m 56.387729,20.084726 c 0.09014,7.22e-4 0.150252,0.0096 0.140377,0.0157 0,0 1.613304,2.108216 -0.05476,3.295613 a 1.4619408,1.7215836 52.206057 0 1 -0.253834,1.664464 1.4619408,1.7215836 52.206057 0 1 -1.865992,0.700707 l 0.62951,-1.519978 -1.624394,0.129882 a 1.4619408,1.7215836 52.206057 0 1 1.241047,-1.573829 1.4619408,1.7215836 52.206057 0 1 1.237758,0.0016 c 0.328934,-0.240918 0.817545,-0.870048 0.0072,-2.203434 -0.268129,-0.4412 0.272621,-0.512767 0.543076,-0.510674 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-down" />
<path
id="path8665"
d="m 83.960168,109.11313 c -0.27485,0.43011 -0.4985,0.69147 -0.49798,0.62578 0,0 -14.965898,1.38701 -15.636208,-10.186101 a 9.7471106,8.2770874 84.335985 0 1 -7.216103,-6.22894 9.7471106,8.2770874 84.335985 0 1 2.259156,-11.0566 l 5.392134,7.59511 4.268531,-8.17939 a 9.7471106,8.2770874 84.335985 0 1 3.80891,10.68931 9.7471106,8.2770874 84.335985 0 1 -3.73468,5.92977 c 0.16465,2.302521 1.70979,6.539611 10.54286,6.669381 2.92274,0.043 1.63761,2.85111 0.81319,4.14154 z"
style="display:none;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-up" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="kani-claw-right-overlay"
style="display:inline"
transform="translate(-40.228511,-7.4385631)">
<path
d="m 43.974461,18.654981 c -0.619835,-0.549959 -0.74118,-1.238386 -0.561022,-2.010566 l 1.305506,0.677034 z"
style="display:inline;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path523"
inkscape:label="under-kani-claw" />
<path
id="path5732-74"
d="m 45.523026,16.052623 c 0.385284,0.251515 0.679119,0.647427 0.81225,1.094423 0.175105,0.591814 0.04525,1.190806 -0.338923,1.563471 0.632215,1.725553 1.775814,1.157848 1.799979,1.135149 0.194316,-0.151243 0.914553,0.641231 -0.09409,0.923623 -1.575527,0.441116 -2.25651,-0.966983 -2.500338,-1.708031 -0.432965,0.03313 -0.886664,-0.138029 -1.245626,-0.469906 -0.54202,-0.502579 -0.767337,-1.275606 -0.557775,-1.913614 l 1.468083,0.74247 -8.5e-4,-0.228231 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 45.523026,16.052623 c 0.385284,0.251516 0.679119,0.647427 0.81225,1.094423 0.175105,0.591814 0.04525,1.190806 -0.338923,1.563471 0.632215,1.725551 1.775814,1.157846 1.799979,1.135147 0.194316,-0.151243 0.914553,0.641231 -0.09409,0.923624 -1.575527,0.441115 -2.25651,-0.966984 -2.500338,-1.70803 -0.432965,0.03313 -0.886664,-0.138029 -1.245631,-0.469906 C 43.414258,18.088773 43.188937,17.315746 43.3985,16.67774 l 1.468086,0.742469 -8.5e-4,-0.228232 z"
id="path5728" />
<path
id="path5732"
d="m 45.523026,16.052623 c 0.385284,0.251516 0.679119,0.647427 0.81225,1.094423 0.175105,0.591814 0.04525,1.190806 -0.338923,1.563471 0.632215,1.725551 1.775814,1.157846 1.799979,1.135147 0.194316,-0.151243 0.914553,0.641231 -0.09409,0.923624 -1.575527,0.441115 -2.25651,-0.966984 -2.500338,-1.70803 -0.432965,0.03313 -0.886664,-0.138029 -1.245631,-0.469906 C 43.414258,18.088773 43.188937,17.315746 43.3985,16.67774 l 1.468086,0.742469 -8.5e-4,-0.228232 z"
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5404-5"
d="m 61.756924,26.737804 c -0.593985,1.435446 1.021813,2.758664 1.028746,2.780044 C 62.490535,28.65211 62.178615,27.95324 62.7523,27.348143"
style="display:inline;fill:#803300;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-rear-claw-left"
inkscape:export-filename="../1d28c49c/party-kani.png"
inkscape:export-xdpi="160.64"
inkscape:export-ydpi="160.64" />
<path
style="display:inline;fill:#803300;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 75.163573,26.64339 c 0.593985,1.435446 -1.021816,2.758664 -1.028744,2.780043 0.29513,-0.865737 0.607052,-1.564607 0.03337,-2.169702"
id="path5408-6"
inkscape:label="kani-rear-claw-right" />
<path
id="path4529-2"
d="m 68.417329,19.139115 c -0.231139,-0.0028 -0.452317,0.415559 -0.863816,1.234018 -1.245538,-1.369863 -1.30925,-1.386448 -1.738394,0.46805 -0.02009,0.0084 -0.04012,0.01694 -0.06015,0.02556 -1.568841,-0.940423 -1.649896,-0.947778 -1.53084,0.890464 -0.03809,0.02825 -0.07616,0.05688 -0.114094,0.08598 -1.607641,-0.636835 -1.677153,-0.596707 -1.270345,1.167752 -0.01907,0.02062 -0.03817,0.04112 -0.05714,0.06198 -1.638525,-0.442689 -1.684371,-0.376169 -1.073286,1.352674 -0.02622,0.03761 -0.05225,0.07587 -0.07832,0.114002 -1.846824,-0.197422 -1.884762,-0.161352 -0.945868,1.559316 -0.0046,0.0085 -0.0093,0.01665 -0.01388,0.02519 -0.756627,0.361696 -1.00711,0.80255 -1.365635,1.394294 -0.02783,1.173597 1.228321,2.185036 3.03448,3.334495 -0.620528,-1.150059 -1.590924,-1.733057 -1.777095,-3.159526 0.272651,-0.513162 0.73631,-0.690429 1.134437,-0.921315 4.348504,2.537775 8.940017,2.567194 13.520809,-0.09721 0.3992,0.232267 0.865612,0.408903 1.139361,0.924145 -0.186169,1.426466 -1.156653,2.009373 -1.77718,3.159432 1.806152,-1.149458 3.062393,-2.160808 3.034567,-3.334404 -0.348577,-0.575327 -0.596425,-1.007823 -1.305212,-1.363809 0.885404,-1.628174 0.801118,-1.643498 -1.057215,-1.444312 -0.01575,-0.02035 -0.03142,-0.04095 -0.04719,-0.06115 0.64197,-1.812444 0.601698,-1.859809 -1.128964,-1.390281 0.438016,-1.890928 0.379488,-1.898639 -1.374033,-1.201706 0.132083,-1.993216 0.08478,-1.989077 -1.569906,-0.996615 -0.441235,-1.910865 -0.493298,-1.905179 -1.755737,-0.516608 -0.47485,-0.871404 -0.724681,-1.307623 -0.959377,-1.310414 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-body" />
<g
id="g5758-8"
transform="matrix(0.17662502,0,0,0.17662502,50.30154,5.9339373)"
style="display:inline"
inkscape:label="kani-face">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399-5"
inkscape:label="kani-smile" />
<g
id="g351-9"
inkscape:label="kani-eye-left">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415-4" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417-3" />
</g>
<g
id="g355-9"
inkscape:label="kani-eye-right">
<path
id="path5433-0"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435-5"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389-6"
d="m 64.017157,26.135819 c -0.09015,7.21e-4 -0.150254,0.0096 -0.140378,0.0157 0,0 -1.613306,2.108215 0.05476,3.295613 a 1.7215836,1.4619408 37.793943 0 0 0.253834,1.664465 1.7215836,1.4619408 37.793943 0 0 1.865992,0.700706 l -0.629511,-1.519981 1.624394,0.129884 a 1.7215836,1.4619408 37.793943 0 0 -1.241046,-1.573829 1.7215836,1.4619408 37.793943 0 0 -1.237759,0.0016 c -0.328933,-0.240918 -0.817544,-0.870047 -0.0072,-2.203434 0.268129,-0.4412 -0.272621,-0.512766 -0.543076,-0.510674 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-down" />
<path
d="m 76.430419,24.706073 c 0.619836,-0.549958 0.741181,-1.238386 0.561023,-2.010565 l -1.305501,0.677033 z"
style="display:inline;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path523-3"
inkscape:label="under-kani-claw" />
<path
id="path5732-74-6"
d="m 74.881859,22.103715 c -0.385284,0.251515 -0.67912,0.647427 -0.812249,1.094423 -0.175107,0.591814 -0.04525,1.190806 0.338922,1.563471 -0.632214,1.725553 -1.775814,1.157848 -1.799979,1.135149 -0.194317,-0.151243 -0.914554,0.641231 0.09409,0.923623 1.575529,0.441116 2.256511,-0.966983 2.500339,-1.708031 0.432965,0.03313 0.886665,-0.138029 1.245622,-0.469905 0.54202,-0.50258 0.767337,-1.275607 0.557775,-1.913613 l -1.468079,0.742469 8.49e-4,-0.228232 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 74.881859,22.103715 c -0.385284,0.251516 -0.67912,0.647427 -0.812249,1.094424 -0.175107,0.591813 -0.04525,1.190805 0.338922,1.56347 -0.632214,1.725552 -1.775814,1.157847 -1.799979,1.135147 -0.194317,-0.151243 -0.914554,0.641232 0.09409,0.923624 1.575529,0.441115 2.256511,-0.966984 2.500339,-1.708029 0.432965,0.03313 0.886665,-0.13803 1.245626,-0.469906 0.542016,-0.50258 0.767337,-1.275607 0.557774,-1.913613 l -1.468082,0.742469 8.49e-4,-0.228231 z"
id="path5728-3" />
<path
id="path5732-0"
d="m 74.881859,22.103715 c -0.385284,0.251516 -0.67912,0.647427 -0.812249,1.094424 -0.175107,0.591813 -0.04525,1.190805 0.338922,1.56347 -0.632214,1.725552 -1.775814,1.157847 -1.799979,1.135147 -0.194317,-0.151243 -0.914554,0.641232 0.09409,0.923624 1.575529,0.441115 2.256511,-0.966984 2.500339,-1.708029 0.432965,0.03313 0.886665,-0.13803 1.245626,-0.469906 0.542016,-0.50258 0.767337,-1.275607 0.557774,-1.913613 l -1.468082,0.742469 8.49e-4,-0.228231 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5404-8"
d="m 46.528932,35.750289 c -0.593985,1.435445 1.021812,2.758664 1.028747,2.780044 -0.295135,-0.865737 -0.607057,-1.564607 -0.03337,-2.169704"
style="display:inline;fill:#803300;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-rear-claw-left"
inkscape:export-filename="../1d28c49c/party-kani.png"
inkscape:export-xdpi="160.64"
inkscape:export-ydpi="160.64" />
<path
style="display:inline;fill:#803300;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 59.935581,35.655876 c 0.593984,1.435446 -1.021816,2.758664 -1.028744,2.780042 0.29513,-0.865736 0.607052,-1.564606 0.03336,-2.169702"
id="path5408-4"
inkscape:label="kani-rear-claw-right" />
<path
id="path4529-0"
d="m 53.189335,28.1516 c -0.231139,-0.0028 -0.452316,0.415558 -0.863816,1.234018 -1.245538,-1.369863 -1.30925,-1.386447 -1.738394,0.468051 -0.02009,0.0084 -0.04012,0.01694 -0.06016,0.02556 -1.568841,-0.940424 -1.649896,-0.947779 -1.530839,0.890464 -0.03809,0.02825 -0.07616,0.05689 -0.114095,0.08598 -1.60764,-0.636836 -1.677152,-0.596707 -1.270343,1.167752 -0.01907,0.02062 -0.03817,0.04112 -0.05714,0.06198 -1.638526,-0.442689 -1.684371,-0.376171 -1.073287,1.352673 -0.02622,0.03761 -0.05225,0.07587 -0.07832,0.114001 -1.846823,-0.197423 -1.884762,-0.161352 -0.945867,1.559317 -0.0046,0.0085 -0.0093,0.01665 -0.01388,0.02519 -0.756628,0.361696 -1.007111,0.802551 -1.365635,1.394294 -0.02783,1.173597 1.228321,2.185037 3.03448,3.334494 -0.620528,-1.150058 -1.590924,-1.733057 -1.777096,-3.159524 0.272652,-0.513163 0.73631,-0.69043 1.134438,-0.921315 4.348502,2.537775 8.940015,2.567192 13.520808,-0.09721 0.399201,0.232267 0.865612,0.408902 1.139366,0.924144 -0.186169,1.426466 -1.156658,2.009375 -1.777184,3.159432 1.806155,-1.149456 3.062395,-2.160806 3.03457,-3.334404 -0.348576,-0.575326 -0.596424,-1.007822 -1.305211,-1.363808 0.885404,-1.628174 0.801118,-1.643498 -1.05722,-1.444313 -0.01575,-0.02035 -0.03142,-0.04095 -0.04719,-0.06115 0.641971,-1.812444 0.601697,-1.859807 -1.128965,-1.390279 0.438017,-1.890929 0.379489,-1.898641 -1.374031,-1.201708 0.132081,-1.993216 0.08476,-1.989076 -1.569909,-0.996614 -0.441235,-1.910865 -0.493297,-1.905179 -1.755736,-0.516608 -0.474851,-0.871405 -0.724682,-1.307623 -0.959378,-1.310414 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:0.176626;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-body" />
<g
id="g5758-4"
transform="matrix(0.17662502,0,0,0.17662502,35.073547,14.946424)"
style="display:inline"
inkscape:label="kani-face">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399-6"
inkscape:label="kani-smile" />
<g
id="g351-4"
inkscape:label="kani-eye-left">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415-6" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417-4" />
</g>
<g
id="g355-3"
inkscape:label="kani-eye-right">
<path
id="path5433-8"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435-4"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389-0"
d="m 48.789163,35.148303 c -0.09015,7.22e-4 -0.150253,0.0096 -0.140378,0.0157 0,0 -1.613304,2.108215 0.05476,3.295613 a 1.7215836,1.4619408 37.793943 0 0 0.253835,1.664464 1.7215836,1.4619408 37.793943 0 0 1.865992,0.700707 l -0.629512,-1.51998 1.624394,0.129883 a 1.7215836,1.4619408 37.793943 0 0 -1.241045,-1.57383 1.7215836,1.4619408 37.793943 0 0 -1.23776,0.0016 c -0.328933,-0.240918 -0.817544,-0.870048 -0.0072,-2.203435 0.268129,-0.4412 -0.272621,-0.512767 -0.543077,-0.510673 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-down" />
<path
id="path5732-5"
d="m 54.978485,39.313353 c 0.01268,-0.459938 0.196247,-0.917525 0.507459,-1.26491 0.412645,-0.458946 0.991007,-0.661794 1.509625,-0.529473 1.139473,-1.441813 0.05721,-2.119162 0.02521,-2.127874 -0.230574,-0.08642 0.06775,-1.114898 0.836266,-0.403225 1.200459,1.111651 0.357013,2.428876 -0.146824,3.024483 0.254828,0.351594 0.346438,0.827773 0.251514,1.307344 -0.144578,0.724888 -0.685353,1.321464 -1.338681,1.476808 l -0.135687,-1.639552 -0.194033,0.120174 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.176626;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g7998"
inkscape:label="party-hat"
transform="matrix(1.0107117,0,0,0.94642879,-34.697969,-6.0098395)"
style="display:none">
<path
style="display:inline;fill:#1da1f2;fill-opacity:1;stroke:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path7024"
inkscape:label="party-hat-background"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 90.866459,64.772976 -1.399384,3.650367 c 0,0 6.297977,-0.0079 9.604616,-2.229612 3.306639,-2.221751 6.641769,-4.763148 6.637539,-5.892882 -0.004,-1.129737 -1.2244,-3.398221 -1.2244,-3.398221 0,0 -1.76379,2.983434 -5.089689,4.759036 -3.325897,1.775606 -6.256176,3.317156 -8.528682,3.111312 z"
id="path7711"
inkscape:label="yellow-middle" />
<path
style="fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 95.283815,50.496253 c 0,0 2.017636,-0.742584 2.746046,-1.209859 0.68409,-0.438843 2.540819,-1.493505 2.505569,-1.916931 -0.0353,-0.423423 0.90713,1.638551 0.90713,1.638551 0,0 -0.43327,0.543192 -1.447199,1.610006 -1.01394,1.066813 -1.72817,1.358505 -2.26456,1.622681 -0.53638,0.264174 -2.0866,0.861497 -2.636383,0.793816 -0.549783,-0.06768 0.189397,-2.538264 0.189397,-2.538264 z"
id="path7713"
sodipodi:nodetypes="csccsssc"
inkscape:label="yellow-top" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 109.92717,71.488689 c 0,0 -2.20481,3.007619 -3.4367,4.221512 -1.06044,1.044951 -3.3987,2.32693 -5.45873,3.108264 -2.060039,0.78133 -7.390321,2.031263 -10.319319,2.035505 -2.53475,0.0037 -3.102127,-0.17882 -4.727213,-0.614261 -1.010264,-0.270699 -0.801047,2.045289 -0.801047,2.045289 l 4.858605,2.209687 10.217314,-0.32013 c 0,0 3.08806,-0.85865 3.26326,-0.9226 0.17519,-0.06395 5.61136,-3.775927 5.91906,-4.176562 0.30769,-0.400637 1.97931,-2.854544 1.85876,-3.106917 -0.12055,-0.25237 -1.37399,-4.479787 -1.37399,-4.479787 z"
id="path7715"
sodipodi:nodetypes="csssscccsssc"
inkscape:label="yellow-bottom" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 88.208385,72.747325 c 0,0 6.251797,0.04012 9.907566,-1.876691 3.875559,-2.032049 5.147419,-2.116642 8.954089,-7.814465 -0.0353,-0.423423 1.59724,3.704345 1.59724,3.704345 0,0 -2.99139,5.115034 -6.19574,6.653752 -4.079519,1.958969 -6.324966,2.675602 -8.934752,3.106262 -1.713747,0.282798 -4.8213,0.352969 -6.27215,-0.01279 -0.537127,-0.135411 0.943747,-3.760409 0.943747,-3.760409 z"
id="path7982"
sodipodi:nodetypes="csccssscc"
inkscape:label="white-middle" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 92.993027,57.289539 c 0,0 3.629855,-0.719631 5.482954,-2.090937 0.65332,-0.483462 3.662839,-3.285322 4.163269,-4.079419 -0.0353,-0.423423 0.98376,2.839616 0.98376,2.839616 0,0 -1.55545,1.919882 -2.68768,2.888013 -1.118619,0.956483 -3.276219,2.193755 -4.815637,2.748644 -1.634013,0.588985 -3.388075,0.798009 -3.937858,0.730328 -0.549783,-0.06768 0.811192,-3.036245 0.811192,-3.036245 z"
id="path7979"
sodipodi:nodetypes="csccsssc"
inkscape:label="white-top" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#ff00ff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path508"
inkscape:label="party-hat-outline"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:#8f57a8;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 95.307706,40.797374 8.863844,3.5236 c -4.511069,-1.476791 -5.455019,1.171445 -8.289058,2.243988 0,0 5.051628,-7.95802 4.765018,-7.734177 -1.618949,1.264417 -2.259979,9.16058 -2.259979,9.16058 l -0.26088,-9.396127 3.698779,7.921822 -7.078677,-2.809491 8.818747,-2.23602 z"
id="path3171-3"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="display:inline;fill:#0056ed;fill-opacity:1;stroke:#ae00ff;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 96.516104,39.086314 5.923406,7.476417 c -3.172509,-3.530708 -5.311942,-1.706696 -8.303144,-2.191314 0,0 8.348654,-4.375959 7.988584,-4.324981 -2.03392,0.28796 -6.529397,6.811072 -6.529397,6.811072 l 4.462297,-8.273038 -0.747319,8.710783 -4.732664,-5.966807 8.758203,2.462516 z"
id="path3171"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="fill:#744eaa;fill-opacity:1;stroke:#8f57a8;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 85.749242,80.805507 -1.299196,-0.793626 -0.72012,1.516668 -1.19957,0.02641 1.07959,1.592728 -0.479079,1.665647 1.727851,-0.04555 0.945116,1.102037 1.595689,-0.420439 1.013886,1.119783 1.616715,-0.611034 0.955484,0.869926 1.417278,-0.82042 1.399024,0.893951 1.62274,-0.936247 1.191522,0.880515 1.692439,-0.626057 1.62431,0.594614 1.273839,-0.730245 1.14228,0.675465 0.83977,-0.542118 1.7433,0.433922 1.37853,-0.883036 1.46376,0.789948 0.98369,-0.723702 1.56885,0.265388 0.83917,-1.064009 1.5817,0.538036 1.04635,-1.072595 1.45989,-0.386437 -0.0243,-1.46382 0.73064,-1.311775 -1.08662,-0.620199 -0.54503,-1.441178 -1.29888,0.821314 -1.72095,-0.803735 -0.89109,1.605976 -1.44802,-0.173747 -0.40144,0.954405 -1.25934,-0.74345 -1.14276,1.025692 -2.05145,-0.719347 c 0,0 -1.1538,1.054828 -1.21655,1.018337 -0.0627,-0.03649 -0.72908,-0.632037 -0.72908,-0.632037 l -2.781839,0.990357 -1.01136,-0.982973 -0.76464,0.897745 -1.265471,-0.508519 -1.392952,0.640072 -1.261957,-0.842494 -1.207529,1.021591 -2.137111,-1.168069 -0.83207,0.565255 -0.716719,-0.940683 -1.262123,0.222665 -0.854411,-1.073467 z"
id="path3419"
inkscape:label="purple-frizzy-bit" />
</g>
<g
id="g571"
transform="matrix(-0.37792196,-0.19227008,-0.19227008,0.37792196,160.95541,83.78303)"
inkscape:export-filename="../fe6a9b8b/kani-stabby.png"
inkscape:export-xdpi="370.63721"
inkscape:export-ydpi="370.63721"
style="display:none"
inkscape:label="knife-left">
<path
d="M 327.32922,104.16963 313.23927,90.079688 246.31471,157.00414 c 0,0 -6.09712,4.9021 -4.45506,8.97393 1.6429,4.07209 4.34482,5.77218 11.55969,6.84567 6.14218,0.99798 6.56385,-1.72199 9.06459,-9.07879 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 5.35834,-9.35496 4.43089,-10.42811 12.07206,-10.21327 4.21357,0.0716 4.88879,-0.46249 6.31789,-1.74872 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439"
style="fill:#c52a34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path561" />
<path
d="M 333.98084,110.82126 313.52961,90.369671 c 0,0 66.27001,-67.178418 73.76936,-74.221626 7.49688,-7.0446203 13.081,-13.0810013 23.41845,-16.14946258 -0.68086,6.58883098 -6.21418,20.98428158 -13.48493,31.66357158 -7.2711,10.681759 -36.32483,51.549656 -63.25165,79.159106"
style="fill:#b9b8b7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path563" />
<path
d="m 404.21431,2.595027 c -6.52039,3.3330447 -11.1573,8.142464 -16.91534,13.553018 -7.49935,7.043208 -73.76936,74.221626 -73.76936,74.221626 l 15.67956,15.678859 c 26.92717,-27.608745 55.97949,-68.476642 63.25094,-79.158049 5.17807,-7.605536 9.46362,-17.0825591 11.7542,-24.295454"
style="fill:#999a99;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path565" />
<path
d="m 255.68936,173.17156 c -0.61955,0 -1.31989,-0.0656 -2.11762,-0.19541 -4.67209,-0.69397 -7.69193,-1.90252 -9.11972,-3.20187 l 0.0139,-0.014 c 1.82658,1.5197 4.5489,2.40839 8.9534,3.06351 0.81082,0.13188 1.52216,0.19899 2.15166,0.19899 4.13826,0 4.74239,-2.8925 6.91293,-9.27778 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 4.98299,-8.6995 4.53108,-10.23726 10.58792,-10.23726 0.4565,0 0.94862,0.009 1.48414,0.024 0.24729,0.004 0.48295,0.006 0.70661,0.006 3.58916,0 4.26614,-0.54433 5.61128,-1.75507 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439 v 0 c 0,0 -5.6,5.75451 -7.02875,7.03933 -1.34549,1.21109 -2.01824,1.7593 -5.61058,1.7593 -0.2226,0 -0.45649,-0.002 -0.70238,-0.006 -0.53269,-0.0152 -1.0227,-0.0236 -1.47708,-0.0236 -6.06319,0 -5.60035,1.54375 -10.5851,10.24714 -5.35693,9.35778 -17.62054,20.09429 -26.55122,29.02274 -8.92845,8.92803 -10.31434,4.2562 -12.81342,11.61408 -2.17593,6.40046 -2.71998,9.34932 -6.87133,9.34932 m 71.63986,-69.00193 -8.63636,-8.63635 v 0 l 8.63636,8.63635"
style="fill:#cdccca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path567" />
<path
d="m 255.571,173.02273 c -0.6295,0 -1.34084,-0.0671 -2.15166,-0.19899 -4.4045,-0.65512 -7.12682,-1.54381 -8.9534,-3.06351 l 74.22692,-74.22695 8.63636,8.63635 c 0,0 -5.60459,5.74958 -7.03334,7.03439 -1.34514,1.21074 -2.02212,1.75507 -5.61128,1.75507 -0.22366,0 -0.45932,-0.002 -0.70661,-0.006 -0.53552,-0.0152 -1.02764,-0.024 -1.48414,-0.024 -6.05684,0 -5.60493,1.53776 -10.58792,10.23726 -5.35587,9.35814 -17.63924,20.0762 -26.56932,29.0037 -8.92912,8.92874 -10.3536,4.21845 -12.85268,11.57524 -2.17054,6.38528 -2.77467,9.27778 -6.91293,9.27778"
style="fill:#9b232b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path569" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 51 KiB

View file

@ -1,834 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg5800"
version="1.1"
viewBox="0 0 7.9374999 7.9374999"
height="30"
width="30"
sodipodi:docname="icon-person.svg"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
inkscape:export-filename="../94e4957b/kani-yellow-sign.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1386"
inkscape:window-height="847"
id="namedview108"
showgrid="false"
inkscape:zoom="13.325447"
inkscape:cx="8.8177156"
inkscape:cy="23.001105"
inkscape:window-x="54"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer3"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showguides="true">
<sodipodi:guide
position="-2.2872575,-58.804357"
orientation="0,1"
id="guide932"
inkscape:locked="false"
inkscape:label="MiddleHorizontal"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="72.712742,16.195644"
orientation="-1,0"
id="guide934"
inkscape:locked="false"
inkscape:label="MiddleVertical"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="2.4708111,-33.581074"
orientation="0,-1"
id="guide1652"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs5794">
<radialGradient
id="Gradient_4"
gradientUnits="userSpaceOnUse"
cx="420.20801"
cy="346.897"
r="21.1">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8111" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8113" />
</radialGradient>
<radialGradient
id="Gradient_5"
gradientUnits="userSpaceOnUse"
cx="385.79401"
cy="98.971001"
r="165.23399">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8116" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8118" />
</radialGradient>
<radialGradient
id="Gradient_6"
gradientUnits="userSpaceOnUse"
cx="154.649"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8121" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8123" />
</radialGradient>
<radialGradient
id="Gradient_7"
gradientUnits="userSpaceOnUse"
cx="120.117"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8126" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8128" />
</radialGradient>
<radialGradient
id="Gradient_8"
gradientUnits="userSpaceOnUse"
cx="660.41699"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8131" />
<stop
offset="1"
stop-color="#F60012"
id="stop8133" />
</radialGradient>
<radialGradient
id="Gradient_9"
gradientUnits="userSpaceOnUse"
cx="625.88501"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8136" />
<stop
offset="1"
stop-color="#F60012"
id="stop8138" />
</radialGradient>
<radialGradient
id="Gradient_10"
gradientUnits="userSpaceOnUse"
cx="283.38901"
cy="577.77899"
r="21.1">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8141" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8143" />
</radialGradient>
<radialGradient
id="Gradient_11"
gradientUnits="userSpaceOnUse"
cx="213.711"
cy="337.353"
r="165.23399">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8146" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8148" />
</radialGradient>
<linearGradient
id="Gradient_12"
gradientUnits="userSpaceOnUse"
x1="395.94601"
y1="909.60699"
x2="398.86099"
y2="889.30902">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8151" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8153" />
</linearGradient>
<linearGradient
id="Gradient_13"
gradientUnits="userSpaceOnUse"
x1="364.41199"
y1="920.81097"
x2="381.88199"
y2="898.88202">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8156" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8158" />
</linearGradient>
<linearGradient
id="Gradient_14"
gradientUnits="userSpaceOnUse"
x1="400.103"
y1="884.01398"
x2="414.327"
y2="886.43201">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8161" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8163" />
</linearGradient>
<linearGradient
id="Gradient_15"
gradientUnits="userSpaceOnUse"
x1="401.52899"
y1="-883.02899"
x2="418.35501"
y2="-884.72101"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8166" />
<stop
offset="1"
stop-color="#90282C"
id="stop8168" />
</linearGradient>
<clipPath
id="Clip_1">
<path
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c -2.74,0.34 -5.51,-0.08 -8.21,-0.56 v 0 c 1.75,0.6 3.93,1.12 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8171" />
</clipPath>
<linearGradient
id="Gradient_16"
gradientUnits="userSpaceOnUse"
x1="406.53"
y1="880.51599"
x2="409.40302"
y2="880.80499">
<stop
offset="0"
stop-color="#AF2525"
id="stop8174" />
<stop
offset="1"
stop-color="#90282C"
id="stop8176" />
</linearGradient>
<linearGradient
id="Gradient_17"
gradientUnits="userSpaceOnUse"
x1="452.20001"
y1="905.45599"
x2="444.604"
y2="892.92102">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8179" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8181" />
</linearGradient>
<linearGradient
id="Gradient_18"
gradientUnits="userSpaceOnUse"
x1="462.254"
y1="893.729"
x2="456.45901"
y2="887.92401">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8184" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8186" />
</linearGradient>
<linearGradient
id="Gradient_19"
gradientUnits="userSpaceOnUse"
x1="446.13501"
y1="882.91602"
x2="429.77499"
y2="883.61603">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8189" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8191" />
</linearGradient>
<linearGradient
id="Gradient_20"
gradientUnits="userSpaceOnUse"
x1="443.24799"
y1="-882.92798"
x2="426.452"
y2="-884.64899"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8194" />
<stop
offset="1"
stop-color="#90282C"
id="stop8196" />
</linearGradient>
<clipPath
id="Clip_2">
<path
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -3.99,0.52 -9.46,1.2 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8199" />
</clipPath>
<linearGradient
id="Gradient_21"
gradientUnits="userSpaceOnUse"
x1="438.23401"
y1="880.48499"
x2="435.36099"
y2="880.78003">
<stop
offset="0"
stop-color="#AF2525"
id="stop8202" />
<stop
offset="1"
stop-color="#90282C"
id="stop8204" />
</linearGradient>
<radialGradient
id="Gradient_22"
gradientUnits="userSpaceOnUse"
cx="421.086"
cy="879.30603"
r="5.8280001">
<stop
offset="0"
stop-color="#E61F19"
id="stop8207" />
<stop
offset="0.988"
stop-color="#9E282C"
id="stop8209" />
<stop
offset="1"
stop-color="#9E282C"
id="stop8211" />
</radialGradient>
<radialGradient
id="Gradient_23"
gradientUnits="userSpaceOnUse"
cx="146.832"
cy="-316.11899"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8214" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8216" />
</radialGradient>
<radialGradient
id="Gradient_24"
gradientUnits="userSpaceOnUse"
cx="55.174999"
cy="-176.464"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8219" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8221" />
</radialGradient>
<radialGradient
id="Gradient_25"
gradientUnits="userSpaceOnUse"
cx="565.53101"
cy="-165.11301"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8224" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8226" />
</radialGradient>
<radialGradient
id="Gradient_26"
gradientUnits="userSpaceOnUse"
cx="333.47"
cy="-63.138"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8229" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8231" />
</radialGradient>
<radialGradient
id="Gradient_27"
gradientUnits="userSpaceOnUse"
cx="536.58099"
cy="578.03198"
r="21.1">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8234" />
<stop
offset="1"
stop-color="#229614"
id="stop8236" />
</radialGradient>
<radialGradient
id="Gradient_28"
gradientUnits="userSpaceOnUse"
cx="553.57397"
cy="328.29001"
r="165.23399">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8239" />
<stop
offset="1"
stop-color="#229614"
id="stop8241" />
</radialGradient>
<radialGradient
id="Gradient_29"
gradientUnits="userSpaceOnUse"
cx="488.81201"
cy="-304.62601"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8244" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8246" />
</radialGradient>
<symbol
id="balloons"
viewBox="0 0 825 962">
<g
style="fill:none;stroke:#eccb78;stroke-width:4.069;stroke-linecap:round;stroke-linejoin:round"
id="g8261">
<path
d="M 291.13,584.61 422.41,889.24 540.38,589.26"
id="path8249" />
<path
d="m 426.95,357.75 -4.54,531.49"
id="path8251" />
<path
d="M 158.17,411.35 421.71,889.24 667.1,402.98"
id="path8253" />
<path
d="m 415.21,958.51 c 8.81,-23.5 6.08,-71.63 6.08,-71.63 -3.66,47 -29.02,70.1 -29.02,70.1"
id="path8255" />
<path
d="m 456.29,956.29 c 0,0 -26.52,-21.77 -32.58,-68.52 0,0 -0.26,48.21 9.75,71.23"
id="path8257" />
<path
d="m 404.06,957.77 c 0,0 13.62,-20.66 18.45,-69.2"
id="path8259" />
</g>
<path
style="fill:url(#Gradient_4)"
d="m 423.73,337.05 -21.46,35.03 c -1.86,3.04 0.32,6.93 3.89,6.93 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.83 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.28 -3.95,-2.28 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8263" />
<path
style="fill:url(#Gradient_5)"
d="m 273.05,178.98 c 0,97.08 68.93,175.78 153.96,175.79 v 0 c 85.03,0 153.96,-78.7 153.97,-175.79 v 0 C 580.98,81.9 512.05,3.2 427.01,3.2 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8265" />
<path
style="fill:url(#Gradient_6)"
d="m 158.17,425.62 -21.46,35.02 c -1.86,3.04 0.32,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 L 166,425.71 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8267" />
<path
style="opacity:0.95;fill:url(#Gradient_7)"
d="m 7.37,267.55 c 0,97.08 68.93,175.78 153.97,175.78 v 0 c 85.03,0 153.96,-78.7 153.96,-175.78 v 0 C 315.3,170.47 246.37,91.77 161.34,91.77 v 0 C 76.31,91.77 7.38,170.47 7.37,267.55 Z"
id="path8269" />
<path
style="fill:url(#Gradient_8)"
d="m 663.94,425.62 -21.46,35.02 c -1.86,3.04 0.33,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8271" />
<path
style="opacity:0.95;fill:url(#Gradient_9)"
d="m 513.14,267.55 c 0,97.08 68.93,175.78 153.96,175.78 v 0 c 85.03,0 153.96,-78.7 153.97,-175.78 v 0 C 821.07,170.47 752.14,91.77 667.1,91.77 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8273" />
<path
style="fill:url(#Gradient_10)"
d="m 285.46,567.53 -16.21,37.74 c -1.41,3.27 1.31,6.82 4.83,6.3 v 0 l 41.28,-5.97 c 3.47,-0.5 5.1,-4.58 2.93,-7.33 v 0 l -25.06,-31.76 c -0.93,-1.17 -2.25,-1.74 -3.57,-1.74 v 0 c -1.72,0 -3.42,0.96 -4.2,2.76 z"
id="path8275" />
<path
style="opacity:0.95;fill:url(#Gradient_11)"
d="m 240.79,236.67 c -84.15,12.18 -141.11,99.94 -127.2,196.02 v 0 c 13.9,96.08 93.39,164.1 177.54,151.92 v 0 c 84.15,-12.18 141.1,-99.94 127.21,-196.02 v 0 C 405.52,300 336.93,235.26 260.34,235.26 v 0 c -6.47,0 -12.99,0.46 -19.55,1.41 z"
id="path8277" />
<path
style="fill:url(#Gradient_27)"
d="M 542.05,569.11 513.87,599 c -2.44,2.59 -1.1,6.85 2.38,7.59 v 0 l 40.83,8.54 c 3.43,0.72 6.36,-2.55 5.25,-5.88 v 0 l -12.64,-38.43 c -0.66,-2.01 -2.48,-3.13 -4.35,-3.14 v 0 c -1.18,0 -2.38,0.46 -3.29,1.43 z"
id="path8279" />
<path
style="opacity:0.95;fill:url(#Gradient_28)"
d="m 426.83,383.51 c -19.89,95.02 31.46,186.18 114.68,203.6 v 0 c 83.23,17.42 166.82,-45.49 186.71,-140.52 v 0 C 748.11,351.57 696.76,260.41 613.54,242.99 v 0 c -9.34,-1.96 -18.68,-2.9 -27.94,-2.89 v 0 c -73.25,0 -141.11,59.05 -158.77,143.41 z"
id="path8281" />
<path
style="opacity:0.7;fill:url(#Gradient_23)"
d="m 126.74,409.71 c 0,0 29.93,-94.86 60.85,-110.42 19.92,-10.03 -4.23,-30.39 -27.01,-14.05 -6.7,5.14 -12.35,11.69 -17.06,18.66 -14.36,21.28 -19.56,46.01 -20.09,71.27 v 0.02 c -0.07,11.6 1.32,23.12 3.31,34.52 z"
id="path8283" />
<path
style="opacity:0.7;fill:url(#Gradient_24)"
d="m 30.74,269.01 c 0,0 34.33,-93.36 65.93,-107.45 20.37,-9.09 -2.81,-30.55 -26.32,-15.3 0,0 -51.89,32.72 -39.61,122.75 z"
id="path8285" />
<path
style="opacity:0.7;fill:url(#Gradient_25)"
d="m 537.49,256.64 c 0,0 37.96,-91.94 70.09,-104.8 20.71,-8.29 -1.61,-30.64 -25.71,-16.31 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8287" />
<path
style="opacity:0.7;fill:url(#Gradient_26)"
d="m 305.43,154.66 c 0,0 37.96,-91.94 70.09,-104.79 20.71,-8.28 -1.61,-30.64 -25.71,-16.32 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8289" />
<path
style="opacity:0.7;fill:url(#Gradient_29)"
d="m 454.08,393.82 c 0,0 44.65,-88.88 77.64,-99.32 21.27,-6.73 0.66,-30.67 -24.42,-18.17 0,0 -55.25,26.66 -53.22,117.49 z"
id="path8291" />
<path
style="fill:url(#Gradient_12)"
d="m 375.83,907.75 9.08,1.65 c -1.21,2.8 0.79,11.89 0.79,11.89 v 0 c 8.26,-15.42 34.5,-34.41 33.81,-33.91 v 0 l 0.71,-5.79 c -24.03,1.57 -44.39,26.16 -44.39,26.16 z"
id="path8293" />
<path
style="fill:url(#Gradient_13)"
d="m 380.2,872.6 c 13.6,7.74 40.82,5.99 40.82,5.99 v 0 c -22.8,-5.81 -32.93,-7.21 -37.4,-7.21 v 0 c -4.17,0 -3.42,1.22 -3.42,1.22 z"
id="path8295" />
<path
style="fill:url(#Gradient_14)"
d="m 380.45,890.39 c 7.74,5.62 42.01,-3.08 42.01,-3.09 v 0 l -0.09,-10.01 c -42.04,-1.73 -42.31,-5.05 -42.31,-5.05 v 0 z"
id="path8297" />
<g
clip-path="url(#Clip_1)"
opacity="0.24"
id="g8301">
<path
style="fill:url(#Gradient_15)"
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c 0,0 -3,0.5 -8.21,-0.56 2.01,0.59 4.41,1.58 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8299" />
</g>
<path
style="fill:url(#Gradient_16)"
d="m 409.38,881 c -1.53,-0.45 -2.83,-0.68 -2.83,-0.68 v 0 c 1.01,0.28 1.95,0.5 2.83,0.68 z"
id="path8303" />
<path
style="fill:url(#Gradient_17)"
d="m 425.53,887.37 c 0,0 25.53,18.33 33.88,33.85 v 0 c 0,0 1.98,-9.1 0.76,-11.89 v 0 l 9.08,-1.67 c 0,0 -20.4,-24.55 -44.44,-26.08 v 0 z"
id="path8305" />
<path
style="fill:url(#Gradient_18)"
d="m 423.74,878.59 c 0,0 40.72,9.96 40.43,-5.69 v 0 c -0.01,-0.45 0.33,-1.72 -3.83,-1.73 v 0 c -4.58,0 -14.62,1.54 -36.6,7.42 z"
id="path8307" />
<path
style="fill:url(#Gradient_19)"
d="m 422.39,877.29 -0.07,10.01 c 0,0 34.28,8.63 42.01,3 v 0 l -0.11,-17.86 c 0,0 -0.29,1.43 -41.83,4.85 z"
id="path8309" />
<g
clip-path="url(#Clip_2)"
opacity="0.24"
id="g8313">
<path
style="fill:url(#Gradient_20)"
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -9.75,1.64 -12.88,-1.55 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8311" />
</g>
<path
style="fill:url(#Gradient_21)"
d="m 435.38,880.98 c 0.88,-0.18 1.83,-0.4 2.83,-0.69 v 0 c 0,0 -1.3,0.23 -2.83,0.69 z"
id="path8315" />
<path
style="fill:url(#Gradient_22)"
d="m 417.09,877.31 c 0,0 -1.77,8.19 0.74,11.61 v 0 l 9.25,-0.01 c 0,0 2.64,-7.59 0.88,-11.59 v 0 c 0,0 -4.62,-1 -7.84,-1 v 0 c -1.6,0 -2.86,0.25 -3.03,0.99 z"
id="path8317" />
</symbol>
</defs>
<metadata
id="metadata5797">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Kani"
style="display:inline"
transform="translate(-42.515769,-15.696706)">
<path
id="path5404"
d="m 43.650626,20.134541 c -0.251481,0.607741 0.432618,1.167966 0.435552,1.177018 -0.124954,-0.366537 -0.257016,-0.662425 -0.01414,-0.918611"
style="fill:#803300;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-rear-claw-left"
inkscape:export-filename="../1d28c49c/party-kani.png"
inkscape:export-xdpi="160.64"
inkscape:export-ydpi="160.64" />
<path
style="fill:#803300;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 49.326742,20.094569 c 0.251483,0.60774 -0.432616,1.167965 -0.435551,1.177016 0.124954,-0.366536 0.257016,-0.662424 0.01414,-0.91861"
id="path5408"
inkscape:label="kani-rear-claw-right" />
<path
id="path4529"
d="m 46.470512,16.917403 c -0.09786,-0.0012 -0.191501,0.17594 -0.365723,0.52246 -0.527336,-0.579974 -0.55431,-0.586996 -0.736002,0.198163 -0.0085,0.0035 -0.01698,0.0072 -0.02547,0.01082 -0.664216,-0.398156 -0.698535,-0.401271 -0.648128,0.377006 -0.01614,0.01196 -0.03225,0.02409 -0.0483,0.0364 -0.680644,-0.269624 -0.710073,-0.252634 -0.537839,0.494404 -0.008,0.0088 -0.01615,0.01741 -0.02419,0.02624 -0.693719,-0.187426 -0.713129,-0.159264 -0.454408,0.572695 -0.0111,0.01592 -0.02212,0.03212 -0.03316,0.04827 -0.78191,-0.08358 -0.797973,-0.06831 -0.400463,0.660184 -0.0019,0.0036 -0.004,0.0071 -0.0059,0.0107 -0.320341,0.153135 -0.426391,0.339784 -0.578183,0.590318 -0.01178,0.496878 0.520047,0.925101 1.28474,1.411761 -0.262719,-0.486913 -0.673566,-0.733744 -0.752387,-1.337683 0.115435,-0.217263 0.311739,-0.292314 0.480299,-0.390067 1.84107,1.074445 3.78503,1.0869 5.724449,-0.04115 0.169012,0.09834 0.366482,0.173122 0.482384,0.391265 -0.07882,0.603939 -0.489707,0.850731 -0.752426,1.337643 0.764692,-0.486658 1.296561,-0.914844 1.284779,-1.411722 -0.147581,-0.243582 -0.252513,-0.426693 -0.552601,-0.57741 0.374863,-0.689338 0.339179,-0.695826 -0.447607,-0.611494 -0.0066,-0.0087 -0.0133,-0.01733 -0.01998,-0.02589 0.271799,-0.767354 0.254748,-0.787407 -0.47798,-0.588618 0.185447,-0.800583 0.160666,-0.803848 -0.58174,-0.508779 0.05592,-0.84389 0.03589,-0.842138 -0.664667,-0.421948 -0.186812,-0.809024 -0.208854,-0.806616 -0.743345,-0.218722 -0.201042,-0.368936 -0.306818,-0.553622 -0.406182,-0.554804 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-body" />
<g
id="g5758"
transform="matrix(0.07477963,0,0,0.07477963,38.800638,11.326586)"
style="display:inline"
inkscape:label="kani-face">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399"
inkscape:label="kani-smile" />
<g
id="g351"
inkscape:label="kani-eye-left">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417" />
</g>
<g
id="g355"
inkscape:label="kani-eye-right">
<path
id="path5433"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389"
d="m 44.607564,19.879672 c -0.03817,2.95e-4 -0.06361,0.0041 -0.05943,0.0066 0,0 -0.683042,0.892578 0.02319,1.395299 a 0.72888529,0.61895752 37.793943 0 0 0.107467,0.704702 0.72888529,0.61895752 37.793943 0 0 0.790026,0.296666 l -0.266524,-0.643531 0.687738,0.05499 a 0.72888529,0.61895752 37.793943 0 0 -0.525436,-0.666329 0.72888529,0.61895752 37.793943 0 0 -0.524043,6.81e-4 c -0.139263,-0.102008 -0.346133,-0.368365 -0.0031,-0.932895 0.11352,-0.186796 -0.115423,-0.217097 -0.22993,-0.21621 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-down" />
<path
id="path8665"
d="m 83.960168,109.11313 c -0.27485,0.43011 -0.4985,0.69147 -0.49798,0.62578 0,0 -14.965898,1.38701 -15.636208,-10.186101 a 9.7471106,8.2770874 84.335985 0 1 -7.216103,-6.22894 9.7471106,8.2770874 84.335985 0 1 2.259156,-11.0566 l 5.392134,7.59511 4.268531,-8.17939 a 9.7471106,8.2770874 84.335985 0 1 3.80891,10.68931 9.7471106,8.2770874 84.335985 0 1 -3.73468,5.92977 c 0.16465,2.302521 1.70979,6.539611 10.54286,6.669381 2.92274,0.043 1.63761,2.85111 0.81319,4.14154 z"
style="display:none;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-up" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="kani-claw-right-overlay"
style="display:inline"
transform="translate(-42.515769,-15.696706)">
<path
d="m 49.863102,19.274345 c 0.262426,-0.232841 0.313802,-0.524307 0.237527,-0.851234 l -0.552725,0.286643 z"
style="display:inline;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path523"
inkscape:label="under-kani-claw" />
<path
id="path5732-74"
d="m 49.20747,18.172558 c -0.16312,0.106491 -0.287526,0.274108 -0.343892,0.463358 -0.07413,0.250562 -0.01916,0.504164 0.143494,0.661943 -0.267667,0.730566 -0.751843,0.49021 -0.762075,0.4806 -0.08228,-0.06403 -0.387206,0.271485 0.03983,0.391045 0.66705,0.186759 0.955364,-0.409403 1.058596,-0.723148 0.183308,0.01403 0.375396,-0.05843 0.527375,-0.198949 0.22948,-0.212782 0.324876,-0.540067 0.23615,-0.810187 l -0.621558,0.314348 4.31e-4,-0.09663 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 49.20747,18.172558 c -0.16312,0.106491 -0.287526,0.274108 -0.343892,0.463358 -0.07413,0.250562 -0.01916,0.504164 0.143494,0.661943 -0.267667,0.730565 -0.751843,0.49021 -0.762075,0.480599 -0.08228,-0.06403 -0.387206,0.271486 0.03984,0.391045 0.667048,0.186759 0.955363,-0.409403 1.058595,-0.723147 0.183308,0.01403 0.375397,-0.05843 0.527375,-0.198949 0.229479,-0.212782 0.324876,-0.540067 0.236151,-0.810187 l -0.621559,0.314348 4.31e-4,-0.09663 z"
id="path5728" />
<path
id="path5732"
d="m 49.20747,18.172558 c -0.16312,0.106491 -0.287526,0.274108 -0.343892,0.463358 -0.07413,0.250562 -0.01916,0.504164 0.143494,0.661943 -0.267667,0.730565 -0.751843,0.49021 -0.762075,0.480599 -0.08228,-0.06403 -0.387206,0.271486 0.03984,0.391045 0.667048,0.186759 0.955363,-0.409403 1.058595,-0.723147 0.183308,0.01403 0.375397,-0.05843 0.527375,-0.198949 0.229479,-0.212782 0.324876,-0.540067 0.236151,-0.810187 l -0.621559,0.314348 4.31e-4,-0.09663 z"
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g7998"
inkscape:label="party-hat"
transform="matrix(1.0107117,0,0,0.94642879,-36.985227,-14.267983)"
style="display:none">
<path
style="display:inline;fill:#1da1f2;fill-opacity:1;stroke:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path7024"
inkscape:label="party-hat-background"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 90.866459,64.772976 -1.399384,3.650367 c 0,0 6.297977,-0.0079 9.604616,-2.229612 3.306639,-2.221751 6.641769,-4.763148 6.637539,-5.892882 -0.004,-1.129737 -1.2244,-3.398221 -1.2244,-3.398221 0,0 -1.76379,2.983434 -5.089689,4.759036 -3.325897,1.775606 -6.256176,3.317156 -8.528682,3.111312 z"
id="path7711"
inkscape:label="yellow-middle" />
<path
style="fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 95.283815,50.496253 c 0,0 2.017636,-0.742584 2.746046,-1.209859 0.68409,-0.438843 2.540819,-1.493505 2.505569,-1.916931 -0.0353,-0.423423 0.90713,1.638551 0.90713,1.638551 0,0 -0.43327,0.543192 -1.447199,1.610006 -1.01394,1.066813 -1.72817,1.358505 -2.26456,1.622681 -0.53638,0.264174 -2.0866,0.861497 -2.636383,0.793816 -0.549783,-0.06768 0.189397,-2.538264 0.189397,-2.538264 z"
id="path7713"
sodipodi:nodetypes="csccsssc"
inkscape:label="yellow-top" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 109.92717,71.488689 c 0,0 -2.20481,3.007619 -3.4367,4.221512 -1.06044,1.044951 -3.3987,2.32693 -5.45873,3.108264 -2.060039,0.78133 -7.390321,2.031263 -10.319319,2.035505 -2.53475,0.0037 -3.102127,-0.17882 -4.727213,-0.614261 -1.010264,-0.270699 -0.801047,2.045289 -0.801047,2.045289 l 4.858605,2.209687 10.217314,-0.32013 c 0,0 3.08806,-0.85865 3.26326,-0.9226 0.17519,-0.06395 5.61136,-3.775927 5.91906,-4.176562 0.30769,-0.400637 1.97931,-2.854544 1.85876,-3.106917 -0.12055,-0.25237 -1.37399,-4.479787 -1.37399,-4.479787 z"
id="path7715"
sodipodi:nodetypes="csssscccsssc"
inkscape:label="yellow-bottom" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 88.208385,72.747325 c 0,0 6.251797,0.04012 9.907566,-1.876691 3.875559,-2.032049 5.147419,-2.116642 8.954089,-7.814465 -0.0353,-0.423423 1.59724,3.704345 1.59724,3.704345 0,0 -2.99139,5.115034 -6.19574,6.653752 -4.079519,1.958969 -6.324966,2.675602 -8.934752,3.106262 -1.713747,0.282798 -4.8213,0.352969 -6.27215,-0.01279 -0.537127,-0.135411 0.943747,-3.760409 0.943747,-3.760409 z"
id="path7982"
sodipodi:nodetypes="csccssscc"
inkscape:label="white-middle" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 92.993027,57.289539 c 0,0 3.629855,-0.719631 5.482954,-2.090937 0.65332,-0.483462 3.662839,-3.285322 4.163269,-4.079419 -0.0353,-0.423423 0.98376,2.839616 0.98376,2.839616 0,0 -1.55545,1.919882 -2.68768,2.888013 -1.118619,0.956483 -3.276219,2.193755 -4.815637,2.748644 -1.634013,0.588985 -3.388075,0.798009 -3.937858,0.730328 -0.549783,-0.06768 0.811192,-3.036245 0.811192,-3.036245 z"
id="path7979"
sodipodi:nodetypes="csccsssc"
inkscape:label="white-top" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#ff00ff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path508"
inkscape:label="party-hat-outline"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:#8f57a8;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 95.307706,40.797374 8.863844,3.5236 c -4.511069,-1.476791 -5.455019,1.171445 -8.289058,2.243988 0,0 5.051628,-7.95802 4.765018,-7.734177 -1.618949,1.264417 -2.259979,9.16058 -2.259979,9.16058 l -0.26088,-9.396127 3.698779,7.921822 -7.078677,-2.809491 8.818747,-2.23602 z"
id="path3171-3"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="display:inline;fill:#0056ed;fill-opacity:1;stroke:#ae00ff;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 96.516104,39.086314 5.923406,7.476417 c -3.172509,-3.530708 -5.311942,-1.706696 -8.303144,-2.191314 0,0 8.348654,-4.375959 7.988584,-4.324981 -2.03392,0.28796 -6.529397,6.811072 -6.529397,6.811072 l 4.462297,-8.273038 -0.747319,8.710783 -4.732664,-5.966807 8.758203,2.462516 z"
id="path3171"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="fill:#744eaa;fill-opacity:1;stroke:#8f57a8;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 85.749242,80.805507 -1.299196,-0.793626 -0.72012,1.516668 -1.19957,0.02641 1.07959,1.592728 -0.479079,1.665647 1.727851,-0.04555 0.945116,1.102037 1.595689,-0.420439 1.013886,1.119783 1.616715,-0.611034 0.955484,0.869926 1.417278,-0.82042 1.399024,0.893951 1.62274,-0.936247 1.191522,0.880515 1.692439,-0.626057 1.62431,0.594614 1.273839,-0.730245 1.14228,0.675465 0.83977,-0.542118 1.7433,0.433922 1.37853,-0.883036 1.46376,0.789948 0.98369,-0.723702 1.56885,0.265388 0.83917,-1.064009 1.5817,0.538036 1.04635,-1.072595 1.45989,-0.386437 -0.0243,-1.46382 0.73064,-1.311775 -1.08662,-0.620199 -0.54503,-1.441178 -1.29888,0.821314 -1.72095,-0.803735 -0.89109,1.605976 -1.44802,-0.173747 -0.40144,0.954405 -1.25934,-0.74345 -1.14276,1.025692 -2.05145,-0.719347 c 0,0 -1.1538,1.054828 -1.21655,1.018337 -0.0627,-0.03649 -0.72908,-0.632037 -0.72908,-0.632037 l -2.781839,0.990357 -1.01136,-0.982973 -0.76464,0.897745 -1.265471,-0.508519 -1.392952,0.640072 -1.261957,-0.842494 -1.207529,1.021591 -2.137111,-1.168069 -0.83207,0.565255 -0.716719,-0.940683 -1.262123,0.222665 -0.854411,-1.073467 z"
id="path3419"
inkscape:label="purple-frizzy-bit" />
</g>
<g
id="g571"
transform="matrix(-0.37792196,-0.19227008,-0.19227008,0.37792196,158.66815,75.524887)"
inkscape:export-filename="../fe6a9b8b/kani-stabby.png"
inkscape:export-xdpi="370.63721"
inkscape:export-ydpi="370.63721"
style="display:none"
inkscape:label="knife-left">
<path
d="M 327.32922,104.16963 313.23927,90.079688 246.31471,157.00414 c 0,0 -6.09712,4.9021 -4.45506,8.97393 1.6429,4.07209 4.34482,5.77218 11.55969,6.84567 6.14218,0.99798 6.56385,-1.72199 9.06459,-9.07879 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 5.35834,-9.35496 4.43089,-10.42811 12.07206,-10.21327 4.21357,0.0716 4.88879,-0.46249 6.31789,-1.74872 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439"
style="fill:#c52a34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path561" />
<path
d="M 333.98084,110.82126 313.52961,90.369671 c 0,0 66.27001,-67.178418 73.76936,-74.221626 7.49688,-7.0446203 13.081,-13.0810013 23.41845,-16.14946258 -0.68086,6.58883098 -6.21418,20.98428158 -13.48493,31.66357158 -7.2711,10.681759 -36.32483,51.549656 -63.25165,79.159106"
style="fill:#b9b8b7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path563" />
<path
d="m 404.21431,2.595027 c -6.52039,3.3330447 -11.1573,8.142464 -16.91534,13.553018 -7.49935,7.043208 -73.76936,74.221626 -73.76936,74.221626 l 15.67956,15.678859 c 26.92717,-27.608745 55.97949,-68.476642 63.25094,-79.158049 5.17807,-7.605536 9.46362,-17.0825591 11.7542,-24.295454"
style="fill:#999a99;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path565" />
<path
d="m 255.68936,173.17156 c -0.61955,0 -1.31989,-0.0656 -2.11762,-0.19541 -4.67209,-0.69397 -7.69193,-1.90252 -9.11972,-3.20187 l 0.0139,-0.014 c 1.82658,1.5197 4.5489,2.40839 8.9534,3.06351 0.81082,0.13188 1.52216,0.19899 2.15166,0.19899 4.13826,0 4.74239,-2.8925 6.91293,-9.27778 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 4.98299,-8.6995 4.53108,-10.23726 10.58792,-10.23726 0.4565,0 0.94862,0.009 1.48414,0.024 0.24729,0.004 0.48295,0.006 0.70661,0.006 3.58916,0 4.26614,-0.54433 5.61128,-1.75507 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439 v 0 c 0,0 -5.6,5.75451 -7.02875,7.03933 -1.34549,1.21109 -2.01824,1.7593 -5.61058,1.7593 -0.2226,0 -0.45649,-0.002 -0.70238,-0.006 -0.53269,-0.0152 -1.0227,-0.0236 -1.47708,-0.0236 -6.06319,0 -5.60035,1.54375 -10.5851,10.24714 -5.35693,9.35778 -17.62054,20.09429 -26.55122,29.02274 -8.92845,8.92803 -10.31434,4.2562 -12.81342,11.61408 -2.17593,6.40046 -2.71998,9.34932 -6.87133,9.34932 m 71.63986,-69.00193 -8.63636,-8.63635 v 0 l 8.63636,8.63635"
style="fill:#cdccca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path567" />
<path
d="m 255.571,173.02273 c -0.6295,0 -1.34084,-0.0671 -2.15166,-0.19899 -4.4045,-0.65512 -7.12682,-1.54381 -8.9534,-3.06351 l 74.22692,-74.22695 8.63636,8.63635 c 0,0 -5.60459,5.74958 -7.03334,7.03439 -1.34514,1.21074 -2.02212,1.75507 -5.61128,1.75507 -0.22366,0 -0.45932,-0.002 -0.70661,-0.006 -0.53552,-0.0152 -1.02764,-0.024 -1.48414,-0.024 -6.05684,0 -5.60493,1.53776 -10.58792,10.23726 -5.35587,9.35814 -17.63924,20.0762 -26.56932,29.0037 -8.92912,8.92874 -10.3536,4.21845 -12.85268,11.57524 -2.17054,6.38528 -2.77467,9.27778 -6.91293,9.27778"
style="fill:#9b232b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path569" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 39 KiB

View file

@ -1,834 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg5800"
version="1.1"
viewBox="0 0 7.9374999 7.9374999"
height="30"
width="30"
sodipodi:docname="icon-robot.svg"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
inkscape:export-filename="../94e4957b/kani-yellow-sign.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1391"
inkscape:window-height="791"
id="namedview108"
showgrid="false"
inkscape:zoom="13.325447"
inkscape:cx="13.958256"
inkscape:cy="23.826593"
inkscape:window-x="49"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer2"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showguides="true">
<sodipodi:guide
position="-2.2872575,-58.804357"
orientation="0,1"
id="guide932"
inkscape:locked="false"
inkscape:label="MiddleHorizontal"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="72.712742,16.195644"
orientation="-1,0"
id="guide934"
inkscape:locked="false"
inkscape:label="MiddleVertical"
inkscape:color="rgb(222,221,218)" />
<sodipodi:guide
position="2.4708111,-33.581074"
orientation="0,-1"
id="guide1652"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs5794">
<radialGradient
id="Gradient_4"
gradientUnits="userSpaceOnUse"
cx="420.20801"
cy="346.897"
r="21.1">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8111" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8113" />
</radialGradient>
<radialGradient
id="Gradient_5"
gradientUnits="userSpaceOnUse"
cx="385.79401"
cy="98.971001"
r="165.23399">
<stop
offset="0"
stop-color="#B1DEF4"
id="stop8116" />
<stop
offset="1"
stop-color="#10A7CE"
id="stop8118" />
</radialGradient>
<radialGradient
id="Gradient_6"
gradientUnits="userSpaceOnUse"
cx="154.649"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8121" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8123" />
</radialGradient>
<radialGradient
id="Gradient_7"
gradientUnits="userSpaceOnUse"
cx="120.117"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7B6D3"
id="stop8126" />
<stop
offset="1"
stop-color="#FF5D73"
id="stop8128" />
</radialGradient>
<radialGradient
id="Gradient_8"
gradientUnits="userSpaceOnUse"
cx="660.41699"
cy="435.46399"
r="21.1">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8131" />
<stop
offset="1"
stop-color="#F60012"
id="stop8133" />
</radialGradient>
<radialGradient
id="Gradient_9"
gradientUnits="userSpaceOnUse"
cx="625.88501"
cy="187.53799"
r="165.23399">
<stop
offset="0"
stop-color="#F7AEB4"
id="stop8136" />
<stop
offset="1"
stop-color="#F60012"
id="stop8138" />
</radialGradient>
<radialGradient
id="Gradient_10"
gradientUnits="userSpaceOnUse"
cx="283.38901"
cy="577.77899"
r="21.1">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8141" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8143" />
</radialGradient>
<radialGradient
id="Gradient_11"
gradientUnits="userSpaceOnUse"
cx="213.711"
cy="337.353"
r="165.23399">
<stop
offset="0"
stop-color="#FBDEAD"
id="stop8146" />
<stop
offset="1"
stop-color="#FF9800"
id="stop8148" />
</radialGradient>
<linearGradient
id="Gradient_12"
gradientUnits="userSpaceOnUse"
x1="395.94601"
y1="909.60699"
x2="398.86099"
y2="889.30902">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8151" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8153" />
</linearGradient>
<linearGradient
id="Gradient_13"
gradientUnits="userSpaceOnUse"
x1="364.41199"
y1="920.81097"
x2="381.88199"
y2="898.88202">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8156" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8158" />
</linearGradient>
<linearGradient
id="Gradient_14"
gradientUnits="userSpaceOnUse"
x1="400.103"
y1="884.01398"
x2="414.327"
y2="886.43201">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8161" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8163" />
</linearGradient>
<linearGradient
id="Gradient_15"
gradientUnits="userSpaceOnUse"
x1="401.52899"
y1="-883.02899"
x2="418.35501"
y2="-884.72101"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8166" />
<stop
offset="1"
stop-color="#90282C"
id="stop8168" />
</linearGradient>
<clipPath
id="Clip_1">
<path
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c -2.74,0.34 -5.51,-0.08 -8.21,-0.56 v 0 c 1.75,0.6 3.93,1.12 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8171" />
</clipPath>
<linearGradient
id="Gradient_16"
gradientUnits="userSpaceOnUse"
x1="406.53"
y1="880.51599"
x2="409.40302"
y2="880.80499">
<stop
offset="0"
stop-color="#AF2525"
id="stop8174" />
<stop
offset="1"
stop-color="#90282C"
id="stop8176" />
</linearGradient>
<linearGradient
id="Gradient_17"
gradientUnits="userSpaceOnUse"
x1="452.20001"
y1="905.45599"
x2="444.604"
y2="892.92102">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8179" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8181" />
</linearGradient>
<linearGradient
id="Gradient_18"
gradientUnits="userSpaceOnUse"
x1="462.254"
y1="893.729"
x2="456.45901"
y2="887.92401">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8184" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8186" />
</linearGradient>
<linearGradient
id="Gradient_19"
gradientUnits="userSpaceOnUse"
x1="446.13501"
y1="882.91602"
x2="429.77499"
y2="883.61603">
<stop
offset="0"
stop-color="#FF1F19"
id="stop8189" />
<stop
offset="1"
stop-color="#B7282C"
id="stop8191" />
</linearGradient>
<linearGradient
id="Gradient_20"
gradientUnits="userSpaceOnUse"
x1="443.24799"
y1="-882.92798"
x2="426.452"
y2="-884.64899"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#AF2525"
id="stop8194" />
<stop
offset="1"
stop-color="#90282C"
id="stop8196" />
</linearGradient>
<clipPath
id="Clip_2">
<path
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -3.99,0.52 -9.46,1.2 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8199" />
</clipPath>
<linearGradient
id="Gradient_21"
gradientUnits="userSpaceOnUse"
x1="438.23401"
y1="880.48499"
x2="435.36099"
y2="880.78003">
<stop
offset="0"
stop-color="#AF2525"
id="stop8202" />
<stop
offset="1"
stop-color="#90282C"
id="stop8204" />
</linearGradient>
<radialGradient
id="Gradient_22"
gradientUnits="userSpaceOnUse"
cx="421.086"
cy="879.30603"
r="5.8280001">
<stop
offset="0"
stop-color="#E61F19"
id="stop8207" />
<stop
offset="0.988"
stop-color="#9E282C"
id="stop8209" />
<stop
offset="1"
stop-color="#9E282C"
id="stop8211" />
</radialGradient>
<radialGradient
id="Gradient_23"
gradientUnits="userSpaceOnUse"
cx="146.832"
cy="-316.11899"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8214" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8216" />
</radialGradient>
<radialGradient
id="Gradient_24"
gradientUnits="userSpaceOnUse"
cx="55.174999"
cy="-176.464"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8219" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8221" />
</radialGradient>
<radialGradient
id="Gradient_25"
gradientUnits="userSpaceOnUse"
cx="565.53101"
cy="-165.11301"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8224" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8226" />
</radialGradient>
<radialGradient
id="Gradient_26"
gradientUnits="userSpaceOnUse"
cx="333.47"
cy="-63.138"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8229" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8231" />
</radialGradient>
<radialGradient
id="Gradient_27"
gradientUnits="userSpaceOnUse"
cx="536.58099"
cy="578.03198"
r="21.1">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8234" />
<stop
offset="1"
stop-color="#229614"
id="stop8236" />
</radialGradient>
<radialGradient
id="Gradient_28"
gradientUnits="userSpaceOnUse"
cx="553.57397"
cy="328.29001"
r="165.23399">
<stop
offset="0"
stop-color="#C0E7C7"
id="stop8239" />
<stop
offset="1"
stop-color="#229614"
id="stop8241" />
</radialGradient>
<radialGradient
id="Gradient_29"
gradientUnits="userSpaceOnUse"
cx="488.81201"
cy="-304.62601"
r="53.613998"
gradientTransform="scale(1,-1)">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop8244" />
<stop
offset="1"
stop-color="#FFFFFF"
stop-opacity="0.001"
id="stop8246" />
</radialGradient>
<symbol
id="balloons"
viewBox="0 0 825 962">
<g
style="fill:none;stroke:#eccb78;stroke-width:4.069;stroke-linecap:round;stroke-linejoin:round"
id="g8261">
<path
d="M 291.13,584.61 422.41,889.24 540.38,589.26"
id="path8249" />
<path
d="m 426.95,357.75 -4.54,531.49"
id="path8251" />
<path
d="M 158.17,411.35 421.71,889.24 667.1,402.98"
id="path8253" />
<path
d="m 415.21,958.51 c 8.81,-23.5 6.08,-71.63 6.08,-71.63 -3.66,47 -29.02,70.1 -29.02,70.1"
id="path8255" />
<path
d="m 456.29,956.29 c 0,0 -26.52,-21.77 -32.58,-68.52 0,0 -0.26,48.21 9.75,71.23"
id="path8257" />
<path
d="m 404.06,957.77 c 0,0 13.62,-20.66 18.45,-69.2"
id="path8259" />
</g>
<path
style="fill:url(#Gradient_4)"
d="m 423.73,337.05 -21.46,35.03 c -1.86,3.04 0.32,6.93 3.89,6.93 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.83 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.28 -3.95,-2.28 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8263" />
<path
style="fill:url(#Gradient_5)"
d="m 273.05,178.98 c 0,97.08 68.93,175.78 153.96,175.79 v 0 c 85.03,0 153.96,-78.7 153.97,-175.79 v 0 C 580.98,81.9 512.05,3.2 427.01,3.2 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8265" />
<path
style="fill:url(#Gradient_6)"
d="m 158.17,425.62 -21.46,35.02 c -1.86,3.04 0.32,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 L 166,425.71 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8267" />
<path
style="opacity:0.95;fill:url(#Gradient_7)"
d="m 7.37,267.55 c 0,97.08 68.93,175.78 153.97,175.78 v 0 c 85.03,0 153.96,-78.7 153.96,-175.78 v 0 C 315.3,170.47 246.37,91.77 161.34,91.77 v 0 C 76.31,91.77 7.38,170.47 7.37,267.55 Z"
id="path8269" />
<path
style="fill:url(#Gradient_8)"
d="m 663.94,425.62 -21.46,35.02 c -1.86,3.04 0.33,6.94 3.89,6.94 v 0 h 41.71 c 3.51,0 5.7,-3.8 3.94,-6.84 v 0 l -20.25,-35.03 c -0.88,-1.51 -2.41,-2.27 -3.95,-2.27 v 0 c -1.5,0 -2.99,0.72 -3.88,2.18 z"
id="path8271" />
<path
style="opacity:0.95;fill:url(#Gradient_9)"
d="m 513.14,267.55 c 0,97.08 68.93,175.78 153.96,175.78 v 0 c 85.03,0 153.96,-78.7 153.97,-175.78 v 0 C 821.07,170.47 752.14,91.77 667.1,91.77 v 0 c -85.03,0 -153.96,78.7 -153.96,175.78 z"
id="path8273" />
<path
style="fill:url(#Gradient_10)"
d="m 285.46,567.53 -16.21,37.74 c -1.41,3.27 1.31,6.82 4.83,6.3 v 0 l 41.28,-5.97 c 3.47,-0.5 5.1,-4.58 2.93,-7.33 v 0 l -25.06,-31.76 c -0.93,-1.17 -2.25,-1.74 -3.57,-1.74 v 0 c -1.72,0 -3.42,0.96 -4.2,2.76 z"
id="path8275" />
<path
style="opacity:0.95;fill:url(#Gradient_11)"
d="m 240.79,236.67 c -84.15,12.18 -141.11,99.94 -127.2,196.02 v 0 c 13.9,96.08 93.39,164.1 177.54,151.92 v 0 c 84.15,-12.18 141.1,-99.94 127.21,-196.02 v 0 C 405.52,300 336.93,235.26 260.34,235.26 v 0 c -6.47,0 -12.99,0.46 -19.55,1.41 z"
id="path8277" />
<path
style="fill:url(#Gradient_27)"
d="M 542.05,569.11 513.87,599 c -2.44,2.59 -1.1,6.85 2.38,7.59 v 0 l 40.83,8.54 c 3.43,0.72 6.36,-2.55 5.25,-5.88 v 0 l -12.64,-38.43 c -0.66,-2.01 -2.48,-3.13 -4.35,-3.14 v 0 c -1.18,0 -2.38,0.46 -3.29,1.43 z"
id="path8279" />
<path
style="opacity:0.95;fill:url(#Gradient_28)"
d="m 426.83,383.51 c -19.89,95.02 31.46,186.18 114.68,203.6 v 0 c 83.23,17.42 166.82,-45.49 186.71,-140.52 v 0 C 748.11,351.57 696.76,260.41 613.54,242.99 v 0 c -9.34,-1.96 -18.68,-2.9 -27.94,-2.89 v 0 c -73.25,0 -141.11,59.05 -158.77,143.41 z"
id="path8281" />
<path
style="opacity:0.7;fill:url(#Gradient_23)"
d="m 126.74,409.71 c 0,0 29.93,-94.86 60.85,-110.42 19.92,-10.03 -4.23,-30.39 -27.01,-14.05 -6.7,5.14 -12.35,11.69 -17.06,18.66 -14.36,21.28 -19.56,46.01 -20.09,71.27 v 0.02 c -0.07,11.6 1.32,23.12 3.31,34.52 z"
id="path8283" />
<path
style="opacity:0.7;fill:url(#Gradient_24)"
d="m 30.74,269.01 c 0,0 34.33,-93.36 65.93,-107.45 20.37,-9.09 -2.81,-30.55 -26.32,-15.3 0,0 -51.89,32.72 -39.61,122.75 z"
id="path8285" />
<path
style="opacity:0.7;fill:url(#Gradient_25)"
d="m 537.49,256.64 c 0,0 37.96,-91.94 70.09,-104.8 20.71,-8.29 -1.61,-30.64 -25.71,-16.31 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8287" />
<path
style="opacity:0.7;fill:url(#Gradient_26)"
d="m 305.43,154.66 c 0,0 37.96,-91.94 70.09,-104.79 20.71,-8.28 -1.61,-30.64 -25.71,-16.32 0,0 -53.13,30.67 -44.38,121.11 z"
id="path8289" />
<path
style="opacity:0.7;fill:url(#Gradient_29)"
d="m 454.08,393.82 c 0,0 44.65,-88.88 77.64,-99.32 21.27,-6.73 0.66,-30.67 -24.42,-18.17 0,0 -55.25,26.66 -53.22,117.49 z"
id="path8291" />
<path
style="fill:url(#Gradient_12)"
d="m 375.83,907.75 9.08,1.65 c -1.21,2.8 0.79,11.89 0.79,11.89 v 0 c 8.26,-15.42 34.5,-34.41 33.81,-33.91 v 0 l 0.71,-5.79 c -24.03,1.57 -44.39,26.16 -44.39,26.16 z"
id="path8293" />
<path
style="fill:url(#Gradient_13)"
d="m 380.2,872.6 c 13.6,7.74 40.82,5.99 40.82,5.99 v 0 c -22.8,-5.81 -32.93,-7.21 -37.4,-7.21 v 0 c -4.17,0 -3.42,1.22 -3.42,1.22 z"
id="path8295" />
<path
style="fill:url(#Gradient_14)"
d="m 380.45,890.39 c 7.74,5.62 42.01,-3.08 42.01,-3.09 v 0 l -0.09,-10.01 c -42.04,-1.73 -42.31,-5.05 -42.31,-5.05 v 0 z"
id="path8297" />
<g
clip-path="url(#Clip_1)"
opacity="0.24"
id="g8301">
<path
style="fill:url(#Gradient_15)"
d="m 408.16,886.73 c 2.63,0.16 5.1,-0.27 7.57,-1.06 3.52,-1.3 2.99,-1.72 1.65,-1.82 v 0 l 0.21,-2.29 c 0,0 -3,0.5 -8.21,-0.56 2.01,0.59 4.41,1.58 4.79,2.94 -0.02,0 -0.03,0 -0.03,0 0,0 -3.11,3.22 -12.86,1.6 2.22,0.78 4.56,0.96 6.88,1.19 z"
id="path8299" />
</g>
<path
style="fill:url(#Gradient_16)"
d="m 409.38,881 c -1.53,-0.45 -2.83,-0.68 -2.83,-0.68 v 0 c 1.01,0.28 1.95,0.5 2.83,0.68 z"
id="path8303" />
<path
style="fill:url(#Gradient_17)"
d="m 425.53,887.37 c 0,0 25.53,18.33 33.88,33.85 v 0 c 0,0 1.98,-9.1 0.76,-11.89 v 0 l 9.08,-1.67 c 0,0 -20.4,-24.55 -44.44,-26.08 v 0 z"
id="path8305" />
<path
style="fill:url(#Gradient_18)"
d="m 423.74,878.59 c 0,0 40.72,9.96 40.43,-5.69 v 0 c -0.01,-0.45 0.33,-1.72 -3.83,-1.73 v 0 c -4.58,0 -14.62,1.54 -36.6,7.42 z"
id="path8307" />
<path
style="fill:url(#Gradient_19)"
d="m 422.39,877.29 -0.07,10.01 c 0,0 34.28,8.63 42.01,3 v 0 l -0.11,-17.86 c 0,0 -0.29,1.43 -41.83,4.85 z"
id="path8309" />
<g
clip-path="url(#Clip_2)"
opacity="0.24"
id="g8313">
<path
style="fill:url(#Gradient_20)"
d="m 436.63,886.65 c 2.32,-0.16 4.66,-0.45 6.88,-1.19 -9.75,1.64 -12.88,-1.55 -12.88,-1.55 0,0 -0.01,0.01 -0.03,0 0.38,-1.36 2.78,-2.34 4.78,-2.94 -5.2,1.07 -8.18,0.51 -8.18,0.51 l 0.23,2.22 0.01,0.01 c -1.35,0.13 -1.9,0.59 1.62,1.88 2.44,0.98 4.99,0.96 7.57,1.06 z"
id="path8311" />
</g>
<path
style="fill:url(#Gradient_21)"
d="m 435.38,880.98 c 0.88,-0.18 1.83,-0.4 2.83,-0.69 v 0 c 0,0 -1.3,0.23 -2.83,0.69 z"
id="path8315" />
<path
style="fill:url(#Gradient_22)"
d="m 417.09,877.31 c 0,0 -1.77,8.19 0.74,11.61 v 0 l 9.25,-0.01 c 0,0 2.64,-7.59 0.88,-11.59 v 0 c 0,0 -4.62,-1 -7.84,-1 v 0 c -1.6,0 -2.86,0.25 -3.03,0.99 z"
id="path8317" />
</symbol>
</defs>
<metadata
id="metadata5797">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Kani"
style="display:inline"
transform="translate(-42.515769,-15.696706)">
<path
id="path5404"
d="m 43.650626,20.134541 c -0.251481,0.607741 0.432618,1.167966 0.435552,1.177018 -0.124954,-0.366537 -0.257016,-0.662425 -0.01414,-0.918611"
style="fill:#803300;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-rear-claw-left"
inkscape:export-filename="../1d28c49c/party-kani.png"
inkscape:export-xdpi="160.64"
inkscape:export-ydpi="160.64" />
<path
style="fill:#803300;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 49.326742,20.094569 c 0.251483,0.60774 -0.432616,1.167965 -0.435551,1.177016 0.124954,-0.366536 0.257016,-0.662424 0.01414,-0.91861"
id="path5408"
inkscape:label="kani-rear-claw-right" />
<path
id="path4529"
d="m 46.470512,16.917403 c -0.09786,-0.0012 -0.191501,0.17594 -0.365723,0.52246 -0.527336,-0.579974 -0.55431,-0.586996 -0.736002,0.198163 -0.0085,0.0035 -0.01698,0.0072 -0.02547,0.01082 -0.664216,-0.398156 -0.698535,-0.401271 -0.648128,0.377006 -0.01614,0.01196 -0.03225,0.02409 -0.0483,0.0364 -0.680644,-0.269624 -0.710073,-0.252634 -0.537839,0.494404 -0.008,0.0088 -0.01615,0.01741 -0.02419,0.02624 -0.693719,-0.187426 -0.713129,-0.159264 -0.454408,0.572695 -0.0111,0.01592 -0.02212,0.03212 -0.03316,0.04827 -0.78191,-0.08358 -0.797973,-0.06831 -0.400463,0.660184 -0.0019,0.0036 -0.004,0.0071 -0.0059,0.0107 -0.320341,0.153135 -0.426391,0.339784 -0.578183,0.590318 -0.01178,0.496878 0.520047,0.925101 1.28474,1.411761 -0.262719,-0.486913 -0.673566,-0.733744 -0.752387,-1.337683 0.115435,-0.217263 0.311739,-0.292314 0.480299,-0.390067 1.84107,1.074445 3.78503,1.0869 5.724449,-0.04115 0.169012,0.09834 0.366482,0.173122 0.482384,0.391265 -0.07882,0.603939 -0.489707,0.850731 -0.752426,1.337643 0.764692,-0.486658 1.296561,-0.914844 1.284779,-1.411722 -0.147581,-0.243582 -0.252513,-0.426693 -0.552601,-0.57741 0.374863,-0.689338 0.339179,-0.695826 -0.447607,-0.611494 -0.0066,-0.0087 -0.0133,-0.01733 -0.01998,-0.02589 0.271799,-0.767354 0.254748,-0.787407 -0.47798,-0.588618 0.185447,-0.800583 0.160666,-0.803848 -0.58174,-0.508779 0.05592,-0.84389 0.03589,-0.842138 -0.664667,-0.421948 -0.186812,-0.809024 -0.208854,-0.806616 -0.743345,-0.218722 -0.201042,-0.368936 -0.306818,-0.553622 -0.406182,-0.554804 z"
style="display:inline;fill:#5791e8;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
inkscape:label="kani-body" />
<g
id="g5758"
transform="matrix(0.07477963,0,0,0.07477963,38.800638,11.326586)"
style="display:inline"
inkscape:label="kani-face">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399"
inkscape:label="kani-smile" />
<g
id="g351"
inkscape:label="kani-eye-left">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417" />
</g>
<g
id="g355"
inkscape:label="kani-eye-right">
<path
id="path5433"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389"
d="m 44.607564,19.879672 c -0.03817,2.95e-4 -0.06361,0.0041 -0.05943,0.0066 0,0 -0.683042,0.892578 0.02319,1.395299 a 0.72888529,0.61895752 37.793943 0 0 0.107467,0.704702 0.72888529,0.61895752 37.793943 0 0 0.790026,0.296666 l -0.266524,-0.643531 0.687738,0.05499 a 0.72888529,0.61895752 37.793943 0 0 -0.525436,-0.666329 0.72888529,0.61895752 37.793943 0 0 -0.524043,6.81e-4 c -0.139263,-0.102008 -0.346133,-0.368365 -0.0031,-0.932895 0.11352,-0.186796 -0.115423,-0.217097 -0.22993,-0.21621 z"
style="display:inline;fill:#5791e8;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-down" />
<path
id="path8665"
d="m 83.960168,109.11313 c -0.27485,0.43011 -0.4985,0.69147 -0.49798,0.62578 0,0 -14.965898,1.38701 -15.636208,-10.186101 a 9.7471106,8.2770874 84.335985 0 1 -7.216103,-6.22894 9.7471106,8.2770874 84.335985 0 1 2.259156,-11.0566 l 5.392134,7.59511 4.268531,-8.17939 a 9.7471106,8.2770874 84.335985 0 1 3.80891,10.68931 9.7471106,8.2770874 84.335985 0 1 -3.73468,5.92977 c 0.16465,2.302521 1.70979,6.539611 10.54286,6.669381 2.92274,0.043 1.63761,2.85111 0.81319,4.14154 z"
style="display:none;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="kani-claw-left-up" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="kani-claw-right-overlay"
style="display:inline"
transform="translate(-42.515769,-15.696706)">
<path
d="m 49.863102,19.274345 c 0.262426,-0.232841 0.313802,-0.524307 0.237527,-0.851234 l -0.552725,0.286643 z"
style="display:inline;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path523"
inkscape:label="under-kani-claw" />
<path
id="path5732-74"
d="m 49.20747,18.172558 c -0.16312,0.106491 -0.287526,0.274108 -0.343892,0.463358 -0.07413,0.250562 -0.01916,0.504164 0.143494,0.661943 -0.267667,0.730566 -0.751843,0.49021 -0.762075,0.4806 -0.08228,-0.06403 -0.387206,0.271485 0.03983,0.391045 0.66705,0.186759 0.955364,-0.409403 1.058596,-0.723148 0.183308,0.01403 0.375396,-0.05843 0.527375,-0.198949 0.22948,-0.212782 0.324876,-0.540067 0.23615,-0.810187 l -0.621558,0.314348 4.31e-4,-0.09663 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;opacity:1;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 49.20747,18.172558 c -0.16312,0.106491 -0.287526,0.274108 -0.343892,0.463358 -0.07413,0.250562 -0.01916,0.504164 0.143494,0.661943 -0.267667,0.730565 -0.751843,0.49021 -0.762075,0.480599 -0.08228,-0.06403 -0.387206,0.271486 0.03984,0.391045 0.667048,0.186759 0.955363,-0.409403 1.058595,-0.723147 0.183308,0.01403 0.375397,-0.05843 0.527375,-0.198949 0.229479,-0.212782 0.324876,-0.540067 0.236151,-0.810187 l -0.621559,0.314348 4.31e-4,-0.09663 z"
id="path5728" />
<path
id="path5732"
d="m 49.20747,18.172558 c -0.16312,0.106491 -0.287526,0.274108 -0.343892,0.463358 -0.07413,0.250562 -0.01916,0.504164 0.143494,0.661943 -0.267667,0.730565 -0.751843,0.49021 -0.762075,0.480599 -0.08228,-0.06403 -0.387206,0.271486 0.03984,0.391045 0.667048,0.186759 0.955363,-0.409403 1.058595,-0.723147 0.183308,0.01403 0.375397,-0.05843 0.527375,-0.198949 0.229479,-0.212782 0.324876,-0.540067 0.236151,-0.810187 l -0.621559,0.314348 4.31e-4,-0.09663 z"
style="display:inline;opacity:1;fill:#5791e8;fill-opacity:1;stroke:#000000;stroke-width:0.0747797;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g7998"
inkscape:label="party-hat"
transform="matrix(1.0107117,0,0,0.94642879,-36.985227,-14.267983)"
style="display:none">
<path
style="display:inline;fill:#1da1f2;fill-opacity:1;stroke:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path7024"
inkscape:label="party-hat-background"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 90.866459,64.772976 -1.399384,3.650367 c 0,0 6.297977,-0.0079 9.604616,-2.229612 3.306639,-2.221751 6.641769,-4.763148 6.637539,-5.892882 -0.004,-1.129737 -1.2244,-3.398221 -1.2244,-3.398221 0,0 -1.76379,2.983434 -5.089689,4.759036 -3.325897,1.775606 -6.256176,3.317156 -8.528682,3.111312 z"
id="path7711"
inkscape:label="yellow-middle" />
<path
style="fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 95.283815,50.496253 c 0,0 2.017636,-0.742584 2.746046,-1.209859 0.68409,-0.438843 2.540819,-1.493505 2.505569,-1.916931 -0.0353,-0.423423 0.90713,1.638551 0.90713,1.638551 0,0 -0.43327,0.543192 -1.447199,1.610006 -1.01394,1.066813 -1.72817,1.358505 -2.26456,1.622681 -0.53638,0.264174 -2.0866,0.861497 -2.636383,0.793816 -0.549783,-0.06768 0.189397,-2.538264 0.189397,-2.538264 z"
id="path7713"
sodipodi:nodetypes="csccsssc"
inkscape:label="yellow-top" />
<path
style="display:inline;fill:#ffd60f;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 109.92717,71.488689 c 0,0 -2.20481,3.007619 -3.4367,4.221512 -1.06044,1.044951 -3.3987,2.32693 -5.45873,3.108264 -2.060039,0.78133 -7.390321,2.031263 -10.319319,2.035505 -2.53475,0.0037 -3.102127,-0.17882 -4.727213,-0.614261 -1.010264,-0.270699 -0.801047,2.045289 -0.801047,2.045289 l 4.858605,2.209687 10.217314,-0.32013 c 0,0 3.08806,-0.85865 3.26326,-0.9226 0.17519,-0.06395 5.61136,-3.775927 5.91906,-4.176562 0.30769,-0.400637 1.97931,-2.854544 1.85876,-3.106917 -0.12055,-0.25237 -1.37399,-4.479787 -1.37399,-4.479787 z"
id="path7715"
sodipodi:nodetypes="csssscccsssc"
inkscape:label="yellow-bottom" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 88.208385,72.747325 c 0,0 6.251797,0.04012 9.907566,-1.876691 3.875559,-2.032049 5.147419,-2.116642 8.954089,-7.814465 -0.0353,-0.423423 1.59724,3.704345 1.59724,3.704345 0,0 -2.99139,5.115034 -6.19574,6.653752 -4.079519,1.958969 -6.324966,2.675602 -8.934752,3.106262 -1.713747,0.282798 -4.8213,0.352969 -6.27215,-0.01279 -0.537127,-0.135411 0.943747,-3.760409 0.943747,-3.760409 z"
id="path7982"
sodipodi:nodetypes="csccssscc"
inkscape:label="white-middle" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ff00ff;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 92.993027,57.289539 c 0,0 3.629855,-0.719631 5.482954,-2.090937 0.65332,-0.483462 3.662839,-3.285322 4.163269,-4.079419 -0.0353,-0.423423 0.98376,2.839616 0.98376,2.839616 0,0 -1.55545,1.919882 -2.68768,2.888013 -1.118619,0.956483 -3.276219,2.193755 -4.815637,2.748644 -1.634013,0.588985 -3.388075,0.798009 -3.937858,0.730328 -0.549783,-0.06768 0.811192,-3.036245 0.811192,-3.036245 z"
id="path7979"
sodipodi:nodetypes="csccsssc"
inkscape:label="white-top" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#ff00ff;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 84.970159,82.659204 98.594291,41.592222 113.931,82.386454 c 0,0 -8.7088,2.847069 -15.025249,2.754354 -6.316442,-0.09272 -13.935592,-2.481604 -13.935592,-2.481604 z"
id="path508"
inkscape:label="party-hat-outline"
sodipodi:nodetypes="cccsc" />
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:#8f57a8;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 95.307706,40.797374 8.863844,3.5236 c -4.511069,-1.476791 -5.455019,1.171445 -8.289058,2.243988 0,0 5.051628,-7.95802 4.765018,-7.734177 -1.618949,1.264417 -2.259979,9.16058 -2.259979,9.16058 l -0.26088,-9.396127 3.698779,7.921822 -7.078677,-2.809491 8.818747,-2.23602 z"
id="path3171-3"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="display:inline;fill:#0056ed;fill-opacity:1;stroke:#ae00ff;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 96.516104,39.086314 5.923406,7.476417 c -3.172509,-3.530708 -5.311942,-1.706696 -8.303144,-2.191314 0,0 8.348654,-4.375959 7.988584,-4.324981 -2.03392,0.28796 -6.529397,6.811072 -6.529397,6.811072 l 4.462297,-8.273038 -0.747319,8.710783 -4.732664,-5.966807 8.758203,2.462516 z"
id="path3171"
sodipodi:nodetypes="cccscccccc"
inkscape:label="pompom" />
<path
style="fill:#744eaa;fill-opacity:1;stroke:#8f57a8;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 85.749242,80.805507 -1.299196,-0.793626 -0.72012,1.516668 -1.19957,0.02641 1.07959,1.592728 -0.479079,1.665647 1.727851,-0.04555 0.945116,1.102037 1.595689,-0.420439 1.013886,1.119783 1.616715,-0.611034 0.955484,0.869926 1.417278,-0.82042 1.399024,0.893951 1.62274,-0.936247 1.191522,0.880515 1.692439,-0.626057 1.62431,0.594614 1.273839,-0.730245 1.14228,0.675465 0.83977,-0.542118 1.7433,0.433922 1.37853,-0.883036 1.46376,0.789948 0.98369,-0.723702 1.56885,0.265388 0.83917,-1.064009 1.5817,0.538036 1.04635,-1.072595 1.45989,-0.386437 -0.0243,-1.46382 0.73064,-1.311775 -1.08662,-0.620199 -0.54503,-1.441178 -1.29888,0.821314 -1.72095,-0.803735 -0.89109,1.605976 -1.44802,-0.173747 -0.40144,0.954405 -1.25934,-0.74345 -1.14276,1.025692 -2.05145,-0.719347 c 0,0 -1.1538,1.054828 -1.21655,1.018337 -0.0627,-0.03649 -0.72908,-0.632037 -0.72908,-0.632037 l -2.781839,0.990357 -1.01136,-0.982973 -0.76464,0.897745 -1.265471,-0.508519 -1.392952,0.640072 -1.261957,-0.842494 -1.207529,1.021591 -2.137111,-1.168069 -0.83207,0.565255 -0.716719,-0.940683 -1.262123,0.222665 -0.854411,-1.073467 z"
id="path3419"
inkscape:label="purple-frizzy-bit" />
</g>
<g
id="g571"
transform="matrix(-0.37792196,-0.19227008,-0.19227008,0.37792196,158.66815,75.524887)"
inkscape:export-filename="../fe6a9b8b/kani-stabby.png"
inkscape:export-xdpi="370.63721"
inkscape:export-ydpi="370.63721"
style="display:none"
inkscape:label="knife-left">
<path
d="M 327.32922,104.16963 313.23927,90.079688 246.31471,157.00414 c 0,0 -6.09712,4.9021 -4.45506,8.97393 1.6429,4.07209 4.34482,5.77218 11.55969,6.84567 6.14218,0.99798 6.56385,-1.72199 9.06459,-9.07879 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 5.35834,-9.35496 4.43089,-10.42811 12.07206,-10.21327 4.21357,0.0716 4.88879,-0.46249 6.31789,-1.74872 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439"
style="fill:#c52a34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path561" />
<path
d="M 333.98084,110.82126 313.52961,90.369671 c 0,0 66.27001,-67.178418 73.76936,-74.221626 7.49688,-7.0446203 13.081,-13.0810013 23.41845,-16.14946258 -0.68086,6.58883098 -6.21418,20.98428158 -13.48493,31.66357158 -7.2711,10.681759 -36.32483,51.549656 -63.25165,79.159106"
style="fill:#b9b8b7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path563" />
<path
d="m 404.21431,2.595027 c -6.52039,3.3330447 -11.1573,8.142464 -16.91534,13.553018 -7.49935,7.043208 -73.76936,74.221626 -73.76936,74.221626 l 15.67956,15.678859 c 26.92717,-27.608745 55.97949,-68.476642 63.25094,-79.158049 5.17807,-7.605536 9.46362,-17.0825591 11.7542,-24.295454"
style="fill:#999a99;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path565" />
<path
d="m 255.68936,173.17156 c -0.61955,0 -1.31989,-0.0656 -2.11762,-0.19541 -4.67209,-0.69397 -7.69193,-1.90252 -9.11972,-3.20187 l 0.0139,-0.014 c 1.82658,1.5197 4.5489,2.40839 8.9534,3.06351 0.81082,0.13188 1.52216,0.19899 2.15166,0.19899 4.13826,0 4.74239,-2.8925 6.91293,-9.27778 2.49908,-7.35679 3.92356,-2.6465 12.85268,-11.57524 8.93008,-8.9275 21.21345,-19.64556 26.56932,-29.0037 4.98299,-8.6995 4.53108,-10.23726 10.58792,-10.23726 0.4565,0 0.94862,0.009 1.48414,0.024 0.24729,0.004 0.48295,0.006 0.70661,0.006 3.58916,0 4.26614,-0.54433 5.61128,-1.75507 1.42875,-1.28481 7.03334,-7.03439 7.03334,-7.03439 v 0 c 0,0 -5.6,5.75451 -7.02875,7.03933 -1.34549,1.21109 -2.01824,1.7593 -5.61058,1.7593 -0.2226,0 -0.45649,-0.002 -0.70238,-0.006 -0.53269,-0.0152 -1.0227,-0.0236 -1.47708,-0.0236 -6.06319,0 -5.60035,1.54375 -10.5851,10.24714 -5.35693,9.35778 -17.62054,20.09429 -26.55122,29.02274 -8.92845,8.92803 -10.31434,4.2562 -12.81342,11.61408 -2.17593,6.40046 -2.71998,9.34932 -6.87133,9.34932 m 71.63986,-69.00193 -8.63636,-8.63635 v 0 l 8.63636,8.63635"
style="fill:#cdccca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path567" />
<path
d="m 255.571,173.02273 c -0.6295,0 -1.34084,-0.0671 -2.15166,-0.19899 -4.4045,-0.65512 -7.12682,-1.54381 -8.9534,-3.06351 l 74.22692,-74.22695 8.63636,8.63635 c 0,0 -5.60459,5.74958 -7.03334,7.03439 -1.34514,1.21074 -2.02212,1.75507 -5.61128,1.75507 -0.22366,0 -0.45932,-0.002 -0.70661,-0.006 -0.53552,-0.0152 -1.02764,-0.024 -1.48414,-0.024 -6.05684,0 -5.60493,1.53776 -10.58792,10.23726 -5.35587,9.35814 -17.63924,20.0762 -26.56932,29.0037 -8.92912,8.92874 -10.3536,4.21845 -12.85268,11.57524 -2.17054,6.38528 -2.77467,9.27778 -6.91293,9.27778"
style="fill:#9b232b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
id="path569" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 39 KiB

View file

@ -1,168 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="256"
height="256"
viewBox="0 0 135.46667 135.46666"
version="1.1"
id="svg1084"
sodipodi:docname="kani-waving.svg"
inkscape:version="1.2 (dc2aeda, 2022-05-15)"
inkscape:export-filename="logo-180.png"
inkscape:export-xdpi="33.75"
inkscape:export-ydpi="33.75"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title332">Kanidm Square Logo</title>
<sodipodi:namedview
id="namedview39"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1"
inkscape:cx="178"
inkscape:cy="304.5"
inkscape:window-width="1389"
inkscape:window-height="847"
inkscape:window-x="51"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg1084">
<sodipodi:guide
position="-52.916665,67.733332"
orientation="0,1"
id="guide145"
inkscape:locked="false"
inkscape:label="vertical_middle"
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="121.97292,175.15416"
orientation="-1,0"
id="guide777"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="13.49375,173.03749"
orientation="-1,0"
id="guide779"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="22.754166,121.97292"
orientation="0,1"
id="guide781"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="4.4979168,13.758333"
orientation="0,1"
id="guide783"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1081" />
<metadata
id="metadata330">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Kanidm Square Logo</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
transform="matrix(1.0952772,0,0,1.0952772,-42.847275,-49.68044)"
inkscape:label="kani"
style="display:inline">
<path
id="path5404"
d="m 62.537712,110.23377 c -3.36297,8.12708 5.78522,15.61876 5.82446,15.73981 -1.67096,-4.90156 -3.43697,-8.85836 -0.189,-12.28424"
style="display:inline;fill:#803300;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;fill:#803300;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 138.4423,109.69923 c 3.36297,8.12708 -5.78522,15.61876 -5.82446,15.7398 1.67096,-4.90155 3.43697,-8.85835 0.189,-12.28423"
id="path5408" />
<path
id="path4529"
d="m 100.24698,67.21217 c -1.308628,-0.01556 -2.560878,2.352773 -4.890668,6.986654 -7.05187,-7.755768 -7.41258,-7.849666 -9.84229,2.649966 -0.11364,0.04758 -0.22709,0.0959 -0.34055,0.144695 -8.88231,-5.324406 -9.34124,-5.366051 -8.66717,5.041551 -0.21569,0.159933 -0.43121,0.322088 -0.64596,0.486794 -9.102,-3.605578 -9.49555,-3.378386 -7.19232,6.611477 -0.10784,0.11671 -0.21599,0.23284 -0.3235,0.35089 -9.27685,-2.506375 -9.53641,-2.129764 -6.07663,7.65845 -0.14847,0.21295 -0.29578,0.42957 -0.44338,0.64544 -10.45618,-1.11775 -10.67098,-0.91353 -5.35523,8.828413 -0.026,0.0482 -0.0526,0.0943 -0.0786,0.14263 -4.28381,2.04782 -5.70197,4.54381 -7.73183,7.89409 -0.15756,6.64457 6.9544,12.37105 17.18035,18.87895 -3.51325,-6.5113 -9.00735,-9.81207 -10.0614,-17.88832 1.54367,-2.90538 4.16877,-3.90901 6.42286,-5.21622 24.61995,14.36815 50.615808,14.53471 76.550928,-0.55035 2.26015,1.31503 4.90084,2.31509 6.45076,5.23224 -1.05405,8.07624 -6.54866,11.3765 -10.06191,17.8878 10.22594,-6.5079 17.33842,-12.23387 17.18086,-18.87844 -1.97354,-3.25733 -3.37677,-5.706 -7.38973,-7.72149 5.01291,-9.218263 4.53571,-9.305023 -5.98567,-8.177293 -0.0891,-0.1152 -0.17795,-0.23186 -0.26717,-0.34623 3.63467,-10.261532 3.40665,-10.529692 -6.39186,-7.87136 2.47993,-10.705895 2.14854,-10.749554 -7.77938,-6.803718 0.74781,-11.285018 0.47993,-11.261581 -8.88835,-5.642548 -2.49815,-10.818768 -2.79293,-10.786576 -9.94048,-2.924887 -2.68846,-4.933638 -4.10296,-7.403383 -5.43173,-7.419184 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5389"
d="m 75.334492,106.8255 c -0.51041,0.004 -0.85068,0.0544 -0.79478,0.0889 0,0 -9.13406,11.93611 0.31006,18.65881 a 9.7471106,8.2770874 37.793943 0 0 1.43712,9.42372 9.7471106,8.2770874 37.793943 0 0 10.56473,3.9672 l -3.56412,-8.60569 9.19685,0.73536 a 9.7471106,8.2770874 37.793943 0 0 -7.02645,-8.91057 9.7471106,8.2770874 37.793943 0 0 -7.00784,0.009 c -1.86231,-1.36401 -4.6287,-4.92596 -0.0408,-12.47521 1.51807,-2.49795 -1.54349,-2.90314 -3.07475,-2.89129 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="path5389" />
<g
id="g5758"
transform="translate(-2.3193819,-7.5517449)"
style="display:inline"
inkscape:label="kani">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399" />
<g
id="g351">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417" />
</g>
<g
id="g355">
<path
id="path5433"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</g>
<path
id="path5732"
d="m 111.51646,83.878113 c -0.0644,2.604234 0.83829,5.245703 2.49515,7.301842 2.19739,2.716867 5.40747,4.034785 8.37885,3.439994 6.01625,8.488971 -0.30315,11.998681 -0.4866,12.038481 -1.32922,0.42046 0.0534,6.32365 4.60901,2.5271 7.11608,-5.93031 2.73673,-13.62732 0.0641,-17.143859 1.54476,-1.912559 2.20351,-4.57778 1.80861,-7.317326 -0.6031,-4.141273 -3.48425,-7.674191 -7.13223,-8.745669 l -1.25194,9.229861 -1.06142,-0.736985 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="arm-up"
transform="matrix(1.0952772,0,0,1.0952772,-42.847275,-49.680439)">
<set id="show_frame0" attributeName="visibility" attributeType="CSS" to="visible"
begin="0ms; hide_frame1.end" dur="500ms" fill="freeze"/>
<set id="hide_frame0" attributeName="visibility" attributeType="CSS" to="hidden"
begin="show_frame0.end" dur="1ms" fill="freeze"/>
</path>
<path
id="path294"
d="m 136.84733,83.996879 c -2.18137,1.42401 -3.84498,3.66555 -4.59874,6.19631 -0.99138,3.35068 -0.25622,6.742 1.91889,8.85192 -3.57942,9.769591 -10.05414,6.555411 -10.19096,6.426891 -1.10017,-0.85629 -5.17794,3.63047 0.53268,5.22929 8.92019,2.49747 12.77572,-5.47478 14.1562,-9.67038 2.45132,0.18756 5.02004,-0.78148 7.05239,-2.660471 3.06875,-2.84546 4.34444,-7.22212 3.15795,-10.83433 l -8.31186,4.20365 0.005,-1.29218 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="arm-down"
transform="matrix(1.0952772,0,0,1.0952772,-42.847275,-49.680439)" visibility="hidden">
<set id="show_frame1" attributeName="visibility" attributeType="CSS" to="visible"
begin="hide_frame0.end" dur="500ms" fill="freeze"/>
<set id="hide_frame1" attributeName="visibility" attributeType="CSS" to="hidden"
begin="show_frame1.end" dur="1ms" fill="freeze"/>
</path>
</svg>

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View file

@ -1,252 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46667 135.46666"
version="1.1"
id="svg1084"
sodipodi:docname="logo-square.svg"
inkscape:version="1.2 (dc2aeda, 2022-05-15)"
inkscape:export-filename="logo-180.png"
inkscape:export-xdpi="33.75"
inkscape:export-ydpi="33.75"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title332">Kanidm Square Logo</title>
<sodipodi:namedview
id="namedview39"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1"
inkscape:cx="234.5"
inkscape:cy="304.5"
inkscape:window-width="1389"
inkscape:window-height="847"
inkscape:window-x="51"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg1084">
<sodipodi:guide
position="-52.916665,67.733332"
orientation="0,1"
id="guide145"
inkscape:locked="false"
inkscape:label="vertical_middle"
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="121.97292,175.15416"
orientation="-1,0"
id="guide777"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="13.49375,173.03749"
orientation="-1,0"
id="guide779"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="22.754166,121.97292"
orientation="0,1"
id="guide781"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="4.4979168,13.758333"
orientation="0,1"
id="guide783"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1081" />
<metadata
id="metadata330">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Kanidm Square Logo</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<rect
style="fill:#ffffff;stroke-width:0.243721"
id="rect443"
width="135.46666"
height="135.46666"
x="0"
y="0"
inkscape:label="background"
sodipodi:insensitive="true" />
<g
id="layer1"
transform="matrix(0.91407203,0,0,0.91407203,-34.121105,-24.362694)"
inkscape:label="kani">
<path
id="path5404"
d="m 62.537712,110.23377 c -3.36297,8.12708 5.78522,15.61876 5.82446,15.73981 -1.67096,-4.90156 -3.43697,-8.85836 -0.189,-12.28424"
style="display:inline;fill:#803300;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;fill:#803300;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 138.4423,109.69923 c 3.36297,8.12708 -5.78522,15.61876 -5.82446,15.7398 1.67096,-4.90155 3.43697,-8.85835 0.189,-12.28423"
id="path5408" />
<path
id="path4529"
d="m 100.24698,67.21217 c -1.308628,-0.01556 -2.560878,2.352773 -4.890668,6.986654 -7.05187,-7.755768 -7.41258,-7.849666 -9.84229,2.649966 -0.11364,0.04758 -0.22709,0.0959 -0.34055,0.144695 -8.88231,-5.324406 -9.34124,-5.366051 -8.66717,5.041551 -0.21569,0.159933 -0.43121,0.322088 -0.64596,0.486794 -9.102,-3.605578 -9.49555,-3.378386 -7.19232,6.611477 -0.10784,0.11671 -0.21599,0.23284 -0.3235,0.35089 -9.27685,-2.506375 -9.53641,-2.129764 -6.07663,7.65845 -0.14847,0.21295 -0.29578,0.42957 -0.44338,0.64544 -10.45618,-1.11775 -10.67098,-0.91353 -5.35523,8.828413 -0.026,0.0482 -0.0526,0.0943 -0.0786,0.14263 -4.28381,2.04782 -5.70197,4.54381 -7.73183,7.89409 -0.15756,6.64457 6.9544,12.37105 17.18035,18.87895 -3.51325,-6.5113 -9.00735,-9.81207 -10.0614,-17.88832 1.54367,-2.90538 4.16877,-3.90901 6.42286,-5.21622 24.61995,14.36815 50.615808,14.53471 76.550928,-0.55035 2.26015,1.31503 4.90084,2.31509 6.45076,5.23224 -1.05405,8.07624 -6.54866,11.3765 -10.06191,17.8878 10.22594,-6.5079 17.33842,-12.23387 17.18086,-18.87844 -1.97354,-3.25733 -3.37677,-5.706 -7.38973,-7.72149 5.01291,-9.218263 4.53571,-9.305023 -5.98567,-8.177293 -0.0891,-0.1152 -0.17795,-0.23186 -0.26717,-0.34623 3.63467,-10.261532 3.40665,-10.529692 -6.39186,-7.87136 2.47993,-10.705895 2.14854,-10.749554 -7.77938,-6.803718 0.74781,-11.285018 0.47993,-11.261581 -8.88835,-5.642548 -2.49815,-10.818768 -2.79293,-10.786576 -9.94048,-2.924887 -2.68846,-4.933638 -4.10296,-7.403383 -5.43173,-7.419184 z"
style="display:inline;fill:#ff6600;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
id="g5758"
transform="translate(-2.3193819,-7.5517449)"
style="display:inline">
<path
style="display:inline;fill:#d45500;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 98.808992,121.55238 c -0.682767,-0.0559 4.468828,10.17565 8.051908,0.17277 0.24133,-0.67372 -3.69223,2.90976 -8.051908,-0.17277 z"
id="path5399" />
<g
id="g351">
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.212672,111.5632 c -5.131386,10.99206 5.618332,14.73472 4.412511,4.11951 -0.319289,-3.2881 -2.328089,-7.17448 -4.412511,-4.11951 z"
id="path5415" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.394393,112.41429 c -1.183441,1.51896 -0.08172,3.83605 -0.09872,3.98526 1.674403,1.64132 1.61267,-4.77034 0.09872,-3.98526 z"
id="path5417" />
</g>
<g
id="g355">
<path
id="path5433"
d="m 109.6169,111.5632 c -5.13141,10.99206 5.61836,14.73472 4.41254,4.11951 -0.31929,-3.2881 -2.3281,-7.17448 -4.41254,-4.11951 z"
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435"
d="m 111.79864,112.41429 c -1.18345,1.51896 -0.0817,3.83605 -0.0987,3.98526 1.67441,1.64132 1.61267,-4.77034 0.0987,-3.98526 z"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<path
id="path5389"
d="m 75.334492,106.8255 c -0.51041,0.004 -0.85068,0.0544 -0.79478,0.0889 0,0 -9.13406,11.93611 0.31006,18.65881 a 9.7471106,8.2770874 37.793943 0 0 1.43712,9.42372 9.7471106,8.2770874 37.793943 0 0 10.56473,3.9672 l -3.56412,-8.60569 9.19685,0.73536 a 9.7471106,8.2770874 37.793943 0 0 -7.02645,-8.91057 9.7471106,8.2770874 37.793943 0 0 -7.00784,0.009 c -1.86231,-1.36401 -4.6287,-4.92596 -0.0408,-12.47521 1.51807,-2.49795 -1.54349,-2.90314 -3.07475,-2.89129 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
transform="translate(-12.063571,-65.570169)"
id="g5751"
style="display:inline">
<path
style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 142.55119,137.92594 27.3632,-15.27364 21.39613,38.33184 -27.36321,15.27364 z"
id="path5730" />
<path
id="path5734"
d="m 142.55119,137.92594 27.3632,-15.27364 21.39613,38.33184 -27.36321,15.27364 -6.27068,-11.23547 c 3.50932,-3.1137 4.19635,-7.01138 3.17635,-11.38324 l -7.39141,3.83317 z"
style="display:inline;opacity:1;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
id="g1027"
transform="matrix(0.86024749,-0.10202883,0.10202883,0.86024749,30.096406,71.933538)"
style="display:inline">
<path
id="path4529-1"
d="m 142.60294,99.070411 c -0.25799,0.104739 -0.3086,0.672471 -0.384,1.773409 -2.02218,-0.940695 -2.10067,-0.929408 -1.71256,1.33048 -0.0183,0.0187 -0.0366,0.0375 -0.0549,0.0564 -2.18101,-0.31296 -2.27447,-0.28333 -1.28505,1.70286 -0.0291,0.0491 -0.0581,0.0987 -0.0866,0.1487 -2.08254,0.0423 -2.14104,0.11931 -0.86643,1.88938 -0.0116,0.0318 -0.0232,0.0635 -0.0346,0.0955 -2.02632,0.27237 -2.04621,0.36762 -0.56133,2.00288 -0.0116,0.054 -0.0226,0.10863 -0.0338,0.16313 -2.1433,0.64191 -2.16862,0.69967 -0.32345,2.17298 -0.001,0.0115 -0.002,0.0228 -0.004,0.0345 -0.67172,0.75455 -0.74436,1.361 -0.86664,2.18542 0.51637,1.31647 2.38318,1.85412 4.92525,2.2886 -1.22549,-0.98801 -2.57514,-1.18304 -3.44709,-2.68058 0.0635,-0.6971 0.49586,-1.1102 0.83039,-1.55228 6.0132,0.79095 11.12666,-1.31742 14.97206,-6.41277 0.55169,0.0718 1.1521,0.0505 1.69641,0.49515 0.45839,1.67116 -0.34771,2.77114 -0.50064,4.33785 1.47008,-2.11891 2.39377,-3.82799 1.81562,-5.11852 -0.65544,-0.47646 -1.13239,-0.84126 -2.08564,-0.90614 0.2242,-2.22125 0.12343,-2.19897 -1.84772,-1.11119 -0.027,-0.0153 -0.054,-0.0308 -0.081,-0.0459 -0.13212,-2.312418 -0.19894,-2.346242 -1.90222,-1.01773 -0.39525,-2.304482 -0.46385,-2.285755 -2.08647,-0.69401 -0.78276,-2.275429 -0.83337,-2.248771 -2.2084,-0.374874 -1.38113,-1.916624 -1.4363,-1.886031 -2.19098,0.244914 -0.93375,-0.746434 -1.41465,-1.114437 -1.67661,-1.008099 z"
style="fill:#2a3455;fill-opacity:1;stroke:#cccccc;stroke-width:0.212762;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5389-8"
d="m 140.97831,108.89338 c -0.0997,0.0432 -0.1624,0.0807 -0.1486,0.0829 0,0 -0.80881,3.09385 1.59758,3.63486 a 2.0738233,1.7610569 15.019669 0 0 1.05807,1.73034 2.0738233,1.7610569 15.019669 0 0 2.39928,-0.0919 l -1.40796,-1.39467 1.86476,-0.61321 a 2.0738233,1.7610569 15.019669 0 0 -2.1123,-1.16933 2.0738233,1.7610569 15.019669 0 0 -1.37407,0.57883 c -0.47768,-0.11421 -1.31374,-0.58513 -1.03548,-2.44396 0.0921,-0.61507 -0.54189,-0.4424 -0.84131,-0.31396 z"
style="fill:#2a3455;fill-opacity:1;stroke:#cccccc;stroke-width:0.212762;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5732-7"
d="m 151.16542,99.348723 c -0.31065,0.459014 -0.45239,1.035767 -0.39183,1.594317 0.0815,0.73897 0.505,1.34373 1.10549,1.57849 0.10244,2.21135 -1.43246,2.11407 -1.46989,2.10013 -0.28634,-0.0774 -0.71677,1.13867 0.53519,0.98198 1.95562,-0.24473 2.05537,-2.12623 1.98064,-3.063 0.49633,-0.1651 0.92044,-0.56676 1.16438,-1.10276 0.36766,-0.81095 0.25745,-1.774612 -0.27281,-2.385521 l -1.28437,1.509221 -0.10531,-0.25395 z"
style="fill:#2a3455;fill-opacity:1;stroke:#cccccc;stroke-width:0.212762;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#b1b3b8;fill-opacity:1;stroke:#b1b3b8;stroke-width:0.165543;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 157.03788,77.939189 c -0.10179,0.05 1.49878,1.067004 1.15909,-0.658823 -0.0229,-0.116239 -0.27783,0.726935 -1.15909,0.658823 z"
id="path5399-5"
transform="matrix(1.1463308,0.13595947,-0.13595947,1.1463308,-22.801274,-2.4500009)" />
<g
id="g364"
transform="matrix(1.1463308,0.13595947,-0.13595947,1.1463308,-22.801274,-2.4500009)">
<path
style="fill:#b1b3b8;fill-opacity:1;stroke:#b1b3b8;stroke-width:0.0438px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 155.39466,76.994381 c 0.20359,1.997829 2.04911,1.617431 0.97679,0.211015 -0.32445,-0.440244 -0.93981,-0.822131 -0.97679,-0.211015 z"
id="path5415-5" />
<path
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.0438px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 155.77699,76.930181 c -0.0393,0.316333 0.31396,0.552158 0.32421,0.574809 0.37728,0.09117 -0.17566,-0.814887 -0.32421,-0.574809 z"
id="path5417-5" />
</g>
<g
id="g374"
transform="matrix(1.1463308,0.13595947,-0.13595947,1.1463308,-22.801274,-2.4500009)">
<path
id="path5433-8"
d="m 157.72622,75.602125 c 0.20358,1.997831 2.04911,1.617429 0.97679,0.211012 -0.32445,-0.440244 -0.93981,-0.82213 -0.97679,-0.211012 z"
style="fill:#b1b3b8;fill-opacity:1;stroke:#b1b3b8;stroke-width:0.0438px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path5435-2"
d="m 158.10855,75.537924 c -0.0393,0.316333 0.31396,0.552158 0.32421,0.574808 0.37728,0.09117 -0.17566,-0.814886 -0.32421,-0.574808 z"
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.0438px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:1.71585px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.0428963"
x="94.641426"
y="157.04494"
id="text167"
transform="rotate(-28.988077,144.6719,16.286147)"><tspan
id="tspan165"
x="94.641426"
y="157.04494"
style="fill:#cccccc;stroke-width:0.0428963">PASSPORT</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.03519px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.0508798"
x="92.063805"
y="154.54707"
id="text163"
transform="rotate(-29.037466,144.42409,16.321288)"><tspan
id="tspan161"
x="92.063805"
y="154.54707"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'.PingFang SC';-inkscape-font-specification:'.PingFang SC';fill:#cccccc;stroke-width:0.0508798">AUTHSTRALIA</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.70471px;line-height:1.25;font-family:sans-serif;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.0676183"
x="93.162766"
y="128.62389"
id="text1127"
transform="rotate(-29.176231,143.73222,16.419399)"><tspan
id="tspan1125"
x="93.162766"
y="128.62389"
style="fill:#cccccc;stroke-width:0.0676183">KANIDM</tspan></text>
</g>
<path
d="m 145.61485,98.730679 c 3.50932,-3.1137 4.19635,-7.01138 3.17635,-11.38324 l -7.3914,3.83317 z"
style="display:inline;fill:#2a3455;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path523" />
<path
id="path5732"
d="m 136.84733,83.996879 c -2.18137,1.42401 -3.84498,3.66555 -4.59874,6.19631 -0.99138,3.35068 -0.25622,6.742 1.91889,8.85192 -3.57942,9.769591 -10.05414,6.555411 -10.19096,6.426891 -1.10017,-0.85629 -5.17794,3.63047 0.53268,5.22929 8.92019,2.49747 12.77572,-5.47478 14.1562,-9.67038 2.45132,0.18756 5.02004,-0.78148 7.05239,-2.660471 3.06875,-2.84546 4.34444,-7.22212 3.15795,-10.83433 l -8.31186,4.20365 0.005,-1.29218 z"
style="display:inline;fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more