fix task #23 conection status. and merge into master

pull/23/head
Niko PLP 2 months ago
commit 3acb85b3ff
  1. 1
      .gitignore
  2. 18
      Cargo.lock
  3. 3
      Cargo.toml
  4. 36
      docker/Dockerfile.alpine
  5. 46
      docker/Dockerfile.fedora
  6. 47
      docker/Dockerfile.ubuntu
  7. 37
      nextgraph/src/local_broker.rs
  8. 1
      ng-app/src-tauri/Cargo.toml
  9. 3
      ng-app/src-tauri/gen/android/buildSrc/src/main/java/org/nextgraph/ng_app_native/kotlin/BuildTask.kt
  10. 49
      ng-app/src-tauri/src/lib.rs
  11. 9
      ng-app/src/App.svelte
  12. 21
      ng-app/src/api.ts
  13. 0
      ng-app/src/apps/JsonLdEditor.svelte
  14. 2
      ng-app/src/assets/nextgraph-nofill.svg
  15. 873
      ng-app/src/classes.ts
  16. 86
      ng-app/src/lib/CenteredLayout.svelte
  17. 187
      ng-app/src/lib/DataClassIcon.svelte
  18. 42
      ng-app/src/lib/FullLayout.svelte
  19. 4
      ng-app/src/lib/Home.svelte
  20. 34
      ng-app/src/lib/Install.svelte
  21. 468
      ng-app/src/lib/Login.svelte
  22. 7
      ng-app/src/lib/NoWallet.svelte
  23. 1
      ng-app/src/lib/Test.svelte
  24. 120
      ng-app/src/lib/ZeraIcon.svelte
  25. 78
      ng-app/src/lib/components/CopyToClipboard.svelte
  26. 34
      ng-app/src/lib/components/Logo.svelte
  27. 76
      ng-app/src/lib/components/PasswordInput.svelte
  28. 16
      ng-app/src/routes/Home.svelte
  29. 8
      ng-app/src/routes/Install.svelte
  30. 2
      ng-app/src/routes/NURI.svelte
  31. 2
      ng-app/src/routes/NotFound.svelte
  32. 35
      ng-app/src/routes/User.svelte
  33. 2
      ng-app/src/routes/UserRegistered.svelte
  34. 448
      ng-app/src/routes/WalletCreate.svelte
  35. 10
      ng-app/src/routes/WalletLogin.svelte
  36. 135
      ng-app/src/store.ts
  37. 28
      ng-app/src/styles.css
  38. 191
      ng-app/src/wallet_emojis.ts
  39. 11
      ng-app/src/worker.js
  40. 629
      ng-app/src/zeras.ts
  41. 8
      ng-app/tailwind.config.cjs
  42. 2
      ng-app/tsconfig.json
  43. 4
      ng-net/src/app_protocol.rs
  44. 4
      ng-oxigraph/src/oxigraph/sparql/update.rs
  45. 1
      ng-repo/src/errors.rs
  46. 5
      ng-repo/src/types.rs
  47. 28
      ng-repo/src/utils.rs
  48. 1
      ng-sdk-js/Cargo.toml
  49. 53
      ng-sdk-js/src/lib.rs
  50. 26
      ng-wallet/src/bip39.rs
  51. 7
      ng-wallet/src/lib.rs
  52. 3
      ng-wallet/src/types.rs
  53. 5
      ngaccount/.env
  54. 15
      ngaccount/README.md
  55. 7
      ngaccount/src/main.rs
  56. 4
      ngaccount/web/src/routes/Create.svelte
  57. 4
      ngaccount/web/src/routes/Delete.svelte
  58. 2
      ngone/web/src/routes/Install.svelte
  59. 2
      ngone/web/src/routes/WalletCreate.svelte

1
.gitignore vendored

@ -14,3 +14,4 @@ node_modules
*/tests/*.mnemonic */tests/*.mnemonic
*/ng-example/* */ng-example/*
.vscode/settings.json .vscode/settings.json
.env.local

18
Cargo.lock generated

@ -3290,6 +3290,7 @@ dependencies = [
"serde", "serde",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"sys-locale",
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-window", "tauri-plugin-window",
@ -3408,7 +3409,7 @@ dependencies = [
"serde", "serde",
"sha1", "sha1",
"sha2 0.10.8", "sha2 0.10.8",
"siphasher 0.3.10", "siphasher 1.0.1",
"thiserror", "thiserror",
"zstd", "zstd",
] ]
@ -3449,7 +3450,7 @@ dependencies = [
[[package]] [[package]]
name = "ng-rocksdb" name = "ng-rocksdb"
version = "0.21.0-ngpreview.4" version = "0.21.0-ngpreview.4"
source = "git+https://git.nextgraph.org/NextGraph/rust-rocksdb.git?branch=master#e566341c319eead6637b1aecce5e92136e946ae5" source = "git+https://git.nextgraph.org/NextGraph/rust-rocksdb.git?branch=master#f1ce2e345f3d729350a02bcffb47c526674edd85"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"bzip2-sys", "bzip2-sys",
@ -3482,6 +3483,7 @@ dependencies = [
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"sys-locale",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-bindgen-test", "wasm-bindgen-test",
@ -5352,6 +5354,18 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "sys-locale"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
dependencies = [
"js-sys",
"libc",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "system-configuration" name = "system-configuration"
version = "0.6.0" version = "0.6.0"

@ -38,6 +38,9 @@ documentation = "https://docs.nextgraph.org/"
lto = true lto = true
opt-level = 's' opt-level = 's'
[profile.dev]
opt-level = 2
[patch.crates-io] [patch.crates-io]
# tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup"} # tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup"}
# tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] } # tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] }

@ -0,0 +1,36 @@
# Use rust's latest alpine image as base image.
FROM rust:alpine
ENV LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH
RUN apk add git nodejs npm llvm-static llvm-dev clang-static clang-dev openssl openssl-dev perl gtk+3.0-dev webkit2gtk-dev librsvg-dev curl wget pkgconf eudev-dev build-base zlib-static bzip2-static build-base ncursers-static && \
# Install Rust and Node.js tools
cargo install cargo-watch && \
cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2 && \
cargo install tauri-cli --version "2.0.0-alpha.11" --locked && \
npm install -g pnpm
# Clone the nextgraph-rs repository
RUN git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git && \
cd /nextgraph-rs/ng-sdk-js && \
wasm-pack build --target bundler && npm install --no-save pkg &&
# Build ng-app web version
cd /nextgraph-rs/ng-app && pnpm install && pnpm webfilebuild
# From here the build fails due to llvm / clang linking issues...
#
# WORKDIR /nextgraph-rs
## Build the nextgraph-rs project and its subprojects
# RUN cd /nextgraph-rs && git pull && cargo update -p ng-rocksdb && \
# cargo build -r && \
# cargo build -r -p ngd && \
# cargo build -r -p ngcli
# TODO: Build the platform-specific ng-app versions
# cd /nextgraph-rs/ng-app && cargo tauri build --target x86_64-unknown-linux-gnu
# ...
# TODO: To remove the image size, remove ~/.cargo, ~/.rustup, and the build dependencies
# To build the image, run:
# docker build -t nextgraph-rs:alpine -f docker/Dockerfile.alpine .

@ -0,0 +1,46 @@
# Use fedora:40 as base image
FROM fedora:40
# Set the environment variable to ensure cargo is available in the PATH
ENV PATH="/root/.cargo/bin:${PATH}"
SHELL ["/bin/bash", "-c"]
# Install the required packages and Rust
RUN dnf install -y git clang-devel webkit2gtk4.1-devel openssl openssl-devel curl wget file libappindicator-gtk3-devel librsvg2-devel perl && \
dnf group install -y "C Development Tools and Libraries" && \
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y && \
# Node.js
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash && \
export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" && \
nvm install 22 && \
npm install -g pnpm && \
# Clear Cache
rm -rf /var/cache/dnf && \
# Install Rust and Node.js tools
cargo install cargo-watch && \
cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2 && \
cargo install tauri-cli --version "2.0.0-alpha.11" --locked && \
# Clone the nextgraph-rs repository (TODO: It might be better to put this into a seperate RUN command to avoid rebuilding the image if the repository changes)
git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git && \
# Build sdk and ng-app web version
cd /nextgraph-rs/ng-sdk-js && wasm-pack build --target bundler && npm install --no-save pkg && \
cd /nextgraph-rs/ng-app && pnpm install && pnpm webfilebuild
# Build the nextgraph-rs project
RUN export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" && \
cd /nextgraph-rs && git pull && cargo update -p ng-rocksdb && \
cargo build -r && \
cargo build -r -p ngd && \
cargo build -r -p ngcli
# TODO: Build the platform-specific ng-app versions
# cd /nextgraph-rs/ng-app && cargo tauri build --target x86_64-unknown-linux-gnu
# ...
# TODO: To remove the image size, remove ~/.cargo, ~/.rustup, and the build dependencies
# To build the image, run:
# docker build -t nextgraph-rs:fedora -f docker/Dockerfile.fedora .

@ -0,0 +1,47 @@
# Use ubuntu 22.04 as base image
FROM ubuntu:22.04
SHELL ["/bin/bash", "-c"]
# Set the environment variable to ensure cargo is available in the PATH
ENV PATH="/root/.cargo/bin:${PATH}"
# Install the required packages and Rust
RUN apt update && \
apt upgrade -y && \
apt install -y git llvm-dev libclang-dev clang libssl-dev perl libappindicator3-dev libwebkit2gtk-4.0-dev librsvg2-dev curl wget pkg-config libudev-dev build-essential && \
rm -rf /var/cache/apt && \
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
# Node.js
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash && \
export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" && \
nvm install 22 && \
npm install -g pnpm && \
# Install Rust and Node.js tools
cargo install cargo-watch && \
cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2 && \
cargo install tauri-cli --version "2.0.0-alpha.11" --locked && \
npm install -g pnpm && \
# Clone the nextgraph-rs repository (TODO: It might be better to put this into a seperate RUN command to avoid rebuilding the image if the repository changes)
git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git && \
# Build sdk and ng-app web version
cd /nextgraph-rs/ng-sdk-js && wasm-pack build --target bundler && npm install --no-save pkg && \
cd /nextgraph-rs/ng-app && \
pnpm install && pnpm webfilebuild
# Build the nextgraph-rs project and its subprojects
WORKDIR /nextgraph-rs
RUN cargo build -r && \
cargo build -r -p ngd && \
cargo build -r -p ngcli
# TODO: Build the platform-specific ng-app versions
# WORKDIR /nextgraph-rs/ng-app
# RUN cargo tauri build --target x86_64-unknown-linux-gnu
# TODO: To remove the image size, remove ~/.cargo, ~/.rustup, and the build dependencies
# To build the image, run:
# docker build -t nextgraph-rs:ubuntu -f docker/Dockerfile.ubuntu .

@ -40,6 +40,7 @@ use ng_verifier::types::*;
use ng_verifier::verifier::Verifier; use ng_verifier::verifier::Verifier;
use ng_wallet::emojis::encode_pazzle; use ng_wallet::emojis::encode_pazzle;
use ng_wallet::bip39::encode_mnemonic;
use ng_wallet::{create_wallet_first_step_v0, create_wallet_second_step_v0, types::*}; use ng_wallet::{create_wallet_first_step_v0, create_wallet_second_step_v0, types::*};
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
@ -1555,6 +1556,27 @@ pub fn wallet_open_with_pazzle(
Ok(opened_wallet) Ok(opened_wallet)
} }
#[doc(hidden)]
/// This is a bit hard to use as the mnemonic words are encoded in u16.
/// prefer the function wallet_open_with_mnemonic_words
pub fn wallet_open_with_mnemonic(
wallet: &Wallet,
mnemonic: Vec<u16>,
pin: [u8; 4],
) -> Result<SensitiveWallet, NgError> {
if mnemonic.len() != 12 {
return Err(NgError::InvalidMnemonic);
}
// Convert from vec to array.
let mut mnemonic_arr = [0u16; 12];
for (place, element) in mnemonic_arr.iter_mut().zip(mnemonic.iter()) {
*place = *element;
}
let opened_wallet = ng_wallet::open_wallet_with_mnemonic(wallet, mnemonic_arr, pin)?;
Ok(opened_wallet)
}
/// Opens a wallet by providing an ordered list of words, and the pin. /// Opens a wallet by providing an ordered list of words, and the pin.
/// ///
/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened]. /// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened].
@ -1569,6 +1591,21 @@ pub fn wallet_open_with_pazzle_words(
wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin) wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin)
} }
/// Opens a wallet by providing an ordered list of mnemonic words, and the pin.
///
/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened].
/// Otherwise, if you are importing, then you must call [wallet_import].
pub fn wallet_open_with_mnemonic_words(
wallet: &Wallet,
mnemonic: &Vec<String>,
pin: [u8; 4],
) -> Result<SensitiveWallet, NgError> {
let encoded: Vec<u16> = encode_mnemonic(mnemonic)?;
wallet_open_with_mnemonic(wallet, encoded, pin)
}
/// Imports a wallet into the LocalBroker so the user can then access its content. /// Imports a wallet into the LocalBroker so the user can then access its content.
/// ///
/// the wallet should have been previous opened with [wallet_open_with_pazzle_words]. /// the wallet should have been previous opened with [wallet_open_with_pazzle_words].

@ -28,6 +28,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
serde_bytes = "0.11.7" serde_bytes = "0.11.7"
async-std = { version = "1.12.0", features = ["attributes", "unstable"] } async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
sys-locale = { version = "0.3.1" }
ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] } ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] }
tauri = { version = "2.0.0-alpha.14", features = [] } tauri = { version = "2.0.0-alpha.14", features = [] }
tauri-plugin-window = "2.0.0-alpha.1" tauri-plugin-window = "2.0.0-alpha.1"

@ -16,7 +16,8 @@ open class BuildTask : DefaultTask() {
@TaskAction @TaskAction
fun assemble() { fun assemble() {
val executable = """/Users/nl/.cargo/bin/cargo-tauri"""; val homePath = System.getProperty("user.home");
val executable = "$homePath/.cargo/bin/cargo-tauri";
try { try {
runTauriCli(executable) runTauriCli(executable)
} catch (e: Exception) { } catch (e: Exception) {

@ -13,6 +13,7 @@ use std::fs::write;
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use sys_locale::get_locales;
use tauri::scope::ipc::RemoteDomainAccessScope; use tauri::scope::ipc::RemoteDomainAccessScope;
use tauri::utils::config::WindowConfig; use tauri::utils::config::WindowConfig;
use tauri::{path::BaseDirectory, App, Manager}; use tauri::{path::BaseDirectory, App, Manager};
@ -38,6 +39,27 @@ pub use mobile::*;
pub type SetupHook = Box<dyn FnOnce(&mut App) -> Result<(), Box<dyn std::error::Error>> + Send>; pub type SetupHook = Box<dyn FnOnce(&mut App) -> Result<(), Box<dyn std::error::Error>> + Send>;
#[tauri::command(rename_all = "snake_case")]
async fn locales() -> Result<Vec<String>, ()> {
Ok(get_locales()
.filter_map(|lang| {
if lang == "C" || lang == "c" {
None
} else {
let mut split = lang.split('.');
let code = split.next().unwrap();
let code = code.replace("_", "-");
let mut split = code.rsplitn(2, '-');
let country = split.next().unwrap();
Some(match split.next() {
Some(next) => format!("{}-{}", next, country.to_uppercase()),
None => country.to_string(),
})
}
})
.collect())
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn test(app: tauri::AppHandle) -> Result<(), ()> { async fn test(app: tauri::AppHandle) -> Result<(), ()> {
let path = app let path = app
@ -86,6 +108,30 @@ async fn wallet_open_with_pazzle(
Ok(wallet) Ok(wallet)
} }
#[tauri::command(rename_all = "snake_case")]
async fn wallet_open_with_mnemonic(
wallet: Wallet,
mnemonic: Vec<u16>,
pin: [u8; 4],
_app: tauri::AppHandle,
) -> Result<SensitiveWallet, String> {
let wallet = nextgraph::local_broker::wallet_open_with_mnemonic(&wallet, mnemonic, pin)
.map_err(|e| e.to_string())?;
Ok(wallet)
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_open_with_mnemonic_words(
wallet: Wallet,
mnemonic_words: Vec<String>,
pin: [u8; 4],
_app: tauri::AppHandle,
) -> Result<SensitiveWallet, String> {
let wallet = nextgraph::local_broker::wallet_open_with_mnemonic_words(&wallet, &mnemonic_words, pin)
.map_err(|e| e.to_string())?;
Ok(wallet)
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn wallet_get_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> { async fn wallet_get_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> {
let ser = nextgraph::local_broker::wallet_get_file(&wallet_name) let ser = nextgraph::local_broker::wallet_get_file(&wallet_name)
@ -490,9 +536,12 @@ impl AppBuilder {
.plugin(tauri_plugin_window::init()) .plugin(tauri_plugin_window::init())
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
test, test,
locales,
wallet_gen_shuffle_for_pazzle_opening, wallet_gen_shuffle_for_pazzle_opening,
wallet_gen_shuffle_for_pin, wallet_gen_shuffle_for_pin,
wallet_open_with_pazzle, wallet_open_with_pazzle,
wallet_open_with_mnemonic,
wallet_open_with_mnemonic_words,
wallet_was_opened, wallet_was_opened,
wallet_create, wallet_create,
wallet_read_file, wallet_read_file,

@ -19,19 +19,20 @@
active_session, active_session,
close_active_session, close_active_session,
disconnections_subscribe, disconnections_subscribe,
select_default_lang,
} from "./store"; } from "./store";
import Home from "./routes/Home.svelte"; import Home from "./routes/Home.svelte";
import Test from "./routes/Test.svelte"; import Test from "./routes/Test.svelte";
import URI from "./routes/URI.svelte"; import NURI from "./routes/NURI.svelte";
import NotFound from "./routes/NotFound.svelte"; import NotFound from "./routes/NotFound.svelte";
import WalletCreate from "./routes/WalletCreate.svelte"; import WalletCreate from "./routes/WalletCreate.svelte";
import Invitation from "./routes/Invitation.svelte"; import Invitation from "./routes/Invitation.svelte";
import WalletLogin from "./routes/WalletLogin.svelte"; import WalletLogin from "./routes/WalletLogin.svelte";
import User from "./routes/User.svelte"; import User from "./routes/User.svelte";
import UserRegistered from "./routes/UserRegistered.svelte"; import UserRegistered from "./routes/UserRegistered.svelte";
import Install from "./lib/Install.svelte"; import Install from "./routes/Install.svelte";
import ng from "./api"; import ng from "./api";
@ -44,7 +45,7 @@
routes.set("/user", User); routes.set("/user", User);
routes.set("/user/registered", UserRegistered); routes.set("/user/registered", UserRegistered);
if (import.meta.env.NG_APP_WEB) routes.set("/install", Install); if (import.meta.env.NG_APP_WEB) routes.set("/install", Install);
routes.set(/^\/ng(.*)/i, URI); routes.set(/^\/did:ng(.*)/i, NURI);
routes.set("*", NotFound); routes.set("*", NotFound);
let unsubscribe = () => {}; let unsubscribe = () => {};
@ -60,7 +61,9 @@
onMount(async () => { onMount(async () => {
try { try {
await disconnections_subscribe(); await disconnections_subscribe();
await select_default_lang();
} catch (e) { } catch (e) {
console.error(e);
//console.log("called disconnections_subscribe twice"); //console.log("called disconnections_subscribe twice");
} }
let tauri_platform = import.meta.env.TAURI_PLATFORM; let tauri_platform = import.meta.env.TAURI_PLATFORM;

@ -16,6 +16,7 @@ const mapping = {
"wallet_gen_shuffle_for_pazzle_opening": ["pazzle_length"], "wallet_gen_shuffle_for_pazzle_opening": ["pazzle_length"],
"wallet_gen_shuffle_for_pin": [], "wallet_gen_shuffle_for_pin": [],
"wallet_open_with_pazzle": ["wallet","pazzle","pin"], "wallet_open_with_pazzle": ["wallet","pazzle","pin"],
"wallet_open_with_mnemonic_words": ["wallet","mnemonic_words","pin"],
"wallet_was_opened": ["opened_wallet"], "wallet_was_opened": ["opened_wallet"],
"wallet_create": ["params"], "wallet_create": ["params"],
"wallet_read_file": ["file"], "wallet_read_file": ["file"],
@ -97,6 +98,19 @@ const handler = {
}; };
//console.log(info,res); //console.log(info,res);
return res; return res;
} else if (path[0] === "locales") {
let from_rust = await tauri.invoke("locales",{});
let from_js = window.navigator.languages;
console.log(from_rust,from_js);
for (let lang of from_js) {
let split = lang.split("-");
if (split[1]) {
lang = split[0] + "-" + split[1].toUpperCase();
}
if (!from_rust.includes(lang)) { from_rust.push(lang);}
}
return from_rust;
} else if (path[0] === "disconnections_subscribe") { } else if (path[0] === "disconnections_subscribe") {
let { getCurrent } = await import("@tauri-apps/plugin-window"); let { getCurrent } = await import("@tauri-apps/plugin-window");
let callback = args[0]; let callback = args[0];
@ -169,7 +183,7 @@ const handler = {
return false; return false;
} else if (path[0] === "get_local_url") { } else if (path[0] === "get_local_url") {
return false; return false;
} else if (path[0] === "wallet_open_with_pazzle") { } else if (path[0] === "wallet_open_with_pazzle" || path[0] === "wallet_open_with_mnemonic_words") {
let arg:any = {}; let arg:any = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el) args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img)); let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img));
@ -177,9 +191,8 @@ const handler = {
arg.wallet = {V0:{id:arg.wallet.V0.id, sig:arg.wallet.V0.sig, content:{}}}; arg.wallet = {V0:{id:arg.wallet.V0.id, sig:arg.wallet.V0.sig, content:{}}};
Object.assign(arg.wallet.V0.content,old_content); Object.assign(arg.wallet.V0.content,old_content);
arg.wallet.V0.content.security_img = img; arg.wallet.V0.content.security_img = img;
return tauri.invoke(path[0],arg) return tauri.invoke(path[0],arg);
} } else {
else {
let arg = {}; let arg = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el) args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
return tauri.invoke(path[0],arg) return tauri.invoke(path[0],arg)

@ -11,6 +11,6 @@
style="fill:#ffffff;stroke:none;stroke-width:0.268375" /> style="fill:#ffffff;stroke:none;stroke-width:0.268375" />
<path <path
d="M 98.343352,190.26108 C 80.403778,187.53354 65.011938,179.57839 52.608228,166.62327 38.602093,151.99448 31.178059,133.41381 31.178059,112.98841 c 0,-10.21889 1.700058,-19.44396 5.221234,-28.332119 4.28678,-10.820699 10.037295,-19.39063 18.535095,-27.62263 4.72982,-4.58187 6.60687,-6.10643 11.28099,-9.16256 11.89869,-7.779841 24.173884,-11.879991 38.095802,-12.724761 19.80437,-1.2017 39.11165,5.11306 54.60284,17.858751 1.50718,1.24006 2.72951,2.35934 2.71628,2.48729 -0.0132,0.12795 -3.85821,3.63443 -8.54442,7.79217 -4.6862,4.157729 -10.04724,8.96276 -11.91342,10.677819 -1.86617,1.715071 -3.54094,3.11831 -3.7217,3.11831 -0.18075,0 -1.39985,-0.745188 -2.70911,-1.655969 -7.53011,-5.23834 -15.99428,-7.82188 -25.62597,-7.82188 -12.731628,0 -23.249192,4.3379 -32.143882,13.257541 -6.39594,6.413868 -10.70387,14.555268 -12.50018,23.623578 -0.69099,3.48832 -0.68968,13.53072 0.002,17.00893 3.70508,18.62577 18.31886,33.10194 36.642322,36.29729 4.16439,0.72621 11.98099,0.71223 15.98975,-0.0286 14.03187,-2.59311 25.86047,-11.36806 32.26533,-23.93578 0.77379,-1.51834 1.26018,-2.88461 1.08086,-3.03616 -0.17934,-0.15156 -6.87448,-1.1779 -14.87813,-2.28078 -9.7795,-1.34758 -14.92353,-2.21379 -15.68471,-2.64117 -1.52067,-0.85379 -2.83611,-2.88806 -2.83611,-4.3859 0,-1.1732 2.02687,-15.86876 2.49085,-18.05962 0.29676,-1.40127 2.42559,-3.4934 3.84317,-3.77691 0.62227,-0.12445 8.82712,0.85555 18.28065,2.18348 9.43343,1.32511 17.26269,2.29453 17.39833,2.15427 0.13566,-0.14026 1.11808,-6.54833 2.18313,-14.24014 1.10778,-8.000208 2.20407,-14.60184 2.56177,-15.426229 0.34392,-0.792599 1.11019,-1.849131 1.70287,-2.34782 2.06321,-1.736079 3.1433,-1.785011 12.20439,-0.55291 9.63637,1.310309 10.70873,1.56224 12.28077,2.88503 1.64359,1.382979 2.2732,2.810909 2.25906,5.123309 -0.007,1.10173 -0.92172,8.29645 -2.03332,15.98826 -1.11158,7.69182 -1.97159,14.04091 -1.91113,14.1091 0.0605,0.0682 7.16644,1.11143 15.79109,2.31832 11.10566,1.55407 16.00827,2.38757 16.80223,2.85657 1.53015,0.90389 2.48023,2.64785 2.45017,4.49756 -0.0462,2.84349 -2.41252,18.12279 -2.97521,19.21089 -0.66164,1.27949 -2.60244,2.54696 -3.92109,2.56074 -0.51973,0.005 -7.87449,-0.95937 -16.34391,-2.144 -8.46944,-1.18464 -15.47588,-2.077 -15.56986,-1.98301 -0.094,0.094 -1.18792,7.34163 -2.43097,16.10589 -1.44004,10.15311 -2.49792,16.43621 -2.91556,17.31631 -0.72531,1.52848 -2.76261,3.06291 -4.53817,3.41802 -0.95688,0.19138 -10.90014,-0.92798 -13.59859,-1.53084 -0.5471,-0.12223 -1.89146,0.67252 -4.50941,2.66588 -11.2627,8.57562 -24.34195,13.90917 -38.35741,15.64164 -4.40038,0.54395 -15.72658,0.43298 -19.853658,-0.19451 z" d="M 98.343352,190.26108 C 80.403778,187.53354 65.011938,179.57839 52.608228,166.62327 38.602093,151.99448 31.178059,133.41381 31.178059,112.98841 c 0,-10.21889 1.700058,-19.44396 5.221234,-28.332119 4.28678,-10.820699 10.037295,-19.39063 18.535095,-27.62263 4.72982,-4.58187 6.60687,-6.10643 11.28099,-9.16256 11.89869,-7.779841 24.173884,-11.879991 38.095802,-12.724761 19.80437,-1.2017 39.11165,5.11306 54.60284,17.858751 1.50718,1.24006 2.72951,2.35934 2.71628,2.48729 -0.0132,0.12795 -3.85821,3.63443 -8.54442,7.79217 -4.6862,4.157729 -10.04724,8.96276 -11.91342,10.677819 -1.86617,1.715071 -3.54094,3.11831 -3.7217,3.11831 -0.18075,0 -1.39985,-0.745188 -2.70911,-1.655969 -7.53011,-5.23834 -15.99428,-7.82188 -25.62597,-7.82188 -12.731628,0 -23.249192,4.3379 -32.143882,13.257541 -6.39594,6.413868 -10.70387,14.555268 -12.50018,23.623578 -0.69099,3.48832 -0.68968,13.53072 0.002,17.00893 3.70508,18.62577 18.31886,33.10194 36.642322,36.29729 4.16439,0.72621 11.98099,0.71223 15.98975,-0.0286 14.03187,-2.59311 25.86047,-11.36806 32.26533,-23.93578 0.77379,-1.51834 1.26018,-2.88461 1.08086,-3.03616 -0.17934,-0.15156 -6.87448,-1.1779 -14.87813,-2.28078 -9.7795,-1.34758 -14.92353,-2.21379 -15.68471,-2.64117 -1.52067,-0.85379 -2.83611,-2.88806 -2.83611,-4.3859 0,-1.1732 2.02687,-15.86876 2.49085,-18.05962 0.29676,-1.40127 2.42559,-3.4934 3.84317,-3.77691 0.62227,-0.12445 8.82712,0.85555 18.28065,2.18348 9.43343,1.32511 17.26269,2.29453 17.39833,2.15427 0.13566,-0.14026 1.11808,-6.54833 2.18313,-14.24014 1.10778,-8.000208 2.20407,-14.60184 2.56177,-15.426229 0.34392,-0.792599 1.11019,-1.849131 1.70287,-2.34782 2.06321,-1.736079 3.1433,-1.785011 12.20439,-0.55291 9.63637,1.310309 10.70873,1.56224 12.28077,2.88503 1.64359,1.382979 2.2732,2.810909 2.25906,5.123309 -0.007,1.10173 -0.92172,8.29645 -2.03332,15.98826 -1.11158,7.69182 -1.97159,14.04091 -1.91113,14.1091 0.0605,0.0682 7.16644,1.11143 15.79109,2.31832 11.10566,1.55407 16.00827,2.38757 16.80223,2.85657 1.53015,0.90389 2.48023,2.64785 2.45017,4.49756 -0.0462,2.84349 -2.41252,18.12279 -2.97521,19.21089 -0.66164,1.27949 -2.60244,2.54696 -3.92109,2.56074 -0.51973,0.005 -7.87449,-0.95937 -16.34391,-2.144 -8.46944,-1.18464 -15.47588,-2.077 -15.56986,-1.98301 -0.094,0.094 -1.18792,7.34163 -2.43097,16.10589 -1.44004,10.15311 -2.49792,16.43621 -2.91556,17.31631 -0.72531,1.52848 -2.76261,3.06291 -4.53817,3.41802 -0.95688,0.19138 -10.90014,-0.92798 -13.59859,-1.53084 -0.5471,-0.12223 -1.89146,0.67252 -4.50941,2.66588 -11.2627,8.57562 -24.34195,13.90917 -38.35741,15.64164 -4.40038,0.54395 -15.72658,0.43298 -19.853658,-0.19451 z"
style="fill:#888;fill-opacity:1;stroke:#888;stroke-width:0.377976;stroke-opacity:1" /> />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,873 @@
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
// "post/rich", "post/md", "post/text", "post/html", "post/asciidoc", "page", "code", "code/*", "app", "app/z", "class", "schema", "schema/owl|rdfs|shacl|shex", "service", "service/rust", "service/deno", "contract", "app/n:xxx.xx.xx:", "app/o:",
// "query/sparql", "query/graphql", "query/text", "query/web",
// "data/graph", "data/json", "data/array", "data/map", "data/xml", "data/table", "data/collection", "data/board", "data/grid", "data/geomap",
// "e/email", "e/web", "e/http://[url of class in ontology]", "e/rdf" (read-only cache of RDF fetched from web2.0)
// "mc/text", "mc/link", "mc/card", "mc/pad",
// "doc/diagram","doc/chart", "doc/pdf", "doc/odf", "doc/latex", "doc/ps", "doc/music", "doc/maths", "doc/chemistry", "doc/braille", "doc/ancientscript",
// "media/image", "media/reel", "media/album", "media/video", "media/audio", "media/song", "media/subtitle", "media/overlay",
// "social/channel", "social/stream", "social/contact", "social/event", "social/calendar", "social/scheduler", "social/reaction"
// "prod/task", "prod/project", "prod/issue", "prod/form", "prod/filling", "prod/cad", "prod/slides", "prod/question", "prod/answer", "prod/poll", "prod/vote"
// "file", "file/iana/*", "file/gimp", "file/inkscape", "file/kdenlive", "file/blender", "file/openscad", "file/lyx", "file/scribus", "file/libreoffice",
// application/vnd.api+json
// application/activity+json
// epub, dejavu,
// animation: snap, lottie, smil editor: https://github.com/HaikuTeam/animator/
export const official_classes = {
"post/rich": {
"ng:crdt": "YXml",
"ng:n": "Post - Rich Text", // editor: y-ProseMirror, viewer: https://www.npmjs.com/package/prosemirror-to-html-js or https://prosemirror.net/docs/ref/version/0.4.0.html#toDOM https://prosemirror.net/docs/ref/version/0.4.0.html#toHTML
"ng:a": "A Post with Rich Text, including images, links, formatted text, and embeds of other content",
"ng:o": "n:g:z:post:rich",
"ng:w": "n:g:z:post_rich_editor",
"ng:x": {
"as":true,
},
"ng:compat": ["as:Article"],
},
"post/md": {
"ng:crdt": "YXml",
"ng:n": "Post - MarkDown", // editor y-MilkDown, viewer: https://github.com/wooorm/markdown-rs
"ng:a": "A Post with MarkDown, including images, links, formatted text, and embeds of other content",
"ng:o": "n:g:z:post:md",
"ng:w": "n:g:z:post_md_editor",
"ng:x": {
"as":true,
},
"ng:compat": ["file/iana/text/markdown", "code/markdown","as:Article"],
},
"post/text": {
"ng:crdt": "YText",
"ng:n": "Post - Plain Text",
"ng:a": "A Post with Plain Text",
"ng:o": "n:g:z:post:text",
"ng:w": "n:g:z:code_editor",
"ng:x": {
"as":true,
},
"ng:compat": ["file/iana/text/plain", "code/plaintext","as:Article"],
},
"post/html": {
"ng:crdt": "YXml",
"ng:n": "Post - TinyMCE",
"ng:x": {
"as":true,
},
"ng:a": "A Post based on TinyMCE, including images, links, formatted text, and embeds of other content",
"ng:compat": ["as:Article"],
},
"post/asciidoc": { // display with https://github.com/asciidoctor/asciidoctor.js/
"ng:crdt": "YText",
"ng:n": "Post - AsciiDoc",
"ng:a": "A Post based on AsciiDoc format",
"ng:x": {
"as":true,
},
"ng:compat": ["as:Article"],
},
"page": {
"ng:crdt": "YXml",
"ng:n": "Page", // based on GrapeJS, VvvebJs, or Astro ?
"ng:a": "A Page and Site builder",
"ng:compat": [],
},
"code": {
"ng:crdt": "YText",
"ng:n": "Source Code", // edited with CodeMirror, displayed with highlight.js
"ng:a": "A Source Code file. many languages supported",
"ng:o": "n:g:z:pre",
"ng:w": "n:g:z:code_editor",
"ng:compat": ["code/*","file/iana/text/javascript","file/iana/text/css","file/iana/text/html","file/iana/text/markdown", "file/iana/application/xml", "file/iana/application/yaml", "file/iana/text/xml", "file/iana/application/xhtml+xml"],
},
"app": {
"ng:n": "Official App",
"ng:a": "App provided by NextGraph platform",
},
"app/z": {
"ng:crdt": "Elmer",
"ng:n": "Application", // Editor: Monaco
"ng:a": "Create an Application based on NextGraph Framework",
"ng:o": "n:g:z:app_store",
"ng:w": "n:g:z:app_editor",
"ng:include": ["schema/*","service/*","code","file"],
"ng:compat": ["code/svelte"],
},
"class": {
"ng:crdt": "Graph",
"ng:n": "Class",
"ng:a": "Define a custom Class for your data",
"ng:x": {
"rdfs":true,
},
"ng:compat": ["rdfs:Class"],
},
"schema/rdfs": {
"ng:crdt": "Graph",
"ng:n": "Schema - RDFS",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with RDFS",
"ng:o": "n:g:z:json_ld_editor", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": {
"rdfs":true,
},
"ng:include": ["data/graph"],
"ng:compat": ["rdfs:*","class"],
},
"schema/owl": { // display with https://github.com/VisualDataWeb/WebVOWL
"ng:crdt": "Graph",
"ng:n": "Schema - OWL",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with OWL",
"ng:o": "n:g:z:owl_viewer", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": {
"owl":true,
},
"ng:include": ["data/graph"],
"ng:compat": ["owl:Ontology"],
},
"schema/shacl": {
"ng:crdt": "Graph",
"ng:n": "Schema - SHACL",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with SHACL",
"ng:o": "n:g:z:json_ld_editor", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": {
"sh":true,
},
"ng:include": ["data/graph"],
"ng:compat": ["sh:Shape", "file/iana/text/shaclc" ],
},
"schema/shex": {
"ng:crdt": "Graph",
"ng:n": "Schema - SHEX",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with SHEX",
"ng:o": "n:g:z:json_ld_editor", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": {
"shex":true,
},
"ng:include": ["data/graph"],
"ng:compat": ["shex:*", "file/iana/text/shex", "code/shexc" ],
},
"service": {
"ng:n": "Internal Service",
"ng:a": "Service provided by NextGraph framework",
"ng:o": "n:g:z:service_invoke", // default viewer
},
"service/rust": {
"ng:crdt": "YText",
"ng:n": "Service - Rust", // edited with CodeMirror, displayed with highlight.js
"ng:a": "Service written in Rust and compiled to WASM",
"ng:o": "external_service_invoke", // default viewer
"ng:w": "n:g:z:service_editor", // default editor
"ng:compat": ["code/rust", "file/iana/application/wasm"],
},
"service/deno": {
"ng:crdt": "YText",
"ng:n": "Service - Deno/JS", // edited with CodeMirror, displayed with highlight.js
"ng:a": "Service written in JS/TS for Deno or NodeJS",
"ng:o": "external_service_invoke", // default viewer
"ng:w": "n:g:z:service_editor", // default editor
"ng:compat": ["code/javascript", "code/typescript", "file/iana/text/javascript", "file/iana/application/node"],
},
"contract": {
"ng:crdt": "YText",
"ng:n": "Contract", // edited with CodeMirror, displayed with highlight.js
"ng:a": "Smart Contract with Rust or JS code",
"ng:compat": ["code/rust", "file/iana/application/wasm", "code/javascript", "code/typescript", "file/iana/text/javascript", "file/iana/application/node"],
},
"query/sparql": {
"ng:crdt": "YText",// uses ng:default_graph and ng:named_graph predicates
"ng:n": "SPARQL Query", // edited with YASGUI or Sparnatural, displayed with highlight.js https://github.com/highlightjs/highlightjs-turtle/tree/master
"ng:a": "Saved SPARQL Query that can be invoked",
"ng:o": "n:g:z:sparql:invoke",
"ng:w": "n:g:z:sparql_query:yasgui",
"ng:compat": ["code/sparql", "file/iana/application/sparql-query"],
},
"query/sparql_update": {
"ng:crdt": "YText",// uses ng:default_graph and ng:named_graph predicates
"ng:n": "SPARQL Update", // edited with YASGUI, displayed with highlight.js https://github.com/highlightjs/highlightjs-turtle/tree/master
"ng:a": "Saved SPARQL Update that can be invoked",
"ng:o": "n:g:z:sparql:invoke",
"ng:w": "n:g:z:sparql_update:yasgui",
"ng:compat": ["code/sparql", "file/iana/application/sparql-update"],
},
"query/graphql": {
"ng:crdt": "YText", // uses ng:default_graph predicate
"ng:n": "GraphQL Query", // edited with https://github.com/graphql/graphiql or https://github.com/graphql-editor/graphql-editor, displayed with highlight.js
"ng:a": "Saved GraphQL Query that can be invoked",
"ng:o": "n:g:z:graphql:invoke",
"ng:w": "n:g:z:graphql_query",
"ng:compat": ["code/graphql", "file/iana/application/graphql+json"],
},
"query/text": {
"ng:crdt": "Graph",
"ng:n": "Text Search",
"ng:a": "Saved Text Search and its results",
"ng:compat": [],
},
"query/web": {
"ng:crdt": "Graph",
"ng:n": "Web Search",
"ng:a": "Saved Web Search and its results",
"ng:compat": [],
},
"data/graph": {
"ng:crdt": "Graph", // https://github.com/highlightjs/highlightjs-turtle/tree/master
"ng:n": "Graph",
"ng:a": "Define the Graph of your data with Semantic Web / Linked Data",
"ng:o": "n:g:z:json_ld_editor", // default viewer
"ng:w": "n:g:z:json_ld_editor", // default editor
"ng:x": {
"rdf":true,
"xsd":true,
},
"ng:compat": [ "rdf:*", "xsd:*", "file/iana/text/n3", "file/iana/text/rdf+n3", "file/iana/text/turtle", "file/iana/application/n-quads", "file/iana/application/trig", "file/iana/application/n-triples", "file/iana/application/rdf+xml", "file/iana/application/ld+json"],
},
"data/json": {
"ng:crdt": "Automerge",
"ng:n": "JSON",
"ng:a": "JSON Data CRDT",
"ng:o": "n:g:z:json_editor", // default viewer
"ng:w": "n:g:z:json_editor", // default editor
"ng:compat": ["file/iana/application/json", "code:json"],
},
"data/array": {
"ng:crdt": "YArray",
"ng:n": "JSON Array",
"ng:a": "JSON Array CRDT",
"ng:o": "n:g:z:json_editor", // default viewer
"ng:w": "n:g:z:json_editor", // default editor
"ng:compat": ["file/iana/application/json", "code:json"],
},
"data/map": {
"ng:crdt": "YMap",
"ng:n": "JSON Map",
"ng:a": "JSON Map CRDT",
"ng:o": "n:g:z:json_editor", // default viewer
"ng:w": "n:g:z:json_editor", // default editor
"ng:compat": ["file/iana/application/json", "code:json"],
},
"data/xml": {
"ng:crdt": "YXml",
"ng:n": "XML",
"ng:a": "XML Data CRDT",
"ng:compat": ["file/iana/text/xml","file/iana/application/xml", "code:xml"],
},
"data/table": {
"ng:crdt": "Automerge", // see https://github.com/frappe/datatable
"ng:n": "Table", // see https://specs.frictionlessdata.io/table-schema displayed with pivot table see https://activetable.io/docs/data https://www.npmjs.com/package/pivottable https://socket.dev/npm/package/svelte-pivottable/alerts/0.2.0?tab=dependencies
"ng:a": "Data in a Table (columns and rows)",
"ng:o": "n:g:z:pivot",
"ng:compat": ["file/iana/application/sparql-results+json","file/iana/application/sparql-results+xml","file/iana/text/csv"],
},
"data/collection": {
"ng:crdt": "Graph",
"ng:n": "Collection",
"ng:a": "An ordered list of items",
"ng:o": "n:g:z:list",
"ng:x": {
"as": true,
"rdf": true,
},
"ng:compat": ["as:Collection","rdf:List","rdf:Seq"],
},
"data/container": {
"ng:crdt": "Graph",
"ng:n": "Container",
"ng:a": "An unordered list of items",
"ng:o": "n:g:z:list",
"ng:x": {
"rdf": true,
"rdfs": true,
"ldp": true,
},
"ng:compat": ["rdfs:member","ldp:contains","rdf:Bag","rdf:Alt"],
},
"data/board": {
"ng:crdt": "Graph",
"ng:n": "Board",
"ng:a": "Whiteboard, infinite canvas to arrange your content in 2D",
"ng:o": "n:g:z:board",
"ng:include": [],
"ng:compat": [], //https://jsoncanvas.org/ https://www.canvasprotocol.org/ https://github.com/orgs/ocwg/discussions/25 https://infinitecanvas.tools/gallery/
},
"data/grid": {
"ng:crdt": "Graph",
"ng:n": "Grid",
"ng:a": "Grid representation of a collection or container",
"ng:o": "n:g:z:grid",
"ng:include": ["data/container","data/collection","data/table","media/album"],
"ng:compat": [],
},
"data/geomap": { // https://github.com/leaflet/leaflet
"ng:crdt": "Graph",
"ng:n": "Geo Map",
"ng:a": "Geographical Map",
"ng:x": {
"wgs": true,
"gn": true,
"as": true,
},
"ng:compat": ["as:Place","wgs:*","gn:*", "file/iana/application/geo+json", "file/iana/application/vnd.mapbox-vector-tile"], // see also https://github.com/topojson/topojson
},
"e/email": {
"ng:crdt": "Graph",
"ng:n": "Email",
"ng:a": "Email content and headers",
"ng:x": {
"email": "http://www.invincea.com/ontologies/icas/1.0/email#" //https://raw.githubusercontent.com/twosixlabs/icas-ontology/master/ontology/email.ttl
},
"ng:compat": ["file/iana/message/rfc822","file/iana/multipart/related"],
},
"e/web": {
"ng:crdt": "Graph",
//https://www.npmjs.com/package/warcio https://github.com/N0taN3rd/node-warc
"ng:n": "Web Archive",
"ng:a": "Archive the content of a web page",
"ng:compat": ["file/iana/application/warc","file/iana/multipart/related"],
},
"e/rdf": {
"ng:crdt": "Graph",
"ng:n": "RDF Archive",
"ng:a": "Archive the triples of an RDF resource dereferenced with HTTP",
"ng:include": ["data/graph"],
},
"mc/text": {
"ng:crdt": "Graph",
"ng:n": "Text Selection",
"ng:a": "Text Selection copied into Magic Carpet",
},
"mc/link": {
"ng:crdt": "Graph",
"ng:n": "Link",
"ng:a": "Link to a document kept in Magic Carpet",
},
"plato/card": {
"ng:crdt": "Graph",
"ng:n": "Card",
"ng:a": "Card representation of a document",
"ng:o": "n:g:z:card",
},
"plato/pad": {
"ng:crdt": "Graph",
"ng:n": "Pad",
"ng:a": "Pad representation of a document",
"ng:o": "n:g:z:pad",
},
"doc/diagram/mermaid" : {
"ng:crdt": "YText",
"ng:n": "Diagram - Mermaid",
"ng:a": "Describe Diagrams with Mermaid",
"ng:compat": ["file/iana/application/vnd.mermaid"]
},
"doc/diagram/drawio" : {
"ng:crdt": "YXml",
"ng:n": "Diagram - DrawIo",
"ng:a": "Draw Diagrams with DrawIo",
"ng:compat": ["file/iana/application/vnd.jgraph.mxfile","file/iana/application/x-drawio"]
},
"doc/diagram/graphviz" : {
"ng:crdt": "YText",
"ng:n": "Diagram - Graphviz",
"ng:a": "Describe Diagrams with Graphviz",
"ng:compat": ["file/iana/text/vnd.graphviz"]
},
"doc/diagram/excalidraw" : {
"ng:crdt": "Automerge",
"ng:n": "Diagram - Excalidraw",
"ng:a": "Collaborate on Diagrams with Excalidraw",
"ng:compat": ["file/iana/application/vnd.excalidraw+json"]
},
"doc/diagram/gantt" : { //https://github.com/frappe/gantt
"ng:crdt": "Automerge",
"ng:n": "Diagram - Gantt",
"ng:a": "Interactive gantt chart",
"ng:compat": []
},
"doc/diagram/flowchart" : { //https://github.com/adrai/flowchart.js
"ng:crdt": "YText",
"ng:n": "Diagram - Flowchart",
"ng:a": "flow chart diagrams",
"ng:compat": []
},
"doc/diagram/sequence" : { //https://github.com/bramp/js-sequence-diagrams
"ng:crdt": "YText",
"ng:n": "Diagram - Sequence",
"ng:a": "sequence diagrams",
"ng:compat": []
},
// checkout https://www.mindmaps.app/ but it is AGPL
"doc/diagram/markmap" : { //https://github.com/markmap/markmap
"ng:crdt": "YText",
"ng:n": "Diagram - Markmap",
"ng:a": "mindmaps with markmap",
"ng:compat": []
},
"doc/diagram/mymind" : { //https://github.com/markmap/markmap
"ng:crdt": "YText", // see MyMind format, MindMup JSON, FreeMind XML and MindMap Architect XML
"ng:n": "Diagram - Mymind",
"ng:a": "mindmaps with mymind",
"ng:compat": [] // https://github.com/ondras/my-mind/wiki/Saving-and-loading#file-formats
},
"doc/diagram/jsmind" : { //https://github.com/hizzgdev/jsmind
"ng:crdt": "Automerge",
"ng:n": "Diagram - jsmind",
"ng:a": "mindmaps with jsmind",
"ng:compat": [] // https://hizzgdev.github.io/jsmind/docs/en/1.usage.html
},
// DC and C3 have Crossfilter https://github.com/dc-js/dc.js http://crossfilter.github.io/crossfilter/ https://github.com/c3js/c3 http://dc-js.github.io/dc.js/
// take inspiration from https://github.com/metabase/metabase
// have a look at https://github.com/observablehq
// another open source dashboard with many data sources https://github.com/getredash/redash
// and another one https://github.com/apache/superset
// https://github.com/Rich-Harris/pancake
// https://github.com/williamngan/pts
// https://visjs.org/
"doc/viz/cytoscape" : {
"ng:crdt": "Automerge",
"ng:n": "Viz - Cytoscape",
"ng:a": "Graph theory (network) visualization",
"ng:compat": [] // https://github.com/cytoscape/cytoscape.js
},
"doc/viz/vega" : {
"ng:crdt": "Automerge",
"ng:n": "Viz - Vega",
"ng:a": "Grammar for interactive graphics",
"ng:compat": [] // https://vega.github.io/vega-lite/docs/ https://github.com/vega/editor
},
"doc/viz/vizzu" : {
"ng:crdt": "Automerge",
"ng:n": "Viz - Vizzu",
"ng:a": "Animated data visualizations and data stories",
"ng:compat": [] // https://github.com/vizzuhq/vizzu-lib
},
"doc/viz/plotly" : { //https://github.com/plotly/plotly.js
"ng:crdt": "Automerge",
"ng:n": "Viz - Plotly",
"ng:a": "Declarative charts",
"ng:compat": [] // https://github.com/cytoscape/cytoscape.js
},
"doc/viz/avail" : {
"ng:crdt": "Automerge",
"ng:n": "Viz - Avail",
"ng:a": "Time Data Availability Visualization",
"ng:compat": [] // https://github.com/flrs/visavail
},
"doc/chart/frappecharts" : {
"ng:crdt": "Automerge",
"ng:n": "Charts - Frappe",
"ng:a": "GitHub-inspired responsive charts",
"ng:compat": [] // https://github.com/frappe/charts
},
"doc/chart/financial" : {
"ng:crdt": "Automerge",
"ng:n": "Charts - Financial",
"ng:a": "Financial charts",
"ng:compat": [] //https://github.com/tradingview/lightweight-charts
},
// have a look at https://github.com/cube-js/cube and https://awesome.cube.dev/ and https://frappe.io/products
"doc/chart/apexcharts" : {
"ng:crdt": "Automerge",
"ng:n": "Charts - ApexCharts",
"ng:a": "Interactive data visualizations",
"ng:compat": [] // https://github.com/apexcharts/apexcharts.js
},
//realtime data with https://github.com/square/cubism
"doc/chart/billboard" : {
"ng:crdt": "Automerge",
"ng:n": "Charts - BillBoard",
"ng:a": "Interactive data visualizations based on D3",
"ng:compat": [] // https://github.com/naver/billboard.js
},
"doc/chart/echarts" : {
"ng:crdt": "Automerge",
"ng:n": "Charts - ECharts",
"ng:a": "Interactive charting and data visualization with Apache ECharts",
"ng:compat": [] // https://github.com/apache/echarts
},
"doc/chart/chartjs" : {
"ng:crdt": "Automerge",
"ng:n": "Charts - Chart.js",
"ng:a": "Simple yet flexible charting for designers & developers with Chart.js",
"ng:compat": [] // https://github.com/chartjs/Chart.js
},
// see if to provide plain D3, and also all the https://github.com/antvis libraries: G2, G6, L7, S2, X6. Have a look at AVA
"doc/pdf": {
"ng:crdt": "Graph",
"ng:n": "PDF",
"ng:a": "upload and display a PDF file",
"ng:compat": ["file/iana/application/pdf"] // https://github.com/mozilla/pdf.js https://viewerjs.org/
},
"doc/odf": { //!!! becareful: AGPL
"ng:crdt": "Graph",
"ng:n": "OpenDocumentFormat (ODF)",
"ng:a": "upload and display an ODF file",
"ng:compat": ["file/iana/application/vnd.oasis.opendocument*"] // https://webodf.org/ https://github.com/webodf/WebODF https://viewerjs.org/
},
// see also https://github.com/Mathpix/mathpix-markdown-it
"doc/latex": {
"ng:crdt": "Graph",
"ng:n": "Latex",
"ng:a": "upload and display a Latex or Tex file",
"ng:compat": ["file/iana/application/x-tex","file/iana/text/x-tex"] // https://github.com/michael-brade/LaTeX.js https://github.com/mathjax/MathJax
},
"doc/ps": { //!!! becareful: AGPL https://github.com/ochachacha/ps-wasm
"ng:crdt": "Graph",
"ng:n": "Postscript",
"ng:a": "upload and display a PostScript file",
"ng:compat": ["file/iana/application/postscript"] // https://www.npmjs.com/package/ghostscript4js
},
"doc/music/abc": { //https://github.com/paulrosen/abcjs
"ng:crdt": "YText",
"ng:n": "Music ABC",
"ng:a": "sheet music notation",
"ng:compat": []
},
"doc/music/guitar": { //https://github.com/birdca/fretboard
"ng:crdt": "YText",
"ng:n": "Music - Guitar",
"ng:a": "charts for guitar chords and scales",
"ng:compat": []
},
"doc/maths": { //https://github.com/KaTeX/KaTeX
"ng:crdt": "YText",
"ng:n": "Maths",
"ng:a": "TeX math rendering",
"ng:compat": ["file/iana/application/x-tex","file/iana/text/x-tex"]
},
"doc/chemistry": { //GPL!! https://github.com/aeris-data/ChemDoodle/tree/master/ChemDoodleWeb-8.0.0 or https://github.com/aseevia/smiles-3d-vue
"ng:crdt": "YText",
"ng:n": "Chemical",
"ng:a": "simplified molecular-input line-entry system (SMILES)",
"ng:compat": ["file/iana/chemical/x-daylight-smiles"] // https://en.wikipedia.org/wiki/SYBYL_line_notation and http://fileformats.archiveteam.org/wiki/Chemical_data
},
"doc/ancientscript": { //https://dn-works.com/ufas/
"ng:crdt": "YText", // use Unicode and special fonts
"ng:n": "Ancient Script",
"ng:a": "Ancient Script",
"ng:compat": []
},
"doc/braille": { //https://en.wikipedia.org/wiki/Braille_Patterns
"ng:crdt": "YText", // use Unicode and special fonts
"ng:n": "Braille Patterns",
"ng:a": "Braille Patterns",
"ng:compat": []
},
"media/image": {
"ng:crdt": "Graph",
"ng:n": "Image",
"ng:a": "upload and display an image",
"ng:o": "n:g:z:media",
"ng:x": {
"as":true,
},
"ng:compat": ["file/iana/image*","as:Image"]
},
"media/reel": {
"ng:crdt": "Graph",
"ng:n": "Reel",
"ng:a": "upload and display a Reel (video from mobile)",
"ng:o": "n:g:z:media",
"ng:compat": ["file/iana/video*"]
},
"media/video": {
"ng:crdt": "Graph",
"ng:n": "Video",
"ng:a": "upload and display a Video (and film)",
"ng:o": "n:g:z:media",
"ng:x": {
"as":true,
},
"ng:compat": ["file/iana/video*","as:Video"]
},
"media/album": {
"ng:crdt": "Graph",
"ng:n": "Album",
"ng:a": "Assemble several images and/or videos into an ordered Album",
"ng:o": "n:g:z:gallery",
"ng:include": ["data/collection"],
"ng:compat": []
},
"media/audio": {
"ng:crdt": "Graph",
"ng:n": "Audio",
"ng:a": "upload and play an Audio file, Audio note or Voice message",
"ng:o": "n:g:z:media",
"ng:x": {
"as":true,
},
"ng:compat": ["file/iana/audio*","as:Audio"]
},
"media/song": {
"ng:crdt": "Graph",
"ng:n": "Song",
"ng:a": "A song from an artist,album and/or lyrics",
"ng:o": "n:g:z:media",
"ng:x": {
"music": "http://purl.org/ontology/mo/",
},
"ng:compat": ["music:MusicalWork","music:MusicArtist", "music:Lyrics"]
// see also https://polifonia-project.eu/wp-content/uploads/2022/01/Polifonia_D2.1_V1.0.pdf
// Music ontology http://musicontology.com/docs/faq.html with data based on existing databases https://musicbrainz.org/doc/MusicBrainz_Database/Schema https://github.com/megaconfidence/open-song-database https://www.discogs.com/developers
},
"media/subtitle": { //https://captioneasy.com/subtitle-file-formats/
"ng:crdt": "YText",
"ng:n": "Subtitles",
"ng:a": "Subtitles",
"ng:compat": [] // TBD
},
"media/overlay": {
"ng:crdt": "Graph",
"ng:n": "Overlay",
"ng:a": "Composition of an image, reel, text, icon, link, mention or other content into a layered content",
"ng:o": "n:g:z:media",
"ng:compat": []
},
"social/channel": {
"ng:crdt": "Graph",
"ng:n": "Channel",
"ng:a": "Broadcast channel with subscribers",
"ng:compat": []
},
"social/stream": {
"ng:crdt": "Graph",
"ng:n": "Stream",
"ng:a": "A document or store's stream branch",
"ng:compat": []
},
"social/contact": {
"ng:crdt": "Graph",
"ng:n": "Contact",
"ng:a": "Contact: an Individual, Organization or Group",
"ng:x": {
"vcard":true,
"foaf": true,
},
"ng:include": ["data/graph"],
"ng:compat": ["foaf:Person","foaf:Agent","vcard:Individual", "vcard:Organization", "vcard:Group", "file/iana/text/vcard", "file/iana/application/vcard+json", "file/iana/application/vcard+xml" ],
},
"social/event": {
"ng:crdt": "Graph",
"ng:n": "Event",
"ng:a": "An event occuring in specific location and time",
"ng:x": {
"as":true,
},
"ng:include": ["post/*"],
"ng:compat": ["as:Event"]
},
"social/calendar": {
"ng:crdt": "Graph",
"ng:n": "Calendar",
"ng:a": "A calendar where events are gathered",
"ng:x": {
"as":true,
"time": true,
},
"ng:include": ["data/collection"],
"ng:compat": ["time:TemporalEntity", "file/iana/text/calendar", "file/iana/application/calendar+xml", "file/iana/application/calendar+json"] //https://www.rfc-editor.org/rfc/rfc5545
},
"social/scheduler": {
"ng:crdt": "Graph",
"ng:n": "Scheduler",
"ng:a": "Helps finding a common time slot for several participants to a future event",
"ng:x": {
"as":true,
},
"ng:compat": ["as:Invite","as:Reject","as:Accept","as:TentativeAccept","as:TentativeReject"]
},
"social/reaction": {
"ng:crdt": "Graph",
"ng:n": "Reaction",
"ng:a": "A reaction by user to some content",
"ng:x": {
"as":true,
},
"ng:compat": ["as:Like", "as:Dislike", "as:Listen", "as:Read", "as:View"]
},
"prod/task": {
"ng:crdt": "Graph",
"ng:n": "Task",
"ng:a": "A task to be done",
"ng:x": {
"as":true,
"pair": "http://virtual-assembly.org/ontologies/pair#",
},
"ng:include": ["post/*"],
"ng:compat": ["pair:Task"] //see VTODO in iCalendar https://www.cs.utexas.edu/~mfkb/RKF/tree/components/specs/ontologies/Calendar-onto.html
// see todo and todoList of Mobilizon https://framagit.org/framasoft/mobilizon/-/blob/main/lib/federation/activity_stream/converter/todo.ex
// https://framagit.org/framasoft/mobilizon/-/blob/main/lib/federation/activity_stream/converter/todo_list.ex
},
"prod/project": {
"ng:crdt": "Graph",
"ng:n": "Project",
"ng:a": "A project management / KanBan",
"ng:x": {
"as":true,
"pair": "http://virtual-assembly.org/ontologies/pair#",
},
"ng:include": ["post/*"],
"ng:compat": ["pair:Project"]
},
// see SRO https://www.researchgate.net/publication/350158531_From_a_Scrum_Reference_Ontology_to_the_Integration_of_Applications_for_Data-Driven_Software_Development
// https://ceur-ws.org/Vol-1442/paper_4.pdf
// see focalbaord, specially for their import scripts https://github.com/mattermost/focalboard/tree/main/import
// and their data model https://github.com/mattermost/focalboard/tree/main/server/model
// https://github.com/leif81/bzkanban
// https://github.com/HigorLoren/donko (react)
// https://github.com/trobonox/kanri (GPL, Vue)
// https://github.com/waterrmalann/kards (vanilla JS)
// see also https://github.com/wekan/wekan
// see also https://taiga.io/ (for inspiration. as it is AGPL and python)
// see also https://github.com/plankanban/planka (for inspiration. as it is AGPL and React)
// see also https://kolaente.dev/vikunja/vikunja (for inspiration. AGPL. Vue and Go)
// see also https://github.com/laurent22/joplin/ (for inspiration. AGPL)
// see also https://github.com/SrGMC/kanbana
/// svelte: https://github.com/V-Py/svelte-kanban
// https://github.com/supabase-community/svelte-kanban
// https://github.com/therosbif/kanban
"prod/issue": {
"ng:crdt": "Graph",
"ng:n": "Issue",
"ng:a": "An issue to be solved",
"ng:x": {
"as":true,
"pair": "http://virtual-assembly.org/ontologies/pair#",
},
"ng:include": ["prod/task"],
"ng:compat": ["pair:Challenge"]
},
//https://github.com/go-gitea/gitea/issues/20232
// datamodel of gitea issues: https://github.com/go-gitea/gitea/blob/165346c15c6d021028a65121e692a17ffc927e2c/models/issue.go#L35-L79
"prod/form": {
"ng:crdt": "Graph",
"ng:n": "Form",
"ng:a": "A form to be filled-in",
"ng:x": {
"form" : "http://rdf.danielbeeke.nl/form/form-dev.ttl#",
},
"ng:compat": ["form:*","file/iana/application/schema+json"]
},
// https://jsonforms.io/docs/
// https://github.com/jsonform/jsonform
// https://jsonforms.io/docs/integrations/vue
// >>> https://github.com/json-editor/json-editor
// or >>> https://github.com/webgme/svelte-jsonschema-form
// or >>> https://github.com/restspace/svelte-schema-form
// see https://ceur-ws.org/Vol-1515/regular14.pdf
// and https://github.com/protegeproject/facsimile
// https://www.drupal.org/project/webform
// see https://www.semantic-mediawiki.org/wiki/Extension:Page_Forms
// https://www.mediawiki.org/wiki/Extension:Page_Forms
// https://rdf-form.danielbeeke.nl/
// consider using Shapes
"prod/filling": {
"ng:crdt": "Graph",
"ng:n": "Form filling",
"ng:a": "A form that has been filled-in",
"ng:compat": []
},
"prod/cad": { // https://mattferraro.dev/posts/cadmium
"ng:crdt": "Automerge",
"ng:n": "CAD",
"ng:a": "CADmium",
"ng:compat": []
},
"prod/slides": { //https://github.com/hakimel/reveal.js
//https://pandoc.org/MANUAL.html#slide-shows
"ng:crdt": "Graph",
"ng:n": "Slides",
"ng:a": "Slides and presentations",
"ng:include": ["post/*"],
"ng:compat": []
},
"prod/question" : {
"ng:crdt": "Graph",
"ng:n": "Question",
"ng:a": "A question that needs answers",
"ng:x": {
"as":true,
},
"ng:include": ["post/*"],
"ng:compat": ["as:Question"]
},
"prod/answer" :{
"ng:crdt": "Graph",
"ng:n": "Answer",
"ng:a": "An answer to a question",
"ng:x": {
"as":true,
},
"ng:include": ["post/*"],
"ng:compat": ["as:Note"]
},
"prod/poll" : {
"ng:crdt": "Graph",
"ng:n": "Poll",
"ng:a": "A poll where people will vote",
"ng:x": {
"as":true,
},
"ng:include": ["post/*"],
"ng:compat": ["as:Question"]
},
"prod/vote" : {
"ng:crdt": "Graph",
"ng:n": "Vote",
"ng:a": "A vote cast for a Poll",
"ng:x": {
"as":true,
},
"ng:compat": ["as:Note"]
},
"file" : {
"ng:crdt": "Graph",
"ng:n": "File",
"ng:a": "Binary file",
"ng:o": "n:g:z:file_viewer",
"ng:w": "n:g:z:file_viewer",
"ng:compat": []
},
"file/ng/wallet" : {
"ng:n": "NextGraph Wallet File",
"ng:a": "NextGraph Wallet File (.ngw)",
"ng:compat": []
},
"file/ng/doc" : {
"ng:n": "NextGraph Document File",
"ng:a": "NextGraph Document File (.ngd)",
"ng:compat": []
},
"file/ng/html" : {
"ng:n": "NextGraph Document Html",
"ng:a": "NextGraph Document Html standalone file",
"ng:compat": []
},
"file/text" : {
"ng:crdt": "Graph",
"ng:n": "File",
"ng:a": "Text file",
"ng:o": "n:g:z:file_viewer",
"ng:w": "n:g:z:file_viewer",
"ng:compat": ["file/iana/text/*", "image/svg+xml", "file/iana/application/n-quads", "file/iana/application/trig", "file/iana/application/n-triples", "file/iana/application/rdf+xml", "file/iana/application/ld+json",
"file/iana/application/xml", "file/iana/application/yaml", "file/iana/application/xhtml+xml", "file/iana/application/node","file/iana/application/sparql-results+json","file/iana/application/sparql-results+xml",
"file/iana/message/rfc822","file/iana/multipart/related", "file/iana/text/vnd.graphviz", "file/iana/application/vnd.excalidraw+json", "file/iana/application/x-tex","file/iana/text/x-tex",
"file/iana/application/vcard+json", "file/iana/application/vcard+xml", "file/iana/text/calendar", "file/iana/application/calendar+xml", "file/iana/application/calendar+json",
"file/iana/application/schema+json", "file/iana/application/geo+json", "file/iana/application/json" ]
},
};

@ -9,9 +9,92 @@
// according to those terms. // according to those terms.
--> -->
<script lang="ts">
import ng from "../api";
import { onMount, tick } from "svelte";
import { current_lang, available_languages } from "../store";
import { Language } from "svelte-heros-v2";
export let displayFooter = false;
let changingLang = false;
const changeLang = () => {
changingLang = true;
scrollToTop();
};
let top;
function scrollToTop() {
top.scrollIntoView();
}
const selectLang = async (lang) => {
current_lang.set(lang);
changingLang = false;
await tick();
scrollToTop();
};
let tauri_platform = import.meta.env.TAURI_PLATFORM;
const displayPopup = async (url, title) => {
if (!tauri_platform || tauri_platform == "android") {
window.open(url, "_blank").focus();
} else {
await ng.open_window(url, "viewer", title);
}
};
const displayNextgraphOrg = async () => {
await displayPopup("https://nextgraph.org", "NextGraph.org");
};
</script>
<div bind:this={top}>
{#if !changingLang}
<div class="centered"> <div class="centered">
<slot /> <slot />
</div> </div>
{#if displayFooter}
<div class="centered">
<div class="mb-20 mt-10">
<button
on:click={changeLang}
class="text-primary-700 bg-[#f6f6f6] bg-none ring-0 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55"
>
<Language
tabindex="-1"
class="w-7 h-7 mr-2 transition duration-75 "
/>Change language <!--note to translator: DO NOT TRANSLATE! it should stay in english always-->
</button>
<br />
<button
on:click={displayNextgraphOrg}
class="text-primary-700 bg-[#f6f6f6] bg-none ring-0 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>About NextGraph
</button>
</div>
</div>
{/if}
{:else}
<div class="centered">
<ul class="mb-20 mt-10">
{#each Object.entries(available_languages) as lang}
<li
tabindex="0"
role="menuitem"
class="flex items-center p-2 text-lg mb-2 font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
on:keypress={() => selectLang(lang[0])}
on:click={() => selectLang(lang[0])}
>
<span class="mx-3">{lang[1]}</span>
</li>
{/each}
</ul>
</div>
{/if}
</div>
<style> <style>
.centered { .centered {
@ -21,4 +104,7 @@
text-align: center; text-align: center;
width: fit-content; width: fit-content;
} }
li.clickable {
cursor: pointer;
}
</style> </style>

@ -0,0 +1,187 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import {
Icon,
BugAnt,
DocumentText,
Window,
CodeBracket,
SquaresPlus,
ViewfinderCircle,
ArrowsPointingOut,
Cube,
Briefcase,
MagnifyingGlass,
RocketLaunch,
Sun,
TableCells,
ListBullet,
RectangleGroup,
Squares2x2,
MapPin,
CircleStack,
Envelope,
GlobeAlt,
DocumentChartBar,
Document,
ClipboardDocumentList,
Photo,
Film,
RectangleStack,
Microphone,
MusicalNote,
Ticket,
CursorArrowRays,
Megaphone,
User,
Clock,
CalendarDays,
Calendar,
Stop,
Flag,
HandRaised,
Newspaper,
PencilSquare,
CubeTransparent,
PresentationChartBar,
QuestionMarkCircle,
CheckCircle,
ChartPie,
Bars3BottomLeft,
Link,
Square2Stack,
Clipboard,
StopCircle,
Bolt,
Heart,
} from "svelte-heros-v2";
export let config = {};
export let dataClass: string;
const exact_mapping = {
page: Window,
"app/z": SquaresPlus,
class: ViewfinderCircle,
contract: Briefcase,
"query/text": MagnifyingGlass,
"query/web": MagnifyingGlass,
"data/graph": Sun,
"data/table": TableCells,
"data/collection": ListBullet,
"data/board": RectangleGroup,
"data/grid": Squares2x2,
"data/geomap": MapPin,
"e/email": Envelope,
"mc/text": Bars3BottomLeft,
"mc/link": Link,
"plato/card": Clipboard,
"plato/pad": Square2Stack,
"media/image": Photo,
"media/reel": Film,
"media/video": Film,
"media/album": RectangleStack,
"media/audio": Microphone,
"media/song": MusicalNote,
"media/subtitle": Ticket,
"media/overlay": CursorArrowRays,
"social/channel": Megaphone,
"social/stream": Bolt,
"social/contact": User,
"social/event": Clock,
"social/calendar": CalendarDays,
"social/scheduler": Calendar,
"social/reaction": Heart,
"prod/task": Stop,
"prod/project": Flag,
"prod/issue": HandRaised,
"prod/form": Newspaper,
"prod/filling": PencilSquare,
"prod/cad": CubeTransparent,
"prod/slides": PresentationChartBar,
"prod/question": QuestionMarkCircle,
"prod/answer": CheckCircle,
"prod/poll": QuestionMarkCircle,
"prod/vote": CheckCircle,
};
const prefix_mapping = {
"post/": DocumentText,
code: CodeBracket,
schema: ArrowsPointingOut,
service: Cube,
"e/": GlobeAlt,
"app/": StopCircle,
"query/": RocketLaunch,
"data/": CircleStack,
"doc/diagram": DocumentChartBar,
"doc/chart": ChartPie,
"doc/viz": ChartPie,
"doc/": ClipboardDocumentList,
file: Document,
};
const find = (t) => {
let e = exact_mapping[t];
if (e) return e;
for (let prefix of Object.entries(prefix_mapping)) {
if (t.startsWith(prefix[0])) return prefix[1];
}
return BugAnt;
};
</script>
<!--
did:ng:n:g:z:[official apps]
did:ng:n:g:ns
did:ng:n:g:x list of context used by nextgraph
rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
rdfs: http://www.w3.org/2000/01/rdf-schema#
schema: https://schema.org/
skos: http://www.w3.org/2004/02/skos/core#
owl: http://www.w3.org/2002/07/owl#
foaf: http://xmlns.com/foaf/0.1/
relationship: http://purl.org/vocab/relationship/
dcterms: http://purl.org/dc/terms/
dcmitype: http://purl.org/dc/dcmitype/
sh: http://www.w3.org/ns/shacl#
shex: http://www.w3.org/ns/shex#
xsd: http://www.w3.org/2001/XMLSchema#
as: https://www.w3.org/ns/activitystreams#
ldp: http://www.w3.org/ns/ldp#
vcard: http://www.w3.org/2006/vcard/ns#
sec: https://w3id.org/security#
wgs: http://www.w3.org/2003/01/geo/wgs84_pos#
cc: http://creativecommons.org/ns#
gn: https://www.geonames.org/ontology#
geo: http://www.opengis.net/ont/geosparql#
time: http://www.w3.org/2006/time#
ng: did:ng:n:g:ns# or http://nextgraph.org/ns#
did:ng:n:g:ns#post/rich
ng:class => shortcut for did:ng:n:g:ns#class
a rdfs:Class
a ng:class
did:ng:o:xxxx:yy:yy
did:ng:n:xx.xx#name
did:ng:n:x: curated list of ontologies
did:ng:k common list of things (keyword)
did:ng:n:c common data
did:ng:n:z: curated list of external apps and services
http://nextgraph.org/ns# => the ng: ontology (did:ng:n:g:ns#)
ng:compat -> owl:unionOf rdf:List (alphabetical order, including itself as first element)
-->
<Icon {...config} variation="outline" color="black" icon={find(dataClass)} />

@ -21,6 +21,8 @@
import MobileBottomBar from "./MobileBottomBar.svelte"; import MobileBottomBar from "./MobileBottomBar.svelte";
import Logo from "./components/Logo.svelte"; import Logo from "./components/Logo.svelte";
import { connection_status } from "../store";
import { onMount, tick } from "svelte"; import { onMount, tick } from "svelte";
import { import {
@ -87,9 +89,9 @@
<div class="full-layout"> <div class="full-layout">
<Sidebar {activeUrl} {asideClass} {nonActiveClass} class="fixed"> <Sidebar {activeUrl} {asideClass} {nonActiveClass} class="fixed">
<SidebarWrapper <SidebarWrapper
divClass="bg-gray-60 overflow-y-auto tall:py-4 px-3 rounded dark:bg-gray-800" divClass="bg-gray-60 overflow-y-auto tall-xs:py-4 px-3 rounded dark:bg-gray-800"
> >
<SidebarGroup ulClass="space-y-1 tall:space-y-2"> <SidebarGroup ulClass="space-y-1 tall-xs:space-y-2">
<SidebarItem label="NextGraph" href="#/user" class="mt-1"> <SidebarItem label="NextGraph" href="#/user" class="mt-1">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Logo className="w-7 h-7 tall:w-10 tall:h-10" /> <Logo className="w-7 h-7 tall:w-10 tall:h-10" />
@ -99,60 +101,64 @@
label="Home" label="Home"
href="#/" href="#/"
on:click={scrollToTop} on:click={scrollToTop}
class="py-1 tall:p-2" class="py-1 tall-xs:p-2"
> >
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Home <Home
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Stream" href="#/stream" class="py-1 tall:p-2"> <SidebarItem label="Stream" href="#/stream" class="py-1 tall-xs:p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Bolt <Bolt
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Search" href="#/search" class="py-1 tall:p-2"> <SidebarItem label="Search" href="#/search" class="py-1 tall-xs:p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<MagnifyingGlass <MagnifyingGlass
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Create" href="#/create" class="py-1 tall:p-2"> <SidebarItem label="Create" href="#/create" class="py-1 tall-xs:p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<PlusCircle <PlusCircle
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Shared" href="#/shared" class="py-1 tall:p-2"> <SidebarItem label="Shared" href="#/shared" class="py-1 tall-xs:p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Users <Users
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Site" href="#/site" class="py-1 tall:p-2"> <SidebarItem label="Site" href="#/site" class="py-1 tall-xs:p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<User <User
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Messages" href="#/messages" class="py-1 tall:p-2"> <SidebarItem
label="Messages"
href="#/messages"
class="py-1 tall-xs:p-2"
>
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<PaperAirplane <PaperAirplane
tabindex="-1" tabindex="-1"
class="-rotate-45 w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="-rotate-45 w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span <span
class="inline-flex justify-center items-center p-3 mt-1 -ml-3 w-3 h-3 text-sm font-medium text-primary-600 bg-primary-200 rounded-full dark:bg-primary-900 dark:text-primary-200" class="inline-flex justify-center items-center p-3 mt-1 -ml-3 w-3 h-3 text-sm font-medium text-primary-600 bg-primary-200 rounded-full dark:bg-primary-900 dark:text-primary-200"
@ -164,12 +170,12 @@
<SidebarItem <SidebarItem
label="Notifications" label="Notifications"
href="#/notifications" href="#/notifications"
class="mt-1 py-1 tall:p-2" class="mt-1 py-1 tall-xs:p-2"
> >
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Bell <Bell
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span <span
class="inline-flex justify-center items-center p-3 mt-1 -ml-3 w-3 h-3 text-sm font-medium text-primary-600 bg-primary-200 rounded-full dark:bg-primary-900 dark:text-primary-200" class="inline-flex justify-center items-center p-3 mt-1 -ml-3 w-3 h-3 text-sm font-medium text-primary-600 bg-primary-200 rounded-full dark:bg-primary-900 dark:text-primary-200"

@ -41,7 +41,7 @@
<a href="#/user" class="flex items-center" on:click> <a href="#/user" class="flex items-center" on:click>
<Logo className="w-7 h-7 tall:w-10 tall:h-10" /> <Logo className="w-7 h-7 tall:w-10 tall:h-10" />
<span <span
class="ml-4 self-center text-lg font-normal text-gray-900 rounded-lg dark:text-white whitespace-nowrap" class="ml-2 self-center text-lg font-normal text-gray-900 rounded-lg dark:text-white whitespace-nowrap"
>NextGraph</span >NextGraph</span
> >
</a> </a>
@ -52,7 +52,7 @@
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none" class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none"
/> />
</a> </a>
<a href="#/messages" class="ml-6 row items-center" on:click> <a href="#/messages" class="ml-4 row items-center" on:click>
<PaperAirplane <PaperAirplane
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none" class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none"

File diff suppressed because one or more lines are too long

@ -9,24 +9,43 @@
// according to those terms. // according to those terms.
--> -->
<!--
The Login Procedure.
Has multiple states (steps) through the user flow.
-->
<script lang="ts"> <script lang="ts">
import { Alert, Toggle } from "flowbite-svelte"; import { Alert, Toggle, Button } from "flowbite-svelte";
import { onMount, createEventDispatcher, tick } from "svelte"; import { onMount, createEventDispatcher, tick } from "svelte";
import ng from "../api"; import ng from "../api";
import { emoji_cat, emojis, load_svg } from "../wallet_emojis"; import { emoji_cat, emojis, load_svg } from "../wallet_emojis";
import { PuzzlePiece } from "svelte-heros-v2"; import {
PuzzlePiece,
XCircle,
Backspace,
ArrowPath,
LockOpen,
Key,
CheckCircle,
ArrowLeft,
} from "svelte-heros-v2";
import PasswordInput from "./components/PasswordInput.svelte";
//import Worker from "../worker.js?worker&inline"; //import Worker from "../worker.js?worker&inline";
export let wallet; export let wallet;
export let for_import = false; export let for_import = false;
let top;
function scrollToTop() {
top.scrollIntoView();
}
let tauri_platform = import.meta.env.TAURI_PLATFORM; let tauri_platform = import.meta.env.TAURI_PLATFORM;
let mobile = tauri_platform == "android" || tauri_platform == "ios";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
onMount(async () => { onMount(async () => {
loaded = false; loaded = false;
await load_svg(); load_svg();
//console.log(wallet); //console.log(wallet);
await init(); await init();
}); });
@ -46,16 +65,28 @@
} }
emojis2 = emojis2; emojis2 = emojis2;
display = 0; pazzlePage = 0;
selection = []; selection = [];
error = undefined; error = undefined;
scrollToTop();
// This is only for awaiting that SVGs are loaded.
await load_svg();
loaded = true; loaded = true;
} }
function letsgo() { function start_with_pazzle() {
loaded = false; loaded = false;
step = "pazzle"; step = "pazzle";
unlockWith = "pazzle";
scrollToTop();
}
function start_with_mnemonic() {
loaded = false;
step = "mnemonic";
unlockWith = "mnemonic";
scrollToTop();
} }
let emojis2 = []; let emojis2 = [];
@ -68,29 +99,33 @@
let pazzle_length = 9; let pazzle_length = 9;
let display = 0; let pazzlePage = 0;
let selection = []; /** The selected emojis by category (one for each pazzle page). First will be the selected of first pazzle page. */
let selection = [].fill(null, 0, pazzle_length);
let pin_code = []; let pin_code = [];
/** The selected order from the order page. */
let ordered = []; let ordered = [];
let last_one = {};
let shuffle_pin; let shuffle_pin;
let error; let error;
let trusted = false; let trusted = true;
let mnemonic = "";
let unlockWith: "pazzle" | "mnemonic" | undefined;
function order() { function order() {
step = "order"; step = "order";
ordered = []; ordered = [];
last_one = {}; // In case, this is called by the cancel button, we need to reset the selection.
for (let i = 0; i < pazzle_length; i++) { selection.forEach((emoji) => (emoji.sel = undefined));
last_one[i] = true; selection = selection;
} scrollToTop();
} }
async function start_pin() { async function start_pin() {
@ -101,20 +136,19 @@
//console.log(shuffle_pin); //console.log(shuffle_pin);
} }
/** Called on selecting emoji in a category. */
function select(val) { function select(val) {
//console.log(emojis2[display][val]); //console.log(emojis2[display][val]);
let cat_idx = shuffle.category_indices[display]; let cat_idx = shuffle.category_indices[pazzlePage];
let cat = emojis[emoji_cat[cat_idx]]; let cat = emojis[emoji_cat[cat_idx]];
let idx = shuffle.emoji_indices[display][val]; let idx = shuffle.emoji_indices[pazzlePage][val];
//console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code);
selection.push({ cat: cat_idx, index: idx }); selection[pazzlePage] = { cat: cat_idx, index: idx };
//console.log(selection);
if (display == pazzle_length - 1) { if (pazzlePage == pazzle_length - 1) {
order(); order();
} else { } else {
display = display + 1; pazzlePage = pazzlePage + 1;
} }
} }
@ -122,20 +156,21 @@
step = "opening"; step = "opening";
let pazzle = []; let pazzle = [];
for (const emoji of ordered) { for (const emoji of ordered) {
pazzle.push((emoji.cat << 4) + emoji.index); pazzle.push((emoji.cat << 4) + emoji.index);
} }
//console.log(pazzle); const mnemonic_words = mnemonic.split(" ");
//console.log(wallet);
// open the wallet // open the wallet
try { try {
if (tauri_platform) { if (tauri_platform) {
let opened_wallet = await ng.wallet_open_with_pazzle( let opened_wallet =
unlockWith === "pazzle"
? await ng.wallet_open_with_pazzle(wallet, pazzle, pin_code)
: await ng.wallet_open_with_mnemonic_words(
wallet, wallet,
pazzle, mnemonic_words,
pin_code pin_code
); );
// try { // try {
@ -172,7 +207,11 @@
myWorker.onmessage = async (msg) => { myWorker.onmessage = async (msg) => {
//console.log("Message received from worker", msg.data); //console.log("Message received from worker", msg.data);
if (msg.data.loaded) { if (msg.data.loaded) {
if (unlockWith === "pazzle") {
myWorker.postMessage({ wallet, pazzle, pin_code }); myWorker.postMessage({ wallet, pazzle, pin_code });
} else {
myWorker.postMessage({ wallet, mnemonic_words, pin_code });
}
//console.log("postMessage"); //console.log("postMessage");
} else if (msg.data.success) { } else if (msg.data.success) {
//console.log(msg.data); //console.log(msg.data);
@ -214,23 +253,17 @@
dispatch("cancel"); dispatch("cancel");
} }
async function pin(val) { async function on_pin_key(val) {
//console.log(val); pin_code = [...pin_code, val];
pin_code.push(val);
if (pin_code.length == 4) {
await finish();
}
} }
async function select_order(val, pos) { async function select_order(val) {
delete last_one[pos];
//console.log(last_one);
//console.log(val);
ordered.push(val); ordered.push(val);
val.sel = ordered.length; val.sel = ordered.length;
selection = selection; selection = selection;
if (ordered.length == pazzle_length - 1) { if (ordered.length == pazzle_length - 1) {
let last = selection[Object.keys(last_one)[0]]; let last = selection.find((emoji) => !emoji.sel);
ordered.push(last); ordered.push(last);
last.sel = ordered.length; last.sel = ordered.length;
selection = selection; selection = selection;
@ -238,12 +271,72 @@
await start_pin(); await start_pin();
} }
} }
function go_back() {
if (step === "mnemonic") {
init();
} else if (step === "pazzle") {
// Go to previous pazzle or init page, if on first pazzle.
if (pazzlePage === 0) {
init();
} else {
pazzlePage -= 1;
}
} else if (step === "order") {
if (ordered.length === 0) {
step = "pazzle";
} else {
const last_selected = ordered.pop();
last_selected.sel = null;
ordered = ordered;
selection = selection;
}
} else if (step === "pin") {
if (pin_code.length === 0) {
if (unlockWith === "mnemonic") {
start_with_mnemonic();
} else {
// Unselect the last two elements.
const to_unselect = ordered.slice(-2);
to_unselect.forEach((val) => {
val.sel = null;
});
ordered = ordered.slice(0, -2);
selection = selection;
step = "order";
}
} else {
pin_code = pin_code.slice(0, pin_code.length - 1);
}
}
}
let width: number;
let height: number;
const breakPointWidth: number = 535;
const breakPointHeight: number = 1005;
let mobile = false;
$: if (width >= breakPointWidth && height >= breakPointHeight) {
mobile = false;
} else {
mobile = true;
}
</script> </script>
<div
class="flex-col justify-center md:max-w-2xl py-4 sm:px-8"
class:h-screen={step !== "load" && height > 660}
class:flex={height > 660}
bind:this={top}
>
{#if step == "load"} {#if step == "load"}
<div class=" max-w-xl lg:px-8 mx-auto px-4 mt-10"> <div class="flex flex-col justify-center p-4 pt-6">
<h2 class="pb-5 text-xl">How to open your wallet, step by step :</h2> <h2 class="pb-5 text-xl self-start">
<ul class="mb-8 ml-3 space-y-4 text-left list-decimal"> How to open your wallet? You have 2 options:
</h2>
<h3 class="pb-2 text-lg self-start">With your Pazzle</h3>
<ul class="mb-8 ml-3 space-y-4 text-justify text-sm list-decimal">
<li> <li>
For each one of the 9 categories of images, you will be presented with For each one of the 9 categories of images, you will be presented with
the 15 possible image choices. The categories are shuffled at every the 15 possible image choices. The categories are shuffled at every
@ -251,35 +344,68 @@
</li> </li>
<li> <li>
At each category, only one of the 15 displayed choices is the correct At each category, only one of the 15 displayed choices is the correct
image that belongs to your pazzle. Find it and tap or click on that one. image that belongs to your pazzle. Find it and tap or click on that
The 15 images are shuffled too, they will not appear at the same one. The 15 images are shuffled too, they will not appear at the same
position at each login. On a computer, you can also use the tab key on position at each login. On a computer, you can also use the tab key on
your keyboard to move to the desired item on the screen, then press the your keyboard to move to the desired item on the screen, then press
space bar to select each one. the space bar to select each one.
</li> </li>
<li> <li>
Once you completed the last category, you will be presented with all the Once you completed the last category, you will be presented with all
images you have previously selected. Their order is displayed as it was the images you have previously selected. Their order is displayed as
when you picked them. But this is not the correct order of the images in it was when you picked them. But this is not the correct order of the
your pazzle. You now have to order them correctly. images in your pazzle. You now have to order them correctly.
</li> </li>
<li> <li>
You must remember which image should be the first one in your pazzle. You must remember which image should be the first one in your pazzle.
Find it on the screen and click or tap on it. It will be greyed out and Find it on the screen and click or tap on it. It will be greyed out
the number 1 will appear on top of it. and the number 1 will appear on top of it.
</li> </li>
<li> <li>
Move on to the second image of your pazzle (that you memorized). Find it Move on to the second image of your pazzle (that you memorized). Find
on the screen and tap on it. Repeat this step until you reached the last it on the screen and tap on it. Repeat this step until you reached the
image. last image.
</li> </li>
<li> <li>
Finally, your PIN code will be asked. enter it by clicking or tapping on Finally, your PIN code will be asked. enter it by clicking or tapping
the digits. on the digits.
</li>
</ul>
<h3 class="pb-2 text-lg self-start">
With your 12 words Mnemonic (passphrase)
</h3>
<ul class="mb-8 ml-3 space-y-4 text-justify text-sm list-decimal">
<li>
Enter your twelve words mnemonic in the input field. The words must be
separated by spaces.
</li> </li>
<li>Enter the PIN code that you chose when you created your wallet.</li>
</ul> </ul>
<!-- Save wallet? -->
{#if for_import}
<div class="max-w-xl lg:px-8 mx-auto px-4 mb-2">
<span class="text-xl">Do you trust this device? </span> <br />
<p class="text-sm">
If you do, if this device is yours or is used by few trusted persons
of your family or workplace, and you would like to login again from
this device in the future, then you can save your wallet on this
device. To the contrary, if this device is public and shared by
strangers, do not save your wallet here. {#if !tauri_platform}By
selecting this option, you agree to saving some cookies on your
browser.{/if}<br />
</p>
<div class="flex justify-center items-center my-4">
<Toggle class="" bind:checked={trusted}
>Yes, save my wallet on this device</Toggle
>
</div> </div>
</div>
{/if}
<div class=" max-w-xl lg:px-8 mx-auto px-4 text-primary-700"> <div class=" max-w-xl lg:px-8 mx-auto px-4 text-primary-700">
<div class="flex flex-col justify-centerspace-x-12 mt-4 mb-4">
{#if !loaded} {#if !loaded}
Loading pazzle... Loading pazzle...
<svg <svg
@ -305,46 +431,88 @@
</svg> </svg>
{:else} {:else}
<button <button
on:click={letsgo} on:click={start_with_pazzle}
class="mt-1 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" class="mt-1 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
> >
<PuzzlePiece <PuzzlePiece
tabindex="-1" tabindex="-1"
class="w-8 h-8 mr-2 -ml-1 transition duration-75 group-hover:text-gray-900 dark:group-hover:text-white" class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white"
/> />
Open my wallet now! Open with Pazzle!
</button> </button>
{/if} {/if}
</div> <button
{#if for_import} on:click={cancel}
<div class=" max-w-xl lg:px-8 mx-auto px-4 mb-8"> class="mt-3 mb-2 text-gray-500 dark:text-gray-400 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55"
<span class="text-xl">Do you trust this device? </span> <br /> ><ArrowLeft
<div class="flex justify-center items-center my-4"> tabindex="-1"
<Toggle class="" bind:checked={trusted} class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white"
>Yes, save my wallet on this device</Toggle />Cancel login</button
>
<span
on:click={start_with_mnemonic}
on:keypress={start_with_mnemonic}
role="link"
tabindex="0"
class="mt-1 text-lg px-5 py-2.5 text-center inline-flex items-center mb-2 underline cursor-pointer"
> >
Open with Mnemonic instead
</span>
</div> </div>
<p class="text-sm">
If you do, if this device is yours or is used by few trusted persons of
your family or workplace, and you would like to login again from this
device in the future, then you can save your wallet on this device. To
the contrary, if this device is public and shared by strangers, do not
save your wallet here. {#if !tauri_platform}By selecting this option,
you agree to save some cookies on your browser.{/if}<br />
</p>
</div> </div>
{/if} </div>
{:else if step == "pazzle"} <!-- The following steps have navigation buttons and fixed layout -->
{:else if step == "pazzle" || step == "order" || step == "pin" || step == "mnemonic"}
<div <div
class="h-screen aspect-[3/5] pazzleline" class="flex-col justify-center h-screen"
class:flex={height > 660}
class:min-w-[310px]={mobile} class:min-w-[310px]={mobile}
class:min-w-[500px]={!mobile} class:min-w-[500px]={!mobile}
class:max-w-[360px]={mobile} class:max-w-[370px]={mobile}
class:max-w-[600px]={!mobile} class:max-w-[600px]={!mobile}
> >
<div class="mt-auto flex flex-col justify-center">
<!-- Unlock Screens -->
{#if step == "mnemonic"}
<form on:submit|preventDefault={start_pin}>
<label
for="mnemonic-input"
class="block mb-2 text-xl text-gray-900 dark:text-white"
>Enter your 12 words mnemonic</label
>
<PasswordInput
id="mnemonic-input"
placeholder="12 words separated by spaces"
bind:value={mnemonic}
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
auto_complete="mnemonic"
/>
<div class="flex">
<Button
type="submit"
class="mt-3 mb-2 ml-auto text-white bg-primary-700 disabled:opacity-65 focus:ring-4 focus:ring-blue-500 focus:border-blue-500 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-blue-500 dark:focus:border-blue-500"
on:click={start_pin}
disabled={mnemonic.split(" ").length !== 12}
><CheckCircle
tabindex="-1"
class="w-8 h-8 mr-2 -ml-1 transition duration-75 group-hover:text-gray-900 dark:group-hover:text-white"
/>Confirm</Button
>
</div>
</form>
{:else if step == "pazzle"}
<p class="max-w-xl mx-auto lg:max-w-2xl">
<span class="text-xl">
<!-- TODO: Internationalization-->
Select your emoji of category:<br />{emoji_cat[
shuffle.category_indices[pazzlePage]
]}</span
>
</p>
{#each [0, 1, 2, 3, 4] as row} {#each [0, 1, 2, 3, 4] as row}
<div class="columns-3 gap-0"> <div class="columns-3 gap-0">
{#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} {#each emojis2[pazzlePage]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i (pazzlePage + "-" + row + "-" + i)}
<div <div
role="button" role="button"
tabindex="0" tabindex="0"
@ -357,16 +525,10 @@
{/each} {/each}
</div> </div>
{/each} {/each}
</div>
{:else if step == "order"} {:else if step == "order"}
<!-- console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); --> <p class="max-w-xl mx-auto lg:max-w-2xl mb-2">
<div <span class="text-xl">Select each image in the correct order</span>
class="h-screen aspect-[3/3] pazzleline" </p>
class:min-w-[320px]={mobile}
class:min-w-[500px]={!mobile}
class:max-w-[360px]={mobile}
class:max-w-[600px]={!mobile}
>
{#each [0, 1, 2] as row} {#each [0, 1, 2] as row}
<div class="columns-3 gap-0"> <div class="columns-3 gap-0">
{#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} {#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
@ -375,53 +537,105 @@
role="button" role="button"
tabindex="0" tabindex="0"
class="w-full aspect-square emoji focus:outline-none focus:bg-gray-300" class="w-full aspect-square emoji focus:outline-none focus:bg-gray-300"
on:click={() => select_order(emoji, row * 3 + i)} on:click={() => select_order(emoji)}
on:keypress={() => select_order(emoji, row * 3 + i)} on:keypress={() => select_order(emoji)}
> >
<svelte:component <svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default} this={emojis[emoji_cat[emoji.cat]][emoji.index].svg
?.default}
/> />
</div> </div>
{:else} {:else}
<div class="w-full aspect-square opacity-25 select-none sel-emoji"> <div
class="w-full aspect-square opacity-25 select-none sel-emoji"
>
<svelte:component <svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default} this={emojis[emoji_cat[emoji.cat]][emoji.index].svg
?.default}
/> />
<span class="sel drop-shadow-[2px_2px_2px_rgba(255,255,255,1)]" <span
>{emoji.sel}</span class="sel drop-shadow-[2px_2px_2px_rgba(255,255,255,1)]"
class:text-[8em]={!mobile}
class:text-[6em]={mobile}>{emoji.sel}</span
> >
</div> </div>
{/if} {/if}
{/each} {/each}
</div> </div>
{/each} {/each}
</div>
{:else if step == "pin"} {:else if step == "pin"}
<div class=" max-w-6xl lg:px-8 mx-auto px-3"> <p class="items-center">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
<span class="text-xl">Enter your PIN code</span> <span class="text-xl">Enter your PIN code</span>
</p> </p>
<div class="w-[295px] mx-auto"> <!-- Chrome requires the columns-3 __flex__ to be set, or else it wraps the lines incorrectly.
However, this leads to the width decreasing and the buttons come together in mobile view.
So we need a way to fix the width across all screens. -->
{#each [0, 1, 2] as row} {#each [0, 1, 2] as row}
<div class=""> <div class="columns-3 flex">
{#each shuffle_pin.slice(0 + row * 3, 3 + row * 3) as num} {#each shuffle_pin.slice(0 + row * 3, 3 + row * 3) as num}
<button <button
tabindex="0" tabindex="0"
class="m-1 select-none align-bottom text-7xl w-[90px] h-[90px] p-0" class="pindigit m-1 disabled:opacity-15 disabled:text-gray-200 select-none align-bottom text-7xl p-0 w-full aspect-square border-0"
on:click={async () => await pin(num)} class:h-[160px]={!mobile}
class:h-[100px]={mobile}
class:text-8xl={!mobile}
on:click={async () => await on_pin_key(num)}
disabled={pin_code.length >= 4}
> >
<span>{num}</span> <span>{num}</span>
</button> </button>
{/each} {/each}
</div> </div>
{/each} {/each}
<div class="columns-3 flex">
<div class="m-1 w-full aspect-square" />
<button <button
tabindex="0" tabindex="0"
class="m-1 select-none mx-auto align-bottom text-7xl w-[90px] h-[90px] p-0" class="pindigit disabled:opacity-15 m-1 disabled:text-gray-200 select-none align-bottom text-7xl p-0 w-full aspect-square border-0"
on:click={async () => await pin(shuffle_pin[9])} class:h-[160px]={!mobile}
class:h-[100px]={mobile}
class:text-8xl={!mobile}
on:click={async () => await on_pin_key(shuffle_pin[9])}
disabled={pin_code.length >= 4}
> >
<span>{shuffle_pin[9]}</span> <span>{shuffle_pin[9]}</span>
</button> </button>
<Button
tabindex="0"
class="w-full bg-green-300 hover:bg-green-300/90 enabled:animate-bounce disabled:bg-gray-200 disabled:opacity-15 m-1 select-none align-bottom text-7xl p-0 aspect-square border-0"
on:click={async () => await finish()}
disabled={pin_code.length < 4}
>
<LockOpen
tabindex="-1"
class="w-[50%] h-[50%] transition duration-75 focus:outline-none select-none group-hover:text-gray-900 dark:group-hover:text-white"
/>
</Button>
</div>
<span class="mt-3 text-9xl min-h-[8rem] text-center"
>{#each pin_code as pin_key}*{/each}</span
>
{/if}
</div>
<!-- Navigation Buttons for pazzle, order pin, mnemonic -->
<div class="flex justify-between mb-6 mt-auto">
<button
on:click={cancel}
class="mt-1 bg-red-100 hover:bg-red-100/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg sm:text-lg px-5 py-2.5 text-center select-none inline-flex items-center dark:focus:ring-primary-700/55"
><XCircle
tabindex="-1"
class="w-8 h-8 mr-2 -ml-1 transition focus:outline-none duration-75 group-hover:text-gray-900 dark:group-hover:text-white"
/>Cancel</button
>
<button
class="mt-1 ml-2 min-w-[141px] focus:ring-4 focus:ring-primary-100/50 rounded-lg sm:text-lg px-5 py-2.5 text-center select-none inline-flex items-center dark:focus:ring-primary-700/55"
on:click={go_back}
><Backspace
tabindex="-1"
class="w-8 h-8 mr-2 -ml-1 transition focus:outline-none duration-75 group-hover:text-gray-900 dark:group-hover:text-white"
/>{#if step === "mnemonic" || (step === "pazzle" && pazzlePage === 0)}Go
back{:else}Correct{/if}</button
>
</div> </div>
</div> </div>
{:else if step == "opening"} {:else if step == "opening"}
@ -452,7 +666,8 @@
</div> </div>
{:else if step == "end"} {:else if step == "end"}
{#if error} {#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> <div class=" max-w-6xl lg:px-8 mx-auto text-red-800">
<div class="mt-auto max-w-6xl lg:px-8">
An error occurred ! An error occurred !
<svg <svg
fill="none" fill="none"
@ -472,8 +687,27 @@
<Alert color="red" class="mt-5"> <Alert color="red" class="mt-5">
{error} {error}
</Alert> </Alert>
<button class="mt-10 select-none" on:click={init}> Try again </button> </div>
<button class="mt-10 ml-5 select-none" on:click={cancel}> Cancel </button> <div class="flex justify-between mt-auto gap-4">
<button
on:click={cancel}
class="mt-10 bg-red-100 hover:bg-red-100/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55"
><XCircle
tabindex="-1"
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white"
/>Cancel</button
>
<button
class="mt-10 ml-2 select-none text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55"
on:click={init}
>
<ArrowPath
tabindex="-1"
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white"
/>
Try again
</button>
</div>
</div> </div>
{:else} {:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
@ -496,20 +730,30 @@
</div> </div>
{/if} {/if}
{/if} {/if}
</div>
<svelte:window bind:innerWidth={width} bind:innerHeight={height} />
<style> <style>
.pazzleline { .pindigit {
min-height: 99px;
}
/* .pazzleline {
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
} } */
.sel { .sel {
position: absolute; position: absolute;
display: flex;
width: 100%; width: 100%;
top: 45%; height: 100%;
top: 0;
left: 0; left: 0;
font-size: 100px;
font-weight: 700; font-weight: 700;
justify-content: center;
align-items: center;
} }
.sel-emoji { .sel-emoji {

@ -9,6 +9,11 @@
// according to those terms. // according to those terms.
--> -->
<!--
Component to inform the user, that no wallet is registered on this device.
Offers login or create wallet buttons.
-->
<script> <script>
// @ts-ignore // @ts-ignore
import Logo from "../assets/nextgraph.svg?component"; import Logo from "../assets/nextgraph.svg?component";
@ -16,7 +21,7 @@
import CenteredLayout from "./CenteredLayout.svelte"; import CenteredLayout from "./CenteredLayout.svelte";
</script> </script>
<CenteredLayout> <CenteredLayout displayFooter={true}>
<div class="container3"> <div class="container3">
<div class="row"> <div class="row">
<Logo class="logo block h-40" alt="NextGraph Logo" /> <Logo class="logo block h-40" alt="NextGraph Logo" />

@ -25,6 +25,7 @@
import { link } from "svelte-spa-router"; import { link } from "svelte-spa-router";
import { onMount, onDestroy, tick } from "svelte"; import { onMount, onDestroy, tick } from "svelte";
import { Button } from "flowbite-svelte"; import { Button } from "flowbite-svelte";
import DataClassIcon from "./DataClassIcon.svelte";
let is_tauri = import.meta.env.TAURI_PLATFORM; let is_tauri = import.meta.env.TAURI_PLATFORM;

@ -0,0 +1,120 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import {
Icon,
BugAnt,
DocumentText,
Window,
CodeBracket,
SquaresPlus,
ViewfinderCircle,
ArrowsPointingOut,
Cube,
Briefcase,
MagnifyingGlass,
RocketLaunch,
Sun,
TableCells,
ListBullet,
RectangleGroup,
Squares2x2,
MapPin,
CircleStack,
Envelope,
GlobeAlt,
DocumentChartBar,
Document,
ClipboardDocumentList,
Photo,
Film,
RectangleStack,
Microphone,
MusicalNote,
Ticket,
CursorArrowRays,
Megaphone,
User,
Clock,
CalendarDays,
Calendar,
Stop,
Flag,
HandRaised,
Newspaper,
PencilSquare,
CubeTransparent,
PresentationChartBar,
QuestionMarkCircle,
CheckCircle,
ChartPie,
Bars3BottomLeft,
Link,
Square2Stack,
Clipboard,
StopCircle,
Share,
Play,
DocumentArrowDown,
DocumentArrowUp,
CloudArrowUp,
CloudArrowDown,
Beaker,
Eye,
} from "svelte-heros-v2";
export let config = {};
export let zera: string;
const exact_mapping = {
json_ld_editor: TableCells,
json_editor: TableCells,
triple_editor: Share,
rdf_viewer: CircleStack,
graph_viewer: Sun,
sparql_query: RocketLaunch,
sparnatural: CursorArrowRays,
graphql: Cube,
invoke: Play,
ontology_viewer: ArrowsPointingOut,
download: DocumentArrowDown,
post_edit: PencilSquare,
file: Document,
source: CodeBracket,
post: DocumentText,
pad: Square2Stack,
card: Clipboard,
gallery: RectangleStack,
load_graph: CloudArrowUp,
load: DocumentArrowUp,
dump: CloudArrowDown,
ext: GlobeAlt,
app_store: SquaresPlus,
app_editor: Beaker,
list: ListBullet,
grid: Squares2x2,
view: Eye,
};
const prefix_mapping = {};
const find = (t) => {
let e = exact_mapping[t];
if (e) return e;
for (let prefix of Object.entries(prefix_mapping)) {
if (t.startsWith(prefix[0])) return prefix[1];
}
return BugAnt;
};
</script>
<Icon {...config} variation="outline" color="black" icon={find(zera)} />

@ -0,0 +1,78 @@
<script lang="ts">
export let value: string = "";
export let id: string;
let has_success: boolean = false;
const tauri_platform = import.meta.env.TAURI_PLATFORM;
const setClipboard = async (text: string) => {
if (tauri_platform) {
// TODO: this won't work for tauri platform.
// const { writeText } = await import("@tauri-apps/api/clipboard");
// await writeText(text);
} else {
navigator.clipboard.writeText(text);
}
};
const on_click = (e) => {
has_success = true;
setTimeout(() => (has_success = false), 2_000);
setClipboard(value);
};
</script>
<div class="w-full mt-2">
<div class="relative">
<textarea
{id}
rows="3"
style="resize: none;"
{value}
class="col-span-6 pr-11 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
disabled
readonly
/>
{#if !tauri_platform}
<button
on:click={on_click}
class="absolute inset-y-0 right-0 p-3 flex items-center text-sm leading-5 bg-transparent shadow-none"
>
<span id="default-icon" class:hidden={has_success}>
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 18 20"
>
<path
d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"
/>
</svg>
</span>
<span
id="success-icon"
class="inline-flex items-center"
class:hidden={!has_success}
>
<svg
class="w-3.5 h-3.5 text-blue-700 dark:text-blue-500"
aria-hidden={!has_success}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 16 12"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 5.917 5.724 10.5 15 1.5"
/>
</svg>
</span>
</button>
{/if}
</div>
</div>

@ -23,42 +23,18 @@ Provide classes using the `className` prop.
<script lang="ts"> <script lang="ts">
import { connection_status } from "../../store"; import { connection_status } from "../../store";
// @ts-ignore // @ts-ignore
import Logo from "../../assets/nextgraph.svg?component"; import Logo from "../../assets/nextgraph-nofill.svg?component";
export let className: string = ""; export let className: string = "";
let connection_status_class = "logo-blue";
// Color is adjusted to connection status. // Color is adjusted to connection status.
let connection_status_class = ""; // Default is blue. $: if ($connection_status === "connecting") {
if ($connection_status === "connecting") {
connection_status_class = "logo-pulse"; connection_status_class = "logo-pulse";
} else if ($connection_status === "disconnected") { } else if ($connection_status === "disconnected") {
connection_status_class = "logo-gray"; connection_status_class = "logo-gray";
} else {
connection_status_class = "logo-blue";
} }
</script> </script>
<Logo class={`${className} ${connection_status_class}`} /> <Logo class={`${className} ${connection_status_class}`} />
<!-- Sorry for the global but this way we can change the Logo's css from this component. -->
<style global>
@keyframes pulse-logo-color {
0%,
100% {
/* Default colors come from the svg. */
}
50% {
/* Mid-transition color */
stroke: #888;
fill: #888;
}
}
.logo-pulse path {
animation: pulse-logo-color 2s infinite;
animation-timing-function: cubic-bezier(0.65, 0.01, 0.59, 0.83);
}
.logo-gray path {
fill: #888;
stroke: #888;
}
</style>

@ -0,0 +1,76 @@
<script lang="ts">
export let value: string | undefined = undefined;
export let placeholder: string | undefined = undefined;
export let className: string | undefined = undefined;
export let id: string | undefined = undefined;
export let auto_complete: string | undefined = undefined;
export let show: boolean = false;
let input;
let type: "password" | "text" = "password";
$: type = show ? "text" : "password";
function handleInput(event: Event) {
const target = event.target as HTMLInputElement;
value = target.value;
}
async function toggle() {
let { selectionStart, selectionEnd } = input;
show = !show;
input.focus();
setTimeout(function () {
input.selectionStart = selectionStart;
input.selectionEnd = selectionEnd;
}, 0);
}
</script>
<div class="relative">
<input
bind:this={input}
{value}
{placeholder}
{id}
{type}
on:input={handleInput}
class={`${className} pr-12 text-md block`}
autocomplete={auto_complete}
/>
<div
class="absolute inset-y-0 right-0 pr-3 flex items-center text-sm leading-5"
>
<svg
fill="none"
on:click={toggle}
on:keypress={toggle}
class={`${show ? "block" : "hidden"} h-6 text-gray-700`}
xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 576 512"
>
<path
fill="currentColor"
d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"
>
</path>
</svg>
<svg
fill="none"
class={`${!show ? "block" : "hidden"} h-6 text-gray-700`}
on:click={toggle}
on:keypress={toggle}
xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 640 512"
>
<path
fill="currentColor"
d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"
>
</path>
</svg>
</div>
</div>

@ -9,18 +9,28 @@
// according to those terms. // according to those terms.
--> -->
<!--
Home page to display for logged in users.
Redirects to no-wallet or login page, if not logged in.
-->
<script> <script>
import { Button } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import Home from "../lib/Home.svelte"; import Home from "../lib/Home.svelte";
import NoWallet from "../lib/NoWallet.svelte"; import NoWallet from "../lib/NoWallet.svelte";
import { push } from "svelte-spa-router"; import { push } from "svelte-spa-router";
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import { active_wallet, has_wallets, derived } from "../store"; import {
active_wallet,
has_wallets,
derived,
cannot_load_offline,
} from "../store";
let display_login_create = !$has_wallets || !$active_wallet; let display_login_create = !$has_wallets || !$active_wallet;
let unsubscribe; let unsubscribe;
onMount(() => { onMount(() => {
cannot_load_offline.set(false);
//setTimeout(function () {}, 2);
const combined = derived([active_wallet, has_wallets], ([$s1, $s2]) => [ const combined = derived([active_wallet, has_wallets], ([$s1, $s2]) => [
$s1, $s1,
$s2, $s2,

@ -18,15 +18,13 @@
import CenteredLayout from "../lib/CenteredLayout.svelte"; import CenteredLayout from "../lib/CenteredLayout.svelte";
import { has_wallets } from "../store"; import { has_wallets } from "../store";
let display_has_wallets_warning: boolean = $has_wallets != 0; let display_has_wallets_warning = $has_wallets != 0;
let unsubscribe; let unsubscribe;
onMount(() => {}); onMount(() => {});
onDestroy(() => { onDestroy(() => {});
unsubscribe();
});
</script> </script>
<CenteredLayout> <CenteredLayout displayFooter={true}>
<Install {display_has_wallets_warning} /> <Install {display_has_wallets_warning} />
</CenteredLayout> </CenteredLayout>

@ -19,6 +19,6 @@
<div <div
class="h-screen aspect-[3/5] pazzleline max-w-[720px] bg-yellow-300 inner" class="h-screen aspect-[3/5] pazzleline max-w-[720px] bg-yellow-300 inner"
> >
<p>nextgraph URI {params[1]}</p> <p>Nextgraph URI {params[1]}</p>
</div> </div>
</div> </div>

@ -14,7 +14,7 @@
import CenteredLayout from "../lib/CenteredLayout.svelte"; import CenteredLayout from "../lib/CenteredLayout.svelte";
</script> </script>
<CenteredLayout> <CenteredLayout displayFooter={true}>
<div class="p-8"> <div class="p-8">
<Alert color="red"> <Alert color="red">
<span class="font-medium">404</span> Page not found. <span class="font-medium">404</span> Page not found.

@ -8,6 +8,13 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
--> -->
<!--
@component
"User Panel" page.
Provides wallet download, logout, offline/online switch, and other user actions.
-->
<script> <script>
// @ts-nocheck // @ts-nocheck
@ -153,7 +160,7 @@
> >
<ArrowLeft <ArrowLeft
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3">Back</span> <span class="ml-3">Back</span>
</li> </li>
@ -164,7 +171,7 @@
{#if $online} {#if $online}
<Signal <Signal
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-green-600 transition duration-75 dark:text-green-400 " class="w-7 h-7 text-green-600 transition duration-75 focus:outline-none dark:text-green-400 "
/> />
<span class="ml-3 text-green-600 dark:text-green-400" <span class="ml-3 text-green-600 dark:text-green-400"
>Online</span >Online</span
@ -172,7 +179,7 @@
{:else} {:else}
<SignalSlash <SignalSlash
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-red-600 transition duration-75 dark:text-red-400 " class="w-7 h-7 text-red-600 transition duration-75 focus:outline-none dark:text-red-400 "
/> />
<span class="ml-3 text-red-600 dark:text-red-400">Offline</span> <span class="ml-3 text-red-600 dark:text-red-400">Offline</span>
{/if} {/if}
@ -187,7 +194,7 @@
> >
<ArrowRightOnRectangle <ArrowRightOnRectangle
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3">Logout</span> <span class="ml-3">Logout</span>
</li> </li>
@ -213,7 +220,7 @@
> >
<DocumentArrowDown <DocumentArrowDown
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3">Download wallet file</span> <span class="ml-3">Download wallet file</span>
</li> </li>
@ -224,7 +231,7 @@
> >
<NoSymbol <NoSymbol
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-red-700 transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-red-700 transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3 text-left" <span class="ml-3 text-left"
>Download failed:<br /> {download_error}</span >Download failed:<br /> {download_error}</span
@ -237,7 +244,7 @@
> >
<DocumentArrowDown <DocumentArrowDown
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-blue-700 transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-blue-700 transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3 text-left">Download in progress...</span> <span class="ml-3 text-left">Download in progress...</span>
</li> </li>
@ -267,7 +274,7 @@
> >
<DocumentArrowDown <DocumentArrowDown
tabindex="-1" tabindex="-1"
class="w-14 h-14 transition duration-75 dark:text-white dark:group-hover:text-white" class="w-14 h-14 transition duration-75 focus:outline-none dark:text-white dark:group-hover:text-white"
/> />
Click here to download the wallet file Click here to download the wallet file
</button> </button>
@ -278,7 +285,7 @@
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Cog6Tooth <Cog6Tooth
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
@ -286,7 +293,7 @@
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<PuzzlePiece <PuzzlePiece
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
@ -294,7 +301,7 @@
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Key <Key
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
@ -302,7 +309,7 @@
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<User <User
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
@ -363,7 +370,7 @@
> >
<Gift <Gift
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3">Donate to NextGraph</span> <span class="ml-3">Donate to NextGraph</span>
</li> </li>
@ -377,7 +384,7 @@
> >
<InformationCircle <InformationCircle
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white" class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/> />
<span class="ml-3">About NextGraph</span> <span class="ml-3">About NextGraph</span>
</li> </li>

@ -45,7 +45,7 @@
}); });
</script> </script>
<CenteredLayout> <CenteredLayout displayFooter={true}>
<div class="container3"> <div class="container3">
<div class="row"> <div class="row">
<a href="#/"> <a href="#/">

@ -9,10 +9,19 @@
// according to those terms. // according to those terms.
--> -->
<!--
@component
Wallet creation page.
This component manages the whole UX flow, gives infos about wallets,
offers available brokers, handles wallet creation,
and shows the wallet pazzle and pin.
-->
<script lang="ts"> <script lang="ts">
import { Button, Alert, Dropzone, Toggle } from "flowbite-svelte"; import { Button, Alert, Dropzone, Toggle, Modal } from "flowbite-svelte";
import { link, querystring } from "svelte-spa-router"; import { link, querystring } from "svelte-spa-router";
import CenteredLayout from "../lib/CenteredLayout.svelte"; import CenteredLayout from "../lib/CenteredLayout.svelte";
import CopyToClipboard from "../lib/components/CopyToClipboard.svelte";
// @ts-ignore // @ts-ignore
import EULogo from "../assets/EU.svg?component"; import EULogo from "../assets/EU.svg?component";
// @ts-ignore // @ts-ignore
@ -27,7 +36,11 @@
APP_WALLET_CREATE_SUFFIX, APP_WALLET_CREATE_SUFFIX,
default as ng, default as ng,
} from "../api"; } from "../api";
import { display_pazzle } from "../wallet_emojis"; import {
display_pazzle,
emojis_from_pazzle_ids,
load_svg,
} from "../wallet_emojis";
import { onMount, onDestroy, tick } from "svelte"; import { onMount, onDestroy, tick } from "svelte";
import { wallets, set_active_session, has_wallets } from "../store"; import { wallets, set_active_session, has_wallets } from "../store";
@ -36,6 +49,11 @@
let tauri_platform = import.meta.env.TAURI_PLATFORM; let tauri_platform = import.meta.env.TAURI_PLATFORM;
let mobile = tauri_platform == "android" || tauri_platform == "ios"; let mobile = tauri_platform == "android" || tauri_platform == "ios";
let is_touch_device =
"ontouchstart" in window ||
navigator.maxTouchPoints > 0 ||
// @ts-ignore
navigator?.msMaxTouchPoints > 0;
const onFileSelected = (image) => { const onFileSelected = (image) => {
animate_bounce = false; animate_bounce = false;
@ -116,7 +134,9 @@
let unsub_register_accepted; let unsub_register_accepted;
let unsub_register_error; let unsub_register_error;
let unsub_register_close; let unsub_register_close;
/** The emojis for the newly created pazzle. */
let pazzle_emojis = [];
let confirm_modal_open = false;
function scrollToTop() { function scrollToTop() {
top.scrollIntoView(); top.scrollIntoView();
} }
@ -183,6 +203,9 @@
} }
} }
scrollToTop(); scrollToTop();
// We need them for display later.
load_svg();
} }
function create_wallet() { function create_wallet() {
@ -246,8 +269,10 @@
window.wallet_channel.postMessage(new_in_mem, location.href); window.wallet_channel.postMessage(new_in_mem, location.href);
} }
} }
console.log(ready.pazzle); console.log("pazzle", ready.pazzle);
console.log(display_pazzle(ready.pazzle)); console.log("pazzle words", display_pazzle(ready.pazzle));
console.log("mnemonic", ready.mnemonic);
console.log("mnemonic words", ready.mnemonic_str);
download_name = "wallet-" + ready.wallet_name + ".ngw"; download_name = "wallet-" + ready.wallet_name + ".ngw";
if (options.cloud) { if (options.cloud) {
cloud_link = "https://nextgraph.one/#/w/" + ready.wallet_name; cloud_link = "https://nextgraph.one/#/w/" + ready.wallet_name;
@ -394,11 +419,106 @@
"Terms of Service NextGraph.one" "Terms of Service NextGraph.one"
); );
}; };
const load_pazzle_emojis = async (pazzle_ids: number[]) => {
// We wait until the SVGs are available. If they are already, we return immediately.
await load_svg();
pazzle_emojis = emojis_from_pazzle_ids(pazzle_ids);
};
$: if (ready?.pazzle) {
load_pazzle_emojis(ready.pazzle);
}
// Loads an example wallet.
// const loadWallet = async () => {
// options = {
// trusted: true,
// cloud: false,
// bootstrap: false,
// pdf: false,
// };
// creating = true;
// let local_invitation = await ng.get_local_bootstrap(location.href);
// let additional_bootstrap;
// if (local_invitation) {
// additional_bootstrap = local_invitation.V0.bootstrap;
// }
// let core_registration;
// if (invitation?.V0.code) {
// core_registration = invitation.V0.code.ChaCha20Key;
// }
// let params = {
// security_img: security_img,
// security_txt,
// pin,
// pazzle_length: 9,
// send_bootstrap: false, //options.cloud || options.bootstrap ? : undefined,
// send_wallet: options.cloud,
// local_save: options.trusted,
// result_with_wallet_file: false, // this will be automatically changed to true for browser app
// core_bootstrap: invitation?.V0.bootstrap,
// core_registration,
// additional_bootstrap,
// };
// try {
// ready = await import("./wallet.json");
// wallets.set(await ng.get_wallets());
// if (!options.trusted && !tauri_platform) {
// let lws = $wallets[ready.wallet_name];
// if (lws.in_memory) {
// let new_in_mem = {
// lws,
// name: ready.wallet_name,
// opened: false,
// cmd: "new_in_mem",
// };
// window.wallet_channel.postMessage(new_in_mem, location.href);
// }
// }
// console.log("pazzle ids", ready.pazzle);
// console.log("pazzle emojis", emojis_from_pazzle_ids(ready.pazzle));
// download_name = "wallet-" + ready.wallet_name + ".ngw";
// if (options.cloud) {
// cloud_link = "https://nextgraph.one/#/w/" + ready.wallet_name;
// }
// if (ready.wallet_file.length) {
// const blob = new Blob([ready.wallet_file], {
// type: "application/octet-stream",
// });
// download_link = URL.createObjectURL(blob);
// }
// } catch (e) {
// console.error(e);
// error = e;
// }
// wait = false;
// registration_error = false;
// intro = false;
// pin = [0, 8, 1, 5];
// pin_confirm = [0, 8, 1, 5];
// invitation = true;
// };
// loadWallet();
let width: number;
let height: number;
const breakPointWidth: number = 450;
const breakPointHeight: number = 500;
let small_screen = false;
$: if (width >= breakPointWidth && height >= breakPointHeight) {
small_screen = false;
} else {
small_screen = true;
}
</script> </script>
<svelte:window bind:innerWidth={width} bind:innerHeight={height} />
<CenteredLayout> <CenteredLayout>
<div class="max-w-2xl lg:px-8 mx-auto">
{#if wait} {#if wait}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700"> <div class="lg:px-8 text-primary-700">
{#if wait === true} {#if wait === true}
Please wait... Please wait...
{:else} {:else}
@ -430,7 +550,7 @@
<div class="container3" bind:this={top}> <div class="container3" bind:this={top}>
<div class="row"> <div class="row">
<a href="#/"> <a href="#/">
<Logo class="logo block h-40" alt="NextGraph Logo" /> <Logo class="logo block h-[8em]" alt="NextGraph Logo" />
</a> </a>
</div> </div>
{#if registration_error} {#if registration_error}
@ -479,11 +599,11 @@
</div> </div>
{:else if intro} {:else if intro}
<div class=" max-w-6xl lg:px-8 mx-auto px-4"> <div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl"> <p class="max-w-xl text-left md:mx-auto lg:max-w-2xl">
A <b>NextGraph Wallet</b> is unique to each person. It stores your A <b>NextGraph Wallet</b> is unique to each person. It stores your
credentials and authorizations to access documents. You need one in credentials and authorizations to access documents. You need one
order to start using NextGraph.<br /><br />If you already have a in order to start using NextGraph.<br /><br />If you already have
wallet, you should not create a new one, instead, a wallet, you should not create a new one. Instead,
<a href="/wallet/login" use:link <a href="/wallet/login" use:link
>login here with your existing wallet.</a >login here with your existing wallet.</a
> >
@ -549,8 +669,8 @@
</svg> </svg>
<span <span
>In your wallet, we store all the permissions to access >In your wallet, we store all the permissions to access
documents you have been granted with, or that you have created documents you have been granted with, or that you have
yourself.</span created yourself.</span
> >
</li> </li>
<li class="flex space-x-3"> <li class="flex space-x-3">
@ -571,7 +691,8 @@
/> />
</svg> </svg>
<span <span
>In order to open it, you will need to enter your <b>pazzle</b >In order to open it, you will need to enter your <b
>pazzle</b
> >
and a and a
<b>PIN code</b> of 4 digits. Your personal pazzle (contraction <b>PIN code</b> of 4 digits. Your personal pazzle (contraction
@ -599,9 +720,10 @@
</svg> </svg>
<span <span
>Don't worry, it is easier to remember 9 images than a >Don't worry, it is easier to remember 9 images than a
password like "69$g&ms%C*%", and it has the same strength as a password like "69$g&ms%C*%", and it has the same strength as
complex password. The entropy of your pazzle is <b>66bits</b>, a complex password. The entropy of your pazzle is <b
which is considered very high by all standards.</span >66bits</b
>, which is considered very high by all standards.</span
> >
</li> </li>
@ -622,10 +744,10 @@
/> />
</svg> </svg>
<span <span
>You should only create <b>one unique wallet for yourself</b>. >You should only create <b>one unique wallet for yourself</b
All your accounts, identities and permissions will be added to >. All your accounts, identities and permissions will be
this unique wallet later on. Do not create another wallet if added to this unique wallet later on. Do not create another
you already have one. Instead, you will wallet if you already have one. Instead, you will
<b>import</b> your existing wallet in all the apps and websites <b>import</b> your existing wallet in all the apps and websites
where you need it</span where you need it</span
> >
@ -671,9 +793,9 @@
/> />
</svg> </svg>
<span <span
>We at NextGraph will never see the content of your wallet. It >We at NextGraph will never see the content of your wallet.
is encrypted and we do not know your pazzle, so we cannot see It is encrypted and we do not know your pazzle, so we cannot
what is inside.</span see what is inside.</span
> >
</li> </li>
<li class="flex space-x-3 under"> <li class="flex space-x-3 under">
@ -696,10 +818,11 @@
<span <span
>For the same reason, we won't be able to help you if you >For the same reason, we won't be able to help you if you
forget your pazzle or PIN code, or if you loose the wallet forget your pazzle or PIN code, or if you loose the wallet
file. There is no "password recovery" option in this case. You file. <span class="text-bold">
can note your pazzle down on a piece of paper until you There is no "password recovery" option</span
remember it, but don't forget to destroy this note after a > in this case. You can note your pazzle down on a piece of paper
while.</span until you remember it, but don't forget to destroy this note
after a while.</span
> >
</li> </li>
</ul> </ul>
@ -725,14 +848,14 @@
d="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z" d="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"
/> />
</svg> </svg>
Ok, I create my wallet now ! I create my wallet now !
</button> </button>
</div> </div>
{:else if !invitation} {:else if !invitation}
<div class=" max-w-6xl lg:px-8 mx-auto px-4"> <div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl"> <p class="max-w-xl md:mx-auto text-left lg:max-w-2xl">
NextGraph is based on an efficient decentralized P2P network, and in NextGraph is based on an efficient decentralized P2P network, and
order to join this network and start using the app, you need to in order to join this network and start using the app, you need to
first select a <b>broker&nbsp;server</b>. first select a <b>broker&nbsp;server</b>.
</p> </p>
</div> </div>
@ -763,8 +886,8 @@
/> />
</svg> </svg>
<span> <span>
The broker helps you keep all your data in <b>sync</b>, as it The broker helps you keep all your data in <b>sync</b>, as
is connected to the internet 24/7 and keeps a copy of the it is connected to the internet 24/7 and keeps a copy of the
updates for you. This way, even if the devices of the other updates for you. This way, even if the devices of the other
participants are offline, you can still see their changes</span participants are offline, you can still see their changes</span
> >
@ -787,8 +910,8 @@
</svg> </svg>
<span> <span>
All your data is secure and <b>end-to-end encrypted</b>, and All your data is secure and <b>end-to-end encrypted</b>, and
the broker cannot see the content of the documents as it does the broker cannot see the content of the documents as it
not have the keys to decrypt them.</span does not have the keys to decrypt them.</span
> >
</li> </li>
<li class="flex space-x-3"> <li class="flex space-x-3">
@ -808,8 +931,9 @@
/> />
</svg> </svg>
<span> <span>
The broker helps you enforce your <b>privacy</b> as it hides your The broker helps you enforce your <b>privacy</b> as it hides
internet address (IP) from other users you share documents with.</span your internet address (IP) from other users you share documents
with.</span
> >
</li> </li>
<li class="flex space-x-3"> <li class="flex space-x-3">
@ -830,9 +954,9 @@
</svg> </svg>
<span> <span>
It will be possible in the future to use NextGraph without any It will be possible in the future to use NextGraph without
broker and to have direct connections between peers, but this any broker and to have direct connections between peers, but
will imply a less smooth experience.</span this will imply a less smooth experience.</span
> >
</li> </li>
<li class="flex space-x-3"> <li class="flex space-x-3">
@ -852,8 +976,8 @@
/> />
</svg> </svg>
<span> <span>
At anytime you can decide to switch to another broker service At anytime you can decide to switch to another broker
provider or host it yourself. Your data is totally <b service provider or host it yourself. Your data is totally <b
>portable</b >portable</b
> >
and can freely move to another broker.</span and can freely move to another broker.</span
@ -877,12 +1001,12 @@
</svg> </svg>
<span> <span>
Soon we will offer you the opportunity to host your own broker Soon we will offer you the opportunity to host your own
at <b>home</b> broker at <b>home</b>
or <b>office</b>. Instead of using a "broker service or <b>office</b>. Instead of using a "broker service
provider", you will own a small device that you connect behind provider", you will own a small device that you connect
your internet router. It is called <b>NG Box</b> and will be available behind your internet router. It is called <b>NG Box</b> and will
soon.</span be available soon.</span
> >
</li> </li>
<li class="flex space-x-3"> <li class="flex space-x-3">
@ -921,7 +1045,10 @@
<div class="row mt-5"> <div class="row mt-5">
<button <button
on:click|once={async () => { on:click|once={async () => {
await select_bsp(pre_invitation.V0.url, pre_invitation.V0.name); await select_bsp(
pre_invitation.V0.url,
pre_invitation.V0.name
);
}} }}
class="choice-button text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" class="choice-button text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
> >
@ -949,7 +1076,10 @@
on:click|once={selectEU} on:click|once={selectEU}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
> >
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" /> <EULogo
class="mr-4 block h-10 w-10"
alt="European Union flag"
/>
For European Union citizens For European Union citizens
</button> </button>
</div> </div>
@ -1087,7 +1217,7 @@
</button> </button>
</div> </div>
{:else if pin.length < 4} {:else if pin.length < 4}
<div class=" max-w-6xl lg:px-8 mx-auto px-3"> <div class=" max-w-6xl lg:px-8 mx-auto">
{#if registration_success} {#if registration_success}
<Alert color="green" class="mb-5"> <Alert color="green" class="mb-5">
<span class="font-bold text-xl" <span class="font-bold text-xl"
@ -1107,24 +1237,25 @@
NextGraph will never see your PIN. NextGraph will never see your PIN.
</Alert> </Alert>
</p> </p>
<p class="text-left mt-5">Here are the rules for the PIN :</p> <p class="text-left mt-5 px-3">Here are the rules for the PIN :</p>
<ul class="text-left list-disc list-inside"> <ul class="text-left list-disc list-inside px-3">
<li>It cannot be a series like 1234 or 8765</li> <li>It cannot be a series like 1234 or 8765</li>
<li> <li>
The same digit cannot repeat more than once. By example 4484 is The same digit cannot repeat more than once. By example 4484 is
invalid invalid
</li> </li>
<li> <li>
Try to avoid birth date, last digits of phone number, or zip code Try to avoid birth date, last digits of phone number, or zip
code
</li> </li>
</ul> </ul>
<Alert color="blue" class="mt-5"> <Alert color="blue" class="mt-5">
You have chosen: {#each pin as digit}<span class="font-bold text-xl" You have chosen: {#each pin as digit}<span
>{digit}</span class="font-bold text-xl">{digit}</span
>{/each} >{/each}
</Alert> </Alert>
<div class="w-[295px] mx-auto mb-4"> <div class="w-[295px] mx-auto mb-10">
{#each [0, 1, 2] as row} {#each [0, 1, 2] as row}
<div class=""> <div class="">
{#each [1, 2, 3] as num} {#each [1, 2, 3] as num}
@ -1195,11 +1326,11 @@
Now let's enter a security phrase and a security image Now let's enter a security phrase and a security image
</h2> </h2>
<p class="max-w-xl md:mx-auto lg:max-w-2xl text-left"> <p class="max-w-xl md:mx-auto lg:max-w-2xl text-left">
As a verification step, this phrase and image will be presented to As a verification step, this phrase and image will be presented
you every time you are about to enter your pazzle and PIN in order to you every time you are about to enter your pazzle and PIN in
to unlock your wallet.<br /> order to unlock your wallet.<br />
This security measure will prevent you from entering your pazzle and This security measure will prevent you from entering your pazzle
PIN on malicious sites and apps. and PIN on malicious sites and apps.
<Alert color="red" class="mt-5"> <Alert color="red" class="mt-5">
Every time you will use your wallet, if you do not see and Every time you will use your wallet, if you do not see and
recognize your own security phrase and image before entering recognize your own security phrase and image before entering
@ -1207,7 +1338,9 @@
would be the victim of a phishing attempt. would be the victim of a phishing attempt.
</Alert> </Alert>
</p> </p>
<p class="max-w-xl md:mx-auto lg:max-w-2xl text-left mt-5 text-sm"> <p
class="max-w-xl md:mx-auto lg:max-w-2xl text-left mt-5 text-sm"
>
Here are the rules for the security phrase and image : Here are the rules for the security phrase and image :
</p> </p>
<ul <ul
@ -1215,15 +1348,15 @@
> >
<li>The phrase should be at least 10 characters long</li> <li>The phrase should be at least 10 characters long</li>
<li> <li>
It should be something you will remember, but not something too It should be something you will remember, but not something
personal. too personal.
</li> </li>
<li> <li>
Do not enter your full name, nor address, nor phone number. Do not enter your full name, nor address, nor phone number.
</li> </li>
<li> <li>
Instead, you can enter a quote, a small sentence that you like, Instead, you can enter a quote, a small sentence that you
or something meaningless to others, but unique to you. like, or something meaningless to others, but unique to you.
</li> </li>
<li> <li>
The image should be minimum 150x150px. There is no need to The image should be minimum 150x150px. There is no need to
@ -1240,8 +1373,8 @@
Do not upload your face picture, this is not a profile pic. Do not upload your face picture, this is not a profile pic.
</li> </li>
<li> <li>
The best would be a landscape you like or any other picture that The best would be a landscape you like or any other picture
you will recognize as unique. that you will recognize as unique.
</li> </li>
<li> <li>
Please be aware that other people who are sharing this device Please be aware that other people who are sharing this device
@ -1296,7 +1429,7 @@
on:change={handleChange} on:change={handleChange}
> >
<p class="mt-2 mb-5 text-gray-500 dark:text-gray-400"> <p class="mt-2 mb-5 text-gray-500 dark:text-gray-400">
{#if mobile} {#if is_touch_device}
<span class="font-semibold">Tap to upload an image</span> <span class="font-semibold">Tap to upload an image</span>
{:else} {:else}
<span class="font-semibold">Click to select an image</span> or <span class="font-semibold">Click to select an image</span> or
@ -1345,34 +1478,35 @@
<p class="max-w-xl mb-10 md:mx-auto lg:max-w-2xl"> <p class="max-w-xl mb-10 md:mx-auto lg:max-w-2xl">
<span class="text-xl">We are almost done !</span><br /> <span class="text-xl">We are almost done !</span><br />
There are 4 options to choose before we can create your wallet. Those There are 4 options to choose before we can create your wallet. Those
options can help you to use and keep your wallet. But we also want to options can help you to use and keep your wallet. But we also want
be careful with your security and privacy.<br /><br /> to be careful with your security and privacy.<br /><br />
Remember that in any case, once your wallet will be created, you will Remember that in any case, once your wallet will be created, you will
download a file that you should keep privately somewhere on your device, download a file that you should keep privately somewhere on your device,
USB key or hard-disk. This is the default way you can use and keep your USB key or hard-disk. This is the default way you can use and keep
wallet. Now let's look at some options that can make your life a bit your wallet. Now let's look at some options that can make your life
easier. a bit easier.
</p> </p>
<p class="max-w-xl md:mx-auto lg:max-w-2xl text-left"> <p class="max-w-xl md:mx-auto lg:max-w-2xl text-left">
<span class="text-xl">Do you trust this device? </span> <br /> <span class="text-xl">Do you trust this device? </span> <br />
If you do, if this device is yours or is used by few trusted persons If you do, if this device is yours or is used by few trusted persons
of your family or workplace, and you would like to login again from this of your family or workplace, and you would like to login again from
device in the future, then you can save your wallet on this device. To this device in the future, then you can save your wallet on this device.
the contrary, if this device is public and shared by strangers, do not To the contrary, if this device is public and shared by strangers,
save your wallet here. {#if !tauri_platform}By selecting this do not save your wallet here. {#if !tauri_platform}By selecting
option, you agree to save some cookies on your browser.{/if}<br /> this option, you agree to save some cookies on your browser.{/if}<br
/>
<Toggle class="mt-3" bind:checked={options.trusted} <Toggle class="mt-3" bind:checked={options.trusted}
>Save my wallet on this device?</Toggle >Save my wallet on this device?</Toggle
> >
</p> </p>
<p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left"> <p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left">
<span class="text-xl">Keep a copy in the cloud? </span> <br /> <span class="text-xl">Keep a copy in the cloud? </span> <br />
Are you afraid that you will loose the file containing your wallet? If Are you afraid that you will loose the file containing your wallet?
this would happen, your wallet would be lost forever, together with all If this would happen, your wallet would be lost forever, together with
your documents. We can keep an encrypted copy of your wallet in our cloud. all your documents. We can keep an encrypted copy of your wallet in
Only you will be able to download it with a special link. You would have our cloud. Only you will be able to download it with a special link.
to keep this link safely though. By selecting this option, you agree You would have to keep this link safely though. By selecting this option,
to the you agree to the
<span <span
style="font-weight: 500;cursor: pointer; color: #646cff;" style="font-weight: 500;cursor: pointer; color: #646cff;"
tabindex="0" tabindex="0"
@ -1388,11 +1522,11 @@
<p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left"> <p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left">
<span class="text-xl">Create a PDF file of your wallet? </span> <span class="text-xl">Create a PDF file of your wallet? </span>
<br /> <br />
We can prepare for you a PDF file containing all the information of your We can prepare for you a PDF file containing all the information of
wallet, unencrypted. You should print this file and then delete the PDF your wallet, unencrypted. You should print this file and then delete
(and empty the trash). Keep this printed document in a safe place. It the PDF (and empty the trash). Keep this printed document in a safe
contains all the information to regenerate your wallet in case you lost place. It contains all the information to regenerate your wallet in
access to it. case you lost access to it.
<br /> <br />
<Toggle disabled class="mt-3" bind:checked={options.pdf} <Toggle disabled class="mt-3" bind:checked={options.pdf}
>Create a PDF of my wallet?</Toggle >Create a PDF of my wallet?</Toggle
@ -1472,10 +1606,11 @@
</svg> </svg>
</div> </div>
{:else} {:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> <div class="text-left mx-4">
Your wallet is ready! <div class="text-green-800 mx-auto flex flex-col items-center">
<div>Your wallet is ready!</div>
<svg <svg
class="my-10 h-16 w-16 mx-auto" class="my-4 h-16 w-16"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"
@ -1489,9 +1624,16 @@
d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
/> />
</svg> </svg>
</div>
<div class="text-center">
{#if download_link} {#if download_link}
Please download your wallet and keep it in a safe location<br /> Please download your wallet and keep it in a safe location<br
<a href={download_link} target="_blank" download={download_name}> />
<a
href={download_link}
target="_blank"
download={download_name}
>
<button <button
tabindex="-1" tabindex="-1"
class:animate-bounce={animateDownload} class:animate-bounce={animateDownload}
@ -1516,30 +1658,92 @@
Download my wallet Download my wallet
</button> </button>
</a><br /> </a>
<br />
{:else if !options.trusted} {:else if !options.trusted}
Your wallet file has been downloaded into your "Downloads" folder, Your wallet file has been downloaded into your "Downloads"
with the name<br /><span class="text-black"> folder, with the name<br /><span class="text-black">
{download_name}</span {download_name}</span
><br /> ><br />
Please move it to a safe and durable place.<br /><br /> <span class="font-bold"
>Please move it to a safe and durable place.</span
><br />
{/if} {/if}
Here is your Pazzle:<br /><br /> <!-- Pazzle -->
{#each display_pazzle(ready.pazzle) as emoji} Here below is your Pazzle.
<span>{emoji}</span><br /> <br />
The <span class="font-bold">order</span> of each image is
<span class="font-bold">important</span> !
</div>
<div
class="mt-2 bg-white shadow-md rounded-lg max-w-2xl w-full mx-auto"
>
{#each pazzle_emojis as emoji, index}
<div
class="flex w-full p-1 tall-sm:p-2 content-center"
class:border-b={index !== pazzle_emojis.length - 1}
class:justify-center={!small_screen}
>
<div class="mr-4 content-center pt-1 my-auto">
{index + 1}
</div>
<div
class="flex justify-center w-[3em] h-[3em]"
title={emoji.code}
>
<svelte:component
this={emoji.svg?.default}
class="w-[3em] h-[3em] "
/>
</div>
<div class="flex flex-col ml-4">
<div class="w-[10em] text-left">
<span>{emoji.cat}</span>
</div>
<div class="font-bold text-lg h-full content-center">
<span>{emoji.code}</span>
</div>
</div>
</div>
{/each} {/each}
</div>
<br />
<!-- Mnemonic (copy button). TODO: once the component is available-->
<label for="mnemonic"
>And here is your mnemonic (your alternative passphrase):</label
>
<CopyToClipboard
id="mnemonic"
value={ready.mnemonic_str.join(" ")}
/>
<br />
You can use both the pazzle or the mnemonic to unlock your wallet.
The pazzle is easier to remember. The mnemonic is useful in some special
cases. We recommend that you use the pazzle.
<span class="font-bold text-xl"
>Copy both on a piece of paper.</span
>
You should try to memorize the pazzle. Once you did, you won't need
the paper anymore.
<br /><br /> <br /><br />
Copy it on a piece of paper. Use that until you memorized it, then throw Now click on "Continue to Login" and select your new wallet.
it away.<br /> The order of each image is important!<br /> <br />
Now click on "Continue to Login" and select your wallet.<br /><br It is important that you <span class="font-bold">login</span> with
/>It is important that you login with this wallet at least once from this wallet
this {#if tauri_platform}device{:else}browser tab{/if},<br /> <span class="font-bold">at least once</span>
from this {#if tauri_platform}device{:else}browser tab{/if},<br />
while connected to the internet, so your personal site can be created while connected to the internet, so your personal site can be created
on your broker.<br /><br /> on your broker.<br /><br />
<a href="/wallet/login" use:link> <div class="flex flex-col items-center">
<button <button
tabindex="-1" tabindex="-1"
class="mb-20 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2" class="mb-20 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2"
on:click={() => (confirm_modal_open = true)}
> >
<svg <svg
class="w-8 h-8 mr-2 -ml-1" class="w-8 h-8 mr-2 -ml-1"
@ -1558,8 +1762,33 @@
</svg> </svg>
Continue to Login Continue to Login
</button> </button>
</div>
<Modal
autoclose
outsideclose
bind:open={confirm_modal_open}
title="Did you write down your login credentials?"
>
<span class="text-lg text-neutral-950">
The pazzle and the mnemonic <span class="font-bold">
will not be shown to you again</span
>. Please make sure, you have written them down.
</span>
<div>
<button
class="m-2"
on:click={() => (confirm_modal_open = false)}
>Take me back</button
>
<a href="/wallet/login" use:link>
<button class="m-2 bg-primary-700 text-white"
>Yes, I did</button
>
<!-- todo: blue button-->
</a> </a>
</div> </div>
</Modal>
</div>
{/if} {/if}
{:else} {:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
@ -1599,6 +1828,7 @@
{/if} {/if}
</div> </div>
{/if} {/if}
</div>
</CenteredLayout> </CenteredLayout>
<style> <style>

@ -9,6 +9,12 @@
// according to those terms. // according to those terms.
--> -->
<!--
"Select a wallet to login with" page.
This page is usually the first page the user sees when they visit the app.
It allows the user to select a wallet to login with, create, or import a wallet.
-->
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy, tick } from "svelte"; import { onMount, onDestroy, tick } from "svelte";
import { link, push } from "svelte-spa-router"; import { link, push } from "svelte-spa-router";
@ -186,7 +192,7 @@
</script> </script>
<div bind:this={top}> <div bind:this={top}>
<CenteredLayout> <CenteredLayout displayFooter={!wallet}>
{#if error} {#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
<svg <svg
@ -232,7 +238,7 @@
<Logo class="logo block h-40" alt="NextGraph Logo" /> <Logo class="logo block h-40" alt="NextGraph Logo" />
</div> </div>
<h2 class="pb-5 text-xl">Select a wallet to login with</h2> <h2 class="pb-5 text-xl">Select a wallet to login with</h2>
<div class="flex flex-wrap justify-center gap-5 mb-20"> <div class="flex flex-wrap justify-center gap-5 mb-10">
{#each Object.entries($wallets) as wallet_entry} {#each Object.entries($wallets) as wallet_entry}
<div <div
class="wallet-box" class="wallet-box"

@ -9,9 +9,129 @@
import { writable, readable, readonly, derived, get, type Writable } from "svelte/store"; import { writable, readable, readonly, derived, get, type Writable } from "svelte/store";
import ng from "./api"; import ng from "./api";
import { official_classes } from "./classes";
import { official_apps, official_services } from "./zeras";
let all_branches = {}; let all_branches = {};
export const available_languages = {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"ru": "Русский",
"es": "Español",
"it": "Italiano",
"zh": "中文",
"pt": "Português",
};
export const current_lang = writable("en");
export const select_default_lang = async() => {
let locales = await ng.locales();
for (let lo of locales) {
if (available_languages[lo]) {
// exact match (if locales is a 2 chars lang code, or if we support regionalized translations)
current_lang.set(lo);
return;
}
lo = lo.substr(0,2);
if (available_languages[lo]) {
current_lang.set(lo);
return;
}
}
};
let loaded_external_apps = {};
export const load_app = async (appName: string) => {
if (appName.startsWith("n:g:z")) {
let app = official_apps[appName];
if (!app) throw new Error("Unknown official app");
return await import(`./apps/${app["ng:b"]}.svelte`);
} else {
//TODO: load external app from its repo
// TODO: return IFrame component
}
};
export const invoke_service = async (serviceName: string, nuri:string, args: object) => {
if (serviceName.startsWith("n:g:z")) {
let service = official_services[serviceName];
if (!service) throw new Error("Unknown official service");
// TODO: do this in WebWorker
// TODO: if on native app or CLI: use deno
//return await ng.app_invoke(serviceName[6..], nuri, args);
} else {
// TODO: if on webapp: only allow those invocations from IFrame of external app or from n:g:z:external_service_invoke (which runs in an IFrame) and run it from webworker
// TODO: if on native app or CLI: use deno
// TODO: load external service from its repo
}
};
export const cur_tab = writable({
cur_store: {
has_outer : {
nuri_trail: ":v:l"
},
type: "public", // "protected", "private", "group", "dialog",
favicon: "",
title: "Group B",
},
cur_branch: {
b: "b:xxx", //branch id (can be null if not of type "branch")
c: "c:xxx", //commit(s) id
type: "main", // "stream", "detached", "branch", "in_memory" (does not save)
display: "c:X", // or main or stream or a:xx or branch:X (only 7 chars)
attachments: 1,
class: "data/graph",
title: false,
icon: false,
description: "",
app: "n:g:z:json_ld_editor", // current app being used
},
view_or_edit: false,
graph_viewer: "n:g:z:json_ld_editor", // selected viewer
graph_editor: "n:g:z:json_ld_editor", // selected editor
discrete_viewer: "n:g:z:json_ld_editor", // selected viewer
discrete_editor: "n:g:z:json_ld_editor", // selected editor
graph_viewers: ["n:g:z:json_ld_editor"], // list of available viewers
graph_editors: ["n:g:z:json_ld_editor"], // list of available editors
discrete_viewers: [], // list of available viewers
discrete_editors: [], // list of available editors
find: false,//or string to find
graph_or_discrete: true,
read_cap: 'r:',
doc: {
is_store: false,
is_member: false,
can_edit: false,
live_edit: true,
title: "Doc A",
authors: "",
icon: "",
description: "",
stream : {
notif: 1,
last: "",
},
live_editors: {
},
},
folders_pane: false,
toc_pane: false,
right_pane: false, // "folders", "toc", "branches", "files", "history", "comments", "info", "chat"
action: false, // "view_as", "edit_with", "share", "react", "repost", "copy", "dm_author", "new_block", "notifs", "schema", "signature", "permissions", "query",
});
export const opened_wallets = writable({}); export const opened_wallets = writable({});
/// { wallet:, id: } /// { wallet:, id: }
@ -52,14 +172,14 @@ const updateConnectionStatus = ($connections: Record<string, any> ) => {
console.log("will try reconnect in 20 sec"); console.log("will try reconnect in 20 sec");
next_reconnect = setTimeout(async () => { next_reconnect = setTimeout(async () => {
await reconnect(); await reconnect();
connection_status.set("connecting");
next_reconnect = null; next_reconnect = null;
}, 20000); }, 20000);
} }
if (is_connected) { if (is_connected) {
connection_status.set("connected"); connection_status.set("connected");
} else if (!is_connected && is_connecting) { } else if (is_connecting) {
connection_status.set("connecting"); connection_status.set("connecting");
} else { } else {
connection_status.set("disconnected"); connection_status.set("disconnected");
@ -73,14 +193,18 @@ export const online = derived(connection_status,($connectionStatus) => $connecti
export const cannot_load_offline = writable(false); export const cannot_load_offline = writable(false);
if (!get(online) && !import.meta.env.TAURI_PLATFORM) { if (get(connection_status) == "disconnected" && !import.meta.env.TAURI_PLATFORM) {
cannot_load_offline.set(true); cannot_load_offline.set(true);
let unsubscribe = online.subscribe(async (value) => { let unsubscribe = connection_status.subscribe(async (value) => {
if (value) { if (value != "disconnected") {
cannot_load_offline.set(false); cannot_load_offline.set(false);
if (value == "connected") {
unsubscribe(); unsubscribe();
} }
} else {
cannot_load_offline.set(true);
}
}); });
} }
@ -142,6 +266,7 @@ export const reconnect = async function() {
return; return;
} }
console.log("attempting to connect..."); console.log("attempting to connect...");
if (!get(online)) connection_status.set("connecting");
try { try {
let info = await ng.client_info() let info = await ng.client_info()
//console.log("Connecting with",get(active_session).user); //console.log("Connecting with",get(active_session).user);

@ -16,6 +16,34 @@
padding-bottom: 1em; padding-bottom: 1em;
} }
@keyframes pulse-logo-color {
0%,
100% {
fill: rgb(73, 114, 165);
stroke: rgb(73, 114, 165);
}
50% {
/* Mid-transition color */
stroke: #bbb;
fill: #bbb;
}
}
.logo-pulse path {
animation: pulse-logo-color 2s infinite;
animation-timing-function: cubic-bezier(0.65, 0.01, 0.59, 0.83);
}
.logo-gray path {
fill: #bbb;
stroke: #bbb;
}
.logo-blue path {
fill: rgb(73, 114, 165);
stroke: rgb(73, 114, 165);
}
.container3 { .container3 {
margin: 0; margin: 0;
min-width: 280px; min-width: 280px;

@ -1243,8 +1243,14 @@ let face = [
}, },
]; ];
let svgs_loaded = Promise.resolve(false);
/** Loads SVGs if they are not yet. TODO: This could probably be sped up by not awaiting sequentially. */
export async function load_svg() { export async function load_svg() {
if (await svgs_loaded) {
return;
}
svgs_loaded = new Promise(async (resolve) => {
face[0].svg = await import("./assets/pazzle/emoji_u1f600.svg?component"); face[0].svg = await import("./assets/pazzle/emoji_u1f600.svg?component");
face[1].svg = await import("./assets/pazzle/emoji_u1f602.svg?component"); face[1].svg = await import("./assets/pazzle/emoji_u1f602.svg?component");
face[2].svg = await import("./assets/pazzle/emoji_u1f607.svg?component"); face[2].svg = await import("./assets/pazzle/emoji_u1f607.svg?component");
@ -1371,48 +1377,28 @@ export async function load_svg() {
/************** EMOTION *********************/ /************** EMOTION *********************/
emotion[0].svg = await import( emotion[0].svg = await import("./assets/pazzle/emoji_u1f48c.svg?component");
"./assets/pazzle/emoji_u1f48c.svg?component"
);
emotion[1].svg = await import("./assets/pazzle/emoji_u2764.svg?component"); emotion[1].svg = await import("./assets/pazzle/emoji_u2764.svg?component");
emotion[2].svg = await import( emotion[2].svg = await import("./assets/pazzle/emoji_u1f495.svg?component");
"./assets/pazzle/emoji_u1f495.svg?component"
);
emotion[3].svg = await import( emotion[3].svg = await import("./assets/pazzle/emoji_u1f48b.svg?component");
"./assets/pazzle/emoji_u1f48b.svg?component" emotion[4].svg = await import("./assets/pazzle/emoji_u1f4af.svg?component");
); emotion[5].svg = await import("./assets/pazzle/emoji_u1f4a5.svg?component");
emotion[4].svg = await import(
"./assets/pazzle/emoji_u1f4af.svg?component"
);
emotion[5].svg = await import(
"./assets/pazzle/emoji_u1f4a5.svg?component"
);
emotion[6].svg = await import( emotion[6].svg = await import("./assets/pazzle/emoji_u1f4a6.svg?component");
"./assets/pazzle/emoji_u1f4a6.svg?component" emotion[7].svg = await import("./assets/pazzle/emoji_u1f91d.svg?component");
); emotion[8].svg = await import("./assets/pazzle/emoji_u1f590.svg?component");
emotion[7].svg = await import(
"./assets/pazzle/emoji_u1f91d.svg?component"
);
emotion[8].svg = await import(
"./assets/pazzle/emoji_u1f590.svg?component"
);
emotion[9].svg = await import("./assets/pazzle/emoji_u270c.svg?component"); emotion[9].svg = await import("./assets/pazzle/emoji_u270c.svg?component");
emotion[10].svg = await import( emotion[10].svg = await import(
"./assets/pazzle/emoji_u1f44d.svg?component" "./assets/pazzle/emoji_u1f44d.svg?component"
); );
emotion[11].svg = await import( emotion[11].svg = await import("./assets/pazzle/emoji_u270a.svg?component");
"./assets/pazzle/emoji_u270a.svg?component"
);
emotion[12].svg = await import( emotion[12].svg = await import(
"./assets/pazzle/emoji_u1f450.svg?component" "./assets/pazzle/emoji_u1f450.svg?component"
); );
emotion[13].svg = await import( emotion[13].svg = await import("./assets/pazzle/emoji_u270d.svg?component");
"./assets/pazzle/emoji_u270d.svg?component"
);
emotion[14].svg = await import( emotion[14].svg = await import(
"./assets/pazzle/emoji_u1f64f.svg?component" "./assets/pazzle/emoji_u1f64f.svg?component"
); );
@ -1463,47 +1449,91 @@ export async function load_svg() {
/************** BIGGER ANIMAL *********************/ /************** BIGGER ANIMAL *********************/
bigger_animal[0].svg = await import("./assets/pazzle/emoji_u1f981.svg?component"); bigger_animal[0].svg = await import(
bigger_animal[1].svg = await import("./assets/pazzle/emoji_u1f406.svg?component"); "./assets/pazzle/emoji_u1f981.svg?component"
bigger_animal[2].svg = await import("./assets/pazzle/emoji_u1f434.svg?component"); );
bigger_animal[1].svg = await import(
"./assets/pazzle/emoji_u1f406.svg?component"
);
bigger_animal[2].svg = await import(
"./assets/pazzle/emoji_u1f434.svg?component"
);
bigger_animal[3].svg = await import("./assets/pazzle/emoji_u1f993.svg?component"); bigger_animal[3].svg = await import(
bigger_animal[4].svg = await import("./assets/pazzle/emoji_u1f416.svg?component"); "./assets/pazzle/emoji_u1f993.svg?component"
bigger_animal[5].svg = await import("./assets/pazzle/emoji_u1f410.svg?component"); );
bigger_animal[4].svg = await import(
"./assets/pazzle/emoji_u1f416.svg?component"
);
bigger_animal[5].svg = await import(
"./assets/pazzle/emoji_u1f410.svg?component"
);
bigger_animal[6].svg = await import("./assets/pazzle/emoji_u1f411.svg?component"); bigger_animal[6].svg = await import(
bigger_animal[7].svg = await import("./assets/pazzle/emoji_u1f42a.svg?component"); "./assets/pazzle/emoji_u1f411.svg?component"
bigger_animal[8].svg = await import("./assets/pazzle/emoji_u1f992.svg?component"); );
bigger_animal[7].svg = await import(
"./assets/pazzle/emoji_u1f42a.svg?component"
);
bigger_animal[8].svg = await import(
"./assets/pazzle/emoji_u1f992.svg?component"
);
bigger_animal[9].svg = await import("./assets/pazzle/emoji_u1f418.svg?component"); bigger_animal[9].svg = await import(
"./assets/pazzle/emoji_u1f418.svg?component"
);
bigger_animal[10].svg = await import( bigger_animal[10].svg = await import(
"./assets/pazzle/emoji_u1f98f.svg?component" "./assets/pazzle/emoji_u1f98f.svg?component"
); );
bigger_animal[11].svg = await import("./assets/pazzle/emoji_u1f9a9.svg?component"); bigger_animal[11].svg = await import(
"./assets/pazzle/emoji_u1f9a9.svg?component"
);
bigger_animal[12].svg = await import("./assets/pazzle/emoji_u1f433.svg?component"); bigger_animal[12].svg = await import(
bigger_animal[13].svg = await import("./assets/pazzle/emoji_u1f42c.svg?component"); "./assets/pazzle/emoji_u1f433.svg?component"
);
bigger_animal[13].svg = await import(
"./assets/pazzle/emoji_u1f42c.svg?component"
);
bigger_animal[14].svg = await import( bigger_animal[14].svg = await import(
"./assets/pazzle/emoji_u1f43b_200d_2744.svg?component" "./assets/pazzle/emoji_u1f43b_200d_2744.svg?component"
); );
/************** SMALLER ANIMAL *********************/ /************** SMALLER ANIMAL *********************/
smaller_animal[0].svg = await import("./assets/pazzle/emoji_u1f413.svg?component"); smaller_animal[0].svg = await import(
smaller_animal[1].svg = await import("./assets/pazzle/emoji_u1f423.svg?component"); "./assets/pazzle/emoji_u1f413.svg?component"
smaller_animal[2].svg = await import("./assets/pazzle/emoji_u1f985.svg?component"); );
smaller_animal[1].svg = await import(
"./assets/pazzle/emoji_u1f423.svg?component"
);
smaller_animal[2].svg = await import(
"./assets/pazzle/emoji_u1f985.svg?component"
);
smaller_animal[3].svg = await import("./assets/pazzle/emoji_u1f986.svg?component"); smaller_animal[3].svg = await import(
smaller_animal[4].svg = await import("./assets/pazzle/emoji_u1f989.svg?component"); "./assets/pazzle/emoji_u1f986.svg?component"
);
smaller_animal[4].svg = await import(
"./assets/pazzle/emoji_u1f989.svg?component"
);
smaller_animal[5].svg = await import( smaller_animal[5].svg = await import(
"./assets/pazzle/emoji_u1f407.svg?component" "./assets/pazzle/emoji_u1f407.svg?component"
); );
smaller_animal[6].svg = await import("./assets/pazzle/emoji_u1f427.svg?component"); smaller_animal[6].svg = await import(
smaller_animal[7].svg = await import("./assets/pazzle/emoji_u1f98e.svg?component"); "./assets/pazzle/emoji_u1f427.svg?component"
smaller_animal[8].svg = await import("./assets/pazzle/emoji_u1f422.svg?component"); );
smaller_animal[7].svg = await import(
"./assets/pazzle/emoji_u1f98e.svg?component"
);
smaller_animal[8].svg = await import(
"./assets/pazzle/emoji_u1f422.svg?component"
);
smaller_animal[9].svg = await import("./assets/pazzle/emoji_u1f40d.svg?component"); smaller_animal[9].svg = await import(
"./assets/pazzle/emoji_u1f40d.svg?component"
);
smaller_animal[10].svg = await import( smaller_animal[10].svg = await import(
"./assets/pazzle/emoji_u1f994.svg?component" "./assets/pazzle/emoji_u1f994.svg?component"
); );
@ -1511,9 +1541,15 @@ export async function load_svg() {
"./assets/pazzle/emoji_u1f987.svg?component" "./assets/pazzle/emoji_u1f987.svg?component"
); );
smaller_animal[12].svg = await import("./assets/pazzle/emoji_u1f41f.svg?component"); smaller_animal[12].svg = await import(
smaller_animal[13].svg = await import("./assets/pazzle/emoji_u1f41a.svg?component"); "./assets/pazzle/emoji_u1f41f.svg?component"
smaller_animal[14].svg = await import("./assets/pazzle/emoji_u1f419.svg?component"); );
smaller_animal[13].svg = await import(
"./assets/pazzle/emoji_u1f41a.svg?component"
);
smaller_animal[14].svg = await import(
"./assets/pazzle/emoji_u1f419.svg?component"
);
/************** PLANTS *********************/ /************** PLANTS *********************/
@ -1552,22 +1588,12 @@ export async function load_svg() {
fruits[8].svg = await import("./assets/pazzle/emoji_u1fad0.svg?component"); fruits[8].svg = await import("./assets/pazzle/emoji_u1fad0.svg?component");
fruits[9].svg = await import("./assets/pazzle/emoji_u1f95d.svg?component"); fruits[9].svg = await import("./assets/pazzle/emoji_u1f95d.svg?component");
fruits[10].svg = await import( fruits[10].svg = await import("./assets/pazzle/emoji_u1f951.svg?component");
"./assets/pazzle/emoji_u1f951.svg?component" fruits[11].svg = await import("./assets/pazzle/emoji_u1f346.svg?component");
);
fruits[11].svg = await import(
"./assets/pazzle/emoji_u1f346.svg?component"
);
fruits[12].svg = await import( fruits[12].svg = await import("./assets/pazzle/emoji_u1f955.svg?component");
"./assets/pazzle/emoji_u1f955.svg?component" fruits[13].svg = await import("./assets/pazzle/emoji_u1f33d.svg?component");
); fruits[14].svg = await import("./assets/pazzle/emoji_u1f336.svg?component");
fruits[13].svg = await import(
"./assets/pazzle/emoji_u1f33d.svg?component"
);
fruits[14].svg = await import(
"./assets/pazzle/emoji_u1f336.svg?component"
);
/************** FOOD *********************/ /************** FOOD *********************/
@ -1606,18 +1632,12 @@ export async function load_svg() {
travel[8].svg = await import("./assets/pazzle/emoji_u1f682.svg?component"); travel[8].svg = await import("./assets/pazzle/emoji_u1f682.svg?component");
travel[9].svg = await import("./assets/pazzle/emoji_u1f695.svg?component"); travel[9].svg = await import("./assets/pazzle/emoji_u1f695.svg?component");
travel[10].svg = await import( travel[10].svg = await import("./assets/pazzle/emoji_u1f3cd.svg?component");
"./assets/pazzle/emoji_u1f3cd.svg?component"
);
travel[11].svg = await import("./assets/pazzle/emoji_u26f5.svg?component"); travel[11].svg = await import("./assets/pazzle/emoji_u26f5.svg?component");
travel[12].svg = await import("./assets/pazzle/emoji_u2708.svg?component"); travel[12].svg = await import("./assets/pazzle/emoji_u2708.svg?component");
travel[13].svg = await import( travel[13].svg = await import("./assets/pazzle/emoji_u1f681.svg?component");
"./assets/pazzle/emoji_u1f681.svg?component" travel[14].svg = await import("./assets/pazzle/emoji_u1f680.svg?component");
);
travel[14].svg = await import(
"./assets/pazzle/emoji_u1f680.svg?component"
);
/************** SKY *********************/ /************** SKY *********************/
@ -1685,6 +1705,8 @@ export async function load_svg() {
house[13].svg = await import("./assets/pazzle/emoji_u1f9fd.svg?component"); house[13].svg = await import("./assets/pazzle/emoji_u1f9fd.svg?component");
house[14].svg = await import("./assets/pazzle/emoji_u1f6d2.svg?component"); house[14].svg = await import("./assets/pazzle/emoji_u1f6d2.svg?component");
resolve(true);
});
} }
export const emojis = { export const emojis = {
@ -1725,7 +1747,7 @@ export const emoji_cat = [
"emotion", "emotion",
]; ];
export function display_pazzle(pazzle) { export function display_pazzle(pazzle: number[]) {
let res = []; let res = [];
for (const emoji of pazzle) { for (const emoji of pazzle) {
let cat = (emoji & 240) >> 4; let cat = (emoji & 240) >> 4;
@ -1734,3 +1756,12 @@ export function display_pazzle(pazzle) {
} }
return res; return res;
} }
export function emojis_from_pazzle_ids(pazzle: number[]) {
return pazzle.map((emoji_id) => {
const cat_id = (emoji_id & 240) >> 4;
const cat_name = emoji_cat[cat_id];
let idx = emoji_id & 15;
return { cat: cat_name, ...emojis[cat_name][idx] };
});
}

@ -7,11 +7,20 @@ onmessage = (e) => {
//console.log("Message received by worker", e.data); //console.log("Message received by worker", e.data);
(async function() { (async function() {
try { try {
let secret_wallet = await ng.wallet_open_with_pazzle( let secret_wallet;
if (e.data.pazzle) {
secret_wallet = await ng.wallet_open_with_pazzle(
e.data.wallet, e.data.wallet,
e.data.pazzle, e.data.pazzle,
e.data.pin_code e.data.pin_code
); );
} else if (e.data.mnemonic_words) {
secret_wallet = await ng.wallet_open_with_mnemonic_words(
e.data.wallet,
e.data.mnemonic_words,
e.data.pin_code
);
}
postMessage({success:secret_wallet}); postMessage({success:secret_wallet});
} catch (e) { } catch (e) {
postMessage({error:e}); postMessage({error:e});

@ -0,0 +1,629 @@
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
// "n:g:z:hide" >> hides when in viewer mode
//"n:g:z:json_ld_editor", "n:g:z:json_editor", "n:g:z:triple_editor", "n:g:z:rdf_viewer:turtle", "n:g:z:rdf_viewer:n3", "n:g:z:rdf_viewer:json_ld", "n:g:z:rdf_viewer:graph",
//"n:g:z:sparql_query:yasgui", "n:g:z:sparql_query:sparnatural", "n:g:z:graphql_query", "n:g:z:sparql_update:yasgui", "n:g:z:ontology_editor", "n:g:z:owl_viewer", "n:g:z:sparql:invoke", "n:g:z:graphql:invoke", "n:g:z:dump_download",
// "n:g:z:post_rich_editor", "n:g:z:post_md_editor", "n:g:z:code_editor", "n:g:z:file_viewer", "n:g:z:file_source", "n:g:z:crdt_source_viewer:xml", "n:g:z:crdt_source_viewer:json", "n:g:z:crdt_source_viewer:text", "n:g:z:crdt_source_viewer:rdf"
//"n:g:z:post:rich", "n:g:z:post:md", "n:g:z:text", "n:g:z:pre", "n:g:z:pad", "n:g:z:card", "n:g:z:gallery", "n:g:z:columns", "n:g:z:tree", "n:g:z:summary", "n:g:z:list", "n:g:z:grid", "n:g:z:list_n_post", "n:g:z:grid_n_post", "n:g:z:board",
//"n:g:z:map", "n:g:z:chart", "n:g:z:pivot", "n:g:z:timeline", "n:g:z:email", "n:g:z:web_archive", "n:g:z:diagram_editor", "n:g:z:pdf", "n:g:z:latex", "n:g:z:media", "n:g:z:media_editor",
//"n:g:z:service_editor", "n:g:z:service_invoke", "n:g:z:external_service_invoke", "n:g:z:contract", "n:g:z:text_query", "n:g:z:web_query", "n:g:z:scan_qrcode", "n:g:z:upload_file",
//"n:g:z:messenger", "n:g:z:group", "n:g:z:contact", "n:g:z:event", "n:g:z:calendar", "n:g:z:scheduler",
//"n:g:z:task", "n:g:z:project", "n:g:z:issue", "n:g:z:form_editor", "n:g:z:form_filler", "n:g:z:cad", "n:g:z:slides", "n:g:z:question", "n:g:z:poll",
//"n:g:z:app_store", "n:g:z:app_editor", "n:xxx.xx.xx:yy", "o:xx:yy:zz"
export const official_apps = {
"n:g:z:json_ld_editor": {
"ng:n": "JSON-LD editor",
"ng:a": "View and edit the RDF Graph as JSON-LD",
"ng:c": "app",
"ng:u": "json_ld_editor",//favicon. can be a did:ng:j
"ng:g": "n:g:z:json_ld_editor",
"ng:b": "JsonLdEditor",
"ng:o": ["data/graph"],
"ng:w": ["data/graph"],
},
"n:g:z:json_editor": {
"ng:n": "JSON editor",
"ng:a": "View and edit your JSON data",
"ng:c": "app",
"ng:u": "json_editor",//favicon. can be a did:ng:j
"ng:g": "n:g:z:json_editor",
"ng:b": "JsonEditor",
"ng:o": ["data/json","data/array","data/map"],
"ng:w": ["data/json","data/array","data/map"],
},
"n:g:z:triple_editor": {
"ng:n": "Graph triples",
"ng:a": "Edit your RDF Graph as triples",
"ng:c": "app",
"ng:u": "triple_editor",//favicon. can be a did:ng:j
"ng:g": "n:g:z:triple_editor",
"ng:b": "TripleEditor",
"ng:o": ["data/graph"],
"ng:w": ["data/graph"],
},
"n:g:z:rdf_viewer:turtle": { // https://github.com/highlightjs/highlightjs-turtle/tree/master
"ng:n": "Turtle",
"ng:a": "View your RDF Graph in Turtle format",
"ng:c": "app",
"ng:u": "rdf_viewer",//favicon. can be a did:ng:j
"ng:g": "n:g:z:rdf_viewer:turtle",
"ng:b": "TurtleViewer",
"ng:o": ["data/graph"],
"ng:w": [],
},
"n:g:z:rdf_viewer:n3": { // ?
"ng:n": "N3",
"ng:a": "View your RDF Graph in N3 format",
"ng:c": "app",
"ng:u": "rdf_viewer",//favicon. can be a did:ng:j
"ng:g": "n:g:z:rdf_viewer:n3",
"ng:b": "N3Viewer",
"ng:o": ["data/graph"],
"ng:w": [],
},
"n:g:z:rdf_viewer:json_ld": { // highlight.js JSON
"ng:n": "JSON-LD",
"ng:a": "View your RDF Graph in N3 format",
"ng:c": "app",
"ng:u": "rdf_viewer",//favicon. can be a did:ng:j
"ng:g": "n:g:z:rdf_viewer:json_ld",
"ng:b": "JsonLdViewer",
"ng:o": ["data/graph"],
"ng:w": [],
},
"n:g:z:rdf_viewer:graph": {
"ng:n": "Graph Explorer",
"ng:a": "View your RDF Graph as interactive visualization",
"ng:c": "app",
"ng:u": "graph_viewer",//favicon. can be a did:ng:j
"ng:g": "n:g:z:rdf_viewer:graph",
"ng:b": "GraphViewer", // GraphExplorer https://github.com/zazuko/graph-explorer !! AGPL
"ng:o": ["data/graph"],
"ng:w": [],
},
"n:g:z:sparql_query:yasgui": {
"ng:n": "SPARQL Query",
"ng:a": "View, edit and invoke your Graph SPARQL query",
"ng:c": "app",
"ng:u": "sparql_query",//favicon. can be a did:ng:j
"ng:g": "n:g:z:sparql_query:yasgui",
"ng:b": "SparqlQueryEditor", // YASGUI of Zazuko https://github.com/zazuko/trifid/tree/main/packages/yasgui
"ng:o": [],
"ng:w": ["query/sparql"],
},
"n:g:z:sparql_query:sparnatural": {
"ng:n": "SPARQL natural Query",
"ng:a": "View, edit and invoke your Graph SPARQL query with SPARnatural tool",
"ng:c": "app",
"ng:u": "sparnatural",//favicon. can be a did:ng:j
"ng:g": "n:g:z:sparql_query:sparnatural",
"ng:b": "SparNaturalEditor",
"ng:o": [],
"ng:w": ["query/sparql"],
},
"n:g:z:graphql_query": {
"ng:n": "GraphQL Query",
"ng:a": "View, edit and invoke your GraphQL query",
"ng:c": "app",
"ng:u": "graphql",//favicon. can be a did:ng:j
"ng:g": "n:g:z:graphql_query",
"ng:b": "GraphqlEditor",
"ng:o": [],
"ng:w": ["query/graphql"],
},
"n:g:z:sparql_update:yasgui": {
"ng:n": "SPARQL Update",
"ng:a": "View, edit and invoke your Graph SPARQL Update",
"ng:c": "app",
"ng:u": "sparql_query",//favicon. can be a did:ng:j
"ng:g": "n:g:z:sparql_update:yasgui",
"ng:b": "SparqlUpdateEditor", // YASGUI of Zazuko https://github.com/zazuko/trifid/tree/main/packages/yasgui
"ng:o": [],
"ng:w": ["query/sparql_update"],
},
"n:g:z:ontology_editor": {
"ng:n": "Ontology editor",
"ng:a": "Edit your Ontology",
"ng:c": "app",
"ng:u": "json_ld_editor",//favicon. can be a did:ng:j
"ng:g": "n:g:z:ontology_editor",
"ng:b": "JsonLdEditor",
"ng:o": [],
"ng:w": ["schema/*"],
},
"n:g:z:owl_viewer": {
"ng:n": "OWL Ontology",
"ng:a": "View your OWL Ontology",
"ng:c": "app",
"ng:u": "ontology_viewer",//favicon. can be a did:ng:j
"ng:g": "n:g:z:owl_viewer",
"ng:b": "OwlViewer", // display with https://github.com/VisualDataWeb/WebVOWL
"ng:o": ["schema/owl"],
"ng:w": [],
},
"n:g:z:sparql:invoke": { // displayed with highlight.js https://github.com/highlightjs/highlightjs-turtle/tree/master
"ng:n": "SPARQL Invoke",
"ng:a": "View and invoke your saved SPARQL query",
"ng:c": "app",
"ng:u": "invoke",//favicon. can be a did:ng:j
"ng:g": "n:g:z:sparql:invoke",
"ng:b": "SparqlInvoker",
"ng:o": ["query/sparql","query/sparql_update"],
"ng:w": [],
},
"n:g:z:graphql:invoke": {
"ng:n": "GraphQL Invoke",
"ng:a": "View and invoke your saved GraphQL query",
"ng:c": "app",
"ng:u": "invoke",//favicon. can be a did:ng:j
"ng:g": "n:g:z:graphql:invoke",
"ng:b": "GraphqlInvoker",
"ng:o": ["query/graphql"],
"ng:w": [],
},
"n:g:z:dump_download": {
"ng:n": "Download",
"ng:a": "Download a file containing a document export data",
"ng:c": "app",
"ng:u": "download",//favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_download",
"ng:b": "Downloader",
"ng:o": ["data/graph","file*","data/*"],
"ng:w": [],
},
"n:g:z:post_rich_editor": {
"ng:n": "Post Editor",
"ng:a": "Edit your post with ProseMirror",
"ng:c": "app",
"ng:u": "post_edit",//favicon. can be a did:ng:j
"ng:g": "n:g:z:post_rich_editor",
"ng:b": "ProseMirrorEditor",
"ng:o": [],
"ng:w": ["post/rich"],
},
"n:g:z:post_md_editor": {
"ng:n": "Post Editor",
"ng:a": "Edit your post with MilkDown",
"ng:c": "app",
"ng:u": "post_edit",//favicon. can be a did:ng:j
"ng:g": "n:g:z:post_md_editor",
"ng:b": "MilkDownEditor",
"ng:o": [],
"ng:w": ["post/md"],
},
"n:g:z:code_editor": {
"ng:n": "Code and Text Editor",
"ng:a": "Edit your code/text with CodeMirror",
"ng:c": "app",
"ng:u": "post_edit",//favicon. can be a did:ng:j
"ng:g": "n:g:z:code_editor",
"ng:b": "CodeMirrorEditor",
"ng:o": [],
"ng:w": ["code*","post/text"],
},
"n:g:z:file_viewer": {
"ng:n": "File details",
"ng:a": "See details about this file",
"ng:c": "app",
"ng:u": "file",//favicon. can be a did:ng:j
"ng:g": "n:g:z:file_viewer",
"ng:b": "FileDetails",
"ng:o": ["file*"],
"ng:w": ["file*"], // in editor mode: can change the name, and delete the file
},
"n:g:z:file_source": { // only works for files containing text source (SVG, HTML, JS, CSS, etc...)
"ng:n": "File source",
"ng:a": "See the source code of this file",
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:file_source",
"ng:b": "FileSource",
"ng:o": ["file/text"],
"ng:w": [],
},
"n:g:z:crdt_source_viewer:xml": {
"ng:n": "XML source",
"ng:a": "See the source code of this document, in XML",
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:xml",
"ng:b": "XmlSource", // displayed with highlight.js , with option to download
"ng:o": ["post/rich","post/md","post/html","page","data/xml", "doc/diagram/drawio"],
"ng:w": [],
},
"n:g:z:crdt_source_viewer:json": {
"ng:n": "JSON source",
"ng:a": "See the source code of this document, in JSON",
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:json",
"ng:b": "JsonSource", // displayed with highlight.js , with option to download
"ng:o": ["data/json", "data/map", "data/array", "data/table", "doc/diagram/jsmind", "doc/diagram/gantt", "doc/diagram/excalidraw", "doc/viz/*", "doc/chart/*", "prod/cad"],
"ng:w": [],
},
"n:g:z:crdt_source_viewer:text": {
"ng:n": "Text source",
"ng:a": "See the source code of this document, in plain-text",
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:text",
"ng:b": "TextViewer", // displayed with highlight.js , with option to download
"ng:o": ["post/text", "post/asciidoc", "code*", "service*", "contract", "query/sparql*","query/graphql","doc/diagram/mermaid","doc/diagram/graphviz","doc/diagram/flowchart",
"doc/diagram/sequence","doc/diagram/markmap","doc/diagram/mymind","doc/music*", "doc/maths", "doc/chemistry", "doc/ancientscript", "doc/braille", "media/subtitle"],
"ng:w": [],
},
"n:g:z:crdt_source_viewer:rdf": {
"ng:n": "RDF source",
"ng:a": "See the source graph of this document, in RDF (turtle)",
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:rdf",
"ng:b": "TurtleViewer", //, with option to download
"ng:o": ["data/graph"],
"ng:w": [],
},
"n:g:z:post:rich": {
"ng:n": "Post",
"ng:a": "View a Rich Post",
"ng:c": "app",
"ng:u": "post",//favicon. can be a did:ng:j
"ng:g": "n:g:z:post:rich",
"ng:b": "PostRichViewer", // https://www.npmjs.com/package/prosemirror-to-html-js or https://prosemirror.net/docs/ref/version/0.4.0.html#toDOM https://prosemirror.net/docs/ref/version/0.4.0.html#toHTML
"ng:o": ["post/rich"],
"ng:w": [],
},
"n:g:z:post:md": {
"ng:n": "Post",
"ng:a": "View a Markdown Post",
"ng:c": "app",
"ng:u": "post",//favicon. can be a did:ng:j
"ng:g": "n:g:z:post:md",
"ng:b": "PostMdViewer", // https://github.com/wooorm/markdown-rs
"ng:o": ["post/md"],
"ng:w": [],
},
"n:g:z:post:text": {
"ng:n": "Text",
"ng:a": "View a Text Post",
"ng:c": "app",
"ng:u": "post",//favicon. can be a did:ng:j
"ng:g": "n:g:z:post:text",
"ng:b": "TextViewer", // displayed with a <p>
"ng:o": ["post/text"],
"ng:w": [],
},
"n:g:z:pre": {
"ng:n": "Source Code",
"ng:a": "View a Source Code",
"ng:c": "app",
"ng:u": "post",//favicon. can be a did:ng:j
"ng:g": "n:g:z:pre",
"ng:b": "PreTextViewer", // displayed with highlight.js
"ng:o": ["code*","post/text"],
"ng:w": [],
},
"n:g:z:pad": {
"ng:n": "Pad",
"ng:a": "Pad view of a document",
"ng:c": "app",
"ng:u": "pad",//favicon. can be a did:ng:j
"ng:g": "n:g:z:pad",
"ng:b": "Pad",
"ng:o": ["plato/pad"],
"ng:w": [],
},
"n:g:z:card": {
"ng:n": "Card",
"ng:a": "Card view of a document",
"ng:c": "app",
"ng:u": "card",//favicon. can be a did:ng:j
"ng:g": "n:g:z:card",
"ng:b": "Card",
"ng:o": ["plato/card"],
"ng:w": [],
},
"n:g:z:gallery": {
"ng:n": "Gallery",
"ng:a": "Gallery view of an album or collection",
"ng:c": "app",
"ng:u": "gallery",//favicon. can be a did:ng:j
"ng:g": "n:g:z:gallery",
"ng:b": "Gallery",
"ng:o": ["media/album","data/collection"],
"ng:w": [],
},
"n:g:z:app_store": {
"ng:n": "App Store",
"ng:a": "Install or remove Apps of NextGraph ecosystem",
"ng:c": "app",
"ng:u": "app_store",//favicon. can be a did:ng:j
"ng:g": "n:g:z:app_store",
"ng:b": "AppStore",
"ng:o": ["app/z"],
"ng:w": [],
},
"n:g:z:app_editor": {
"ng:n": "App editor",
"ng:a": "Create and modify your App with NextGraph IDE",
"ng:c": "app",
"ng:u": "app_editor",//favicon. can be a did:ng:j
"ng:g": "n:g:z:app_editor",
"ng:b": "AppEditor",
"ng:o": ["app/z"],
"ng:w": ["app/z"],
},
"n:g:z:list": {
"ng:n": "List view",
"ng:a": "See the content of document as a list",
"ng:c": "app",
"ng:u": "list",//favicon. can be a did:ng:j
"ng:g": "n:g:z:list",
"ng:b": "ListView",
"ng:o": ["data/graph"],
"ng:w": ["data/graph"],
},
"n:g:z:grid": {
"ng:n": "Grid view",
"ng:a": "See the content of document as a grid",
"ng:c": "app",
"ng:u": "grid",//favicon. can be a did:ng:j
"ng:g": "n:g:z:grid",
"ng:b": "GridView",
"ng:o": ["data/graph"],
"ng:w": ["data/graph"],
},
"n:g:z:media": {
"ng:n": "Media view",
"ng:a": "View media",
"ng:c": "app",
"ng:u": "view",//favicon. can be a did:ng:j
"ng:g": "n:g:z:media",
"ng:b": "MediaView",
"ng:o": ["media/*"],
"ng:w": [],
},
"n:g:z:service_editor": {
"ng:n": "Service Editor",
"ng:a": "Write and define your Service in Rust or JS/Deno",
"ng:c": "app",
"ng:u": "post_edit",//favicon. can be a did:ng:j
"ng:g": "n:g:z:service_editor",
"ng:b": "CodeMirrorEditor",
"ng:o": [],
"ng:w": ["service/*"],
},
"n:g:z:service_invoke": {
"ng:n": "Service Invoker",
"ng:a": "Invoke internal Service, with optional arguments",
"ng:c": "app",
"ng:u": "invoke",//favicon. can be a did:ng:j
"ng:g": "n:g:z:service_invoke",
"ng:b": "ServiceInvoker",
"ng:o": ["service"],
"ng:w": [],
},
"n:g:z:external_service_invoke": {
"ng:n": "Service Invoker",
"ng:a": "Invoke the Service, with optional arguments",
"ng:c": "app",
"ng:u": "invoke",//favicon. can be a did:ng:j
"ng:g": "n:g:z:external_service_invoke",
"ng:b": "ExternalServiceInvoker",
"ng:o": ["service/*"],
"ng:w": [],
},
"n:g:z:upload_file": {
"ng:n": "Upload file",
"ng:a": "Upload a binary file into a document",
"ng:c": "app",
"ng:u": "load",//favicon. can be a did:ng:j
"ng:g": "n:g:z:upload_file",
"ng:b": "UploadFile",
"ng:o": [],
"ng:w": ["data/graph"],
},
// TODO: "n:g:z:columns", "n:g:z:tree", "n:g:z:summary", "n:g:z:list_n_post", "n:g:z:grid_n_post", "n:g:z:board",
// TODO: "n:g:z:map", "n:g:z:chart", "n:g:z:pivot", "n:g:z:timeline", "n:g:z:email", "n:g:z:web_archive", "n:g:z:diagram_editor", "n:g:z:pdf", "n:g:z:latex", "n:g:z:media_editor",
// TODO: "n:g:z:contract", "n:g:z:text_query", "n:g:z:web_query", "n:g:z:scan_qrcode",
// TODO: "n:g:z:messenger", "n:g:z:group", "n:g:z:contact", "n:g:z:event", "n:g:z:calendar", "n:g:z:scheduler",
// TODO: "n:g:z:task", "n:g:z:project", "n:g:z:issue", "n:g:z:form_editor", "n:g:z:form_filler", "n:g:z:cad", "n:g:z:slides", "n:g:z:question", "n:g:z:poll",
};
// OFFICIAL SERVICES
//"n:g:z:dump_rdf:turtle", "n:g:z:dump_rdf:n3", "n:g:z:dump_rdf:json_ld", "n:g:z:load_rdf:turtle", "n:g:z:load_rdf:n3", "n:g:z:load_rdf:json_ld", "n:g:z:load_file", "n:g:z:dump_file",
//"n:g:z:dump_json", "n:g:z:dump_xml", "n:g:z:dump_text", "n:g:z:load_json", "n:g:z:load_xml", "n:g:z:load_text", "n:g:z:load_md", "n:g:z:sparql_query", "n:g:z:sparql_update", "n:g:z:dump_crdt_source", "n:g:z:dump_ng_html_file", "n:g:z:dump_ng_file"
export const official_services = {
"n:g:z:dump_rdf:turtle": {
"ng:n": "Turtle export",
"ng:a": "Export quads of RDF Graphs in Turtle format",
"ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_rdf:turtle",
"ng:o": ["data/graph"],
"ng:w": [],
"ng:result": ["file/iana/text/turtle"],
},
"n:g:z:dump_rdf:n3": {
"ng:n": "N3 export",
"ng:a": "Export quads of RDF Graphs in N3 format",
"ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_rdf:n3",
"ng:o": ["data/graph"],
"ng:w": [],
"ng:result": ["file/iana/text/n3"],
},
"n:g:z:dump_rdf:json_ld": {
"ng:n": "JSON-LD export",
"ng:a": "Export quads of RDF Graphs in JSON-LD format",
"ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_rdf:json_ld",
"ng:o": ["data/graph"],
"ng:w": [],
"ng:result": ["file/iana/application/ld+json"],
},
"n:g:z:load_rdf:turtle": {
"ng:n": "Import Turtle triples",
"ng:a": "Import Turtle triples into the document",
"ng:c": "service",
"ng:u": "load_graph",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_rdf:turtle",
"ng:o": [],
"ng:w": ["data/graph"],
},
"n:g:z:load_rdf:n3": {
"ng:n": "Import N3 triples",
"ng:a": "Import N3 triples into the document",
"ng:c": "service",
"ng:u": "load_graph",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_rdf:n3",
"ng:o": [],
"ng:w": ["data/graph"],
},
"n:g:z:load_rdf:json_ld": {
"ng:n": "Import JSON-LD triples",
"ng:a": "Import JSON-LD triples into the document",
"ng:c": "service",
"ng:u": "load_graph",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_rdf:json_ld",
"ng:o": [],
"ng:w": ["data/graph"],
},
"n:g:z:load_file": {
"ng:n": "Add file",
"ng:a": "Add a binary file in the document",
"ng:c": "service",
"ng:u": "load",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_file",
"ng:o": [],
"ng:w": ["data/graph"],
},
"n:g:z:dump_file": {
"ng:n": "Export file",
"ng:a": "Get the binary content of a file",
"ng:c": "service",
"ng:u": "dump",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_file",
"ng:o": ["file*"],
"ng:result": ["file/iana/*"],
},
"n:g:z:dump_json": {
"ng:n": "JSON export",
"ng:a": "Export JSON content of document",
"ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_json",
"ng:o": ["data/json", "data/map", "data/array", "data/table", "doc/diagram/jsmind", "doc/diagram/gantt", "doc/diagram/excalidraw", "doc/viz/*", "doc/chart/*", "prod/cad"],
"ng:w": [],
"ng:result": ["file/iana/application/json"],
},
"n:g:z:dump_xml": {
"ng:n": "XML export",
"ng:a": "Export XML content of document",
"ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_xml",
"ng:o": ["post/rich","post/md","post/html","page","data/xml", "doc/diagram/drawio"],
"ng:w": [],
"ng:result": ["file/iana/text/xml"],
},
"n:g:z:dump_text": {
"ng:n": "Text export",
"ng:a": "Export plain-text content of document",
"ng:c": "service",
"ng:u": "dump",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_text",
"ng:o": ["post/text", "post/asciidoc", "code*", "service*", "contract", "query/sparql*","query/graphql","doc/diagram/mermaid","doc/diagram/graphviz","doc/diagram/flowchart",
"doc/diagram/sequence","doc/diagram/markmap","doc/diagram/mymind","doc/music*", "doc/maths", "doc/chemistry", "doc/ancientscript", "doc/braille", "media/subtitle"],
"ng:w": [],
"ng:result": ["file/iana/text/plain"],
},
"n:g:z:dump_ng_html_file": {
"ng:n": "NextGraph Standalone file",
"ng:a": "Get a standalone HTML file of this Document",
"ng:c": "service",
"ng:u": "ext",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_ng_html_file",
"ng:o": ["data/graph"],
"ng:w": [],
"ng:result": ["file/iana/text/html"],
},
"n:g:z:load_json": {
"ng:n": "Import JSON",
"ng:a": "Import some JSON into the document",
"ng:c": "service",
"ng:u": "load",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_json",
"ng:o": [],
"ng:w": ["data/json","data/map", "data/array"],
},
"n:g:z:load_xml": {
"ng:n": "Import XML",
"ng:a": "Import some XML into the document",
"ng:c": "service",
"ng:u": "load",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_xml",
"ng:o": [],
"ng:w": ["data/xml"],
},
"n:g:z:load_text": {
"ng:n": "Import Text",
"ng:a": "Import some text into the document",
"ng:c": "service",
"ng:u": "load",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_text",
"ng:o": [],
"ng:w": ["post/text","post/rich","post/md"],
},
"n:g:z:load_md": {
"ng:n": "Import Markdown",
"ng:a": "Import some Markdown into the document",
"ng:c": "service",
"ng:u": "load",// favicon. can be a did:ng:j
"ng:g": "n:g:z:load_md",
"ng:o": [],
"ng:w": ["post/md"],
},
"n:g:z:sparql_query": {
"ng:n": "SPARQL query",
"ng:a": "Invoke a SPARQL Query",
"ng:c": "service",
"ng:u": "sparql_query",// favicon. can be a did:ng:j
"ng:g": "n:g:z:sparql_query",
"ng:o": ["data/graph"],
"ng:w": [],
"ng:result": ["file/iana/application/sparql-results+json","file/iana/application/json"]
},
"n:g:z:sparql_update": {
"ng:n": "SPARQL update",
"ng:a": "Invoke a SPARQL Update",
"ng:c": "service",
"ng:u": "sparql_query",// favicon. can be a did:ng:j
"ng:g": "n:g:z:sparql_update",
"ng:o": [],
"ng:w": ["data/graph"],
},
"n:g:z:dump_crdt_source": { // uses dump_rdf, dump_text, dump_json or dump_xml depending on the CRDT type
"ng:n": "Export source",
"ng:a": "Export source of document as text file",
"ng:c": "service",
"ng:u": "source",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_crdt_source",
"ng:o": ["data/graph"],
"ng:w": [],
"ng:result": ["file/iana/*"]
},
};

@ -16,7 +16,15 @@ const config = {
'xxs': '400px', 'xxs': '400px',
'xs': '500px', 'xs': '500px',
...defaultTheme.screens, ...defaultTheme.screens,
'tall': { 'raw': '(min-height: 450px)' }, 'tall': { 'raw': '(min-height: 450px)' },
'tall-xxs': { 'raw': '(min-height: 360px)' },
'tall-xs': { 'raw': '(min-height: 480px)' },
'tall-sm': { 'raw': '(min-height: 640px)' },
'tall-md': { 'raw': '(min-height: 800px)' },
'tall-l': { 'raw': '(min-height: 1000px)' },
'tall-xl': { 'raw': '(min-height: 1200px)' },
'tall-xxl': { 'raw': '(min-height: 1400px)' },
}, },
}, },

@ -2,7 +2,7 @@
"extends": "@tsconfig/svelte/tsconfig.json", "extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": false,
"module": "ESNext", "module": "ESNext",
"resolveJsonModule": true, "resolveJsonModule": true,
"baseUrl": ".", "baseUrl": ".",

@ -560,7 +560,7 @@ pub struct DocAddFile {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DocCreate { pub struct DocCreate {
store: StoreRepo, store: StoreRepo,
content_type: BranchContentType, class: String,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -579,7 +579,7 @@ pub enum AppRequestPayloadV0 {
Delete(DocDelete), Delete(DocDelete),
//Invoke(InvokeArguments), //Invoke(InvokeArguments),
SmallFilePut(SmallFile), SmallFilePut(SmallFile),
RandomAccessFilePut(String), // content_type RandomAccessFilePut(String), // content_type (iana media type)
RandomAccessFilePutChunk((u32, serde_bytes::ByteBuf)), // end the upload with an empty vec RandomAccessFilePutChunk((u32, serde_bytes::ByteBuf)), // end the upload with an empty vec
} }

@ -214,9 +214,7 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> {
GraphName::NamedNode(graph_name) => graph_name.into(), GraphName::NamedNode(graph_name) => graph_name.into(),
GraphName::DefaultGraph => { GraphName::DefaultGraph => {
if let Some(default_graph) = &self.options.query_options.default_graph { if let Some(default_graph) = &self.options.query_options.default_graph {
crate::oxrdf::GraphNameRef::NamedNode(NamedNodeRef::new_unchecked( GraphNameRef::NamedNode(NamedNodeRef::new_unchecked(&default_graph))
&default_graph,
))
} else { } else {
return Err(EvaluationError::NoDefaultGraph); return Err(EvaluationError::NoDefaultGraph);
} }

@ -37,6 +37,7 @@ pub enum NgError {
InvalidArgument, InvalidArgument,
PermissionDenied, PermissionDenied,
InvalidPazzle, InvalidPazzle,
InvalidMnemonic,
CommitLoadError(CommitLoadError), CommitLoadError(CommitLoadError),
ObjectParseError(ObjectParseError), ObjectParseError(ObjectParseError),
StorageError(StorageError), StorageError(StorageError),

@ -1293,7 +1293,7 @@ pub enum Quorum {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum BranchContentType { pub enum BranchContentType {
GraphOnly, Graph,
YMap, YMap,
YXml, YXml,
YText, YText,
@ -1302,8 +1302,7 @@ pub enum BranchContentType {
//Owl, //Owl,
//Shacl, //Shacl,
//Shex, //Shex,
None, // this is used by Store, Overlay and User BranchTypes None, // this is used by Overlay and User BranchTypes
//Chat,
} }
/// Branch definition /// Branch definition

@ -230,3 +230,31 @@ pub fn display_timestamp(ts: &Timestamp) -> String {
} }
pub(crate) type Receiver<T> = mpsc::UnboundedReceiver<T>; pub(crate) type Receiver<T> = mpsc::UnboundedReceiver<T>;
#[cfg(test)]
mod test {
use crate::log::*;
#[test]
pub fn test_locales() {
let list = vec!["C", "c", "aa-bb-cc-dd", "aa-ff_bb.456d"];
let res: Vec<String> = list
.iter()
.filter_map(|lang| {
if *lang == "C" || *lang == "c" {
None
} else {
let mut split = lang.split('.');
let code = split.next().unwrap();
let code = code.replace("_", "-");
let mut split = code.rsplitn(2, '-');
let country = split.next().unwrap();
Some(match split.next() {
Some(next) => format!("{}-{}", next, country.to_uppercase()),
None => country.to_string(),
})
}
})
.collect();
log_debug!("{:?}", res);
}
}

@ -29,6 +29,7 @@ once_cell = "1.17.1"
getrandom = { version = "0.1.1", features = ["wasm-bindgen"] } getrandom = { version = "0.1.1", features = ["wasm-bindgen"] }
rand = { version = "0.7", features = ["getrandom"] } rand = { version = "0.7", features = ["getrandom"] }
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
sys-locale = { version = "0.3.1", features = ["js"] }
ng-repo = { path = "../ng-repo" } ng-repo = { path = "../ng-repo" }
ng-net = { path = "../ng-net" } ng-net = { path = "../ng-net" }
ng-client-ws = { path = "../ng-client-ws" } ng-client-ws = { path = "../ng-client-ws" }

@ -26,6 +26,7 @@ use serde_json::json;
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use js_sys::Array; use js_sys::Array;
use oxrdf::Triple; use oxrdf::Triple;
use sys_locale::get_locales;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::future_to_promise;
use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::JsFuture;
@ -54,6 +55,11 @@ use nextgraph::local_broker::*;
use crate::model::*; use crate::model::*;
#[wasm_bindgen]
pub async fn locales() -> Result<JsValue, JsValue> {
Ok(serde_wasm_bindgen::to_value(&get_locales().collect::<Vec<_>>()).unwrap())
}
#[wasm_bindgen] #[wasm_bindgen]
pub async fn get_local_bootstrap(location: String, invite: JsValue) -> JsValue { pub async fn get_local_bootstrap(location: String, invite: JsValue) -> JsValue {
let res = retrieve_local_bootstrap(location, invite.as_string(), false).await; let res = retrieve_local_bootstrap(location, invite.as_string(), false).await;
@ -134,6 +140,53 @@ pub fn wallet_open_with_pazzle(
} }
} }
#[wasm_bindgen]
pub fn wallet_open_with_mnemonic(
wallet: JsValue,
mnemonic: Vec<u16>,
pin: JsValue,
) -> Result<JsValue, JsValue> {
let encrypted_wallet = serde_wasm_bindgen::from_value::<Wallet>(wallet)
.map_err(|_| "Deserialization error of wallet")?;
let pin = serde_wasm_bindgen::from_value::<[u8; 4]>(pin)
.map_err(|_| "Deserialization error of pin")?;
let res = nextgraph::local_broker::wallet_open_with_mnemonic(&encrypted_wallet, mnemonic, pin);
match res {
Ok(r) => Ok(r
.serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
.unwrap()),
Err(e) => Err(e.to_string().into()),
}
}
#[wasm_bindgen]
pub fn wallet_open_with_mnemonic_words(
wallet: JsValue,
mnemonic_words: Array,
pin: JsValue,
) -> Result<JsValue, JsValue> {
let encrypted_wallet = serde_wasm_bindgen::from_value::<Wallet>(wallet)
.map_err(|_| "Deserialization error of wallet")?;
let pin = serde_wasm_bindgen::from_value::<[u8; 4]>(pin)
.map_err(|_| "Deserialization error of pin")?;
let mnemonic_vec: Vec<String> = mnemonic_words
.iter()
.map(|word| word.as_string().unwrap())
.collect();
let res = nextgraph::local_broker::wallet_open_with_mnemonic_words(
&encrypted_wallet,
&mnemonic_vec,
pin,
);
match res {
Ok(r) => Ok(r
.serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
.unwrap()),
Err(e) => Err(e.to_string().into()),
}
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn wallet_update(wallet_id: JsValue, operations: JsValue) -> Result<JsValue, JsValue> { pub fn wallet_update(wallet_id: JsValue, operations: JsValue) -> Result<JsValue, JsValue> {
let _wallet = serde_wasm_bindgen::from_value::<WalletId>(wallet_id) let _wallet = serde_wasm_bindgen::from_value::<WalletId>(wallet_id)

@ -1,3 +1,6 @@
use ng_repo::errors::NgError;
use std::collections::HashMap;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub const bip39_wordlist: [&str; 2048] = [ pub const bip39_wordlist: [&str; 2048] = [
"abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd",
@ -212,3 +215,26 @@ pub const bip39_wordlist: [&str; 2048] = [
"write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone", "write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone",
"zoo", "zoo",
]; ];
lazy_static! {
pub static ref BIP39_WORD_MAP: HashMap<String, u16> = {
let mut m = HashMap::new();
for (i, word) in bip39_wordlist.iter().enumerate() {
m.insert(word.to_string(), i as u16);
}
m
};
}
/// Taking a list of bip39 words, returns a list of u16 codes
pub fn encode_mnemonic(words: &Vec<String>) -> Result<Vec<u16>, NgError> {
let mut res = vec![];
for word in words {
res.push(
*BIP39_WORD_MAP
.get(word.as_str())
.ok_or(NgError::InvalidMnemonic)?,
);
}
Ok(res)
}

@ -362,7 +362,7 @@ pub fn open_wallet_with_pazzle(
} }
pub fn open_wallet_with_mnemonic( pub fn open_wallet_with_mnemonic(
wallet: Wallet, wallet: &Wallet,
mut mnemonic: [u16; 12], mut mnemonic: [u16; 12],
mut pin: [u8; 4], mut pin: [u8; 4],
) -> Result<SensitiveWallet, NgWalletError> { ) -> Result<SensitiveWallet, NgWalletError> {
@ -388,7 +388,7 @@ pub fn open_wallet_with_mnemonic(
mnemonic_key.zeroize(); mnemonic_key.zeroize();
Ok(SensitiveWallet::V0(dec_encrypted_block( Ok(SensitiveWallet::V0(dec_encrypted_block(
v0.content.encrypted, v0.content.encrypted.clone(),
master_key, master_key,
v0.content.peer_id, v0.content.peer_id,
v0.content.nonce, v0.content.nonce,
@ -786,6 +786,7 @@ pub async fn create_wallet_second_step_v0(
wallet_file, wallet_file,
pazzle, pazzle,
mnemonic: mnemonic.clone(), mnemonic: mnemonic.clone(),
mnemonic_str: display_mnemonic(&mnemonic),
wallet_name: params.wallet_name.clone(), wallet_name: params.wallet_name.clone(),
client: params.client.clone(), client: params.client.clone(),
user, user,
@ -893,7 +894,7 @@ mod test {
let _opening_mnemonic = Instant::now(); let _opening_mnemonic = Instant::now();
let _w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), res.mnemonic, pin.clone()) let _w = open_wallet_with_mnemonic(&Wallet::V0(v0.clone()), res.mnemonic, pin.clone())
.expect("open with mnemonic"); .expect("open with mnemonic");
//log_debug!("encrypted part {:?}", w); //log_debug!("encrypted part {:?}", w);

@ -1303,6 +1303,8 @@ pub struct CreateWalletResultV0 {
/// randomly generated mnemonic. It is an alternate way to open the wallet. /// randomly generated mnemonic. It is an alternate way to open the wallet.
/// A BIP39 list of 12 words. We argue that the Pazzle is easier to remember than this. /// A BIP39 list of 12 words. We argue that the Pazzle is easier to remember than this.
pub mnemonic: [u16; 12], pub mnemonic: [u16; 12],
/// The words of the mnemonic, in a human readable form.
pub mnemonic_str: Vec<String>,
#[zeroize(skip)] #[zeroize(skip)]
/// a string identifying uniquely the wallet /// a string identifying uniquely the wallet
pub wallet_name: String, pub wallet_name: String,
@ -1368,6 +1370,7 @@ pub enum NgWalletError {
InvalidPin, InvalidPin,
InvalidPazzle, InvalidPazzle,
InvalidPazzleLength, InvalidPazzleLength,
InvalidMnemonic,
InvalidSecurityImage, InvalidSecurityImage,
InvalidSecurityText, InvalidSecurityText,
SubmissionError, SubmissionError,

@ -0,0 +1,5 @@
NG_ACCOUNT_DOMAIN=
NG_ACCOUNT_ADMIN=
NG_ACCOUNT_LOCAL_PEER_KEY=
NG_ACCOUNT_SERVER=127.0.0.1,1440,[the broker's peer ID]
RUST_LOG=

@ -12,16 +12,23 @@ pnpm --ignore-workspace install
## Dev ## Dev
``` ```bash
cd web cd web
pnpm run dev --host pnpm run dev --host
// in another terminal
# In another terminal...
cd ../ cd ../
export NG_ACCOUNT_DOMAIN=[?]; export NG_ACCOUNT_ADMIN=[?]; export NG_ACCOUNT_LOCAL_PEER_KEY=[?]; export NG_ACCOUNT_SERVER=127.0.0.1,14400,[?]; export RUST_LOG=debug
# Please set the required environment variables in the .env and then source it it with:
source .env
cargo watch -c -w src -x run cargo watch -c -w src -x run
// then open http://localhost:5173/ # Then open http://localhost:5173/#/create
``` ```
> Currently, the ng-account server api is listening on http://127.0.0.1:3031 only which might cause you trouble (coded in `main.rs`, `Create.svelte` and `Delete.svelte`).
> If you need to test from a (virtual) android device, you can use adb to tunnel the connection like: [`adb reverse tcp:3031 tcp:3031`](https://justinchips.medium.com/proxying-adb-client-connections-2ab495f774eb).
## Prod ## Prod
``` ```

@ -258,7 +258,7 @@ async fn main() -> anyhow::Result<()> {
"Content-Security-Policy", "Content-Security-Policy",
HeaderValue::from_static( HeaderValue::from_static(
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
"default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self' http://192.168.192.2:3031", "default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self' http://localhost:3031",
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
"default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self'", "default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self'",
@ -304,9 +304,10 @@ async fn main() -> anyhow::Result<()> {
{ {
log_debug!("CORS: any origin"); log_debug!("CORS: any origin");
cors = cors.allow_any_origin(); cors = cors.allow_any_origin();
log::info!("Starting server on http://192.168.192.2:3031"); log::info!("Starting server on http://localhost:3031");
warp::serve(api_v1.or(static_files).with(cors).with(incoming_log)) warp::serve(api_v1.or(static_files).with(cors).with(incoming_log))
.run(([192, 168, 192, 2], 3031)) // TODO: Change this to local network ip?
.run(([127, 0, 0, 1], 3031))
.await; .await;
} }

@ -27,7 +27,7 @@
let top; let top;
const api_url = import.meta.env.PROD const api_url = import.meta.env.PROD
? "api/v1/" ? "api/v1/"
: "http://192.168.192.2:3031/api/v1/"; : "http://127.0.0.1:3031/api/v1/";
async function register() { async function register() {
wait = true; wait = true;
@ -375,7 +375,7 @@
</div> </div>
</div> </div>
{#if ca} {#if ca}
<div class="row mb-20"> <div class="row mb-10">
<button <button
on:click|once={accept} on:click|once={accept}
class="mr-5 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" class="mr-5 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"

@ -27,7 +27,7 @@
let top; let top;
const api_url = import.meta.env.PROD const api_url = import.meta.env.PROD
? "api/v1/" ? "api/v1/"
: "http://192.168.192.2:3031/api/v1/"; : "http://127.0.0.1:3031/api/v1/";
async function bootstrap() {} async function bootstrap() {}
let error; let error;
@ -154,7 +154,7 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="row mb-20"> <div class="row mb-10">
<button <button
on:click|once={accept} on:click|once={accept}
class="mr-5 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" class="mr-5 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"

@ -21,7 +21,7 @@
onMount(() => {}); onMount(() => {});
onDestroy(() => { onDestroy(() => {
unsubscribe(); //unsubscribe();
}); });
</script> </script>

@ -51,7 +51,7 @@
A <b>NextGraph Wallet</b> is unique to each person.<br /> It stores your A <b>NextGraph Wallet</b> is unique to each person.<br /> It stores your
credentials to access documents. You need one in order to start using credentials to access documents. You need one in order to start using
NextGraph.<br />If you already have a wallet, you should not create a new NextGraph.<br />If you already have a wallet, you should not create a new
one, instead, one. Instead,
{#if display_note_on_local_wallets} {#if display_note_on_local_wallets}
<a href="/" use:link>login here with your existing wallet.</a> <a href="/" use:link>login here with your existing wallet.</a>
{:else} {:else}

Loading…
Cancel
Save