@ -0,0 +1,20 @@ |
||||
{ |
||||
"$schema": "../gen/schemas/desktop-schema.json", |
||||
"identifier": "remote-tag-capability", |
||||
"windows": ["registration"], |
||||
"remote": { |
||||
"urls": [ |
||||
"https://account.nextgraph.eu", |
||||
"http://account-dev.nextgraph.eu:5173", |
||||
"https://account.nextgraph.one", |
||||
"https://account-staging.nextgraph.eu", |
||||
"https://account.allelo.eco" |
||||
] |
||||
}, |
||||
"permissions": [ |
||||
"core:event:default", |
||||
"core:window:default", |
||||
"core:window:allow-close", |
||||
"core:window:allow-destroy" |
||||
] |
||||
} |
||||
@ -1,70 +1,218 @@ |
||||
* { box-sizing: border-box; } |
||||
@import "tailwindcss"; |
||||
|
||||
/** To format paths, like Settings > Wallet > Generate Wallet QR */ |
||||
.path { |
||||
font-family: monospace; |
||||
background-color: rgba(73, 114, 165, 0.1); |
||||
} |
||||
/* .splash-loaded { |
||||
display: none; |
||||
} */ |
||||
|
||||
.toggle * { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.error-popover h3 { |
||||
text-align: center; |
||||
color: rgb(200 30 30); |
||||
} |
||||
.error-popover > div:first-child { |
||||
background-color: rgb(200 30 30); |
||||
} |
||||
.error-popover > div:first-child > h3 { |
||||
color: white; |
||||
} |
||||
|
||||
.logo { |
||||
padding: 1.5em; |
||||
will-change: filter; |
||||
transition: 0.75s; |
||||
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); |
||||
} |
||||
|
||||
.jse-absolute-popup-content { |
||||
left: 0 !important; |
||||
} |
||||
|
||||
.container3 { |
||||
margin: 0; |
||||
|
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
text-align: center; |
||||
} |
||||
|
||||
.container3 aside { |
||||
width: 20rem !important; |
||||
} |
||||
|
||||
div[role="alert"] div { |
||||
display: block; |
||||
} |
||||
|
||||
.spinner-overlay button { |
||||
display: none; |
||||
} |
||||
|
||||
.choice-button { |
||||
min-width: 305px; |
||||
} |
||||
|
||||
.clickable { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.row { |
||||
display: flex; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.deactivated-menu > svg { |
||||
color: rgb(156 163 175) !important; |
||||
} |
||||
|
||||
:root { |
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; |
||||
line-height: 1.5; |
||||
font-weight: 400; |
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif; |
||||
font-size: 16px; |
||||
line-height: 24px; |
||||
font-weight: 400; |
||||
|
||||
color-scheme: light; |
||||
color: #213547; |
||||
background-color: #ffffff; |
||||
color: #0f0f0f; |
||||
background-color: white; |
||||
|
||||
font-synthesis: none; |
||||
text-rendering: optimizeLegibility; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
-webkit-text-size-adjust: 100%; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
display: flex; |
||||
place-items: center; |
||||
min-width: 305px; |
||||
min-height: 100vh; |
||||
} |
||||
|
||||
font-synthesis: none; |
||||
text-rendering: optimizeLegibility; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
#root { |
||||
width: 100%; |
||||
min-height: 100vh; |
||||
} |
||||
/* #app { |
||||
/*max-width: 1280px; |
||||
margin: 0 auto; |
||||
padding: 0rem; |
||||
text-align: center; |
||||
} */ |
||||
|
||||
/* .container2 { |
||||
padding-top: 10vh; |
||||
} */ |
||||
|
||||
a { |
||||
font-weight: 500; |
||||
color: #646cff; |
||||
text-decoration: inherit; |
||||
font-weight: 500; |
||||
color: #646cff; |
||||
text-decoration: inherit; |
||||
} |
||||
|
||||
a:hover { |
||||
color: #535bf2; |
||||
color: #535bf2; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
display: flex; |
||||
place-items: center; |
||||
min-width: 320px; |
||||
min-height: 100vh; |
||||
.toast { |
||||
left: 50%; |
||||
transform: translateX(-50%); |
||||
z-index: 49; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
h1 { |
||||
font-size: 3.2em; |
||||
line-height: 1.1; |
||||
input, |
||||
button { |
||||
border-radius: 8px; |
||||
border: 1px solid transparent; |
||||
padding: 0.6em 1.2em; |
||||
font-size: 1em; |
||||
font-weight: 500; |
||||
font-family: inherit; |
||||
color: #0f0f0f; |
||||
background-color: #ffffff; |
||||
transition: border-color 0.25s; |
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); |
||||
} |
||||
|
||||
button { |
||||
border-radius: 8px; |
||||
border: 1px solid transparent; |
||||
padding: 0.6em 1.2em; |
||||
font-size: 1em; |
||||
font-weight: 500; |
||||
font-family: inherit; |
||||
background-color: #1a1a1a; |
||||
cursor: pointer; |
||||
transition: border-color 0.25s; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
button:hover { |
||||
border-color: #646cff; |
||||
border-color: #396cd8; |
||||
} |
||||
button:active { |
||||
border-color: #396cd8; |
||||
background-color: #e8e8e8; |
||||
} |
||||
|
||||
/* input, |
||||
button { |
||||
outline: none; |
||||
} */ |
||||
|
||||
button:focus, |
||||
button:focus-visible { |
||||
outline: 4px auto -webkit-focus-ring-color; |
||||
outline: 4px auto -webkit-focus-ring-color; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: light) { |
||||
:root { |
||||
color: #213547; |
||||
background-color: #ffffff; |
||||
} |
||||
a:hover { |
||||
color: #747bff; |
||||
} |
||||
button { |
||||
background-color: #f9f9f9; |
||||
} |
||||
/* @media (prefers-color-scheme: dark) { |
||||
:root { |
||||
color: #f6f6f6; |
||||
background-color: #2f2f2f; |
||||
} |
||||
|
||||
a:hover { |
||||
color: #24c8db; |
||||
} |
||||
|
||||
input, |
||||
button { |
||||
color: #ffffff; |
||||
background-color: #0f0f0f98; |
||||
} |
||||
button:active { |
||||
background-color: #0f0f0f69; |
||||
} |
||||
} */ |
||||
|
||||
* { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
@ -0,0 +1,18 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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"> |
||||
export let params: { invitation: string } = { invitation: "" }; |
||||
import { onMount } from "svelte"; |
||||
onMount( |
||||
() => (window.location.href = "/#/wallet/create?i=" + params.invitation) |
||||
); |
||||
</script> |
||||
@ -0,0 +1,502 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
@component |
||||
Wallet creation page. |
||||
This component manages the whole UX flow, gives infos about wallets, |
||||
offers available brokers, handles wallet creation, |
||||
and shows the generated pazzle and mnemonic (if applicable). |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { Button, Alert } from "flowbite-svelte"; |
||||
import { t } from "svelte-i18n"; |
||||
import CenteredLayout from "./lib/CenteredLayout.svelte"; |
||||
import PasswordInput from "./lib/components/PasswordInput.svelte"; |
||||
import { redirect_server, bootstrap_redirect, base64UrlEncode, push } from "./index"; |
||||
import { |
||||
NG_EU_BSP_REGISTER, |
||||
NG_ONE_BSP_REGISTER, |
||||
APP_WALLET_CREATE_SUFFIX, |
||||
default as ng, |
||||
} from "../.auth-react/api"; |
||||
|
||||
// @ts-ignore |
||||
import Logo from "./assets/nextgraph.svg?component"; |
||||
|
||||
import { onMount, onDestroy, tick } from "svelte"; |
||||
import { wallets, display_error } from "./store"; |
||||
import Spinner from "./lib/components/Spinner.svelte"; |
||||
|
||||
console.log("WalletCreate called") |
||||
|
||||
let search = window.location.href.split("?")[1] || ""; |
||||
const param = new URLSearchParams(search); |
||||
for (const [key, value] of param) { |
||||
console.log("PARAM",key,value); |
||||
} |
||||
|
||||
let tauri_platform = import.meta.env.TAURI_ENV_PLATFORM; |
||||
|
||||
let wait: any = false; |
||||
let registration_error; |
||||
let registration_success; |
||||
let top; |
||||
let error; |
||||
let ready; |
||||
let invitation; |
||||
let pre_invitation; |
||||
let username = ""; |
||||
let password = ""; |
||||
let username_pass_ok = false; |
||||
let password_input; |
||||
let username_input; |
||||
|
||||
const username_password_ok = async (e) => { |
||||
if (!e || e.key == "Enter" || e.keyCode == 13 || e.type == "enter") { |
||||
await tick(); |
||||
if (!password) { |
||||
password_input.scrollIntoView(); |
||||
password_input.focus(); |
||||
} else if (!username) { |
||||
username_input.scrollIntoView(); |
||||
username_input.focus(); |
||||
} else { |
||||
username_pass_ok = true; |
||||
await do_wallet(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
function scrollToTop() { |
||||
top.scrollIntoView(); |
||||
} |
||||
|
||||
async function bootstrap() { |
||||
//console.log(await ng.client_info()); |
||||
if (!tauri_platform || tauri_platform == "android") { |
||||
if (param.get("re")) { |
||||
registration_error = param.get("re"); |
||||
console.error("registration_error", registration_error); |
||||
} else if ( |
||||
(param.get("rs") || param.get("i")) && |
||||
!tauri_platform && |
||||
!param.get("ab") && |
||||
!import.meta.env.NG_ENV_NO_REDIRECT |
||||
) { |
||||
//registration_success = param.get("rs"); |
||||
|
||||
// doing the bootstrap recording at nextgraph.net |
||||
let i = param.get("i"); |
||||
invitation = await ng.decode_invitation(i); |
||||
let bootstrap_iframe_msgs = await ng.bootstrap_to_iframe_msgs( |
||||
invitation.V0.bootstrap |
||||
); |
||||
let local_invitation = await ng.get_local_bootstrap(location.href); |
||||
if (local_invitation) { |
||||
bootstrap_iframe_msgs.push( |
||||
...(await ng.bootstrap_to_iframe_msgs( |
||||
local_invitation.V0.bootstrap |
||||
)) |
||||
); |
||||
} |
||||
let encoded = base64UrlEncode(JSON.stringify(bootstrap_iframe_msgs)); |
||||
window.location.href = |
||||
bootstrap_redirect + |
||||
encoded + |
||||
"&m=add&ab=" + |
||||
encodeURIComponent(window.location.href); |
||||
return; |
||||
} else if (param.get("rs")) { |
||||
registration_success = param.get("rs"); |
||||
invitation = await ng.decode_invitation(param.get("i")); |
||||
//window.location.replace(window.location.href.split("?")[0]); |
||||
} else if (param.get("i")) { |
||||
invitation = await ng.get_local_bootstrap_with_public( |
||||
location.href, |
||||
param.get("i"), |
||||
false //import.meta.env.PROD |
||||
); |
||||
console.log("invitation", invitation); |
||||
if (invitation && invitation.V0.url) { |
||||
pre_invitation = invitation; |
||||
invitation = undefined; |
||||
} else if (!invitation) { |
||||
let redirect = await ng.get_ngnet_url_of_invitation(param.get("i")); |
||||
if (redirect) { |
||||
console.error("got an invitation for another broker. redirecting"); |
||||
window.location.href = redirect; |
||||
} else { |
||||
//let i = await ng.decode_invitation(param.get("i")); |
||||
console.error("invalid invitation. ignoring it"); |
||||
} |
||||
} else { |
||||
registration_success = window.location.host; |
||||
} |
||||
} else { |
||||
pre_invitation = await ng.get_local_bootstrap_with_public( |
||||
location.href, |
||||
undefined, |
||||
true |
||||
); |
||||
console.log("pre_invitation", pre_invitation); |
||||
} |
||||
} |
||||
scrollToTop(); |
||||
if (!invitation) { |
||||
if (pre_invitation) { |
||||
await select_bsp(pre_invitation.V0.url, pre_invitation.V0.name); |
||||
} else if (!registration_error) { |
||||
selectEU(false); |
||||
} |
||||
} else { |
||||
//await do_wallet(); |
||||
} |
||||
} |
||||
|
||||
async function do_wallet() { |
||||
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 = { |
||||
pazzle_length: 0, |
||||
security_txt: username, |
||||
security_img: undefined, |
||||
password: password, |
||||
mnemonic: false, |
||||
send_bootstrap: false, //options.cloud || options.bootstrap ? : undefined, |
||||
send_wallet: false, |
||||
local_save: true, |
||||
result_with_wallet_file: false, // this will be automatically changed to true for browser app |
||||
core_bootstrap: invitation.V0.bootstrap, |
||||
core_registration, |
||||
additional_bootstrap, |
||||
device_name: "", |
||||
pdf: false, |
||||
}; |
||||
//console.log("do wallet with params", params); |
||||
try { |
||||
ready = await ng.wallet_create(params); |
||||
wallets.set(await ng.get_wallets()); |
||||
console.log($wallets); |
||||
push("#/wallet/login"); |
||||
} catch (e) { |
||||
console.error(e); |
||||
error = e; |
||||
} |
||||
} |
||||
|
||||
onMount(async () => await bootstrap()); |
||||
|
||||
ready = false; |
||||
|
||||
let unsub_register = () => {}; |
||||
|
||||
onDestroy(async () => { |
||||
if (unsub_register) unsub_register(); |
||||
unsub_register = undefined; |
||||
}); |
||||
|
||||
const select_bsp = async (bsp_url, bsp_name) => { |
||||
console.log("select bsp") |
||||
if (!tauri_platform || tauri_platform == "android") { |
||||
let redirect_url; |
||||
if (tauri_platform) { |
||||
redirect_url = window.location.href; |
||||
} else { |
||||
let local_url; |
||||
if (!import.meta.env.PROD) { |
||||
local_url = "http://localhost:1421"; |
||||
} else { |
||||
let from_url = window.location.href; |
||||
if (from_url.startsWith("https://")) from_url = `https://${bsp_name}`; |
||||
local_url = await ng.get_local_url(from_url); |
||||
} |
||||
if (local_url) redirect_url = local_url + APP_WALLET_CREATE_SUFFIX; |
||||
} |
||||
|
||||
let create = { |
||||
V0: { |
||||
redirect_url, |
||||
}, |
||||
}; |
||||
let ca = await ng.encode_create_account(create); |
||||
wait = $t("pages.wallet_create.redirecting_to_registration_page"); |
||||
window.location.href = bsp_url + "?ca=" + ca; |
||||
//window.open(), "_self").focus(); |
||||
} else { |
||||
let create = { |
||||
V0: { |
||||
redirect_url: undefined, |
||||
}, |
||||
}; |
||||
wait = $t("pages.wallet_create.complete_in_popup"); |
||||
let ca = await ng.encode_create_account(create); |
||||
let unsub_register; |
||||
let temp = await ng.open_window( |
||||
bsp_url + "?ca=" + ca, |
||||
"registration", |
||||
"Registration at a Broker", |
||||
async (result, payload) => { |
||||
if (result == "accepted") { |
||||
wait = false; |
||||
console.log("got accepted with payload", payload); |
||||
registration_success = bsp_name; |
||||
invitation = await ng.decode_invitation(payload.invite); |
||||
unsub_register = undefined; |
||||
} else if (result == "error") { |
||||
wait = false; |
||||
console.log("got error with payload", payload); |
||||
if (payload) registration_error = payload.error; |
||||
unsub_register = undefined; |
||||
} else if (result == "close") { |
||||
console.log("onCloseOfRegistrationWindow"); |
||||
wait = false; |
||||
registration_error = "You cancelled the registration"; |
||||
unsub_register = undefined; |
||||
} |
||||
} |
||||
); |
||||
console.log("temp",temp) |
||||
if (temp) unsub_register = temp; |
||||
} |
||||
}; |
||||
const selectONE = async (event) => { |
||||
await select_bsp(NG_ONE_BSP_REGISTER, "nextgraph.one"); |
||||
}; |
||||
const selectEU = async (event) => { |
||||
await select_bsp( |
||||
NG_EU_BSP_REGISTER, |
||||
import.meta.env.NG_ENV_ALT ? import.meta.env.NG_ENV_ALT : "nextgraph.eu" |
||||
); |
||||
}; |
||||
</script> |
||||
|
||||
<CenteredLayout> |
||||
<div class="max-w-2xl lg:px-8 mx-auto mb-20"> |
||||
{#if wait} |
||||
<div class="lg:px-8 text-primary-700"> |
||||
{#if wait === true} |
||||
{$t("pages.wallet_create.please_wait")}... |
||||
{:else} |
||||
{wait} |
||||
{/if} |
||||
<Spinner className="mt-10 h-14 w-14 mx-auto" /> |
||||
</div> |
||||
{:else} |
||||
<div class="container3" bind:this={top}> |
||||
<div class="row"> |
||||
<a href="#/"> |
||||
<Logo class="logo block h-[8em]" alt={$t("common.logo")} /> |
||||
</a> |
||||
</div> |
||||
{#if registration_error} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
<svg |
||||
class="animate-bounce mt-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
{#if registration_error == "AlreadyExists"} |
||||
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5"> |
||||
{@html $t("pages.user_registered.already_exists")} |
||||
</p> |
||||
<a href="#/wallet/login"> |
||||
<button |
||||
tabindex="-1" |
||||
class="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" |
||||
> |
||||
{$t("buttons.login")} |
||||
</button> |
||||
</a> |
||||
{:else} |
||||
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5"> |
||||
{@html $t("errors.error_occurred", { |
||||
values: { message: display_error(registration_error) }, |
||||
})} |
||||
</p> |
||||
<a href="#/"> |
||||
<button |
||||
tabindex="-1" |
||||
class="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" |
||||
> |
||||
{$t("buttons.back_to_homepage")} |
||||
</button> |
||||
</a> |
||||
{/if} |
||||
</div> |
||||
{:else if !username_pass_ok} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto"> |
||||
{#if registration_success} |
||||
<Alert color="green" class="mb-5"> |
||||
<span class="font-bold text-xl" |
||||
>{$t("pages.wallet_create.registration_success", { |
||||
values: { broker: registration_success }, |
||||
})}</span |
||||
> |
||||
</Alert> |
||||
{/if} |
||||
<p class="max-w-xl md:mx-auto lg:max-w-2xl"> |
||||
<span class="text-xl" |
||||
>{$t("pages.wallet_create.choose_username.title")}</span |
||||
> |
||||
<Alert color="yellow" class="mt-5"> |
||||
{@html $t("pages.wallet_create.choose_username.warning")} |
||||
</Alert> |
||||
</p> |
||||
<input |
||||
bind:this={username_input} |
||||
class="mt-10 mr-0 mb-5 text-md 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" |
||||
id="username-input" |
||||
placeholder={$t("pages.wallet_create.type_username_placeholder")} |
||||
autocomplete="username" |
||||
autofocus |
||||
bind:value={username} |
||||
on:keypress={username_password_ok} |
||||
/> |
||||
|
||||
<PasswordInput |
||||
bind:this={password_input} |
||||
id="password-input" |
||||
placeholder={$t("pages.wallet_create.type_password_placeholder")} |
||||
bind:value={password} |
||||
className="mb-5 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="password" |
||||
on:enter={username_password_ok} |
||||
/> |
||||
<Button |
||||
disabled={!username || !password} |
||||
onclick={() => { |
||||
username_password_ok(false); |
||||
}} |
||||
class="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" |
||||
> |
||||
<svg |
||||
class="w-8 h-8 mr-2 -ml-1" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
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> |
||||
{@html $t("pages.wallet_create.create_wallet_now")} |
||||
</Button> |
||||
</div> |
||||
{:else if !error} |
||||
{#if !ready} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700"> |
||||
{$t("pages.wallet_create.creating")} |
||||
<svg |
||||
class="animate-spin mt-10 h-6 w-6 mx-auto" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
viewBox="0 0 24 24" |
||||
> |
||||
<circle |
||||
class="opacity-25" |
||||
cx="12" |
||||
cy="12" |
||||
r="10" |
||||
stroke="currentColor" |
||||
stroke-width="4" |
||||
/> |
||||
<path |
||||
class="opacity-75" |
||||
fill="currentColor" |
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" |
||||
/> |
||||
</svg> |
||||
</div> |
||||
{:else} |
||||
<div class="text-left mx-4"> |
||||
<div class="text-green-800 mx-auto flex flex-col items-center"> |
||||
<div>{$t("pages.wallet_create.ready")}</div> |
||||
<svg |
||||
class="my-4 h-16 w-16" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
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> |
||||
</div> |
||||
</div> |
||||
{/if} |
||||
{:else} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
{$t("errors.an_error_occurred")} |
||||
<svg |
||||
fill="none" |
||||
class="animate-bounce mt-10 h-10 w-10 mx-auto" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
<Alert color="red" class="mt-5"> |
||||
{display_error(error)} |
||||
</Alert> |
||||
<button |
||||
class="mt-10 select-none" |
||||
on:click={async () => { |
||||
window.location.href = window.location.origin; |
||||
}} |
||||
> |
||||
{$t("buttons.start_over")} |
||||
</button> |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
</CenteredLayout> |
||||
|
||||
<style> |
||||
</style> |
||||
@ -0,0 +1,592 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
"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"> |
||||
import { onMount, onDestroy, tick } from "svelte"; |
||||
import { t, locale } from "svelte-i18n"; |
||||
import Login from "./lib/Login.svelte"; |
||||
import CenteredLayout from "./lib/CenteredLayout.svelte"; |
||||
import { |
||||
default as ng, |
||||
} from "../.auth-react/api"; |
||||
import { Fileupload, Button } from "flowbite-svelte"; |
||||
import { |
||||
redirect_server, |
||||
bootstrap_redirect, |
||||
base64UrlEncode, |
||||
push |
||||
} from "./index"; |
||||
// @ts-ignore |
||||
import Logo from "./assets/nextgraph.svg?component"; |
||||
import { |
||||
wallets, |
||||
active_wallet, |
||||
opened_wallets, |
||||
active_session, |
||||
set_active_session, |
||||
has_wallets, |
||||
display_error, |
||||
wallet_from_import, |
||||
redirect_after_login, |
||||
redirect_if_wallet_is, |
||||
} from "./store"; |
||||
import { |
||||
CheckBadge, |
||||
ExclamationTriangle, |
||||
QrCode, |
||||
Cloud, |
||||
ArrowRightEndOnRectangle, |
||||
} from "svelte-heros-v2"; |
||||
|
||||
let tauri_platform = import.meta.env.TAURI_ENV_PLATFORM; |
||||
let mobile = tauri_platform == "android" || tauri_platform == "ios"; |
||||
|
||||
let wallet; |
||||
let selected; |
||||
let step; |
||||
let error; |
||||
let importing = false; |
||||
let top; |
||||
|
||||
let wallets_unsub; |
||||
let opened_wallets_unsub; |
||||
let active_wallet_unsub; |
||||
|
||||
export let without_create = false; |
||||
|
||||
function convert_img_to_url(buffer) { |
||||
var blob = new Blob([buffer], { |
||||
type: "image/jpeg", |
||||
}); |
||||
var imageUrl = URL.createObjectURL(blob); |
||||
return imageUrl; |
||||
} |
||||
|
||||
console.log("WalletLogin called") |
||||
|
||||
onMount(async () => { |
||||
step = "open"; |
||||
|
||||
wallets_unsub = wallets.subscribe((value) => { |
||||
console.log("wallets.subscribe(", wallet, selected); |
||||
wallet = wallet || selected && $wallets[selected]?.wallet; |
||||
//console.log("wallet found locally", wallet); |
||||
}); |
||||
opened_wallets_unsub = opened_wallets.subscribe(async (value) => { |
||||
if (!$active_wallet && selected && value[selected]) { |
||||
//await tick(); |
||||
active_wallet.set({ wallet: value[selected], id: selected }); |
||||
} |
||||
}); |
||||
active_wallet_unsub = active_wallet.subscribe(async (value) => { |
||||
if (value && value.wallet) { |
||||
console.log("active_wallet.subscribe(", value.wallet, wallet, selected); |
||||
step = "loggedin"; |
||||
await tick(); |
||||
if (!$active_session) { |
||||
try { |
||||
let session = await ng.session_start( |
||||
value.id, |
||||
value.wallet.V0.personal_site |
||||
); |
||||
//console.log(session); |
||||
if (session) { |
||||
set_active_session(session); |
||||
loggedin(); |
||||
} |
||||
} catch (e) { |
||||
step = "open"; |
||||
error = e; |
||||
importing = false; |
||||
wallet = undefined; |
||||
selected = undefined; |
||||
active_wallet.set(undefined); |
||||
} |
||||
} else { |
||||
loggedin(); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Coming from the import Wallet with QR / TextCode ... |
||||
if ($wallet_from_import) { |
||||
wallet = $wallet_from_import; |
||||
importing = true; |
||||
} |
||||
}); |
||||
|
||||
async function loggedin() { |
||||
step = "loggedin"; |
||||
|
||||
if ($redirect_after_login) { |
||||
if ( |
||||
!$redirect_if_wallet_is || |
||||
$redirect_if_wallet_is == $active_wallet?.id |
||||
) { |
||||
let redir = $redirect_after_login; |
||||
$redirect_after_login = undefined; |
||||
$redirect_if_wallet_is = undefined; |
||||
push("#" + redir); |
||||
} else { |
||||
$redirect_after_login = undefined; |
||||
$redirect_if_wallet_is = undefined; |
||||
push("#/"); |
||||
} |
||||
} else { |
||||
push("#/"); |
||||
} |
||||
} |
||||
|
||||
function start_login_from_import() { |
||||
// Login button was clicked and `wallet` was set in `onMount`. |
||||
// Unset variable from store, to show login screen. |
||||
wallet_from_import.set(null); |
||||
} |
||||
|
||||
onDestroy(() => { |
||||
console.log("onDestroy called"); |
||||
if (wallets_unsub) wallets_unsub(); |
||||
if (opened_wallets_unsub) opened_wallets_unsub(); |
||||
if (active_wallet_unsub) active_wallet_unsub(); |
||||
wallet_from_import.set(null); |
||||
}); |
||||
async function gotError(event) { |
||||
//importing = false; |
||||
console.error(event.detail); |
||||
} |
||||
async function gotWallet(event) { |
||||
try { |
||||
console.log("gotWallet",event) |
||||
if (importing) { |
||||
step = "loggedin"; |
||||
$redirect_after_login = undefined; |
||||
$redirect_if_wallet_is = undefined; |
||||
let in_memory = !event.detail.trusted; |
||||
console.log("IMPORTING", in_memory, event.detail.wallet, wallet); |
||||
//register bootstrap when importing |
||||
if (!in_memory && !tauri_platform) { |
||||
let bootstrap_iframe_msgs = |
||||
await ng.get_bootstrap_iframe_msgs_for_brokers( |
||||
event.detail.wallet.V0.brokers |
||||
); |
||||
let encoded = base64UrlEncode(JSON.stringify(bootstrap_iframe_msgs)); |
||||
let register_bootstrap_url = |
||||
bootstrap_redirect + |
||||
encoded + |
||||
"&close=1&m=add&ab=" + |
||||
encodeURIComponent(window.location.href); |
||||
console.log(register_bootstrap_url); |
||||
window.open(register_bootstrap_url, "_blank"); |
||||
} |
||||
let client = await ng.wallet_import( |
||||
wallet, |
||||
event.detail.wallet, |
||||
in_memory |
||||
); |
||||
event.detail.wallet.V0.client = client; |
||||
// refreshing the wallets |
||||
wallets.set(await ng.get_wallets()); |
||||
//console.log($wallets); |
||||
let session = await ng.session_start( |
||||
event.detail.id, |
||||
event.detail.wallet.V0.personal_site |
||||
); |
||||
//console.log(session); |
||||
if (session) { |
||||
set_active_session(session); |
||||
} |
||||
if (in_memory && !tauri_platform) { |
||||
// send a message in BroadcastChannel new_in_mem(lws, opened_wallet=event.detail.wallet). |
||||
let name = event.detail.id; |
||||
let lws = $wallets[name]; |
||||
if (lws.in_memory) { |
||||
let new_in_mem = { |
||||
lws, |
||||
name, |
||||
opened: event.detail.wallet, |
||||
cmd: "new_in_mem", |
||||
}; |
||||
window.wallet_channel.postMessage(new_in_mem, location.href); |
||||
} |
||||
} |
||||
} else { |
||||
let client = await ng.wallet_was_opened(event.detail.wallet); |
||||
event.detail.wallet.V0.client = client; |
||||
} |
||||
} catch (e) { |
||||
if (importing) { |
||||
wallet = undefined; |
||||
} |
||||
importing = false; |
||||
error = e; |
||||
step = "open"; |
||||
return; |
||||
} |
||||
//await tick(); |
||||
active_wallet.set(event.detail); |
||||
// { wallet, |
||||
// id } |
||||
} |
||||
function cancelLogin(event) { |
||||
console.log("cancelLogin"); |
||||
importing = false; |
||||
selected = undefined; |
||||
wallet = undefined; |
||||
} |
||||
function select(id) { |
||||
selected = id; |
||||
if ($opened_wallets[selected]) { |
||||
active_wallet.set({ wallet: $opened_wallets[selected], id: selected }); |
||||
} else { |
||||
wallet = $wallets[selected]?.wallet; |
||||
} |
||||
importing = false; |
||||
console.log("select", wallet, selected) |
||||
} |
||||
function handleWalletUpload(event) { |
||||
const files = event.target.files; |
||||
if (files.length > 0) { |
||||
let reader = new FileReader(); |
||||
reader.readAsArrayBuffer(files[0]); |
||||
reader.onload = async (e) => { |
||||
try { |
||||
//console.log(e.target.result); |
||||
wallet = await ng.wallet_read_file(e.target.result); |
||||
importing = true; |
||||
} catch (e) { |
||||
error = e; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
function scrollToTop() { |
||||
top.scrollIntoView(); |
||||
} |
||||
onMount(() => scrollToTop()); |
||||
</script> |
||||
|
||||
<div bind:this={top}> |
||||
<CenteredLayout displayFooter={!wallet && !selected}> |
||||
{#if error} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
<ExclamationTriangle class="animate-bounce mt-10 h-16 w-16 mx-auto" /> |
||||
|
||||
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5"> |
||||
{@html $t("errors.error_occurred", { |
||||
values: { message: display_error(error) }, |
||||
})} |
||||
</p> |
||||
<button |
||||
on:click={() => { |
||||
importing = false; |
||||
error = undefined; |
||||
}} |
||||
class="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" |
||||
> |
||||
{$t("buttons.start_over")} |
||||
</button> |
||||
</div> |
||||
{:else if step == "loggedin"} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> |
||||
{@html $t("pages.wallet_login.logged_in")}... |
||||
<svg |
||||
class="my-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
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> |
||||
</div> |
||||
{:else if $wallet_from_import} |
||||
<!-- Imported a wallet --> |
||||
|
||||
<!-- Title --> |
||||
<div> |
||||
<h2 class="text-xl mb-6"> |
||||
{$t("pages.wallet_login.from_import.title")} |
||||
</h2> |
||||
</div> |
||||
|
||||
<span class="text-green-800"> |
||||
<CheckBadge class="w-full mt-4" size="3em" /> |
||||
|
||||
<div class="mt-4"> |
||||
{@html $t("pages.wallet_login.from_import.description")} |
||||
</div> |
||||
</span> |
||||
|
||||
<!-- Show wallet security image and phrase. --> |
||||
<div |
||||
class="wallet-box mt-4 mx-auto" |
||||
role="button" |
||||
tabindex="0" |
||||
on:click={start_login_from_import} |
||||
on:keypress={start_login_from_import} |
||||
> |
||||
{#if $wallet_from_import.V0.content.password} |
||||
<div class="pt-5"> |
||||
<ArrowRightEndOnRectangle |
||||
class="w-16 h-16" |
||||
style="display:inline;" |
||||
/> |
||||
<div> |
||||
{#if mobile}Tap{:else}Click{/if} here to login with your wallet |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="p-5"> |
||||
<button |
||||
tabindex="-1" |
||||
style="overflow-wrap: anywhere;" |
||||
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-1.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
{$wallet_from_import.V0.content.security_txt} |
||||
</button> |
||||
</div> |
||||
{:else} |
||||
<span class="securitytxt" |
||||
>{$wallet_from_import.V0.content.security_txt} |
||||
</span> |
||||
<img |
||||
alt={$wallet_from_import.V0.content.security_txt} |
||||
class="securityimg" |
||||
src={convert_img_to_url( |
||||
$wallet_from_import.V0.content.security_img |
||||
)} |
||||
/> |
||||
{/if} |
||||
</div> |
||||
|
||||
<!-- Login to finish import instructions--> |
||||
<div class="my-4"> |
||||
{@html $t("pages.wallet_login.from_import.instruction")} |
||||
</div> |
||||
|
||||
<div> |
||||
<button |
||||
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" |
||||
on:click={start_login_from_import} |
||||
> |
||||
{$t("buttons.login")} |
||||
</button> |
||||
</div> |
||||
{:else if wallet} |
||||
<Login |
||||
{wallet} |
||||
bind:for_import={importing} |
||||
on:error={gotError} |
||||
on:opened={gotWallet} |
||||
on:cancel={cancelLogin} |
||||
/> |
||||
{:else if !$active_wallet && !selected} |
||||
<div class="row"> |
||||
<a href="#/"> |
||||
<Logo class="logo block h-40" alt="NextGraph Logo" /> |
||||
</a> |
||||
</div> |
||||
<h2 class="pb-5 text-xl">{$t("pages.wallet_login.select_wallet")}</h2> |
||||
<div class="flex flex-wrap justify-center gap-5 mb-10"> |
||||
{#each Object.entries($wallets) as wallet_entry} |
||||
<div |
||||
class="wallet-box" |
||||
role="button" |
||||
tabindex="0" |
||||
on:click={() => { |
||||
select(wallet_entry[0]); |
||||
}} |
||||
on:keypress={() => { |
||||
select(wallet_entry[0]); |
||||
}} |
||||
> |
||||
{#if wallet_entry[1].wallet.V0.content.password} |
||||
<div class="pt-5"> |
||||
<ArrowRightEndOnRectangle |
||||
class="w-16 h-16" |
||||
style="display:inline;" |
||||
/> |
||||
<div> |
||||
{#if mobile}Tap{:else}Click{/if} here to login with your wallet |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="p-5"> |
||||
<button |
||||
tabindex="-1" |
||||
style="overflow-wrap: anywhere;" |
||||
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-1.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
{wallet_entry[1].wallet.V0.content.security_txt} |
||||
</button> |
||||
</div> |
||||
{:else} |
||||
<span class="securitytxt" |
||||
>{wallet_entry[1].wallet.V0.content.security_txt} |
||||
</span> |
||||
<img |
||||
alt={wallet_entry[1].wallet.V0.content.security_txt} |
||||
class="securityimg" |
||||
src={convert_img_to_url( |
||||
wallet_entry[1].wallet.V0.content.security_img |
||||
)} |
||||
/> |
||||
{/if} |
||||
</div> |
||||
{/each} |
||||
<div class="wallet-box"> |
||||
<!-- <a href="#/wallet/username"> |
||||
<button |
||||
style="justify-content: left;" |
||||
tabindex="-1" |
||||
class:mt-10={without_create} |
||||
class:mt-2.5={!without_create} |
||||
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-1.5 text-center inline-flex items-center justify-center dark:focus:ring-primary-100/55 mb-2" |
||||
> |
||||
<Cloud class="w-8 h-8 mr-2 -ml-1" tabindex="-1" /> |
||||
{$t("pages.wallet_login.with_username")} |
||||
</button> |
||||
</a> --> |
||||
<Fileupload |
||||
style="display:none;" |
||||
id="import_wallet_file" |
||||
accept="application/octet-stream, .ngw" |
||||
on:change={handleWalletUpload} |
||||
/> |
||||
<button |
||||
class="mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-1.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" |
||||
on:click={() => { |
||||
document.getElementById("import_wallet_file").click(); |
||||
}} |
||||
> |
||||
<svg |
||||
class="w-8 h-8 mr-2 -ml-1" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M9 8.25H7.5a2.25 2.25 0 00-2.25 2.25v9a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25H15M9 12l3 3m0 0l3-3m-3 3V2.25" |
||||
/> |
||||
</svg> |
||||
{$t("pages.wallet_login.import_file")} |
||||
</button> |
||||
<a href="#/wallet/login-qr"> |
||||
<button |
||||
style="justify-content: left;" |
||||
tabindex="-1" |
||||
class="mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-1.5 text-center inline-flex items-center justify-center dark:focus:ring-primary-100/55 mb-2" |
||||
> |
||||
<QrCode class="w-8 h-8 mr-2 -ml-1" tabindex="-1" /> |
||||
{$t("pages.wallet_login.import_qr")} |
||||
</button> |
||||
</a> |
||||
<a href="#/wallet/login-text-code"> |
||||
<button |
||||
style="justify-content: left;" |
||||
tabindex="-1" |
||||
class="mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-1.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" |
||||
> |
||||
<svg |
||||
class="w-8 h-8 mr-2 -ml-1" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" |
||||
/> |
||||
</svg> |
||||
{$t("pages.wallet_login.import_link")} |
||||
</button> |
||||
</a> |
||||
{#if !without_create} |
||||
<a href="#/wallet/create"> |
||||
<button |
||||
tabindex="-1" |
||||
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-1.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<svg |
||||
class="w-8 h-8 mr-2 -ml-1" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
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> |
||||
{$t("pages.wallet_login.new_wallet")} |
||||
</button> |
||||
</a> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
{/if} |
||||
</CenteredLayout> |
||||
</div> |
||||
|
||||
<style> |
||||
.wallet-box { |
||||
width: 300px; |
||||
height: 300px; |
||||
background-color: #f6f6f6; |
||||
position: relative; |
||||
cursor: pointer; |
||||
} |
||||
.wallet-box button { |
||||
min-width: 262px; |
||||
} |
||||
.securitytxt { |
||||
z-index: 100; |
||||
width: 300px; |
||||
position: absolute; |
||||
left: 0; |
||||
padding: 5px; |
||||
background-color: #ffffffd0; |
||||
overflow-wrap: break-word; |
||||
} |
||||
.wallet-box:focus .securitytxt { |
||||
background-color: #ffffffff; |
||||
} |
||||
.securityimg { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
} |
||||
</style> |
||||
|
After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,59 @@ |
||||
/* |
||||
* Base64URL-ArrayBuffer |
||||
* https://github.com/herrjemand/Base64URL-ArrayBuffer
|
||||
* |
||||
* Copyright (c) 2017 Yuriy Ackermann <ackermann.yuriy@gmail.com> |
||||
* Copyright (c) 2012 Niklas von Hertzen |
||||
* Licensed under the MIT license. |
||||
*
|
||||
*/ |
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
||||
|
||||
// Use a lookup table to find the index.
|
||||
var lookup = new Uint8Array(256); |
||||
for (var i = 0; i < chars.length; i++) { |
||||
lookup[chars.charCodeAt(i)] = i; |
||||
} |
||||
|
||||
export const encode = function(arraybuffer) { |
||||
var bytes = new Uint8Array(arraybuffer), |
||||
i, len = bytes.length, base64 = ""; |
||||
|
||||
for (i = 0; i < len; i+=3) { |
||||
base64 += chars[bytes[i] >> 2]; |
||||
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; |
||||
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; |
||||
base64 += chars[bytes[i + 2] & 63]; |
||||
} |
||||
|
||||
if ((len % 3) === 2) { |
||||
base64 = base64.substring(0, base64.length - 1); |
||||
} else if (len % 3 === 1) { |
||||
base64 = base64.substring(0, base64.length - 2); |
||||
} |
||||
|
||||
return base64; |
||||
}; |
||||
|
||||
export const decode = function(base64) { |
||||
var bufferLength = base64.length * 0.75, |
||||
len = base64.length, i, p = 0, |
||||
encoded1, encoded2, encoded3, encoded4; |
||||
|
||||
var arraybuffer = new ArrayBuffer(bufferLength), |
||||
bytes = new Uint8Array(arraybuffer); |
||||
|
||||
for (i = 0; i < len; i+=4) { |
||||
encoded1 = lookup[base64.charCodeAt(i)]; |
||||
encoded2 = lookup[base64.charCodeAt(i+1)]; |
||||
encoded3 = lookup[base64.charCodeAt(i+2)]; |
||||
encoded4 = lookup[base64.charCodeAt(i+3)]; |
||||
|
||||
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); |
||||
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); |
||||
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); |
||||
} |
||||
|
||||
return arraybuffer; |
||||
}; |
||||
|
||||
@ -0,0 +1,962 @@ |
||||
// Copyright (c) 2022-2025 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:mail", "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",
|
||||
// "diagram","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", "social:chatroom",
|
||||
// "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", "file:audacity", "file:godot", "file:obsstudio", "file:ardor", "file:krita"
|
||||
|
||||
|
||||
// application/vnd.api+json
|
||||
|
||||
// application/activity+json
|
||||
|
||||
// epub, dejavu,
|
||||
// animation: snap, lottie, smil editor: https://github.com/HaikuTeam/animator/
|
||||
|
||||
export const has_toc = (class_name) => { |
||||
return class_name === "post:rich" || class_name === "post:md" || class_name === "post:html" || class_name === "post:asciidoc" || class_name === "app:z" || class_name === "class"
|
||||
|| class_name.startsWith("schema") || class_name === "doc:pdf" || class_name === "doc:odf" || class_name === "doc:latex" || class_name === "doc:ps" || class_name === "prod:project" || class_name === "prod:slides"
|
||||
}; |
||||
|
||||
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"], |
||||
}, |
||||
"code:js": { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "JavaScript", // edited with CodeMirror, displayed with highlight.js
|
||||
"ng:a": "A JavaScript Source Code file", |
||||
"ng:o": "n:g:z:pre", |
||||
"ng:w": "n:g:z:code_editor", |
||||
"ng:compat": ["file:iana:text:javascript"], |
||||
"implemented": true |
||||
}, |
||||
"code:ts": { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "TypeScript", // edited with CodeMirror, displayed with highlight.js
|
||||
"ng:a": "A TypeScript Source Code file", |
||||
"ng:o": "n:g:z:pre", |
||||
"ng:w": "n:g:z:code_editor", |
||||
"ng:compat": ["file:iana:text:typescript"], |
||||
"implemented": true |
||||
}, |
||||
"code:rust": { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "Rust", // edited with CodeMirror, displayed with highlight.js
|
||||
"ng:a": "A Rust Source Code file", |
||||
"ng:o": "n:g:z:pre", |
||||
"ng:w": "n:g:z:code_editor", |
||||
"ng:compat": [], |
||||
"implemented": true |
||||
}, |
||||
"code:svelte": { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "Svelte", // edited with CodeMirror, displayed with highlight.js
|
||||
"ng:a": "A Svelte Source Code file", |
||||
"ng:o": "n:g:z:pre", |
||||
"ng:w": "n:g:z:code_editor", |
||||
"ng:compat": [], |
||||
"implemented": true |
||||
}, |
||||
"code:react": { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "React", // edited with CodeMirror, displayed with highlight.js
|
||||
"ng:a": "A React Source Code file", |
||||
"ng:o": "n:g:z:pre", |
||||
"ng:w": "n:g:z:code_editor", |
||||
"ng:compat": [], |
||||
"implemented": true |
||||
}, |
||||
"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": { // display with https://github.com/VisualDataWeb/WebVOWL
|
||||
"ng:crdt": "Graph", |
||||
"ng:n": "Schema - RDFS/OWL",
|
||||
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with RDFS and/or OWL", |
||||
"ng:o": "n:g:z:ontology_viewer", // default viewer
|
||||
"ng:w": "n:g:z:ontology_editor", // default editor
|
||||
"ng:x": { |
||||
"rdfs":true, |
||||
"owl":true, |
||||
}, |
||||
"ng:include": ["data:graph"], |
||||
"ng:compat": ["rdfs:*","class","owl:Ontology"], |
||||
}, |
||||
"schema:shacl": { |
||||
"ng:crdt": "Graph", |
||||
"ng:n": "Schema - SHACL",
|
||||
"ng:a": "Define the rules for your data with SHACL", |
||||
"ng:o": "n:g:z:ontology_viewer", // 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 rules for your data with SHEX", |
||||
"ng:o": "n:g:z:ontology_viewer", // 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": "Smart 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", |
||||
"ng:compat": ["code:sparql", "file:iana:application:sparql-query","file:iana:application:x-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", |
||||
"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_viewer", // 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"], |
||||
"implemented": true |
||||
}, |
||||
"data:json": { |
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "JSON (Automerge)",
|
||||
"ng:a": "Automerge JSON Data CRDT", |
||||
"ng:o": "n:g:z:json_automerge_viewer", // default viewer
|
||||
"ng:w": "n:g:z:json_automerge_editor", // default editor
|
||||
"ng:compat": ["file:iana:application:json", "code:json"], |
||||
"implemented": true |
||||
}, |
||||
"data:array": { |
||||
"ng:crdt": "YArray", |
||||
"ng:n": "Array (Yjs)",
|
||||
"ng:a": "Yjs Array CRDT", |
||||
"ng:o": "n:g:z:json_yarray_viewer", // default viewer
|
||||
"ng:w": "n:g:z:json_yarray_editor", // default editor
|
||||
"ng:compat": ["file:iana:application:json", "code:json"], |
||||
"implemented": true |
||||
}, |
||||
"data:map": { |
||||
"ng:crdt": "YMap", |
||||
"ng:n": "Object (Yjs)",
|
||||
"ng:a": "Yjs Map CRDT (JSON Object)", |
||||
"ng:o": "n:g:z:json_ymap_viewer", // default viewer
|
||||
"ng:w": "n:g:z:json_ymap_editor", // default editor
|
||||
"ng:compat": ["file:iana:application:json", "code:json"], |
||||
"implemented": true |
||||
}, |
||||
"data:xml": { |
||||
"ng:crdt": "YXml", |
||||
"ng:n": "XML (Yjs)",
|
||||
"ng:a": "Yjs XML 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:container", |
||||
"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 set of items", |
||||
"ng:o": "n:g:z:container", |
||||
"ng:x": { |
||||
"rdf": true, |
||||
"rdfs": true, |
||||
"ldp": true, |
||||
}, |
||||
"ng:compat": ["rdfs:member","ldp:contains","rdf:Bag","rdf:Alt"], |
||||
"implemented": true |
||||
}, |
||||
"data:plato": { |
||||
"ng:crdt": "Graph", |
||||
"ng:n": "Plato", |
||||
"ng:a": "A tree of files and folders", |
||||
"ng:o": "n:g:z:tree", |
||||
"ng:compat": ["ng:plato","ng:has_plato"], |
||||
}, |
||||
"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:mail": { |
||||
"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 // https://www.semanticdesktop.org/ontologies/2007/03/22/nmo/
|
||||
}, |
||||
"ng:compat": ["file:iana:message:rfc822","file:iana:multipart:related"], |
||||
}, |
||||
"e:link": { |
||||
"ng:crdt": "Graph", |
||||
"ng:n": "Web Link", |
||||
"ng:a": "HTTP link to a page on the Web", |
||||
"ng:compat": [], |
||||
}, |
||||
"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:compose" : { |
||||
"ng:crdt": "YArray", |
||||
"ng:n": "Composition", |
||||
"ng:a": "Compose several blocks into a single document", |
||||
"ng:o": "n:g:z:compose:viewer", |
||||
"ng:w": "n:g:z:compose:editor", |
||||
}, |
||||
"diagram:mermaid" : { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "Diagram - Mermaid", |
||||
"ng:a": "Describe Diagrams with Mermaid", |
||||
"ng:compat": ["file:iana:application:vnd.mermaid"] |
||||
}, |
||||
"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"] |
||||
}, |
||||
"diagram:graphviz" : { |
||||
"ng:crdt": "YText", |
||||
"ng:n": "Diagram - Graphviz", |
||||
"ng:a": "Describe Diagrams with Graphviz", |
||||
"ng:compat": ["file:iana:text:vnd.graphviz"] |
||||
}, |
||||
"diagram:excalidraw" : { |
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Diagram - Excalidraw", |
||||
"ng:a": "Collaborate on Diagrams with Excalidraw", |
||||
"ng:compat": ["file:iana:application:vnd.excalidraw+json"] |
||||
}, |
||||
"diagram:gantt" : { //https://github.com/frappe/gantt
|
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Diagram - Gantt", |
||||
"ng:a": "Interactive gantt chart", |
||||
"ng:compat": [] |
||||
}, |
||||
"diagram:flowchart" : { //https://github.com/adrai/flowchart.js
|
||||
"ng:crdt": "YText", |
||||
"ng:n": "Diagram - Flowchart", |
||||
"ng:a": "flow chart diagrams", |
||||
"ng:compat": [] |
||||
}, |
||||
"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
|
||||
"diagram:markmap" : { //https://github.com/markmap/markmap
|
||||
"ng:crdt": "YText", |
||||
"ng:n": "Diagram - Markmap", |
||||
"ng:a": "mindmaps with markmap", |
||||
"ng:compat": [] |
||||
}, |
||||
"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
|
||||
}, |
||||
"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/
|
||||
"viz:cytoscape" : { |
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Viz - Cytoscape", |
||||
"ng:a": "Graph theory (network) visualization", |
||||
"ng:compat": [] // https://github.com/cytoscape/cytoscape.js
|
||||
}, |
||||
"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
|
||||
}, |
||||
"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
|
||||
}, |
||||
"viz:plotly" : { //https://github.com/plotly/plotly.js
|
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Viz - Plotly", |
||||
"ng:a": "Declarative charts", |
||||
"ng:compat": []
|
||||
}, |
||||
"viz:avail" : {
|
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Viz - Avail", |
||||
"ng:a": "Time Data Availability Visualization", |
||||
"ng:compat": [] // https://github.com/flrs/visavail
|
||||
}, |
||||
"chart:frappecharts" : { |
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Charts - Frappe", |
||||
"ng:a": "GitHub-inspired responsive charts", |
||||
"ng:compat": [] // https://github.com/frappe/charts
|
||||
}, |
||||
"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
|
||||
"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
|
||||
"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
|
||||
}, |
||||
"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
|
||||
}, |
||||
"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": "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": "Musical Notation", |
||||
"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": "Molecules (SMILES)", |
||||
"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:activity": { |
||||
"ng:crdt": "Graph", |
||||
"ng:n": "Activity", |
||||
"ng:a": "Activity sent in a Stream", |
||||
"ng:x": { |
||||
"as":true, |
||||
}, |
||||
"ng:compat": ["as:Activity"] |
||||
}, |
||||
"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"] |
||||
}, |
||||
"social:chatroom": { |
||||
"ng:crdt": "Graph", |
||||
"ng:n": "ChatRoom", |
||||
"ng:a": "A room for group chat", |
||||
}, |
||||
"social:live": { |
||||
"ng:crdt": "Graph", |
||||
"ng:n": "Live", |
||||
"ng:a": "A live session of video or audio, with optional chat", |
||||
}, |
||||
"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:spreadsheet": {
|
||||
"ng:crdt": "Automerge", |
||||
"ng:n": "Spreadsheet", |
||||
"ng:a": "Spreadsheet", |
||||
"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: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:compat": ["file:iana:text:*", "file:iana: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" ]
|
||||
},
|
||||
|
||||
}; |
||||
@ -0,0 +1,21 @@ |
||||
|
||||
export const redirect_server = import.meta.env.NG_REDIR_SERVER || "nextgraph.net"; |
||||
export const bootstrap_redirect = import.meta.env.NG_DEV |
||||
? "http://localhost:1421/bootstrap.html#/?b=" |
||||
: import.meta.env.DEV |
||||
? "http://localhost:14403/#/?b=" |
||||
: import.meta.env.NG_DEV3 |
||||
? "http://127.0.0.1:3033/bootstrap/#/?b=" |
||||
: `https://${redirect_server}/bootstrap/#/?b=`; |
||||
|
||||
export function base64UrlEncode(str) { |
||||
const base64 = btoa(str); // Standard Base64 encoding
|
||||
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); |
||||
} |
||||
|
||||
export function push(location) { |
||||
if (!location || location.length < 1 || (location.charAt(0) != '/' && location.indexOf('#/') !== 0)) { |
||||
throw Error('Invalid parameter location') |
||||
} |
||||
window.location.hash = (location.charAt(0) == '#' ? '' : '#') + location |
||||
} |
||||
@ -0,0 +1,50 @@ |
||||
// Copyright (c) 2022-2025 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.
|
||||
|
||||
import { register, init, locale, format } from "svelte-i18n"; |
||||
import { default as ng } from "../.auth-react/api"; |
||||
|
||||
// Make sure that a file named `locales/<lang>.json` exists when adding it here.
|
||||
export const available_languages = { |
||||
en: "English", |
||||
//de: "Deutsch",
|
||||
//fr: "Français",
|
||||
//ru: "Русский",
|
||||
//es: "Español",
|
||||
//it: "Italiano",
|
||||
//zh: "中文",
|
||||
//pt: "Português",
|
||||
}; |
||||
|
||||
export const select_default_lang = async () => { |
||||
|
||||
for (const lang of Object.keys(available_languages)) { |
||||
let json = await import(`./locales/${lang}.json`); |
||||
register(lang, async ()=>{return json}) |
||||
} |
||||
|
||||
init({ |
||||
fallbackLocale: "en", |
||||
initialLocale: "en", |
||||
}); |
||||
|
||||
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)
|
||||
locale.set(lo); |
||||
return; |
||||
} |
||||
lo = lo.substr(0, 2); |
||||
if (available_languages[lo]) { |
||||
locale.set(lo); |
||||
return; |
||||
} |
||||
} |
||||
}; |
||||
@ -0,0 +1,110 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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 * as ng from "../../.auth-react/api"; |
||||
import { onMount, tick } from "svelte"; |
||||
import { locale, t } from "svelte-i18n"; |
||||
import { available_languages } from "../lang"; |
||||
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) => { |
||||
locale.set(lang); |
||||
changingLang = false; |
||||
await tick(); |
||||
scrollToTop(); |
||||
}; |
||||
|
||||
let tauri_platform = import.meta.env.TAURI_ENV_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"> |
||||
<slot /> |
||||
</div> |
||||
{#if displayFooter} |
||||
<div class="centered"> |
||||
<div class="mb-20 mt-10"> |
||||
<button |
||||
on:click={changeLang} |
||||
class="text-primary-700 bg-white 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-white 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" |
||||
> |
||||
{$t("common.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> |
||||
.centered { |
||||
/*max-width: 1280px;*/ |
||||
margin: 0 auto; |
||||
padding: 0rem; |
||||
text-align: center; |
||||
width: fit-content; |
||||
} |
||||
|
||||
</style> |
||||
@ -0,0 +1,379 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
The Login Procedure. |
||||
Has multiple states (steps) through the user flow. |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { Alert, Toggle, Button } from "flowbite-svelte"; |
||||
import { onMount, createEventDispatcher, tick } from "svelte"; |
||||
import { t } from "svelte-i18n"; |
||||
import { |
||||
default as ng, |
||||
} from "../../.auth-react/api"; |
||||
|
||||
import { |
||||
PuzzlePiece, |
||||
XCircle, |
||||
Backspace, |
||||
ArrowPath, |
||||
LockOpen, |
||||
CheckCircle, |
||||
ArrowLeft, |
||||
} from "svelte-heros-v2"; |
||||
import PasswordInput from "./components/PasswordInput.svelte"; |
||||
import Spinner from "./components/Spinner.svelte"; |
||||
import { display_error } from "../store"; |
||||
//import Worker from "../worker.js?worker&inline"; |
||||
export let wallet; |
||||
export let for_import = false; |
||||
|
||||
let top; |
||||
function scrollToTop() { |
||||
top.scrollIntoView(); |
||||
} |
||||
|
||||
let tauri_platform = import.meta.env.TAURI_ENV_PLATFORM; |
||||
|
||||
const dispatch = createEventDispatcher(); |
||||
|
||||
function init_simple() { |
||||
error = undefined; |
||||
step = "password"; |
||||
scrollToTop(); |
||||
} |
||||
|
||||
onMount(async () => { |
||||
loaded = false; |
||||
if (for_import) { |
||||
device_name = await ng.get_device_name(); |
||||
step = "import"; |
||||
} |
||||
//load_svg(); |
||||
//console.log(wallet); |
||||
//await init(); |
||||
init_simple(); |
||||
|
||||
if (!tauri_platform) { |
||||
try { |
||||
localStorage; |
||||
} catch (e) { |
||||
trusted = false; |
||||
no_local_storage = true; |
||||
console.log("no access to localStorage"); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
|
||||
let step = "password"; |
||||
|
||||
let loaded = false; |
||||
|
||||
let error; |
||||
|
||||
let trusted = true; |
||||
let no_local_storage = false; |
||||
|
||||
let password = ""; |
||||
|
||||
let unlockWith: "pazzle" | "mnemonic" | "password" | undefined = "password"; |
||||
|
||||
let device_name; |
||||
|
||||
async function finish() { |
||||
step = "opening"; |
||||
await tick(); |
||||
// open the wallet |
||||
try { |
||||
if (tauri_platform) { |
||||
// TODO @niko: Add device_name as param to open_with_* APIs |
||||
let opened_wallet = |
||||
await ng.wallet_open_with_password(password); |
||||
// try { |
||||
// let client = await ng.wallet_was_opened(opened_wallet); |
||||
// opened_wallet.V0.client = client; |
||||
// } catch (e) { |
||||
// console.log(e); |
||||
// error = e; |
||||
// step = "end"; |
||||
// dispatch("error", { error: e }); |
||||
// return; |
||||
// } |
||||
step = "end"; |
||||
dispatch("opened", { |
||||
wallet: opened_wallet, |
||||
id: opened_wallet.V0.wallet_id, |
||||
trusted, |
||||
device_name, |
||||
}); |
||||
} else { |
||||
let worker_import = await ng.get_worker(); |
||||
const myWorker = new worker_import.default(); |
||||
myWorker.onerror = (e) => { |
||||
console.error(e); |
||||
error = "WebWorker error"; |
||||
step = "end"; |
||||
dispatch("error", { error }); |
||||
}; |
||||
myWorker.onmessageerror = (e) => { |
||||
console.error(e); |
||||
error = e; |
||||
step = "end"; |
||||
dispatch("error", { error: e }); |
||||
}; |
||||
myWorker.onmessage = async (msg) => { |
||||
//console.log("Message received from worker", msg.data); |
||||
if (msg.data.loaded) { |
||||
if (unlockWith === "password") { |
||||
myWorker.postMessage({ wallet, password, device_name }); |
||||
} |
||||
//console.log("postMessage"); |
||||
} else if (msg.data.success) { |
||||
//console.log(msg.data); |
||||
// try { |
||||
// let client = await ng.wallet_was_opened(msg.data.success); |
||||
// msg.data.success.V0.client = client; |
||||
// } catch (e) { |
||||
// console.log(e); |
||||
// error = e; |
||||
// step = "end"; |
||||
// dispatch("error", { error: e }); |
||||
// return; |
||||
// } |
||||
step = "end"; |
||||
dispatch("opened", { |
||||
wallet: msg.data.success, |
||||
id: msg.data.success.V0.wallet_id, |
||||
trusted, |
||||
device_name, |
||||
}); |
||||
} else { |
||||
console.error(msg.data.error); |
||||
error = msg.data.error; |
||||
step = "end"; |
||||
dispatch("error", { error: msg.data.error }); |
||||
} |
||||
}; |
||||
} |
||||
} catch (e) { |
||||
console.error(e); |
||||
if ( |
||||
(e.message && e.message.includes("constructor")) || |
||||
(typeof e === "string" && e.includes("constructor")) |
||||
) |
||||
e = "BrowserTooOld"; |
||||
error = e; |
||||
step = "end"; |
||||
dispatch("error", { error: e }); |
||||
} |
||||
|
||||
// display the result |
||||
} |
||||
|
||||
function cancel() { |
||||
dispatch("cancel"); |
||||
} |
||||
|
||||
|
||||
function go_back() { |
||||
if (step === "password") { |
||||
init_simple(); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<div |
||||
class="flex-col justify-center md:max-w-2xl py-4 sm:px-8" |
||||
bind:this={top} |
||||
> |
||||
|
||||
{#if step == "import"} |
||||
{#if no_local_storage} |
||||
<div class="max-w-xl lg:px-8 mx-auto px-4 mb-2"> |
||||
<Alert color="orange" class=""> |
||||
Access to local storage is denied. <br />You won't be able to save |
||||
your wallet in this browser.<br /> |
||||
If you wanted to save it, please allow storing local data<br /> |
||||
for the websites {location.origin} <br /> |
||||
and https://nextgraph.net and then reload the page. <br /> |
||||
</Alert> |
||||
</div> |
||||
{:else} |
||||
<div class="max-w-xl lg:px-8 mx-auto px-4 mb-2"> |
||||
<span class="text-xl" |
||||
>{$t("pages.wallet_create.save_wallet_options.trust")} |
||||
</span> <br /> |
||||
<p class="text-sm"> |
||||
{$t("pages.wallet_create.save_wallet_options.trust_description")} |
||||
{#if !tauri_platform} |
||||
{$t("pages.login.trust_device_allow_cookies")}{/if}<br /> |
||||
</p> |
||||
<div class="flex justify-center items-center my-4"> |
||||
<Toggle class="" bind:checked={trusted} |
||||
>{$t("pages.login.trust_device_yes")}</Toggle |
||||
> |
||||
</div> |
||||
</div> |
||||
{/if} |
||||
|
||||
<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"> |
||||
<!-- Device Name, if trusted--> |
||||
{#if trusted} |
||||
<label for="device-name-input" class="text-sm text-black"> |
||||
{$t("pages.login.device_name_label")} |
||||
</label> |
||||
<input |
||||
id="device-name-input" |
||||
bind:value={device_name} |
||||
placeholder={$t("pages.login.device_name_placeholder")} |
||||
type="text" |
||||
class="w-full mb-10 lg:px-8 mx-auto px-4 bg-gray-50 border border-gray-300 text-xs rounded-lg focus:ring-blue-500 focus:border-blue-500 block 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" |
||||
/> |
||||
{/if} |
||||
|
||||
<button |
||||
on:click={start_with_password} |
||||
on:keypress={start_with_password} |
||||
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" |
||||
> |
||||
<LockOpen |
||||
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" |
||||
/> |
||||
{$t("pages.login.open")} |
||||
</button> |
||||
|
||||
<button |
||||
on:click={cancel} |
||||
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" |
||||
><ArrowLeft |
||||
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" |
||||
/>{$t("pages.login.login_cancel")} |
||||
</button> |
||||
</div> |
||||
</div> |
||||
{:else if step == "password"} |
||||
|
||||
<label |
||||
for="password-input" |
||||
class="block mb-2 text-xl text-gray-900 dark:text-white" |
||||
>{$t("pages.login.enter_password")}</label |
||||
> |
||||
<PasswordInput |
||||
id="password-input" |
||||
bind:value={password} |
||||
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="password" |
||||
autofocus={true} |
||||
on:enter={finish} |
||||
/> |
||||
<div class="flex"> |
||||
<button |
||||
on:click={cancel} |
||||
class="mt-3 mr-2 mb-2 ml-auto bg-red-100 hover:bg-red-100/90 disabled:opacity-65 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 focus:outline-none duration-75 group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/>{$t("buttons.cancel")}</button |
||||
> |
||||
<Button |
||||
onclick={finish} |
||||
class="mt-3 mb-2 ml-auto text-white bg-primary-700 hover:bg-primary-700/90 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" |
||||
disabled={password.trim().length < 2} |
||||
><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" |
||||
/>{$t("buttons.confirm")}</Button |
||||
> |
||||
</div> |
||||
|
||||
<!-- The following steps have navigation buttons and fixed layout --> |
||||
{:else if step == "opening"} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700"> |
||||
{@html $t("pages.login.opening_wallet")} |
||||
<Spinner className="mt-10 h-14 w-14 mx-auto" /> |
||||
</div> |
||||
{:else if step == "end"} |
||||
{#if error} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto text-red-800"> |
||||
<div class="mt-auto max-w-6xl lg:px-8"> |
||||
{$t("errors.an_error_occurred")} |
||||
<svg |
||||
fill="none" |
||||
class="animate-bounce mt-10 h-10 w-10 mx-auto" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
<Alert color="red" class="mt-5"> |
||||
{display_error(error)} |
||||
</Alert> |
||||
</div> |
||||
<div class="flex justify-between mt-auto gap-4 mr-3 ml-3"> |
||||
<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" |
||||
/>{$t("buttons.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_simple} |
||||
> |
||||
<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" |
||||
/> |
||||
{$t("buttons.try_again")} |
||||
</button> |
||||
</div> |
||||
</div> |
||||
{:else} |
||||
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> |
||||
{@html $t("pages.login.wallet_opened")} |
||||
<svg |
||||
class="my-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
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> |
||||
</div> |
||||
{/if} |
||||
{/if} |
||||
</div> |
||||
|
||||
<style> |
||||
|
||||
</style> |
||||
@ -0,0 +1,95 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
Component to inform the user, that no wallet is registered on this device. |
||||
Offers login or create wallet buttons. |
||||
--> |
||||
|
||||
<script> |
||||
// @ts-ignore |
||||
import Logo from "../assets/nextgraph.svg?component"; |
||||
import { link } from "svelte-spa-router"; |
||||
import CenteredLayout from "./CenteredLayout.svelte"; |
||||
import { t } from "svelte-i18n"; |
||||
|
||||
export let without_create = false; |
||||
</script> |
||||
|
||||
<CenteredLayout displayFooter={true}> |
||||
<div class="container3"> |
||||
<div class="row"> |
||||
<Logo class="logo block h-40" alt={$t("common.logo")} /> |
||||
</div> |
||||
<h1 class="text-2xl text-center mb-10">{$t("pages.no_wallet.welcome")}</h1> |
||||
|
||||
<p class="max-w-sm"> |
||||
{@html $t("pages.no_wallet.description")}<br /> |
||||
{@html $t("pages.no_wallet.instructions_login")}{#if !without_create}{@html $t("pages.no_wallet.instructions_create")} |
||||
{:else} |
||||
{@html $t("pages.no_wallet.instructions_nocreate")} |
||||
{/if} |
||||
|
||||
</p> |
||||
{#if !without_create} |
||||
<div class="row mt-5"> |
||||
<a href="/wallet/create" use:link> |
||||
<button |
||||
tabindex="-1" |
||||
class="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" |
||||
> |
||||
<svg |
||||
class="w-8 h-8 -ml-1 mr-2" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
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> |
||||
{$t("pages.no_wallet.create_wallet")} |
||||
</button> |
||||
</a> |
||||
</div> |
||||
{/if} |
||||
<div class="row mt-5"> |
||||
<a href="/wallet/login" use:link> |
||||
<button |
||||
tabindex="-1" |
||||
class="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" |
||||
> |
||||
<svg |
||||
class="w-8 h-8 mr-2 -ml-1" |
||||
fill="currentColor" |
||||
stroke="currentColor" |
||||
stroke-width="2" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" |
||||
/> |
||||
</svg> |
||||
{$t("buttons.login")} |
||||
</button> |
||||
</a> |
||||
</div> |
||||
</div> |
||||
</CenteredLayout> |
||||
@ -0,0 +1,33 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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 ng from "../api"; |
||||
import { |
||||
branch_subscribe, |
||||
active_session, |
||||
online, |
||||
} from "../store"; |
||||
|
||||
import { onMount, onDestroy, tick } from "svelte"; |
||||
import { Button, Progressbar, Spinner } from "flowbite-svelte"; |
||||
import { t } from "svelte-i18n"; |
||||
let is_tauri = import.meta.env.TAURI_ENV_PLATFORM; |
||||
|
||||
onMount(async () => { |
||||
|
||||
}); |
||||
|
||||
|
||||
</script> |
||||
|
||||
|
||||
@ -0,0 +1 @@ |
||||
export { default as LogoSimple } from "./components/LogoSimple.svelte"; |
||||
@ -0,0 +1,91 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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"> |
||||
export let value: string = ""; |
||||
export let id: string | undefined = undefined; |
||||
export let rows: number = 3; |
||||
|
||||
let has_success: boolean = false; |
||||
|
||||
const tauri_platform = import.meta.env.TAURI_ENV_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} |
||||
style="resize: none;" |
||||
{value} |
||||
class="col-span-6 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" |
||||
class:pr-11={!tauri_platform} |
||||
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> |
||||
@ -0,0 +1,40 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
@component Logo |
||||
The NextGraph Logo svg with color changing between blue and gray, |
||||
depending on connection status: |
||||
- connected: blue |
||||
- connecting: pulse between blue and gray |
||||
- disconnected: gray |
||||
|
||||
Provide classes using the `className` prop. |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { connection_status } from "../../store"; |
||||
// @ts-ignore |
||||
import Logo from "../../assets/nextgraph-nofill.svg?component"; |
||||
|
||||
export let className: string = ""; |
||||
let connection_status_class = "logo-blue"; |
||||
// Color is adjusted to connection status. |
||||
$: if ($connection_status === "connecting" || $connection_status === "starting") { |
||||
connection_status_class = "logo-pulse"; |
||||
} else if ($connection_status === "disconnected") { |
||||
connection_status_class = "logo-gray"; |
||||
} else { |
||||
connection_status_class = "logo-blue"; |
||||
} |
||||
</script> |
||||
|
||||
<Logo class={`${className} ${connection_status_class}`} /> |
||||
@ -0,0 +1,23 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
@component LogoSimple |
||||
The NextGraph Logo svg |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
// @ts-ignore |
||||
import Logo from "../../assets/nextgraph.svg?component"; |
||||
import { t } from "svelte-i18n"; |
||||
</script> |
||||
<Logo class="logo block h-[8em]" alt={$t("common.logo")} /> |
||||
|
||||
@ -0,0 +1,64 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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 { |
||||
Toast, |
||||
} from "flowbite-svelte"; |
||||
|
||||
import { |
||||
remove_toast |
||||
} from "../../store"; |
||||
import { onMount, onDestroy, tick } from "svelte"; |
||||
|
||||
const toast_color = { |
||||
"error":"red", |
||||
"warning":"orange", |
||||
"success":"green", |
||||
"info":"blue" |
||||
}; |
||||
|
||||
const toast_icon = { |
||||
"error": XCircle, |
||||
"warning": ExclamationCircle, |
||||
"success": CheckCircle, |
||||
"info": InformationCircle, |
||||
} |
||||
import { |
||||
CheckCircle, |
||||
XCircle, |
||||
ExclamationCircle, |
||||
InformationCircle, |
||||
Icon, |
||||
} from "svelte-heros-v2"; |
||||
|
||||
export let toast; |
||||
export let i; |
||||
|
||||
onMount(()=>{ |
||||
toast.i = i; |
||||
if (toast.level=="success") |
||||
{ |
||||
toast.timer = setTimeout(()=>{remove_toast(i);}, toast.timeout || 10000); |
||||
} |
||||
}); |
||||
|
||||
</script> |
||||
|
||||
<div class="toast fixed flex w-full max-w-xs" style="top:{16+i*74}px;" |
||||
on:click|capture|stopPropagation={()=>{remove_toast(toast.i);}} |
||||
on:keypress={()=>{}} |
||||
> |
||||
<Toast color="{toast_color[toast.level]}" > |
||||
<Icon tabindex="-1" slot="icon" class="w-8 h-8 p-1 focus:outline-none" variation="outline" color="currentColor" icon={toast_icon[toast.level]} /> |
||||
{toast.text} |
||||
</Toast> |
||||
</div> |
||||
@ -0,0 +1,98 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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"> |
||||
export let value: string | undefined = undefined; |
||||
export let placeholder: string | undefined = undefined; |
||||
export let className: string | undefined = undefined; |
||||
export let classNameToggle: string | undefined = "right-0"; |
||||
export let id: string | undefined = undefined; |
||||
export let auto_complete: string | undefined = undefined; |
||||
import { createEventDispatcher } from "svelte"; |
||||
export let show: boolean = false; |
||||
export let autofocus = false; |
||||
export let input = undefined; |
||||
|
||||
let type: "password" | "text" = "password"; |
||||
$: type = show ? "text" : "password"; |
||||
|
||||
function handleInput(event: Event) { |
||||
const target = event.target as HTMLInputElement; |
||||
value = target.value; |
||||
} |
||||
|
||||
const dispatch = createEventDispatcher(); |
||||
|
||||
async function toggle() { |
||||
let { selectionStart, selectionEnd } = input; |
||||
show = !show; |
||||
input.focus(); |
||||
setTimeout(function () { |
||||
input.selectionStart = selectionStart; |
||||
input.selectionEnd = selectionEnd; |
||||
}, 0); |
||||
} |
||||
const key_pressed = async (e: any) => { |
||||
if (e.key == "Enter" || e.keyCode == 13) { |
||||
dispatch("enter"); |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<div class="relative"> |
||||
<!-- svelte-ignore a11y-autofocus --> |
||||
<input |
||||
bind:this={input} |
||||
{value} |
||||
{placeholder} |
||||
{id} |
||||
{type} |
||||
{autofocus} |
||||
on:input={handleInput} |
||||
class={`${className} pr-12 text-md block`} |
||||
autocomplete={auto_complete} |
||||
on:keypress={key_pressed} |
||||
/> |
||||
|
||||
<div |
||||
class={`${classNameToggle} clickable absolute inset-y-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> |
||||
@ -0,0 +1,38 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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"> |
||||
export let className: string = ""; |
||||
export let color: string = "currentColor"; |
||||
export let width: number = 24; |
||||
</script> |
||||
|
||||
<svg |
||||
class={"animate-spin " + className} |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
fill="none" |
||||
stroke={color} |
||||
viewBox={`0 0 ${width} ${width}`} |
||||
> |
||||
<circle |
||||
class="opacity-25" |
||||
cx={width / 2} |
||||
cy={width / 2} |
||||
r={width / 2.4} |
||||
stroke={color} |
||||
stroke-width={width / 6} |
||||
/> |
||||
<path |
||||
class="opacity-75" |
||||
fill={color} |
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" |
||||
/> |
||||
</svg> |
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 841 B |
@ -0,0 +1,191 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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, |
||||
ClipboardDocumentCheck, |
||||
MagnifyingGlass, |
||||
RocketLaunch, |
||||
Sun, |
||||
TableCells, |
||||
ListBullet, |
||||
RectangleGroup, |
||||
Squares2x2, |
||||
MapPin, |
||||
CircleStack, |
||||
Envelope, |
||||
GlobeAlt, |
||||
DocumentChartBar, |
||||
Document, |
||||
ClipboardDocumentList, |
||||
Photo, |
||||
Film, |
||||
RectangleStack, |
||||
SpeakerWave, |
||||
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, |
||||
Cog, |
||||
Square3Stack3d, |
||||
ChatBubbleLeftRight, |
||||
Fire, |
||||
ReceiptPercent, |
||||
ArrowTrendingUp, |
||||
CursorArrowRipple, |
||||
VideoCamera, |
||||
Variable, |
||||
Language, |
||||
QueueList, |
||||
} from "svelte-heros-v2"; |
||||
|
||||
import PdfIcon from "./PdfIcon.svelte"; |
||||
import BrailleIcon from "./BrailleIcon.svelte"; |
||||
import ChemistryIcon from "./ChemistryIcon.svelte"; |
||||
import GuitarIcon from "./GuitarIcon.svelte"; |
||||
import JsonIcon from "./JsonIcon.svelte"; |
||||
import JsIcon from "./JsIcon.svelte"; |
||||
import TsIcon from "./TsIcon.svelte"; |
||||
import RustIcon from "./RustIcon.svelte"; |
||||
import SvelteIcon from "./SvelteIcon.svelte"; |
||||
import ReactIcon from "./ReactIcon.svelte"; |
||||
import GraphQLIcon from "./GraphQLIcon.svelte"; |
||||
|
||||
export let config = {}; |
||||
export let dataClass: string; |
||||
export let color = "currentColor"; |
||||
|
||||
const exact_mapping = { |
||||
page: Window, |
||||
"app": Cog, |
||||
"app:z": SquaresPlus, |
||||
class: ViewfinderCircle, |
||||
contract: ClipboardDocumentCheck, |
||||
"query:text": MagnifyingGlass, |
||||
"query:web": MagnifyingGlass, |
||||
"query:graphql": GraphQLIcon, |
||||
"data:graph": Sun, |
||||
"data:json": JsonIcon, |
||||
"data:map": JsonIcon, |
||||
"data:array": JsonIcon, |
||||
"data:table": TableCells, |
||||
"data:collection": ListBullet, |
||||
"data:container": Square3Stack3d, |
||||
"data:board": RectangleGroup, |
||||
"data:grid": Squares2x2, |
||||
"data:geomap": MapPin, |
||||
"e:mail": Envelope, |
||||
"e:link": Link, |
||||
"mc:text": Bars3BottomLeft, |
||||
"mc:link": Link, |
||||
"plato:card": Clipboard, |
||||
"plato:pad": Square2Stack, |
||||
"media:image": Photo, |
||||
"media:reel": VideoCamera, |
||||
"media:video": Film, |
||||
"media:album": RectangleStack, |
||||
"media:audio": SpeakerWave, |
||||
"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, |
||||
"social:chatroom": ChatBubbleLeftRight, |
||||
"social:live": Fire, |
||||
"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": CursorArrowRipple, |
||||
"prod:vote": CheckCircle, |
||||
"prod:spreadsheet": ReceiptPercent, |
||||
"doc:compose": QueueList, |
||||
"doc:maths": Variable, |
||||
"doc:music:abc": MusicalNote, |
||||
"doc:pdf": PdfIcon, |
||||
"doc:braille": BrailleIcon, |
||||
"doc:ancientscript": Language, |
||||
"doc:chemistry": ChemistryIcon, |
||||
"doc:music:guitar": GuitarIcon, |
||||
"code:js": JsIcon, |
||||
"code:ts": TsIcon, |
||||
"code:rust": RustIcon, |
||||
"code:svelte": SvelteIcon, |
||||
"code:react": ReactIcon, |
||||
}; |
||||
|
||||
const prefix_mapping = { |
||||
"post:": DocumentText, |
||||
code: CodeBracket, |
||||
schema: ArrowsPointingOut, |
||||
service: Cube, |
||||
"e:": GlobeAlt, |
||||
"app:": StopCircle, |
||||
"query:": RocketLaunch, |
||||
"data:": CircleStack, |
||||
"diagram": DocumentChartBar, |
||||
"chart": ChartPie, |
||||
"viz": ArrowTrendingUp, |
||||
"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> |
||||
|
||||
<Icon {...config} variation="outline" {color} icon={find(dataClass)} /> |
||||
@ -0,0 +1,57 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
@component DeviceIcon |
||||
Display an icon for a device class provided by the `device` attribute. |
||||
Pass `config` for custom attributes. |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { |
||||
Icon, |
||||
Cube, |
||||
GlobeAlt, |
||||
QuestionMarkCircle, |
||||
DevicePhoneMobile, |
||||
ComputerDesktop, |
||||
ServerStack, |
||||
Key, |
||||
CommandLine, |
||||
} from "svelte-heros-v2"; |
||||
|
||||
export let config = {}; |
||||
export let device: string; |
||||
|
||||
const mapping = { |
||||
Web: GlobeAlt, |
||||
NativeIos: DevicePhoneMobile, |
||||
NativeAndroid: DevicePhoneMobile, |
||||
NativeMacOS: ComputerDesktop, |
||||
NativeLinux: ComputerDesktop, |
||||
NativeWin: ComputerDesktop, |
||||
NativeService: ServerStack, |
||||
NodeService: ServerStack, |
||||
Verifier: ServerStack, |
||||
VerifierLocal: ServerStack, |
||||
ClientBroker: ServerStack, |
||||
WalletMaster: ServerStack, |
||||
Box: Cube, |
||||
Stick: Key, // VerifierStick |
||||
Cli: CommandLine, |
||||
}; |
||||
|
||||
const find = (dataClass: string) => { |
||||
return mapping[dataClass] || QuestionMarkCircle; |
||||
}; |
||||
</script> |
||||
|
||||
<Icon {...config} variation="outline" color="black" icon={find(device)} /> |
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 725 B |
@ -0,0 +1,64 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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. |
||||
--> |
||||
|
||||
<!-- |
||||
@component DeviceIcon |
||||
Display an icon for a device class provided by the `device` attribute. |
||||
Pass `config` for custom attributes. |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { |
||||
Icon, |
||||
Bolt, |
||||
Megaphone, |
||||
QuestionMarkCircle, |
||||
ExclamationCircle, |
||||
Key, |
||||
LockClosed, |
||||
GlobeAlt, |
||||
UserGroup, |
||||
PaperAirplane, |
||||
} from "svelte-heros-v2"; |
||||
|
||||
import DataClassIcon from "./DataClassIcon.svelte"; |
||||
|
||||
export let config = {}; |
||||
export let img: string; |
||||
|
||||
const mapping = { |
||||
stream: Bolt, |
||||
channel: Megaphone, |
||||
private: Key, |
||||
protected: LockClosed, |
||||
public: GlobeAlt, |
||||
group: UserGroup, |
||||
dialog: PaperAirplane, |
||||
unknown_doc: ExclamationCircle, |
||||
}; |
||||
|
||||
const find = (dataClass: string) => { |
||||
return mapping[dataClass] || QuestionMarkCircle; |
||||
}; |
||||
</script> |
||||
|
||||
{#if img.startsWith("blob:")} |
||||
<img style="aspect-ratio:1;" class="rounded-full" src={img} alt="profile"/> |
||||
{:else if img.startsWith("class:")} |
||||
<DataClassIcon {config} dataClass={img.slice(6)} /> |
||||
{:else if img.startsWith("nav:")} |
||||
<Icon {...config} variation="outline" color="currentColor" icon={find(img.slice(4))} /> |
||||
{:else} |
||||
<QuestionMarkCircle {...config}/> |
||||
{/if} |
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 429 B |
@ -0,0 +1,130 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 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, |
||||
Square3Stack3d, |
||||
QueueList, |
||||
} from "svelte-heros-v2"; |
||||
import JsonIcon from "./JsonIcon.svelte"; |
||||
import JsonLdIcon from "./JsonLdIcon.svelte"; |
||||
import RdfIcon from "./RdfIcon.svelte"; |
||||
import TurtleIcon from "./TurtleIcon.svelte"; |
||||
import GraphQLIcon from "./GraphQLIcon.svelte"; |
||||
|
||||
export let config = {}; |
||||
export let zera: string; |
||||
|
||||
const exact_mapping = { |
||||
json_ld_editor: JsonLdIcon, |
||||
json_editor: JsonIcon, |
||||
triple_editor: RdfIcon, |
||||
turtle_viewer: TurtleIcon, |
||||
rdf_viewer: CircleStack, |
||||
graph_viewer: Sun, |
||||
compose: QueueList, |
||||
sparql_query: RocketLaunch, |
||||
sparnatural: CursorArrowRays, |
||||
graphql: GraphQLIcon, |
||||
invoke: Play, |
||||
ontology_viewer: ArrowsPointingOut, |
||||
download: DocumentArrowDown, |
||||
edit: PencilSquare, |
||||
file: Document, |
||||
source: CodeBracket, |
||||
post: DocumentText, |
||||
pad: Square2Stack, |
||||
container: Square3Stack3d, |
||||
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="currentColor" icon={find(zera)} /> |
||||
@ -0,0 +1,2 @@ |
||||
export { default as NoWallet} from "./NoWallet.svelte"; |
||||
export { default as CenteredLayout} from "./CenteredLayout.svelte"; |
||||
@ -0,0 +1,956 @@ |
||||
{ |
||||
"app": { |
||||
"sparql_update_editor" : { |
||||
"success": "SPARQL Update successful!" |
||||
} |
||||
}, |
||||
"doc": { |
||||
"doc": "Document", |
||||
"protected_store": "Protected Profile", |
||||
"public_store": "Public Site", |
||||
"private_store": "Private Store", |
||||
"group_store": "Group Store", |
||||
"dialog_store": "Dialog Store", |
||||
"rich": "Post or Article", |
||||
"markdown": "Markdown", |
||||
"text": "Plain Text", |
||||
"social": "Social", |
||||
"group": "Group", |
||||
"pro": "Pro", |
||||
"media": "Media", |
||||
"chart": "Chart", |
||||
"viz": "Visualization", |
||||
"diagram": "Diagram", |
||||
"other": "Other formats", |
||||
"data": "Data", |
||||
"code": "Code", |
||||
"apps": "Apps", |
||||
"new_app": "Create a new App", |
||||
"record_reel": "Record Reel", |
||||
"record_voice": "Record Audio", |
||||
"take_picture": "Take a picture", |
||||
"select_class": "Select a type of document to create", |
||||
"destination": { |
||||
"mc": "Keep in Magic Carpet", |
||||
"stream": "Send in current Stream", |
||||
"dialog": "Send in Dialog", |
||||
"store": "Save in current Store" |
||||
}, |
||||
"sparql_query": "SPARQL Query", |
||||
"run_query": "Run Query", |
||||
"run_update": "Run Update", |
||||
"query_all_docs": "Query all docs", |
||||
"view_as_turtle": "View as Turtle", |
||||
"view_graph": "View Graph", |
||||
"results": "Results", |
||||
"empty": "Empty", |
||||
"type_your_text_here": "Type your text here", |
||||
"sparql_update": "SPARQL Update", |
||||
"no_triples": "No triples in this Graph.", |
||||
"start_editing": "Start editing", |
||||
"create": "Create a Document", |
||||
"creating": "Please wait while your Document is being created", |
||||
"not_found" : "Document not found", |
||||
"empty_container": "Container is empty.", |
||||
"sign_snapshot": "Create a signed Snapshot", |
||||
"sign_heads": "Sign current heads", |
||||
"take_snapshot": "and take a Snapshot", |
||||
"signed_snapshot_is_on_its_way": "Your request for a signed snapshot has been sent to all signers. You will be notified when it is ready", |
||||
"signature_is_on_its_way": "Your request for a signature has been sent to all signers. You will be notified when it is ready", |
||||
"signature_is_ready": "Your signature is ready.", |
||||
"not_found_details_online" : "The document could not be found locally on this device, nor on the broker.", |
||||
"not_found_details_offline" : "The document could not be found locally on this device, and it seems like you are offline, so it could not be retrieved from any broker neither.<br/><br/>If you are opening this document for the first time on this device, you have to be online now so the document can be fetched.<br/><br/> We will try connecting and fetching it every 5 seconds.", |
||||
"cannot_load_offline": "You are offline and using the web app. There is currently a limitation on local storage within the Web App, and you need to connect to the broker every time you login with the Web App.<br/><br/>For now, the Web App does not keep a local copy of your documents. due to the limit of 5MB in localStorage. We will remove this limitation soon. Stay tuned!<br/><br/>Check your connectivity status in the ", |
||||
"header": { |
||||
"buttons": { |
||||
"edit": "Edit", |
||||
"edit_intro": "Edit title & intro", |
||||
"edit_profile": "Edit profile", |
||||
"bookmarked": "Saved", |
||||
"all_docs": "All Docs", |
||||
"groups": "Groups", |
||||
"channels": "Channels", |
||||
"inbox": "Inbox", |
||||
"chat": "Chat", |
||||
"save": "Save" |
||||
}, |
||||
"doc":{ |
||||
"title": "Title", |
||||
"about": "Introduction" |
||||
}, |
||||
"profile": { |
||||
"title": "Name", |
||||
"about": "Bio" |
||||
} |
||||
}, |
||||
"signature": { |
||||
"title": "Signature", |
||||
"current_heads": "Current heads", |
||||
"or_click_on_head": "or click on one of the signed heads to get its link.", |
||||
"here_is_the_link": "Here is its link that you can share.", |
||||
"cli_warning": "For now the link is only usable with the CLI, by running the following command", |
||||
"signed_snap_at_head": "A signed snapshot is currently at the head." |
||||
}, |
||||
"file": { |
||||
"download": "Download", |
||||
"upload_progress": "Uploading...", |
||||
"upload": "Upload file" |
||||
}, |
||||
"errors": { |
||||
"InvalidNuri": "Invalid NURI", |
||||
"no_session": "No active session" |
||||
}, |
||||
"errors_details": { |
||||
"InvalidNuri": "The provided NextGraph URI is invalid", |
||||
"no_session": "Please refresh the page or close and reopen the app" |
||||
}, |
||||
"graph" : "Graph", |
||||
"discrete" : "Document", |
||||
"menu" : { |
||||
"edit_with": "Edit with", |
||||
"view_as": "View as", |
||||
"install_app_to_edit": "Install App to edit", |
||||
"live_editing": "Live editing (real-time)", |
||||
"live_editing_description": "Collaborate in real-time with other users. If deactivated, you will have to save manually your edits", |
||||
"items": { |
||||
"new_block": { |
||||
"label": "New Block", |
||||
"desc": "Create a new block that can be embedded/included in this doc or other docs" |
||||
}, |
||||
"editor_chat": { |
||||
"label": "Editors' Chat", |
||||
"desc": "Chat with other Editors about the Document you are editing" |
||||
}, |
||||
"folders": { |
||||
"label": "Folders", |
||||
"desc": "Organize your documents in folders and platos" |
||||
}, |
||||
"toc": { |
||||
"label": "Table of Content", |
||||
"desc": "Table of Content for Graph and Document" |
||||
}, |
||||
"files": { |
||||
"label": "Attachments and Files", |
||||
"desc": "List all attached files and resources files, and download them if needed" |
||||
}, |
||||
"share": { |
||||
"label": "Share", |
||||
"desc": "Share with others, or react" |
||||
}, |
||||
"comments": { |
||||
"label": "Comments", |
||||
"desc": "See comments or annotations, replies, and write your own." |
||||
}, |
||||
"branches": { |
||||
"label": "Branches", |
||||
"desc": "View, edit or fork branches and blocks" |
||||
}, |
||||
"history": { |
||||
"label": "History", |
||||
"desc": "Open history of modifications, and travel back in time, to previous revisions" |
||||
}, |
||||
"find": { |
||||
"label": "Find", |
||||
"desc": "Information about the document, statistics, and keywords" |
||||
}, |
||||
"bookmark": { |
||||
"label": "Save", |
||||
"desc": "Keep a bookmark of this Document in your private store." |
||||
}, |
||||
"annotate": { |
||||
"label": "Annotate", |
||||
"desc": "Write private annotations on this Document. Only you can see those annotations. You will also have an option to share them with others." |
||||
}, |
||||
"info": { |
||||
"label": "Information", |
||||
"desc": "Information about the document, statistics, and keywords" |
||||
}, |
||||
"repost": { |
||||
"label": "Post in my Stream", |
||||
"desc": "Post in your Stream (publish, repost, reply, boost)" |
||||
}, |
||||
"dm": { |
||||
"label": "Send in Direct Message", |
||||
"desc": "Send as Direct Message to a contact or in a group chat" |
||||
}, |
||||
"react": { |
||||
"label": "Like and React", |
||||
"desc": "Like, react, rate" |
||||
}, |
||||
"author": { |
||||
"label": "Reply to Author(s)", |
||||
"desc": "Sends a private message to the author(s) in reply to this Document" |
||||
}, |
||||
"quote": { |
||||
"label": "Quote/Mention in new Post", |
||||
"desc": "Write a Post that quotes/cites this Document" |
||||
}, |
||||
"forward": { |
||||
"label": "Share via other apps", |
||||
"desc": "Share this document to users with other platforms and apps" |
||||
}, |
||||
"link": { |
||||
"label": "Links", |
||||
"desc": "Get the links to access this Document on the web" |
||||
}, |
||||
"qr": { |
||||
"label": "QR-Code", |
||||
"desc": "Display or download a QR-Code that links to this Document" |
||||
}, |
||||
"download": { |
||||
"label": "Export and Download", |
||||
"desc": "Export to several formats and Download the file, or create a self-contained HTML file of this Document that works offline" |
||||
}, |
||||
"notifs": { |
||||
"label": "Notifications", |
||||
"desc": "Change your notification settings for this Document" |
||||
}, |
||||
"permissions": { |
||||
"label": "Permissions", |
||||
"desc": "See and change permissions and members for this Document" |
||||
}, |
||||
"settings": { |
||||
"label": "Settings", |
||||
"desc": "Change settings for this Document" |
||||
}, |
||||
"copy": { |
||||
"label": "Copy or Move", |
||||
"desc": "Copy, Move, Duplicate, Fork" |
||||
}, |
||||
"embed": { |
||||
"label": "Embed", |
||||
"desc": "Embed this Document or Block into another Document, or insert a Block into this Document" |
||||
}, |
||||
"schema": { |
||||
"label": "Schema", |
||||
"desc": "See and manage the Schema of this Document (Model, Ontology, Vocabulary)" |
||||
}, |
||||
"signature": { |
||||
"label": "Signature", |
||||
"desc": "Obtain and verify the Signature of the Document to prove its authenticity. Or request a signature if there isn't any." |
||||
}, |
||||
"translations": { |
||||
"label": "Translations", |
||||
"desc": "Manage the translations of your document in order to offer it in multiple languages" |
||||
}, |
||||
"services": { |
||||
"label": "Services", |
||||
"desc": "See and invoke services available for this Document. Services can be read-only, or read-write" |
||||
}, |
||||
"print": { |
||||
"label": "Print", |
||||
"desc": "Print or save to PDF" |
||||
}, |
||||
"console": { |
||||
"label": "Console", |
||||
"desc": "Open a console and run interactive commands" |
||||
}, |
||||
"source": { |
||||
"label": "Source", |
||||
"desc": "View and Download the source code of this Document" |
||||
}, |
||||
"dev": { |
||||
"label": "Developer", |
||||
"desc": "Open the developer toolbox" |
||||
}, |
||||
"tools": { |
||||
"label": "Tools", |
||||
"desc": "Select a tool" |
||||
}, |
||||
"mc": { |
||||
"label": "Magic Carpet", |
||||
"desc": "Opens the Magic Carpet (like a clipboard, but better)" |
||||
}, |
||||
"archive": { |
||||
"label": "Archive", |
||||
"desc": "Archive documents that you don't need anymore" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"pages": { |
||||
"not_found": { |
||||
"title": "Page Not Found", |
||||
"message": "The page you are looking for does not exist.<br/>It has probably not been implemented yet" |
||||
}, |
||||
"user_panel": { |
||||
"title": "User Panel", |
||||
"personal": "Personal" |
||||
}, |
||||
"user_registered": { |
||||
"success": "You have been successfully registered.", |
||||
"success_with_invitation": "You have been successfully registered to {invitation_name}." |
||||
}, |
||||
"wallet_info": { |
||||
"title": "Wallet", |
||||
"download": "Download Wallet File", |
||||
"download_failed": "Download Failed:<br/>{error}", |
||||
"download_in_progress": "Download in progress...", |
||||
"download_successful": "You will find the file named \"{wallet_file}\" in your Downloads folder", |
||||
"download_file_button": "Click here to download the wallet file", |
||||
"remove_wallet": "Remove wallet from Device", |
||||
"remove_wallet_modal.title": "Remove wallet?", |
||||
"remove_wallet_modal.confirm": "Are you sure you want to remove this wallet from your device?", |
||||
"create_text_code": "Export by generating a TextCode", |
||||
"scan_qr.title": "Export by scanning a QR-Code", |
||||
"scan_qr.no_camera": "If to the contrary, the other device does not have a camera, ", |
||||
"scan_qr.other_has_camera": "If the other device where you want to import the Wallet, has a camera, then you can just click on the Back button and select <span class=\"path\">Export by generating a QR-Code</span>", |
||||
"scan_qr.notes": "You will now scan the QR-Code that appears on the screen of the other device (the one you want to transfer your wallet to). You will be asked to allow the app to use your camera.", |
||||
"scan_qr.scanner.title": "Scan your QR-Code", |
||||
"scan_qr.scanner.loading": "Loading scanner", |
||||
"scan_qr.syncing": "Synchronizing wallet", |
||||
"scan_qr.scan_successful": "Success!<br />Your wallet has been transferred to the other device.", |
||||
"gen_qr.title": "Export by generating a QR-Code", |
||||
"gen_qr.notes": "In order to transfer your wallet to another device, you will now display a QR-Code here on this device, and you will then scan it with the other device where you want to transfer your wallet to.", |
||||
"gen_qr.no_camera": "If the device where you want to import the Wallet, does not have a camera, then you will have to choose another method.", |
||||
"gen_qr.img_title": "Your Export QR-Code to Scan", |
||||
"gen_qr.img_alt": "Your Export QR-Code to Scan", |
||||
"gen_qr.gen_button": "Display QR-Code", |
||||
"gen_text_code.title": "Export with TextCode", |
||||
"gen_text_code.gen_btn": "Generate TextCode", |
||||
"gen_text_code.label": "Your TextCode:" |
||||
}, |
||||
"settings": { |
||||
"title": "Settings" |
||||
}, |
||||
"admin": { |
||||
"title": "Admin" |
||||
}, |
||||
"accounts": { |
||||
"title": "Accounts" |
||||
}, |
||||
"full_layout": { |
||||
"home": "Home", |
||||
"stream": "Stream", |
||||
"search": "Search", |
||||
"create": "Create", |
||||
"shared": "Shared", |
||||
"site": "Site", |
||||
"messages": "Messages", |
||||
"notifications": "Notifications" |
||||
}, |
||||
"install": { |
||||
"app_availability": "<b>NextGraph App</b> is available for download as a native app for your mobile, tablet, laptop and desktop.<br /> The app supports iOS, Android, Linux, macOS, Windows, or any other platform with a modern browser.", |
||||
"has_wallet_warning": "A wallet is saved in this browser. <br />If it is yours, once the installation of the app will be finished,<br /> choose the option \"Login\" on the app.<br /> (do not create another wallet from the app).", |
||||
"android_play_store": "Android Play Store", |
||||
"download_apk": "Download APK", |
||||
"ios_app_store": "iOS App Store", |
||||
"download_mac_os": "Download for MacOS", |
||||
"download_linux": "Download Linux Package", |
||||
"download_windows": "Download for Windows", |
||||
"other_platforms": "Other platforms" |
||||
}, |
||||
"no_wallet": { |
||||
"welcome": "Welcome to NextGraph", |
||||
"description": "We could not find a wallet on this device.", |
||||
"instructions_login": "If you already have a wallet, select \"Login\"", |
||||
"instructions_create": ", otherwise, select \"Create Wallet\" here below.", |
||||
"instructions_nocreate": ". Creation of a Wallet is not possible while<br/> logging-in to a third-party app.", |
||||
"create_wallet": "Create Wallet" |
||||
}, |
||||
"login": { |
||||
"heading": "How to open your wallet? You have 2 options:", |
||||
"with_pazzle": "With your Pazzle", |
||||
"pazzle_steps": { |
||||
"1": "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 login. They will not always appear in the same order.", |
||||
"2": "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. 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 your keyboard to move to the desired item on the screen, then press the space bar to select each one.", |
||||
"3": "Once you completed the last category, you will be presented with all the images you have previously selected. Their order is displayed as it was when you picked them. But this is not the correct order of the images in your pazzle. You now have to order them correctly.", |
||||
"4": "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 the number 1 will appear on top of it.", |
||||
"5": "Move on to the second image of your pazzle (that you memorized). Find it on the screen and tap on it. Repeat this step until you reached the last image.", |
||||
"6": "Finally, you are asked for your PIN code. Enter it by clicking or tapping on the digits." |
||||
}, |
||||
"with_mnemonic": "With your 12 words Mnemonic (passphrase)", |
||||
"mnemonic_steps": { |
||||
"1": "Enter your twelve words mnemonic in the input field. The words must be separated by spaces.", |
||||
"2": "Enter the PIN code that you chose when you created your wallet." |
||||
}, |
||||
"trust_device_allow_cookies": "By selecting this option, you agree to saving some cookies on your browser.", |
||||
"trust_device_yes": "Yes, save my wallet on this device", |
||||
"device_name_label": "Name of this device. You can edit it now", |
||||
"device_name_placeholder": "Enter name of this device", |
||||
"loading_pazzle": "Loading Pazzle", |
||||
"open_with_pazzle": "Open With Pazzle instead", |
||||
"login_cancel": "Cancel Login", |
||||
"open_with_mnemonic": "Open with Mnemonic", |
||||
"open": "Open my wallet", |
||||
"enter_mnemonic": "Enter your 12 words mnemonic", |
||||
"mnemonic_placeholder": "12 words separated by spaces", |
||||
"enter_password": "Enter your password", |
||||
"select_emoji": "Select your image for category:<br />{category}", |
||||
"order_emojis": "Select each image in the correct order", |
||||
"enter_pin": "Enter your PIN code", |
||||
"opening_wallet": "Opening your wallet...<br /> Please wait", |
||||
"wallet_opened": "Your wallet is opened! <br />Please wait while the app is loading...", |
||||
"redirecting": "Redirecting to", |
||||
"qr_code": "Wallet QRCode", |
||||
"qr_modal_title": "My Wallet QRCode", |
||||
"qr_modal_description": "Use this QRCode to log in with your wallet on new devices.", |
||||
"keep_wallet": "Save to Device for Future Logins" |
||||
}, |
||||
"account_info": { |
||||
"title": "Accounts Info", |
||||
"site": "{name} account", |
||||
"devices": "Devices", |
||||
"brokers": "Brokers", |
||||
"no_brokers_connected": "No brokers connected" |
||||
}, |
||||
"wallet_create": { |
||||
"redirecting_to_registration_page": "Redirecting to the Broker Service Provider registration page", |
||||
"complete_in_popup": "Complete the registration in the popup window", |
||||
"own_your_ngbox": "Own your NG-Box", |
||||
"self-host_broker": "Self-host a broker", |
||||
"tos_ng_net": "Terms of Service NextGraph.net", |
||||
"wallet_description": "A <b>NextGraph Wallet</b> is unique to each person. It stores your credentials and authorizations to access documents. You need one in order to start using NextGraph.<br /><br />If you already have a wallet, you should not create a new one. Instead, <a href=\"#/wallet/login\" >login here with your existing wallet.</a > If you never created a NextGraph Wallet before, please follow the instructions below in order to create your unique personal wallet.", |
||||
"has_wallet": "Some wallets are saved on this device,<br /> to log in with one of them, <a href=\"#/wallet/login\" use:link>click here.</a>", |
||||
"please_read": "Please read", |
||||
"wallet_about": { |
||||
"title": "What is a wallet?", |
||||
"1": "Your wallet is a secure and encrypted small file that contains some important information that only you should have access to.", |
||||
"2": "In your wallet, we store all the permissions to access documents you have been granted with, or that you have created yourself.<br/><b>There are 3 ways to unlock your wallet:</b>", |
||||
"password": "With a regular <b>username and password</b>. This is the default option and the easiest way. It is probably also the less secure one as you will probably choose a weak password. Also, it implies that we store your wallet on the Broker, in an encrypted form.", |
||||
"mnemonic": "With a <b>mnemonic</b> (like a passphrase) composed of 12 english words, which will be randomly generated for you. This is more secure but not easy to remember.", |
||||
"pazzle": "With a <b>pazzle</b> (contraction of puzzle and password) which is a new method that is both secure and easy to remember. It is composed of 9 images that will be generated for you in a specific order. Unlocking your wallet is then like solving a small puzzle.", |
||||
"3": "You will choose which option you prefer in the following screens. You can change option later on, after your wallet has been created. In any case, you will also have to choose a <b>PIN code</b> of 4 digits.", |
||||
"4": "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 complex password. The entropy of your pazzle is <b >66bits</b >, which is considered very high by all standards.", |
||||
"5": "You should only create <b>one unique wallet for yourself</b >. All your accounts, identities and permissions will be added into this unique wallet. Do not create another wallet if you already have one. Instead, you will <b>import</b> your existing wallet in all the devices you want to use.", |
||||
"6": "Your wallet can be transferred from one device to another with the help of a small file that you download, or with a QRcode, or with a TextCode that you copy/paste. In any case, you should never share this file or code with anybody else.", |
||||
"7": "We at NextGraph will never see the content of your wallet. It is encrypted and we do not know your credentials, so we cannot see what is inside.", |
||||
"8": "We offer a Wallet Recovery mechanism, that you can use in the event of lost or compromised devices or credentials. It is optional and implies storing an encrypted recovery kit on your broker.", |
||||
"9": "Please note that we will never ask for your email address. We cannot contact you by email, and even the recovery mechanism doesn't rely on emails. If you receive an email from us, delete it immediately as it must be spam (unless you subscribed to our newsletter, of course). " |
||||
}, |
||||
"create_wallet_now": "I create my wallet now!", |
||||
"select_server": "NextGraph is based on an efficient decentralized P2P network, and in order to join this network and start using the app, you need to first select a <b>broker server</b>.", |
||||
"broker_about": { |
||||
"title": "What is a broker?", |
||||
"1": "The broker helps you keep all your data in <b>sync</b>, as 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 participants are offline, you can still see what they shared with you.", |
||||
"2": "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 not have the keys to decrypt them.", |
||||
"3": "The broker helps you enforce your <b>privacy</b> as it hides your internet address (IP) from other users you share documents with.", |
||||
"4": "It will be possible in the future to use NextGraph without any broker and to have direct connections between peers, but this will imply a less smooth experience.", |
||||
"5": "At anytime you can decide to switch to another broker service provider or host it yourself. Your data is totally <b >portable</b > and can freely move to another broker.", |
||||
"6": "Soon we will offer you the opportunity to host your own broker at <b>home</b> or <b>office</b>. Instead of using a \"broker service provider\", you will own a small device that you connect behind your internet router. It is called <b>NG Box</b> and will be available soon.", |
||||
"7": "Organizations and companies have the opportunity to host a broker <b>on-premise</b> or in the <b>cloud</b>, as the software is open source. Individuals can also <b>self-host</b> a broker on any VPS server or at home, on their dedicated hardware.", |
||||
"8": "You can also start by installing the Native App and create the wallet from there, and then select the broker from the app. <br/>At any time you will be able to transfer your Wallet from one device to another (including from the web-app and the native apps)." |
||||
}, |
||||
"choose_broker": "Please choose one broker among the list", |
||||
"register_with_broker": "Register with {broker}", |
||||
"for_eu_citizens": "European Union Server", |
||||
"for_rest": "International Server", |
||||
"this_broker": "this Broker", |
||||
"enter_invite_link": "Enter an invitation link", |
||||
"scan_invite_qr": "Scan an invitation QR-code", |
||||
"self_hosted_broker": "Self-hosted broker", |
||||
"ng_box": "NG Box (owned or invited)", |
||||
"install_app": "Install the app", |
||||
"registration_success": "You are creating an account at {broker}", |
||||
"choose_username": { |
||||
"title": "Now choose your username and password", |
||||
"warning": "Please note that we do not offer password recovery mechanism for now.<br/> We won't be able to help you if you forget your password." |
||||
}, |
||||
"type_username_placeholder": "Type in your username", |
||||
"type_password_placeholder": "Type in your password", |
||||
"choose_pin": { |
||||
"title": "Let's start creating your wallet by choosing a PIN code", |
||||
"description": "We recommend you to choose a PIN code that you already know very well. <br />We at NextGraph will never see your PIN.", |
||||
"rules": "Here are the rules for the PIN:", |
||||
"1": "It cannot be a series like 1234 or 8765.", |
||||
"2": "The same digit cannot repeat more than once. By example 4484 is invalid.", |
||||
"3": "Try to avoid birth dates, last digits of phone numbers, or zip codes" |
||||
}, |
||||
"chosen_pin": "You have chosen:", |
||||
"confirm_pin": "Please confirm your PIN code.", |
||||
"confirm_pin_description": "Enter the same PIN again.", |
||||
"pin_confirmed_as": "You PIN is confirmed as: ", |
||||
"security_phrase_invalid": "The security phrase your entered is invalid.", |
||||
"choose_security_phrase_and_image": { |
||||
"title": "Now let's enter a security phrase and a security image", |
||||
"description": "As a verification step, this phrase and image will be presented to you every time you are about to unlock your wallet.<br /> This security measure will prevent you from entering your credentials on malicious sites and apps.", |
||||
"warning": "Every time you will unlock your wallet, if you do not see and recognize your own security phrase and image before entering your credentials, please stop and DO NOT enter your credentials, as you would otherwise be the victim of a phishing attempt.", |
||||
"rules_title": "Here are the rules for the security phrase and image:", |
||||
"1": "The phrase should be at least (minimum) 10 characters long and maximum 100.", |
||||
"2": "It should be something you will remember, but not something too personal.", |
||||
"3": "Do not enter your full name, nor address, nor phone number.", |
||||
"4": "Instead, you can enter a quote, a small sentence that you like, or something meaningless to others, but unique to you.", |
||||
"5": "The image should be minimum 150x150px. There is no need to provide more than 400x400px as it will be scaled down anyway.", |
||||
"6": "We accept several formats like JPEG, PNG, GIF, WEBP and more.", |
||||
"7": "The image should be unique to you. But it should not be too personal neither.", |
||||
"8": "Do not upload your face picture, this is not a profile pic.", |
||||
"9": "The best would be a landscape you like or any other picture that you will recognize as unique.", |
||||
"10": "Please be aware that other people who are sharing this device with you, will be able to see this image and phrase." |
||||
}, |
||||
"type_security_phrase_placeholder": "Type a security phrase...", |
||||
"save_security_phrase_and_image": "Save security phrase & image", |
||||
"tap_to_upload": "Tap to upload an image", |
||||
"click_to_upload": "Click to select an image", |
||||
"or_drag_drop": "or drag and drop", |
||||
"pins_no_match": "You didn't enter the same PIN twice", |
||||
"pin_invalid": "The pin your entered is invalid.", |
||||
"almost_done": "We are almost done!", |
||||
"save_wallet_options": { |
||||
"description": "There are 2 options to choose before we can create your wallet.", |
||||
"trust": "Do you trust this device?", |
||||
"trust_description": "If you do, if this device is yours, or it is used by a 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.", |
||||
"trust_toggle": "Yes, save my wallet on this device", |
||||
"device_name_description": "To identify which device you are connected with, every device should have a name (e.g. Bob's laptop). Please enter it here.", |
||||
"cloud": "Keep a copy in the cloud?", |
||||
"cloud_description": "Are you afraid that you will loose the file containing your wallet? If this would happen, your wallet would be lost forever, together with all your documents. We can keep an encrypted copy of your wallet in our cloud. Only you will be able to download it with a special link. You would have to keep this link safely though. By selecting this option, you agree to the", |
||||
"cloud_toggle": "Save my wallet in the cloud?", |
||||
"cloud_tos": "Terms of Service of our cloud", |
||||
"pdf": "Should we create a PDF file of your wallet?", |
||||
"pdf_description": "We can prepare for you a PDF file containing all the information of your wallet, unencrypted. In the next screen, once your wallet is ready, you will be able to download such PDF file. You should print this file and then delete the PDF (and empty the trash). Keep this printed document in a safe place. It contains all the information to regenerate your wallet in case you lost access to it.", |
||||
"pdf_toggle": "Yes, create a PDF of my wallet", |
||||
"link": "Create a link to access your wallet easily?", |
||||
"link_description": "When you want to use your wallet on the web or from other devices, we can help you find your wallet by creating a simple link accessible from anywhere. Only you will have access to this link. In order to do so, we will keep your wallet ID and some information about your broker on our cloud servers. If you prefer to opt out, just uncheck this option. By selecting this option, you agree to the", |
||||
"link_toggle": "Create a link to my wallet?" |
||||
}, |
||||
"lets_create": "Let's create this wallet", |
||||
"creating": "Your wallet is being created", |
||||
"ready": "Your wallet is ready!", |
||||
"download_wallet_description": "Please download your wallet and keep it in a safe location", |
||||
"download_wallet": "Download my wallet", |
||||
"download_wallet_done": "Your wallet file has been downloaded into your \"Downloads\" folder, with the name<br /><span class=\"text-black\"> {download_name}</span ><br /> <span class=\"font-bold\" >Please move it to a safe and durable place.</span ><br />", |
||||
"download_pdf_description": "Please download your Recovery PDF, print it, and delete it.", |
||||
"download_pdf": "Download my PDF", |
||||
"download_pdf_done": "Your Recovery PDF file has been downloaded into your \"Downloads\" folder,<br /> with the name <span class=\"text-black\"> {pdf_name}</span ><br /><br /> <span class=\"font-bold\" >Please print it and then delete it.</span ><br /><br />", |
||||
"your_pazzle": "Here below is your Pazzle. <br /> The <span class=\"font-bold\">order</span> of each image is <span class=\"font-bold\">important</span>!", |
||||
"your_mnemonic": "And here is your mnemonic (your alternative passphrase):", |
||||
"unlock_tips_1": "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.", |
||||
"unlock_tips_2": "Now click on \"Continue to Login\" and select your new wallet.", |
||||
"unlock_tips_3": "It is important that you <span class=\"font-bold\">login</span> with this wallet <span class=\"font-bold\">at least once</span> from this {platform, select, browser {browser tab} other {device}}, while connected to the internet, so your personal site can be created on your broker.", |
||||
"continue_to_login": "Continue to Login", |
||||
"continue_confirm": "Did you write down your login credentials?", |
||||
"continue_confirm_description": "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.", |
||||
"continue_confirm_go_back": "Take me back", |
||||
"continue_confirm_yes": "Yes, I did" |
||||
}, |
||||
"wallet_login": { |
||||
"select_wallet": "Select a wallet to login with", |
||||
"from_import.title": "Your wallet has been transferred", |
||||
"from_import.description": "Your wallet has been received from the other device!", |
||||
"from_import.instruction": "To finish the import, please log in.", |
||||
"with_username": "Import via Username", |
||||
"import_file": "Import a Wallet File", |
||||
"import_qr": "Import with QR-Code", |
||||
"import_link": "Import with TextCode", |
||||
"new_wallet": "Create a new Wallet", |
||||
"logged_in": "You are logged in.<br /> please wait while the app is loading", |
||||
"offline_advice": "If you do not have internet on this device, you can use the \"Import a Wallet file\" method instead." |
||||
}, |
||||
"wallet_login_username": { |
||||
"title": "Import Wallet with your Username", |
||||
"description": "If you have created a username and password for your Wallet, then you can enter the details here below in order to import your wallet from your broker. You only have to do that once on this device.", |
||||
"username": "Username", |
||||
"username_placeholder_domain": "with the @domain part", |
||||
"username_placeholder_without_domain": "without @{domain}", |
||||
"username_placeholder_without_at": "without the @domain part", |
||||
"password": "Password", |
||||
"password_placeholder": "Enter your password here", |
||||
"next": "Next", |
||||
"connect": "Connect", |
||||
"redirect": "Redirect me", |
||||
"retrieve_button": "Connect & Retrieve Wallet", |
||||
"success_btn": "Continue to Login", |
||||
"error.username": "The username you entered is invalid. Only alphanumerical characters are allowed (together with hyphen and underscore)", |
||||
"error.nodomainplease": "Please enter your username without a trailing @domain", |
||||
"warning.nodomainplease": "You shouldn't enter any @domain part for your username", |
||||
"warning.nospecificdomainplease": "You don't need to enter the @{domain} part of your username", |
||||
"error.invalid_domain": "the @domain part is invalid", |
||||
"error.mandatory_domain": "You must enter the @domain part of your username.", |
||||
"error.need_redirect": "This user is from another broker. Do you want to be redirected to that broker?" |
||||
}, |
||||
"wallet_login_qr": { |
||||
"title": "Import Wallet with QR-Code", |
||||
"scan.description": "To import your wallet from another device, generate an export QR-Code there. In order to do so, on the other device, go to<br /><span class=\"path\">User Panel > Wallet > Export by generating a QR-Code</span>.", |
||||
"scan.modal.title": "Scan Wallet QR-Code", |
||||
"gen.button": "Generate QR-Code", |
||||
"gen.description": "To import your wallet from another device, you have to generate a QR-Code here on this device, and then scan it with your other device (the one where your wallet is located for now).<br/>If your other device does not have a camera, then you have to use another method for importing your wallet here.", |
||||
"gen.letsgo": "Ready? On your other device, you first have to be logged-in (wallet is opened) and then you go to<br /><span class=\"path\">User Panel > Wallet > Export by scanning a QR-code</span>.<br />Then on this present device, click below on the<br/><span class=\"path\">Generate QR-Code</span> button.", |
||||
"gen.generated": "Scan this QR-Code from the other device.<br/>You have 5 minutes to do so.", |
||||
"success_btn": "Continue to Login" |
||||
}, |
||||
"wallet_login_textcode": { |
||||
"title": "Import Wallet from TextCode", |
||||
"description": "To generate a TextCode, open a logged in device and go to<br /><span class=\"path\">User Panel > Wallet > Export by generating a TextCode</span>.", |
||||
"import_btn": "Import with TextCode", |
||||
"enter_here": "Enter your TextCode here below and click on Import button" |
||||
}, |
||||
"scan_qr": { |
||||
"scanning": "Scanning the QR-Code" |
||||
} |
||||
}, |
||||
"buttons": { |
||||
"ok": "Ok", |
||||
"go_back": "Go back", |
||||
"back": "Back", |
||||
"confirm": "Confirm", |
||||
"cancel": "Cancel", |
||||
"remove": "Remove", |
||||
"correct": "Correct", |
||||
"try_again": "Try again", |
||||
"back_to_homepage": "Go back to Homepage", |
||||
"logout": "Logout", |
||||
"login": "Login", |
||||
"start_over": "Start over", |
||||
"scan_qr": "Scan QR-Code" |
||||
}, |
||||
"errors": { |
||||
"an_error_occurred": "An error occurred", |
||||
"error_occurred": "An error occurred:<br />{message}", |
||||
"AlreadyExists": "The user is already registered with the selected broker.<br />Try logging in instead.", |
||||
"InvalidSignature": "The signature is invalid.", |
||||
"IncompleteSignature": "The signature is incomplete.", |
||||
"SerializationError": "The data could not be serialized.", |
||||
"EncryptionError": "Your wallet could not be opened. You probably did a mistake when entering your credentials.", |
||||
"DecryptionError": "Error with decryption.", |
||||
"InvalidValue": "The value is invalid.", |
||||
"ConnectionNotFound": "The connection was not found.", |
||||
"InvalidSecurityImage": "The security image you chose is not valid.", |
||||
"InvalidSecurityText": "The security text you chose is too short. It must be at least 10 characters long.", |
||||
"InvalidKey": "The key is invalid.", |
||||
"InvalidInvitation": "The invitation is invalid.", |
||||
"InvalidCreateAccount": "An error occurred creating the account.", |
||||
"InvalidFileFormat": "The file format is invalid.", |
||||
"InvalidArgument": "The parameter is invalid.", |
||||
"PermissionDenied": "Permission denied.", |
||||
"InvalidPazzle": "The pazzle is not correct.", |
||||
"InvalidMnemonic": "The mnemonic is not correct.", |
||||
"CommitLoadError": "Error loading commit.", |
||||
"ObjectParseError": "Error parsing object.", |
||||
"StorageError": "Storage error.", |
||||
"NotFound": "Not Found", |
||||
"JsStorageKeyNotFound": "Could not find JavaScript storage key.", |
||||
"IoError": "Input/Output error.", |
||||
"CommitVerifyError": "Verifying commit failed.", |
||||
"LocalBrokerNotInitialized": "The local broker is not initialized.", |
||||
"JsStorageReadError": "Could not read from JavaScript Storage.", |
||||
"JsStorageWriteError": "Could not write to JavaScript Storage.", |
||||
"CannotSaveWhenInMemoryConfig": "Saving while configured for In-Memory is not possible.", |
||||
"WalletNotFound": "The wallet was not found.", |
||||
"WalletAlreadyAdded": "The wallet is already added.", |
||||
"WalletAlreadyOpened": "The wallet is already open.", |
||||
"WalletError": "Error with wallet.", |
||||
"BrokerError": "Error with broker.", |
||||
"SessionNotFound": "The session cannot be found.", |
||||
"SessionAlreadyStarted": "The session is already started.", |
||||
"RepoNotFound": "The repository cannot be found.", |
||||
"BranchNotFound": "The branch cannot be found.", |
||||
"StoreNotFound": "The store cannot be found.", |
||||
"UserNotFound": "The user cannot be found.", |
||||
"TopicNotFound": "The topic cannot be found.", |
||||
"NotConnected": "You are not connected.", |
||||
"ActorError": "Actor error.", |
||||
"ProtocolError": "Protocol error.", |
||||
"ServerError": "Server error.", |
||||
"InvalidResponse": "Received an invalid response.", |
||||
"BootstrapError": "Error while bootstrapping", |
||||
"NotAServerError": "Not a server.", |
||||
"VerifierError": "Error during verification.", |
||||
"SiteNotFoundOnBroker": "The site cannot be found on the broker", |
||||
"BrokerConfigErrorStr": "{error}", |
||||
"BrokerConfigError": "Error in the broker configuration.", |
||||
"MalformedEvent": "The event has an invalid format.", |
||||
"InvalidPayload": "The payload is invalid.", |
||||
"WrongUploadId": "The upload ID is incorrect.", |
||||
"FileError": "Error with file.", |
||||
"InternalError": "Internal Error", |
||||
"OxiGraphError": "Error in OxiGraph database.", |
||||
"ConfigError": "Error in configuration.", |
||||
"LocalBrokerIsHeadless": "The local broker is headless.", |
||||
"LocalBrokerIsNotHeadless": "The local broker is not headless.", |
||||
"InvalidNuri": "Invalid NextGraph URI.", |
||||
"InvalidTarget": "Cannot resolve target.", |
||||
"ExportWalletTimeOut": "The wallet-export session has expired. Try again.", |
||||
"ConnectionError": "Could not connect to the server.", |
||||
"IncompatibleQrCode": "You scanned a NextGraph QR-Code that is of the wrong type.", |
||||
"NotARendezVous": "You scanned an invalid QR-Code.", |
||||
"camera_unavailable": "Camera is unavailable.", |
||||
"ServerAlreadyRunningInOtherProcess": "App is already running on this device. Check it and close it.", |
||||
"WsError": "WebSocket error", |
||||
"cannot_load_this_file": "Cannot load this file", |
||||
"graph_not_found": "Graph not found", |
||||
"SocialQueryAlreadyStarted": "Social Query already started", |
||||
"ContactAlreadyExists": "Contact already added to your account", |
||||
"ContactNotFound": "You don't have any contact. We cannot start the Social Query", |
||||
"InvalidProfile": "Your profile is incomplete. You should add a name before you can share your profile with others", |
||||
"no_wasm_on_old_safari": "Your Safari browser is too old (version before 14.1). As a result we cannot load Automerge, needed for this document. Please upgrade your macOS or iOS system", |
||||
"BrowserTooOld": "Your browser is too old. Please upgrade it, use another browser, or install our native app. If you are using jshelter or another javascript protection mechanism, please deactivate it as we need access to the WebWorker facility of your browser.", |
||||
"NoLocalStorage": "You have disabled local storage in your browser. Please allow the current website (and also https://nextgraph.net website) to store data in this browser as otherwise we cannot proceed with Wallet creation. After allowing storage, please refresh the current page." |
||||
}, |
||||
"auth":{ |
||||
"select_broker":"<b>{origin}</b><br/>wants to access your wallet<br/><br/>Please select your broker in the list below:", |
||||
"unexpected_error":"An error occurred during the auth process" |
||||
}, |
||||
"connectivity": { |
||||
"stopped": "Stopped", |
||||
"personal": "Personal", |
||||
"connecting": "Connecting", |
||||
"connected": "Connected", |
||||
"loading": "Loading", |
||||
"connection_error_short": "{error}", |
||||
"online": "Online", |
||||
"offline": "Offline" |
||||
}, |
||||
"common": { |
||||
"version": "Version: {version}", |
||||
"logo": "NextGraph Logo", |
||||
"support_nextgraph": "Support NextGraph", |
||||
"about_nextgraph": "About NextGraph", |
||||
"donate_nextgraph": "Donate to NextGraph" |
||||
}, |
||||
"wallet_sync": { |
||||
"offline_warning": "You cannot transfer your wallet when offline.<br />Please make sure you are connected to the internet first.", |
||||
"textcode.usage_warning": "You have to exchange this TextCode with the other device by any means available to you (email, other messenger apps, etc...). It is highly recommended to use a tool that is end-to-end encrypted. If you can, you should use the \"Import with QR-Code\" option instead, as it is safer and simpler. If your devices are not connected to the internet, then you can use the \"Import a Wallet File\" option. In this case, you will transfer the wallet file with a USB key, from one device to the other, or for mobile, by connecting your mobile with the USB cable, to the computer, and then transferring the file with the File Transfer utility on Android, or AirDrop/Finder/iTunes on an iPhone/mac/PC. We do not recommend uploading your wallet file to any cloud service.", |
||||
"server_transfer_notice": "Both devices need to be online.<br />During this wallet transfer, your wallet will be temporarily and securely stored on our servers for up to 5 minutes, using two levels of encryption.<br/> We at NextGraph will never be able to read your wallet, your PIN, your pazzle nor your mnemonic.", |
||||
"importing": "Importing wallet", |
||||
"expiry": "The TextCode will be valid for 5 minutes.", |
||||
"error": "An error occurred while synchronizing your wallet:<br />{error}", |
||||
"no_camera": "Unfortunately, your device does not have a camera.<br /> You cannot scan any QR-Code.<br /> In order to export your wallet to another device, you will have to use another method.", |
||||
"no_camera_alternatives": "You have 2 other options: \"Import a Wallet file\" (transfer it using a USB key by example, useful if you are offline) or \"Import a TextCode\" (which is a text you will have to transfer with another messaging app)." |
||||
}, |
||||
"emojis": { |
||||
"category": { |
||||
"face": "Face", |
||||
"face_unwell": "Bad Face", |
||||
"face_costume": "Costumed", |
||||
"emotion": "Emotion", |
||||
"body": "Body", |
||||
"sport": "Sport", |
||||
"bigger_animal": "Big Animal", |
||||
"smaller_animal": "Small Animal", |
||||
"plants": "Plants and Insects", |
||||
"fruits": "Fruits and Veggies", |
||||
"food": "Food and Drinks", |
||||
"travel": "Travel", |
||||
"sky": "Sky and Weather", |
||||
"play": "Leisure", |
||||
"house": "Household" |
||||
}, |
||||
"codes": { |
||||
"COMMENT": "YOU CAN FIND I18N UNICODE DESCRIPTIONS AT https://github.com/unicode-org/cldr-json", |
||||
"happy": "happy face", |
||||
"happy_tears": "face with tears of joy", |
||||
"halo": "smiling face with halo ring", |
||||
"three_hearts": "face with 3 hearts", |
||||
"with_two_hearts": "face with 2 heart-eyes", |
||||
"one_heart": "face blowing one heart kiss", |
||||
"with_tongue": "face with tongue", |
||||
"with_two_hands": "face with two hands", |
||||
"one_hand": "face with hand over mouth", |
||||
"silenced": "silenced zipper-mouth face", |
||||
"celebrating": "partying face", |
||||
"sunglasses": "face with sunglasses", |
||||
"eyes_up": "face with rolling eyes up", |
||||
"monocle": "face with monocle", |
||||
"sleeping": "sleeping face", |
||||
"mask": "face with medical mask", |
||||
"fever": "fever face with thermometer", |
||||
"bandage": "face with head-bandage", |
||||
"vomit": "face vomiting", |
||||
"tissue": "tissue sneezing face", |
||||
"hot": "red face with sweat", |
||||
"cold": "cold face", |
||||
"crossed_eyes": "face with crossed-out eyes", |
||||
"exploding": "exploding head", |
||||
"sad": "frowning sad face", |
||||
"long_nose": "long nose lying face", |
||||
"many_tears": "loudly crying face", |
||||
"fear": "face screaming in fear", |
||||
"tired": "tired yawning face", |
||||
"annoyed": "annoyed face with steam from nose", |
||||
"clown": "clown face", |
||||
"ghost": "ghost", |
||||
"dog": "dog face", |
||||
"happy_cat": "happy cat with smiling eyes", |
||||
"scared_cat": "weary scared cat", |
||||
"sad_cat": "crying cat", |
||||
"monkey_no_see": "see-no-evil monkey", |
||||
"monkey_no_hear": "hear-no-evil monkey", |
||||
"monkey_no_talk": "speak-no-evil monkey", |
||||
"builder": "construction worker", |
||||
"princess": "princess", |
||||
"firefighter": "firefighter", |
||||
"mage": "mage", |
||||
"mermaid": "mermaid", |
||||
"fairy": "fairy", |
||||
"letter_heart": "love letter", |
||||
"red_heart": "red heart", |
||||
"two_hearts": "two hearts", |
||||
"kiss": "kiss mark", |
||||
"hundred": "hundred points", |
||||
"explosion": "explosion", |
||||
"drops": "sweat droplets", |
||||
"handshake": "handshake", |
||||
"hand_five_fingers": "hand with 5 fingers splayed", |
||||
"hand_two_fingers": "victory hand with 2 fingers", |
||||
"thumbs_up": "thumbs up", |
||||
"fist": "raised fist", |
||||
"two_hands": "two open hands", |
||||
"writing": "writing hand", |
||||
"praying": "praying folded hands", |
||||
"arm": "flexed arm", |
||||
"leg": "leg", |
||||
"foot": "foot", |
||||
"ear": "ear", |
||||
"nose": "nose", |
||||
"brain": "brain", |
||||
"tooth": "tooth", |
||||
"bone": "bone", |
||||
"eye": "eye", |
||||
"tongue": "tongue", |
||||
"mouth": "mouth", |
||||
"shirt": "t-shirt", |
||||
"pants": "pants", |
||||
"dress": "dress", |
||||
"shoe": "running shoe", |
||||
"fencing": "fencing", |
||||
"horse_riding": "horse riding", |
||||
"ski": "skier", |
||||
"rowing_boat": "rowing boat", |
||||
"swim": "swimming", |
||||
"surf": "surfing", |
||||
"gym": "lifting weights", |
||||
"wrestling": "two people wrestling", |
||||
"bike": "biking", |
||||
"parachute": "parachute", |
||||
"football": "football", |
||||
"basketball": "basketball", |
||||
"tennis": "tennis", |
||||
"ping_pong": "ping pong", |
||||
"martial": "judo uniform kimono", |
||||
"lion": "lion", |
||||
"leopard": "leopard", |
||||
"horse": "horse face", |
||||
"zebra": "zebra", |
||||
"pig": "pig", |
||||
"goat": "goat", |
||||
"sheep": "sheep", |
||||
"camel": "camel", |
||||
"giraffe": "giraffe", |
||||
"elephant": "elephant", |
||||
"rhinoceros": "rhinoceros", |
||||
"flamingo": "flamingo", |
||||
"whale": "whale", |
||||
"dolphin": "dolphin", |
||||
"bear": "bear", |
||||
"rooster": "rooster", |
||||
"chick": "hatching chick", |
||||
"eagle": "eagle", |
||||
"duck": "duck", |
||||
"owl": "owl", |
||||
"rabbit": "rabbit", |
||||
"penguin": "penguin", |
||||
"lizard": "lizard", |
||||
"turtle": "turtle", |
||||
"snake": "snake", |
||||
"hedgehog": "hedgehog", |
||||
"bat": "bat", |
||||
"fish": "fish", |
||||
"shell": "shell", |
||||
"octopus": "octopus", |
||||
"snail": "snail", |
||||
"butterfly": "butterfly", |
||||
"ant": "ant", |
||||
"bee": "honeybee", |
||||
"beetle": "beetle", |
||||
"rose": "rose", |
||||
"sunflower": "sunflower", |
||||
"fir": "evergreen fir tree", |
||||
"palm_tree": "palm tree", |
||||
"cactus": "cactus", |
||||
"clover": "four leaf clover", |
||||
"potted_plant": "potted plant", |
||||
"bouquet": "bouquet", |
||||
"three_leaves": "three fallen leaf", |
||||
"mushroom": "mushroom", |
||||
"grapes": "grapes", |
||||
"watermelon": "watermelon", |
||||
"lemon": "lemon", |
||||
"banana": "banana", |
||||
"pineapple": "pineapple", |
||||
"apple": "red apple", |
||||
"cherries": "cherries", |
||||
"strawberry": "strawberry", |
||||
"three_blueberries": "three blueberries", |
||||
"kiwi": "kiwi", |
||||
"avocado": "avocado", |
||||
"eggplant": "eggplant", |
||||
"carrot": "carrot", |
||||
"corn": "corn", |
||||
"pepper": "red hot pepper", |
||||
"croissant": "croissant", |
||||
"bread": "baguette bread", |
||||
"pretzel": "pretzel", |
||||
"cheese": "cheese", |
||||
"pizza": "slice of pizza", |
||||
"egg": "fried egg", |
||||
"ice_cream": "ice cream", |
||||
"cookie": "cookie", |
||||
"cake": "piece of cake", |
||||
"chocolate": "chocolate", |
||||
"sweet": "candy", |
||||
"coffee": "coffee", |
||||
"champagne_bottle": "champagne bottle", |
||||
"glass_wine": "wine glass", |
||||
"two_glasses": "two glasses of cava", |
||||
"mountain": "mountain", |
||||
"camping": "camping", |
||||
"beach": "beach", |
||||
"compass": "compass", |
||||
"museum": "museum", |
||||
"house": "house", |
||||
"fountain": "fountain", |
||||
"circus": "circus", |
||||
"train": "train", |
||||
"taxi": "taxi", |
||||
"motorcycle": "motorcycle", |
||||
"sailboat": "sailboat", |
||||
"airplane": "airplane", |
||||
"helicopter": "helicopter", |
||||
"rocket": "rocket", |
||||
"sun": "sun", |
||||
"moon": "moon", |
||||
"planet": "planet", |
||||
"star": "star", |
||||
"night_sky": "night sky", |
||||
"cloud": "cloud with rain", |
||||
"umbrella": "umbrella with rain drops", |
||||
"lightning": "lightning", |
||||
"snowflake": "snowflake", |
||||
"snowman": "snowman", |
||||
"thermometer": "thermometer", |
||||
"fire": "fire", |
||||
"balloon": "balloon", |
||||
"kite": "kite", |
||||
"rainbow": "rainbow", |
||||
"guitar": "guitar", |
||||
"saxophone": "saxophone", |
||||
"music": "musical note", |
||||
"painting": "painting", |
||||
"chess": "chess", |
||||
"gift": "gift", |
||||
"die": "game die", |
||||
"puzzle": "puzzle", |
||||
"teddy_bear": "teddy bear", |
||||
"firecracker": "firecracker", |
||||
"bullseye": "bullseye", |
||||
"roller_skate": "roller skate", |
||||
"kick_scooter": "kick scooter", |
||||
"anchor": "anchor", |
||||
"scuba_diving": "scuba diving", |
||||
"broom": "broom", |
||||
"magnifying_glass": "magnifying glass", |
||||
"bulb": "light bulb", |
||||
"three_books": "three books", |
||||
"package": "package", |
||||
"pencil": "pencil", |
||||
"pin": "pin", |
||||
"paperclip": "paperclip", |
||||
"scissors": "scissors", |
||||
"key": "key", |
||||
"lock": "lock", |
||||
"chair": "chair", |
||||
"bathtub": "bathtub", |
||||
"sponge": "sponge", |
||||
"shopping_cart": "shopping cart" |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,667 @@ |
||||
// Copyright (c) 2022-2025 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.
|
||||
|
||||
import { |
||||
writable, |
||||
readable, |
||||
readonly, |
||||
derived, |
||||
get, |
||||
type Writable, |
||||
} from "svelte/store"; |
||||
|
||||
import { official_classes } from "./classes"; |
||||
import { official_apps, official_services } from "./zeras"; |
||||
import { format } from "svelte-i18n"; |
||||
|
||||
let loaded_external_apps = {}; |
||||
|
||||
export const load_app = async (appName: string) => { |
||||
|
||||
let app = await get_app(appName); |
||||
return await import(`./apps/${app["ng:b"]}.svelte`); |
||||
|
||||
}; |
||||
|
||||
export const load_official_app = async (app) => { |
||||
//console.log(app);
|
||||
//console.log(app["ng:b"]);
|
||||
let component = await import(`./apps/${app["ng:b"]}.svelte`); |
||||
//console.log(component.default);
|
||||
return component.default; |
||||
}; |
||||
|
||||
export const get_app = (appName: string) => { |
||||
|
||||
if (appName.startsWith("n:g:z")) { |
||||
let app = official_apps[appName]; |
||||
if (!app) throw new Error("Unknown official app"); |
||||
return app; |
||||
} else { |
||||
//TODO: load external app from its repo
|
||||
// TODO: keep it in cache in loaded_external_apps
|
||||
} |
||||
|
||||
}; |
||||
|
||||
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 get_class = (class_name) => { |
||||
if (class_name.startsWith("app:") && class_name !== "app:z") { |
||||
//TODO: load external app from its repo
|
||||
// cache it in loaded_external_apps
|
||||
// return the class
|
||||
} else { |
||||
return official_classes[class_name]; |
||||
} |
||||
}; |
||||
|
||||
const find_viewers_for_class = (class_name: string) => { |
||||
let found = []; |
||||
for (const zera of Object.entries(official_apps)) { |
||||
if (zera[0].includes("dump") || zera[0].includes("source")) continue; |
||||
let viewers = zera[1]["ng:o"]; |
||||
if (viewers && viewers.includes(class_name)) { |
||||
found.push(zera[0]); |
||||
} |
||||
} |
||||
return found; |
||||
} |
||||
|
||||
const find_editors_for_class = (class_name: string) => { |
||||
let found = []; |
||||
for (const zera of Object.entries(official_apps)) { |
||||
let viewers = zera[1]["ng:w"]; |
||||
if (viewers && viewers.includes(class_name)) { |
||||
found.push(zera[0]); |
||||
} |
||||
} |
||||
return found; |
||||
} |
||||
|
||||
const find_source_viewer_for_class = (class_def, class_name) => { |
||||
switch (class_def["ng:crdt"]) { |
||||
case 'Graph': |
||||
return "n:g:z:crdt_source_viewer:rdf"; |
||||
case 'YMap': |
||||
return "n:g:z:crdt_source_viewer:ymap"; |
||||
case 'YArray': |
||||
return "n:g:z:crdt_source_viewer:yarray"; |
||||
case 'Automerge': |
||||
case 'Elmer': |
||||
return "n:g:z:crdt_source_viewer:json"; |
||||
case 'YXml': |
||||
return "n:g:z:crdt_source_viewer:xml"; |
||||
case 'YText': |
||||
if (class_name === "post:text" || class_name.startsWith("code")) return false; |
||||
return "n:g:z:crdt_source_viewer:text"; |
||||
} |
||||
} |
||||
|
||||
const class_to_viewers_editors = (class_name: string) => { |
||||
let class_def = get_class(class_name); |
||||
let has_discrete = class_def["ng:crdt"] !== "Graph"; |
||||
let discrete_viewer = has_discrete ? class_def["ng:o"] : undefined; |
||||
let discrete_editor = has_discrete ? class_def["ng:w"] : undefined; |
||||
|
||||
let graph_viewers = []; |
||||
let graph_editors = []; |
||||
if (!has_discrete) { |
||||
if (class_def["ng:o"]) graph_viewers.push(class_def["ng:o"]); |
||||
if (class_def["ng:w"]) graph_editors.push(class_def["ng:w"]); |
||||
} |
||||
for (const additional_g_v of find_viewers_for_class("data:graph")){ |
||||
if (!graph_viewers.includes(additional_g_v)) graph_viewers.push(additional_g_v); |
||||
} |
||||
for (const additional_g_e of find_editors_for_class("data:graph")){ |
||||
if (!graph_editors.includes(additional_g_e)) graph_editors.push(additional_g_e); |
||||
} |
||||
|
||||
let graph_viewer = graph_viewers[0]; |
||||
let graph_editor = graph_editors[0]; |
||||
|
||||
let discrete_viewers = []; |
||||
let discrete_editors = []; |
||||
if (has_discrete) { |
||||
if (discrete_viewer) discrete_viewers.push(discrete_viewer); |
||||
if (discrete_editor) discrete_editors.push(discrete_editor); |
||||
for (const v of find_viewers_for_class(class_name)) { |
||||
if (v!==discrete_viewer) discrete_viewers.push(v); |
||||
} |
||||
for (const e of find_editors_for_class(class_name)) { |
||||
if (e!==discrete_editor) discrete_editors.push(e); |
||||
} |
||||
let source_viewer = find_source_viewer_for_class(class_def, class_name); |
||||
if (source_viewer) discrete_viewers.push(source_viewer); |
||||
if (!discrete_viewer) discrete_viewer = discrete_viewers[0]; |
||||
} |
||||
return { |
||||
graph_viewers, |
||||
graph_editors, |
||||
discrete_viewers, |
||||
discrete_editors, |
||||
graph_viewer, |
||||
graph_editor, |
||||
discrete_viewer, |
||||
discrete_editor |
||||
} |
||||
} |
||||
|
||||
export const open_branch = (nuri: string, in_tab: boolean) => { |
||||
if (!get(all_tabs)[nuri]) { |
||||
all_tabs.update((old) => { |
||||
if (!old[nuri]) { |
||||
//console.log("creating tab for ",nuri)
|
||||
old[nuri] = JSON.parse(JSON.stringify(old[""])); |
||||
old[nuri].branch.nuri = nuri.substring(0,nuri.length-47); // we remove the ":v:[char;44]" part
|
||||
old[nuri].store.overlay = nuri.substring(nuri.length-47); // and put it in store.overlay
|
||||
} |
||||
return old; |
||||
}); |
||||
} |
||||
|
||||
if (in_tab) { |
||||
cur_branch.set(nuri); |
||||
let store_type = get(all_tabs)[nuri].store.store_type; |
||||
if (store_type) change_nav_bar(`nav:${store_type}`,get(format)(`doc.${store_type}_store`), undefined);
|
||||
} |
||||
|
||||
} |
||||
|
||||
export const update_class = (cur_tab, class_name) => { |
||||
cur_tab.branch.class = class_name; |
||||
cur_tab.graph_or_discrete = get_class(class_name)["ng:crdt"] === "Graph"; |
||||
cur_tab.branch.has_discrete = !cur_tab.graph_or_discrete; |
||||
return {...cur_tab, ...class_to_viewers_editors(class_name)}; |
||||
} |
||||
|
||||
export const update_branch_display = (cur_tab) => { |
||||
if (cur_tab.doc.nuri == cur_tab.branch.nuri) { |
||||
cur_tab.branch.type = "main"; |
||||
cur_tab.branch.display = "main"; |
||||
} |
||||
} |
||||
|
||||
export const show_modal_menu = writable(false); |
||||
export const show_spinner = writable(false); |
||||
export const show_doc_popup = writable(false); |
||||
export const cur_doc_popup = writable(""); |
||||
export const show_modal_create = writable(false); |
||||
|
||||
export const in_memory_graph = writable(""); |
||||
export const in_memory_discrete = writable(""); |
||||
|
||||
//TODO: call it also when switching branches inside same repo
|
||||
export const reset_in_memory = () => { |
||||
//console.log("reset_in_memory");
|
||||
in_memory_graph.update((d)=> {return "";}); |
||||
in_memory_discrete.update((d)=> {return "";}); |
||||
//console.log(get(in_memory_graph));
|
||||
//console.log(get(in_memory_discrete));
|
||||
}; |
||||
|
||||
export const all_tabs = writable({ |
||||
"":{ |
||||
store: { |
||||
repo: false, // a StoreRepo serialization
|
||||
overlay: "", // "v:"
|
||||
has_outer: "", // "l:"
|
||||
store_type: "", //"public" "protected", "private", "group", "dialog",
|
||||
readcap: "", // "r:" readcap of main
|
||||
is_member: "", // "r:" readcap of store root branch
|
||||
can_edit: true, |
||||
inner: "", // "w:l:"
|
||||
|
||||
stream: { // only if not a Dialog
|
||||
notif: 0, |
||||
last: "", |
||||
nuri: "", |
||||
}, |
||||
|
||||
// comes from main branch of store
|
||||
title: "", |
||||
icon: "", |
||||
description: "", |
||||
}, |
||||
plato: { |
||||
nuri: "", |
||||
can_edit: false, |
||||
}, |
||||
branch: { |
||||
nuri: "", // :o or :o:b
|
||||
readcap: "", // "r:"
|
||||
comment_branch: "", // nuri
|
||||
class: "", |
||||
has_discrete: false, |
||||
id: "", //"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: "", // or main or stream or a:xx or branch:X or c:X (only 7 chars)
|
||||
attachments: 0, |
||||
files: 0, |
||||
comments: 0, |
||||
|
||||
title: "", |
||||
icon: "", |
||||
description: "", |
||||
|
||||
app: "", // current app being used
|
||||
}, |
||||
view_or_edit: true, // true=> view, false=> edit
|
||||
graph_viewer: "", // selected viewer
|
||||
graph_editor: "", // selected editor
|
||||
discrete_viewer: "", // selected viewer
|
||||
discrete_editor: "", // selected editor
|
||||
graph_viewers: [], // list of available viewers
|
||||
graph_editors: [], // list of available editors
|
||||
discrete_viewers: [], // list of available viewers
|
||||
discrete_editors: [], // list of available editors
|
||||
find: "",//or string to find
|
||||
graph_or_discrete: false, // set to branch.crdt === "data/graph"
|
||||
doc: { |
||||
nuri: "",// :o
|
||||
is_store: false, |
||||
is_member: "", // ":r" readcap of root branch
|
||||
authors: [], |
||||
inbox: "", |
||||
can_edit: false, |
||||
|
||||
live_edit: false, |
||||
|
||||
title: "", |
||||
icon: "", |
||||
description: "", |
||||
|
||||
live_editors: { |
||||
|
||||
}, |
||||
branches : {}, |
||||
|
||||
}, |
||||
folders_pane: false, |
||||
toc_pane: false, |
||||
messenger_pane: false, |
||||
right_pane: "", // "branches", "files", "history", "comments", "info", "chat", "mc"
|
||||
action: false, // "repost", "dm", "react", "author", "copy", "forward", "link", "qr", "download", "embed", "new_block", "notifs", "schema", "signature", "permissions", "settings", "print", "console", "source", "services", "dev",
|
||||
|
||||
show_menu: false, |
||||
persistent_error: false, |
||||
header_in_view: true, |
||||
} |
||||
}); |
||||
|
||||
export let in_memory_save = []; |
||||
let in_memory_save_callback = async (updates) => {}; |
||||
|
||||
export const cur_tab_register_on_save = (f:(updates) => Promise<void>) => { |
||||
in_memory_save_callback = f; |
||||
in_memory_save = []; |
||||
} |
||||
|
||||
export const cur_tab_deregister_on_save = async () => { |
||||
await save(); |
||||
in_memory_save_callback = async (updates) => {}; |
||||
} |
||||
|
||||
export const save = async () => { |
||||
// saving the doc
|
||||
// TODO fetch updates from local storage
|
||||
|
||||
nav_bar.update((o) => { if (o.save === true) o.save = false; return o; }); |
||||
if (in_memory_save.length > 0) { |
||||
let temp = in_memory_save; |
||||
in_memory_save = []; |
||||
await in_memory_save_callback(temp); |
||||
} |
||||
|
||||
} |
||||
|
||||
export const set_header_in_view = function(val) { |
||||
cur_tab_update((old) => { old.header_in_view = val; return old;}); |
||||
} |
||||
|
||||
export const cur_branch = writable(""); |
||||
|
||||
export const cur_tab = derived([cur_branch, all_tabs], ([cb, all]) => {return all[cb];}); |
||||
|
||||
export const can_have_header = derived(cur_tab, ($cur_tab) => { |
||||
return !($cur_tab.doc.is_store); // && ( $cur_tab.store.store_type === "private" || $cur_tab.store.store_type === "dialog"));
|
||||
}); |
||||
export const cur_tab_branch_nuri = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.branch.nuri; |
||||
}); |
||||
export const cur_tab_doc_can_edit = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.doc.can_edit; |
||||
}); |
||||
export const cur_tab_doc_is_store = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.doc.is_store; |
||||
}); |
||||
export const cur_tab_doc_is_member = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.doc.is_member; |
||||
}); |
||||
export const cur_tab_store_type = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.store.store_type; |
||||
}); |
||||
export const cur_tab_persistent_error = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.persistent_error; |
||||
}); |
||||
export const cur_tab_header_in_view = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.header_in_view; |
||||
}); |
||||
export const cur_tab_right_pane = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.right_pane; |
||||
}); |
||||
export const cur_tab_folders_pane = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.folders_pane; |
||||
}); |
||||
export const cur_tab_toc_pane = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.toc_pane; |
||||
}); |
||||
export const cur_tab_show_menu = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.show_menu; |
||||
}); |
||||
export const cur_tab_branch_class = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.branch.class; |
||||
}); |
||||
export const cur_tab_branch_has_discrete = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.branch.has_discrete; |
||||
}); |
||||
export const cur_tab_graph_or_discrete = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.graph_or_discrete; |
||||
}); |
||||
export const cur_tab_view_or_edit = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.view_or_edit; |
||||
}); |
||||
|
||||
export const edit_header_button = derived(cur_tab, ($cur_tab) => { |
||||
return ($cur_tab.doc.is_store && ( $cur_tab.store.store_type === "public" || $cur_tab.store.store_type === "protected"))? "doc.header.buttons.edit_profile" : "doc.header.buttons.edit_intro"; |
||||
}); |
||||
export const in_private_store = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.store.store_type === "private"; |
||||
}); |
||||
|
||||
export const header_title = derived(cur_tab, ($cur_tab) => { |
||||
if ($cur_tab.doc.is_store) { |
||||
if ($cur_tab.store.store_type !== "private" && $cur_tab.store.store_type !== "dialog") { |
||||
return $cur_tab.store.title; |
||||
} |
||||
} else { |
||||
let title = $cur_tab.branch.title || $cur_tab.doc.title; |
||||
if (title) return title; |
||||
let app = get_app($cur_tab.branch.class); |
||||
if (app) return app["ng:n"]; |
||||
} |
||||
return false; |
||||
}); |
||||
|
||||
/* to be used with <NavIcon img={$cur_tab_store_icon_override || $nav_bar.icon} config={{ |
||||
tabindex:"-1", |
||||
class:"w-8 h-8 focus:outline-none" |
||||
}}/> |
||||
*/ |
||||
export const header_icon = derived(cur_tab, ($cur_tab) => { |
||||
if ($cur_tab.doc.is_store) { |
||||
if ($cur_tab.store.store_type !== "private") { |
||||
return $cur_tab.store.icon;// TODO: fetch image and return blob:
|
||||
} |
||||
} else { |
||||
let icon = $cur_tab.branch.icon || $cur_tab.doc.icon; |
||||
if (icon) return icon;// TODO: fetch image and return blob:
|
||||
let class_type = get_class($cur_tab.branch.class); |
||||
if (class_type) return "class:" + $cur_tab.branch.class; |
||||
} |
||||
return false; |
||||
}); |
||||
|
||||
export const header_description = derived(cur_tab, ($cur_tab) => { |
||||
if ($cur_tab.doc.is_store) { |
||||
if ($cur_tab.store.store_type !== "private") { |
||||
return $cur_tab.store.description; |
||||
} |
||||
} else { |
||||
let description = $cur_tab.branch.description || $cur_tab.doc.description; |
||||
if (description) return description; |
||||
} |
||||
return false; |
||||
}); |
||||
|
||||
export const cur_tab_store_name_override = derived(cur_tab, ($cur_tab) => { if ($cur_tab.doc.is_store && $cur_tab.store.store_type !== "private" && $cur_tab.store.title && !$cur_tab.header_in_view) return $cur_tab.store.title; else return false; }); |
||||
|
||||
export const cur_tab_store_icon_override = derived(cur_tab, ($cur_tab) => { if ($cur_tab.doc.is_store && $cur_tab.store.store_type !== "private" && $cur_tab.store.icon && !$cur_tab.header_in_view) return $cur_tab.store.icon; else return false; }); |
||||
|
||||
export const tab_update = function( tab, fn ) { |
||||
all_tabs.update((all) => { |
||||
all[tab] = fn(all[tab]); |
||||
return all; |
||||
}); |
||||
}; |
||||
|
||||
export const cur_tab_update = function( fn ) { |
||||
all_tabs.update((all) => { |
||||
all[get(cur_branch)] = fn(all[get(cur_branch)]); |
||||
return all; |
||||
}); |
||||
}; |
||||
|
||||
export const live_editing = writable(false); |
||||
|
||||
export const showMenu = async () => { |
||||
await save(); |
||||
show_modal_menu.set(true); |
||||
cur_tab_update(ct => { |
||||
ct.show_menu = true; |
||||
return ct; |
||||
}); |
||||
} |
||||
|
||||
export const hideMenu = () => { |
||||
show_modal_menu.set(false); |
||||
cur_tab_update(ct => { |
||||
ct.show_menu = false; |
||||
return ct; |
||||
}); |
||||
} |
||||
|
||||
export const nav_bar = writable({ |
||||
//icon: "class:post:rich",
|
||||
icon: "", |
||||
//icon: "blob:http://localhost:1421/be6f968f-ff51-4e8f-bd32-36c60b7af49a",
|
||||
title: "", |
||||
back: false, |
||||
newest: 0, |
||||
save: undefined, |
||||
}); |
||||
|
||||
live_editing.subscribe((val) => { |
||||
cur_tab_update((old)=> { |
||||
old.doc.live_edit = val; |
||||
nav_bar.update((o) => { |
||||
o.save = old.doc.live_edit ? undefined : ( in_memory_save.length > 0 ? true : false ) |
||||
return o; |
||||
}); |
||||
return old; |
||||
}); |
||||
}); |
||||
|
||||
export const nav_bar_newest = derived(nav_bar, ($nav_bar) => { |
||||
return $nav_bar.newest; |
||||
}); |
||||
|
||||
export const nav_bar_save = derived(nav_bar, ($nav_bar) => { |
||||
return $nav_bar.save; |
||||
}); |
||||
|
||||
export const nav_bar_back = derived(nav_bar, ($nav_bar) => { |
||||
return $nav_bar.back; |
||||
}); |
||||
|
||||
export const nav_bar_title = derived(nav_bar, ($nav_bar) => { |
||||
return $nav_bar.title; |
||||
}); |
||||
|
||||
export const nav_bar_icon = derived(nav_bar, ($nav_bar) => { |
||||
return $nav_bar.icon; |
||||
}); |
||||
|
||||
export const nav_bar_reset_newest = () => { |
||||
nav_bar.update((old) => { |
||||
old.newest = 0; |
||||
return old; |
||||
}); |
||||
} |
||||
|
||||
export const change_nav_bar = (icon, title, back) => { |
||||
nav_bar.update((old) => { |
||||
if (icon !== undefined) { |
||||
old.icon = icon; |
||||
} |
||||
if (title !== undefined) { |
||||
old.title = title; |
||||
} |
||||
if (back !== undefined) { |
||||
old.back = back; |
||||
} |
||||
return old; |
||||
}); |
||||
live_editing.set(get(cur_tab).doc.live_edit); |
||||
}; |
||||
|
||||
export const persistent_error = (nuri, pe) => { |
||||
tab_update(nuri, tab => { |
||||
tab.persistent_error = pe; |
||||
return tab; |
||||
}); |
||||
} |
||||
|
||||
export const all_files_count = derived(cur_tab, ($cur_tab) => { |
||||
let total = $cur_tab.branch.files; |
||||
return total ? `(${total})` : ""; |
||||
}); |
||||
|
||||
export const all_comments_count = derived(cur_tab, ($cur_tab) => { |
||||
let total = $cur_tab.branch.comments; |
||||
return total ? `(${total})` : ""; |
||||
}); |
||||
|
||||
export const has_editor_chat = derived(cur_tab, ($cur_tab) => { |
||||
return $cur_tab.doc.can_edit && $cur_tab.store.store_type !== "private" && $cur_tab.store.store_type !== "dialog"; |
||||
}); |
||||
|
||||
export const toggle_live_edit = async () => { |
||||
let is_live; |
||||
cur_tab_update(ct => { |
||||
ct.doc.live_edit = !ct.doc.live_edit; |
||||
is_live = ct.doc.live_edit; |
||||
live_editing.set(ct.doc.live_edit); |
||||
return ct; |
||||
}); |
||||
if (is_live) { |
||||
//send all the updates with live_discrete_update
|
||||
await save(); |
||||
} |
||||
} |
||||
|
||||
export const set_viewer = (app_name: string) => { |
||||
if (get(cur_tab).graph_or_discrete) { |
||||
cur_tab_update(ct => {ct.graph_viewer = app_name; ct.branch.app = app_name; return ct;}); |
||||
} else { |
||||
cur_tab_update(ct => {ct.discrete_viewer = app_name; ct.branch.app = app_name; return ct;}); |
||||
} |
||||
} |
||||
|
||||
export const set_editor = (app_name: string) => { |
||||
if (get(cur_tab).graph_or_discrete) { |
||||
cur_tab_update(ct => {ct.graph_editor = app_name; ct.branch.app = app_name; return ct;}); |
||||
} else { |
||||
cur_tab_update(ct => {ct.discrete_editor = app_name; ct.branch.app = app_name; return ct;}); |
||||
} |
||||
} |
||||
|
||||
export const toggle_graph_discrete = () => { |
||||
cur_tab_update(ct => { |
||||
ct.graph_or_discrete = !ct.graph_or_discrete; |
||||
return ct; |
||||
}); |
||||
} |
||||
|
||||
export const set_graph_discrete = (val:boolean) => { |
||||
cur_tab_update(ct => { |
||||
ct.graph_or_discrete = val; |
||||
return ct; |
||||
}); |
||||
} |
||||
|
||||
export const set_view_or_edit = (val:boolean) => { |
||||
cur_tab_update(ct => { |
||||
ct.view_or_edit = val; |
||||
return ct; |
||||
}); |
||||
} |
||||
|
||||
export const open_viewer = () => { |
||||
set_view_or_edit(true); |
||||
} |
||||
|
||||
export const cur_viewer = derived(cur_tab, ($cur_tab) => { |
||||
let app_name = $cur_tab.graph_or_discrete ? $cur_tab.graph_viewer : $cur_tab.discrete_viewer; |
||||
if (app_name) { |
||||
let app = get_app(app_name); |
||||
return app; |
||||
} |
||||
}); |
||||
|
||||
export const cur_editor = derived(cur_tab, ($cur_tab) => { |
||||
let app_name = $cur_tab.graph_or_discrete ? $cur_tab.graph_editor : $cur_tab.discrete_editor; |
||||
if (app_name) { |
||||
let app = get_app(app_name); |
||||
return app; |
||||
} |
||||
}); |
||||
|
||||
export const cur_app = derived(cur_tab, ($cur_tab) => { |
||||
let app_name = $cur_tab.view_or_edit ? $cur_tab.graph_or_discrete ? $cur_tab.graph_viewer : $cur_tab.discrete_viewer : $cur_tab.graph_or_discrete ? $cur_tab.graph_editor : $cur_tab.discrete_editor; |
||||
if (app_name) { |
||||
let app = get_app(app_name); |
||||
return app; |
||||
} |
||||
}); |
||||
|
||||
export const available_viewers = derived(cur_tab, ($cur_tab) => { |
||||
let list = $cur_tab.graph_or_discrete ? $cur_tab.graph_viewers : $cur_tab.discrete_viewers; |
||||
return list.map((viewer) => {
|
||||
let app = { ...get_app(viewer) };
|
||||
if (!app["ng:u"]) app["ng:u"] = "view"; |
||||
return app
|
||||
}); |
||||
}); |
||||
|
||||
export const available_editors = derived(cur_tab, ($cur_tab) => { |
||||
let list = $cur_tab.graph_or_discrete ? $cur_tab.graph_editors : $cur_tab.discrete_editors; |
||||
return list.map((editor) => {
|
||||
let app = { ...get_app(editor) };
|
||||
if (!app["ng:u"]) app["ng:u"] = "edit"; |
||||
return app
|
||||
}); |
||||
}); |
||||
|
||||
@ -0,0 +1,776 @@ |
||||
|
||||
// Copyright (c) 2022-2025 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:compose" >> a viewer made of a list of viewers (useful when different views on the same branch needs to be aggregated, or when the discrete is not composable - i.e it is not a Post)
|
||||
//"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:md", "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:sparql_update": { |
||||
"ng:n": "SPARQL Update", |
||||
"ng:a": "View, edit and invoke a Graph SPARQL Update", |
||||
"ng:c": "app", |
||||
"ng:u": "sparql_query",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:sparql_update", |
||||
"ng:b": "SparqlUpdateEditor", // YASGUI of Zazuko https://github.com/zazuko/trifid/tree/main/packages/yasgui
|
||||
"ng:o": [], |
||||
"ng:w": ["query:sparql_update","data:graph"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:json_ld_editor": { |
||||
"ng:n": "JSON-LD Editor", |
||||
"ng:a": "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:w": ["data:graph"], |
||||
}, |
||||
"n:g:z:json_yarray_editor": { |
||||
"ng:n": "JSON Editor", |
||||
"ng:a": "Edit the JSON data", |
||||
"ng:c": "app",
|
||||
"ng:u": "json_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:json_yarray_editor", |
||||
"ng:b": "YArrayEditor", |
||||
"ng:w": ["data:array"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:json_automerge_editor": { |
||||
"ng:n": "JSON Editor", |
||||
"ng:a": "Edit the JSON data", |
||||
"ng:c": "app",
|
||||
"ng:u": "json_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:json_automerge_editor", |
||||
"ng:b": "AutomergeEditor", |
||||
"ng:w": ["data:json"], |
||||
"full_width": true, |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:json_ymap_editor": { |
||||
"ng:n": "JSON Editor", |
||||
"ng:a": "Edit the JSON data", |
||||
"ng:c": "app",
|
||||
"ng:u": "json_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:json_ymap_editor", |
||||
"ng:b": "YMapEditor", |
||||
"ng:w": ["data:map"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:triple_editor": { |
||||
"ng:n": "Graph Triples Editor", |
||||
"ng:a": "Edit the 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:w": ["data:graph"], |
||||
}, |
||||
"n:g:z:rdf_viewer:turtle": { // https://github.com/highlightjs/highlightjs-turtle/tree/master
|
||||
"ng:n": "Turtle", |
||||
"ng:a": "View the RDF Graph in Turtle format", |
||||
"ng:c": "app",
|
||||
"ng:u": "turtle_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": [], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:sparql_query": { |
||||
"ng:n": "SPARQL Query", |
||||
"ng:a": "View, edit and invoke a Graph SPARQL query", |
||||
"ng:c": "app",
|
||||
"ng:u": "sparql_query",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:sparql_query", |
||||
"ng:b": "SparqlQueryEditor", // YASGUI of Zazuko https://github.com/zazuko/trifid/tree/main/packages/yasgui
|
||||
"ng:o": ["data:graph"], |
||||
"ng:w": ["query:sparql"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:json_ld_viewer": { |
||||
"ng:n": "JSON-LD", |
||||
"ng:a": "View 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_viewer", |
||||
"ng:b": "JsonLdViewer", |
||||
"ng:o": ["data:graph"], |
||||
}, |
||||
"n:g:z:rdf_viewer:graph": { |
||||
"ng:n": "Graph Explorer", |
||||
"ng:a": "View the 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:json_ymap_viewer": { |
||||
"ng:n": "JSON", |
||||
"ng:a": "View the JSON data", |
||||
"ng:c": "app",
|
||||
"ng:u": "json_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:json_ymap_viewer", |
||||
"ng:b": "YMapViewer", |
||||
"ng:o": ["data:map"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:json_yarray_viewer": { |
||||
"ng:n": "JSON", |
||||
"ng:a": "View the JSON data", |
||||
"ng:c": "app",
|
||||
"ng:u": "json_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:json_yarray_viewer", |
||||
"ng:b": "YArrayViewer", |
||||
"ng:o": ["data:array"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:json_automerge_viewer": { |
||||
"ng:n": "JSON", |
||||
"ng:a": "View the JSON data", |
||||
"ng:c": "app",
|
||||
"ng:u": "json_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:json_automerge_viewer", |
||||
"ng:b": "AutomergeViewer", |
||||
"ng:o": ["data:json"], |
||||
"full_width": true, |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:triple_viewer": { |
||||
"ng:n": "Graph Triples", |
||||
"ng:a": "View the RDF Graph as triples", |
||||
"ng:c": "app",
|
||||
"ng:u": "triple_editor",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:triple_viewer", |
||||
"ng:b": "TripleViewer", |
||||
"ng:o": ["data:graph"], |
||||
}, |
||||
"n:g:z:sparql_query:sparnatural": { |
||||
"ng:n": "SPARNatural Query", |
||||
"ng:a": "View, edit and invoke a 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": ["data:graph"], |
||||
"ng:w": ["query:sparql"], |
||||
}, |
||||
"n:g:z:graphql_query": { |
||||
"ng:n": "GraphQL Query", |
||||
"ng:a": "View, edit and invoke a 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": ["data:graph"], |
||||
"ng:w": ["query:graphql"], |
||||
}, |
||||
"n:g:z:rdf_viewer:n3": { // ?
|
||||
"ng:n": "N3", |
||||
"ng:a": "View the 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 Source", |
||||
"ng:a": "View the RDF Graph in JSON-LD format", |
||||
"ng:c": "app",
|
||||
"ng:u": "source",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:rdf_viewer:json_ld", |
||||
"ng:b": "JsonLdSourceViewer", |
||||
"ng:o": ["data:graph"], |
||||
"ng:w": [], |
||||
}, |
||||
"n:g:z:ontology_editor": { |
||||
"ng:n": "Ontology Editor", |
||||
"ng:a": "Edit the 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": "OntologyEditor", |
||||
"ng:o": [], |
||||
"ng:w": ["schema*"], |
||||
}, |
||||
"n:g:z:ontology_viewer": { |
||||
"ng:n": "Ontology", |
||||
"ng:a": "View the OWL Ontology", |
||||
"ng:c": "app", |
||||
"ng:u": "ontology_viewer",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:ontology_viewer", |
||||
"ng:b": "OntologyViewer", // display with https://github.com/VisualDataWeb/WebVOWL
|
||||
"ng:o": ["schema*"], |
||||
"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 the 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 the 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 exported 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 the post with ProseMirror", |
||||
"ng:c": "app",
|
||||
"ng:u": "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"], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:post_md_editor": { |
||||
"ng:n": "Post MD Editor", |
||||
"ng:a": "Edit the post with MilkDown", |
||||
"ng:c": "app",
|
||||
"ng:u": "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"], |
||||
"full_width": true, |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:code_editor": { |
||||
"ng:n": "Text Editor", |
||||
"ng:a": "Edit the code/text with CodeMirror", |
||||
"ng:c": "app",
|
||||
"ng:u": "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"], |
||||
implemented: true, |
||||
}, |
||||
"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", "diagram:drawio"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:viewer:md": {
|
||||
"ng:n": "MarkDown source", |
||||
"ng:a": "See the MarkDown source of this document", |
||||
"ng:c": "app",
|
||||
"ng:u": "source",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:viewer:md", |
||||
"ng:b": "MdSource", // displayed with highlight.js , with option to download
|
||||
"ng:o": ["post:md"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
"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": "AutomergeJsonSource", // displayed with highlight.js , with option to download
|
||||
"ng:o": ["data:json", "data:table", "diagram:jsmind", "diagram:gantt", "diagram:excalidraw", "viz:*", "chart:*", "prod:cad"], |
||||
"ng:w": [], |
||||
"full_width": true, |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:crdt_source_viewer:ymap": {
|
||||
"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:ymap", |
||||
"ng:b": "YMapSource", // displayed with highlight.js , with option to download
|
||||
"ng:o": ["data:map"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:crdt_source_viewer:yarray": {
|
||||
"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:yarray", |
||||
"ng:b": "YArraySource", // displayed with highlight.js , with option to download
|
||||
"ng:o": ["data:array"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
"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 and copy paste
|
||||
"ng:o": ["post:asciidoc", "service*", "contract", "query:sparql*","query:graphql","diagram:mermaid","diagram:graphviz","diagram:flowchart", |
||||
"diagram:sequence","diagram:markmap","diagram:mymind","doc:music*", "doc:maths", "doc:chemistry", "doc:ancientscript", "doc:braille", "media:subtitle"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
// "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": [],
|
||||
// implemented: true,
|
||||
// },
|
||||
"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": "ProseMirrorViewer", // 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": [], |
||||
implemented: true, |
||||
}, |
||||
"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": [], |
||||
implemented: true, |
||||
}, |
||||
"n:g:z:compose:editor": { |
||||
"ng:n": "Composition Editor", |
||||
"ng:a": "Compose several blocks into a single document", |
||||
"ng:c": "app",
|
||||
"ng:u": "compose",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:compose:editor", |
||||
"ng:b": "ComposeEditor", |
||||
"ng:w": ["doc:compose"], |
||||
}, |
||||
"n:g:z:compose:viewer": { |
||||
"ng:n": "Composition", |
||||
"ng:a": "Composition of several blocks", |
||||
"ng:c": "app",
|
||||
"ng:u": "compose",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:compose:viewer", |
||||
"ng:b": "ComposeViewer", |
||||
"ng:o": ["doc:compose"], |
||||
}, |
||||
"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", |
||||
"ng:o": ["post:text"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
"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": "TextViewer", // displayed with highlight.js
|
||||
"ng:o": ["code*"], |
||||
"ng:w": [], |
||||
implemented: true, |
||||
}, |
||||
"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 Apps 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:container": { |
||||
"ng:n": "Container", |
||||
"ng:a": "See the content of document as a Container", |
||||
"ng:c": "app",
|
||||
"ng:u": "container",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:container",
|
||||
"ng:b": "ContainerView", |
||||
implemented: true, |
||||
"ng:o": ["data:collection","data:container"], |
||||
"ng:w": ["data:collection","data:container"], |
||||
}, |
||||
"n:g:z:grid": { |
||||
"ng:n": "Grid", |
||||
"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:grid"], |
||||
"ng:w": ["data:grid"], |
||||
}, |
||||
"n:g:z:media": { |
||||
"ng:n": "Media", |
||||
"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 a Service in Rust or JS/Deno", |
||||
"ng:c": "app",
|
||||
"ng:u": "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 binary file", |
||||
"ng:a": "Upload a binary file into the 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": [], |
||||
}, |
||||
"n:g:z:import_file": { |
||||
"ng:n": "Import from external file", |
||||
"ng:a": "Import an external file with compatible format", |
||||
"ng:c": "app",
|
||||
"ng:u": "load_graph",//favicon. can be a did:ng:j
|
||||
"ng:g": "n:g:z:import_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": "Export JSON", |
||||
"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", "diagram:jsmind", "diagram:gantt", "diagram:excalidraw", "viz:*", "chart:*", "prod:cad"], |
||||
"ng:w": [], |
||||
"ng:result": ["file:iana:application:json"], |
||||
}, |
||||
"n:g:z:dump_xml": { |
||||
"ng:n": "Export XML", |
||||
"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", "diagram:drawio"], |
||||
"ng:w": [], |
||||
"ng:result": ["file:iana:text:xml"], |
||||
}, |
||||
"n:g:z:dump_text": { |
||||
"ng:n": "Export Text", |
||||
"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","diagram:mermaid","diagram:graphviz","diagram:flowchart", |
||||
"diagram:sequence","diagram:markmap","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 plain 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","code*"], |
||||
}, |
||||
"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:*"] |
||||
}, |
||||
}; |
||||
@ -0,0 +1,11 @@ |
||||
// svelte.config.js
|
||||
export default { |
||||
compilerOptions: { |
||||
experimental: { |
||||
async: true |
||||
} |
||||
}, |
||||
vitePlugin: { |
||||
//inspector: true
|
||||
} |
||||
}; |
||||
@ -0,0 +1,5 @@ |
||||
/** @type {import('tailwindcss').Config} */ |
||||
export default { |
||||
content: ["./index.html","./src/**/*.{js,ts,jsx,svelte,tsx,html}"], |
||||
plugins: [], |
||||
}; |
||||