@ -0,0 +1,28 @@ |
|||||||
|
# Logs |
||||||
|
logs |
||||||
|
*.log |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
pnpm-debug.log* |
||||||
|
lerna-debug.log* |
||||||
|
|
||||||
|
index.html |
||||||
|
|
||||||
|
node_modules |
||||||
|
dist |
||||||
|
dist-web |
||||||
|
dist-file |
||||||
|
dist-ssr |
||||||
|
*.local |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.vscode/* |
||||||
|
!.vscode/extensions.json |
||||||
|
.idea |
||||||
|
.DS_Store |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
*.sw? |
@ -0,0 +1,6 @@ |
|||||||
|
{ |
||||||
|
"recommendations": [ |
||||||
|
"svelte.svelte-vscode", |
||||||
|
"rust-lang.rust-analyzer" |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
# NextGraph common Svelte components |
||||||
|
|
@ -0,0 +1,68 @@ |
|||||||
|
{ |
||||||
|
"name": "@nextgraph-monorepo/common", |
||||||
|
"private": true, |
||||||
|
"version": "0.1.1-alpha", |
||||||
|
"type": "module", |
||||||
|
"scripts": { |
||||||
|
"dev": "vite", |
||||||
|
"preview": "vite preview", |
||||||
|
"check": "svelte-check --tsconfig ./tsconfig.json" |
||||||
|
}, |
||||||
|
"main": "src/main.ts", |
||||||
|
"exports": { |
||||||
|
".": "./src/main.ts", |
||||||
|
"./store": "./src/store.ts", |
||||||
|
"./api": "./src/api.ts", |
||||||
|
"./routes": "./src/routes/index.ts", |
||||||
|
"./lib": "./src/lib/index.ts", |
||||||
|
"./lang": "./src/lang.ts", |
||||||
|
"./components": "./src/lib/component.ts" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@sindresorhus/is": "4.6.0", |
||||||
|
"@tailwindcss/typography": "^0.5.13", |
||||||
|
"async-proxy": "^0.4.1", |
||||||
|
"char-regex": "1.0.2", |
||||||
|
"classnames": "^2.3.2", |
||||||
|
"debug": "^4.3.6", |
||||||
|
"emojilib": "2.4.0", |
||||||
|
"extend": "3.0.2", |
||||||
|
"flowbite": "^1.6.5", |
||||||
|
"flowbite-svelte": "^0.43.3", |
||||||
|
"html5-qrcode": "^2.3.8", |
||||||
|
"immutable-json-patch": "^6.0.1", |
||||||
|
"lodash.debounce": "4.0.8", |
||||||
|
"skin-tone": "2.0.0", |
||||||
|
"style-mod": "^4.1.2", |
||||||
|
"svelte-i18n": "^4.0.0", |
||||||
|
"svelte-inview": "^4.0.2", |
||||||
|
"svelte-spa-router": "^3.3.0", |
||||||
|
"vite-plugin-top-level-await": "1.3.1", |
||||||
|
"xml-beautifier": "^0.5.0" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@sveltejs/vite-plugin-svelte": "2.0.0", |
||||||
|
"@swc/core": "~1.6.0", |
||||||
|
"@tsconfig/svelte": "^3.0.0", |
||||||
|
"@types/node": "^18.7.10", |
||||||
|
"autoprefixer": "^10.4.14", |
||||||
|
"dayjs": "^1.11.10", |
||||||
|
"internal-ip": "^7.0.0", |
||||||
|
"postcss": "^8.4.23", |
||||||
|
"postcss-load-config": "^4.0.1", |
||||||
|
"prettier": "^3.3.2", |
||||||
|
"prettier-plugin-svelte": "^3.2.5", |
||||||
|
"shx": "^0.3.4", |
||||||
|
"svelte": "^3.54.0", |
||||||
|
"svelte-check": "^3.0.0", |
||||||
|
"svelte-heros-v2": "^0.10.12", |
||||||
|
"svelte-preprocess": "^5.0.3", |
||||||
|
"svelte-time": "^0.8.0", |
||||||
|
"tailwindcss": "^3.3.1", |
||||||
|
"tslib": "^2.4.1", |
||||||
|
"typescript": "^4.9.5", |
||||||
|
"vite": "4.2.1", |
||||||
|
"vite-plugin-svelte-svg": "2.2.1", |
||||||
|
"vite-plugin-wasm": "3.2.2" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,310 @@ |
|||||||
|
<!-- |
||||||
|
// 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 { push, default as Router } from "svelte-spa-router"; |
||||||
|
import { isLoading } from "svelte-i18n"; |
||||||
|
|
||||||
|
import { onMount, tick, onDestroy } from "svelte"; |
||||||
|
import { |
||||||
|
wallets, |
||||||
|
active_wallet, |
||||||
|
opened_wallets, |
||||||
|
close_active_session, |
||||||
|
disconnections_subscribe, |
||||||
|
} from "./store"; |
||||||
|
|
||||||
|
import { select_default_lang } from "./lang"; |
||||||
|
|
||||||
|
import Home from "./routes/Home.svelte"; |
||||||
|
import Test from "./routes/Test.svelte"; |
||||||
|
|
||||||
|
import NURI from "./routes/NURI.svelte"; |
||||||
|
import NotFound from "./routes/NotFound.svelte"; |
||||||
|
import WalletCreate from "./routes/WalletCreate.svelte"; |
||||||
|
import Invitation from "./routes/Invitation.svelte"; |
||||||
|
import WalletLogin from "./routes/WalletLogin.svelte"; |
||||||
|
import WalletInfo from "./routes/WalletInfo.svelte"; |
||||||
|
import User from "./routes/User.svelte"; |
||||||
|
import UserRegistered from "./routes/UserRegistered.svelte"; |
||||||
|
import Install from "./routes/Install.svelte"; |
||||||
|
import ScanQR from "./routes/ScanQR.svelte"; |
||||||
|
import Shared from "./routes/Shared.svelte"; |
||||||
|
import Site from "./routes/Site.svelte"; |
||||||
|
|
||||||
|
import ng from "./api"; |
||||||
|
import AccountInfo from "./routes/AccountInfo.svelte"; |
||||||
|
import WalletLoginUsername from "./routes/WalletLoginUsername.svelte"; |
||||||
|
import WalletLoginQr from "./routes/WalletLoginQr.svelte"; |
||||||
|
import WalletLoginTextCode from "./routes/WalletLoginTextCode.svelte"; |
||||||
|
|
||||||
|
const routes = new Map(); |
||||||
|
routes.set("/", Home); |
||||||
|
routes.set("/test", Test); |
||||||
|
routes.set("/wallet/login", WalletLogin); |
||||||
|
routes.set("/wallet/username", WalletLoginUsername); |
||||||
|
routes.set("/wallet/login-qr", WalletLoginQr); |
||||||
|
routes.set("/wallet/login-text-code", WalletLoginTextCode); |
||||||
|
routes.set("/wallet/create", WalletCreate); |
||||||
|
routes.set("/i/:invitation", Invitation); |
||||||
|
routes.set("/user", User); |
||||||
|
routes.set("/user/registered", UserRegistered); |
||||||
|
routes.set("/wallet", WalletInfo); |
||||||
|
routes.set("/user/accounts", AccountInfo); |
||||||
|
routes.set("/wallet/scanqr", ScanQR); |
||||||
|
routes.set("/install", Install); |
||||||
|
routes.set("/shared", Shared); |
||||||
|
routes.set("/site", Site); |
||||||
|
routes.set(/^\/did:ng:(.*)/i, NURI); |
||||||
|
routes.set("*", NotFound); |
||||||
|
|
||||||
|
let unsubscribe = () => {}; |
||||||
|
|
||||||
|
let wallet_channel; |
||||||
|
let unsub_main_close; |
||||||
|
|
||||||
|
// window.refresh_wallets = async () => { |
||||||
|
// let walls = await ng.get_wallets(); |
||||||
|
// wallets.set(walls); |
||||||
|
// }; |
||||||
|
|
||||||
|
onMount(async () => { |
||||||
|
|
||||||
|
//console.log("hide splash", window.supported); |
||||||
|
if (window.supported) { |
||||||
|
window.document.getElementById("splash").className="noshow"; |
||||||
|
window.document.getElementById("app").className=""; |
||||||
|
} |
||||||
|
|
||||||
|
//window.document.getElementById("splash").className="splash-loaded"; |
||||||
|
try { |
||||||
|
await disconnections_subscribe(); |
||||||
|
await select_default_lang(); |
||||||
|
} catch (e) { |
||||||
|
console.warn(e); |
||||||
|
//console.log("called disconnections_subscribe twice"); |
||||||
|
} |
||||||
|
let tauri_platform = import.meta.env.TAURI_PLATFORM; |
||||||
|
//console.log(await ng.test()); |
||||||
|
if (tauri_platform) { |
||||||
|
let walls = await ng.get_wallets(); |
||||||
|
wallets.set(walls); |
||||||
|
|
||||||
|
unsubscribe = active_wallet.subscribe(async (value) => { |
||||||
|
if (value) { |
||||||
|
if (value.wallet) { |
||||||
|
opened_wallets.update((w) => { |
||||||
|
w[value.id] = value.wallet; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
} else { |
||||||
|
await ng.wallet_close(value.id); |
||||||
|
active_wallet.set(undefined); |
||||||
|
opened_wallets.update((w) => { |
||||||
|
delete w[value.id]; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
push("#/wallet/login"); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
let window_api = await import("@tauri-apps/plugin-window"); |
||||||
|
let event_api = await import("@tauri-apps/api/event"); |
||||||
|
let main = window_api.Window.getByLabel("main"); |
||||||
|
unsub_main_close = await main.onCloseRequested(async (event) => { |
||||||
|
//console.log("onCloseRequested main"); |
||||||
|
await event_api.emit("close_all", {}); |
||||||
|
let registration = window_api.Window.getByLabel("registration"); |
||||||
|
if (registration) { |
||||||
|
await registration.close(); |
||||||
|
} |
||||||
|
let viewer = window_api.Window.getByLabel("viewer"); |
||||||
|
if (viewer) { |
||||||
|
await viewer.close(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
// ON WEB CLIENTS |
||||||
|
window.addEventListener("storage", async (event) => { |
||||||
|
//console.log("localStorage event", event); |
||||||
|
if (event.storageArea != localStorage) return; |
||||||
|
if (event.key === "ng_wallets") { |
||||||
|
//console.log("localStorage", JSON.stringify($wallets)); |
||||||
|
await ng.wallets_reload(); |
||||||
|
wallets.set(await ng.get_wallets()); |
||||||
|
//console.log("localStorage after", JSON.stringify($wallets)); |
||||||
|
} |
||||||
|
}); |
||||||
|
wallets.set(await ng.get_wallets()); |
||||||
|
// TODO: check the possibility of XS-Leaks. I don't see any, but it should be checked |
||||||
|
// https://github.com/privacycg/storage-partitioning |
||||||
|
// https://github.com/whatwg/html/issues/5803 |
||||||
|
// https://w3cping.github.io/privacy-threat-model/ |
||||||
|
// https://chromium.googlesource.com/chromium/src/+/fa17a6142f99d58de533d65cd8f3cd0e9a8ee58e |
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=229814 |
||||||
|
wallet_channel = new BroadcastChannel("ng_wallet"); |
||||||
|
window.wallet_channel = wallet_channel; |
||||||
|
wallet_channel.postMessage({ cmd: "startup" }, location.href); |
||||||
|
wallet_channel.onmessage = async (event) => { |
||||||
|
// console.log(event.data.cmd, event.data); |
||||||
|
if (!location.href.startsWith(event.origin)) return; |
||||||
|
switch (event.data.cmd) { |
||||||
|
case "startup": |
||||||
|
for (let saved_id of Object.keys($wallets)) { |
||||||
|
if ($wallets[saved_id].in_memory) { |
||||||
|
wallet_channel.postMessage( |
||||||
|
{ |
||||||
|
cmd: "new_in_mem", |
||||||
|
name: saved_id, |
||||||
|
lws: $wallets[saved_id], |
||||||
|
}, |
||||||
|
location.href |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
// if ($active_wallet && $active_wallet.wallet) { |
||||||
|
// wallet_channel.postMessage( |
||||||
|
// { cmd: "opened", wallet: $active_wallet }, |
||||||
|
// location.href |
||||||
|
// ); |
||||||
|
// } |
||||||
|
for (let opened of Object.keys($opened_wallets)) { |
||||||
|
wallet_channel.postMessage( |
||||||
|
{ |
||||||
|
cmd: "opened", |
||||||
|
wallet: { wallet: $opened_wallets[opened], id: opened }, |
||||||
|
}, |
||||||
|
location.href |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
case "opened": |
||||||
|
if (!$opened_wallets[event.data.wallet.id]) { |
||||||
|
//await tick(); |
||||||
|
// console.log( |
||||||
|
// "ADDING TO OPENED", |
||||||
|
// event.data.wallet.id, |
||||||
|
// JSON.stringify($opened_wallets), |
||||||
|
// event.data.wallet.wallet |
||||||
|
// ); |
||||||
|
if (event.data.ng_wallets) { |
||||||
|
localStorage.setItem("ng_wallets", event.data.ng_wallets); |
||||||
|
await ng.wallets_reload(); |
||||||
|
wallets.set(await ng.get_wallets()); |
||||||
|
} |
||||||
|
try { |
||||||
|
await ng.wallet_was_opened(event.data.wallet.wallet); |
||||||
|
} catch (e) { |
||||||
|
console.error(e); |
||||||
|
} |
||||||
|
opened_wallets.update((w) => { |
||||||
|
w[event.data.wallet.id] = event.data.wallet.wallet; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
} |
||||||
|
break; |
||||||
|
case "new_in_mem": |
||||||
|
//console.log("GOT new_in_mem", event.data); |
||||||
|
if (event.data.lws) { |
||||||
|
if (!$wallets[event.data.name]) { |
||||||
|
await ng.add_in_memory_wallet(event.data.lws); |
||||||
|
wallets.update((w) => { |
||||||
|
w[event.data.name] = event.data.lws; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
if (event.data.opened) { |
||||||
|
if (!$opened_wallets[event.data.name]) { |
||||||
|
await ng.wallet_was_opened(event.data.opened); |
||||||
|
opened_wallets.update((w) => { |
||||||
|
w[event.data.name] = event.data.opened; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case "closed": |
||||||
|
opened_wallets.update((w) => { |
||||||
|
delete w[event.data.walletid]; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
await ng.wallet_close(event.data.walletid); |
||||||
|
if ($active_wallet && $active_wallet.id == event.data.walletid) { |
||||||
|
await close_active_session(); |
||||||
|
active_wallet.set(undefined); |
||||||
|
push("#/wallet/login"); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
}; |
||||||
|
unsubscribe = active_wallet.subscribe(async (value) => { |
||||||
|
if (value) { |
||||||
|
if (value.wallet) { |
||||||
|
opened_wallets.update((w) => { |
||||||
|
w[value.id] = value.wallet; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
//await tick(); |
||||||
|
//console.log("posting opened"); |
||||||
|
wallet_channel.postMessage( |
||||||
|
{ |
||||||
|
cmd: "opened", |
||||||
|
wallet: value, |
||||||
|
ng_wallets: localStorage.getItem("ng_wallets"), |
||||||
|
}, |
||||||
|
location.href |
||||||
|
); |
||||||
|
} else { |
||||||
|
wallet_channel.postMessage( |
||||||
|
{ cmd: "closed", walletid: value.id }, |
||||||
|
location.href |
||||||
|
); |
||||||
|
active_wallet.set(undefined); |
||||||
|
await ng.wallet_close(value.id); |
||||||
|
//active_session.set(undefined); |
||||||
|
opened_wallets.update((w) => { |
||||||
|
delete w[value.id]; |
||||||
|
return w; |
||||||
|
}); |
||||||
|
push("#/wallet/login"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
onDestroy(() => { |
||||||
|
unsubscribe(); |
||||||
|
if (unsub_main_close) unsub_main_close(); |
||||||
|
}); |
||||||
|
|
||||||
|
// import { to_debug } from "./wallet_emojis"; |
||||||
|
// to_debug(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<!-- <p> |
||||||
|
{!$active_session} |
||||||
|
{JSON.stringify(Object.keys($wallets))} |
||||||
|
{JSON.stringify($active_wallet)} |
||||||
|
{JSON.stringify(Object.keys($opened_wallets))} |
||||||
|
{JSON.stringify($active_session)} |
||||||
|
</p> --> |
||||||
|
|
||||||
|
{#if $isLoading} |
||||||
|
<p class="text-center">Loading translations...</p> |
||||||
|
{:else} |
||||||
|
<Router {routes} /> |
||||||
|
{/if} |
@ -0,0 +1,44 @@ |
|||||||
|
// 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 {createAsyncProxy} from "async-proxy"; |
||||||
|
|
||||||
|
let proxy = null; |
||||||
|
|
||||||
|
let api = createAsyncProxy({},{ |
||||||
|
async apply(target, path, caller, args) { |
||||||
|
if (proxy) { |
||||||
|
//console.log("calling ",path, args);
|
||||||
|
return Reflect.apply(proxy[path], caller, args) |
||||||
|
} |
||||||
|
else |
||||||
|
throw new Error("You must call init_api() before using the API. load an API from @nextgraph-monorepo/app_api_tauri or @nextgraph-monorepo/app_api_web"); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
export default api; |
||||||
|
|
||||||
|
export const NG_EU_BSP = "https://nextgraph.eu"; |
||||||
|
export const NG_EU_BSP_REGISTER = import.meta.env.PROD |
||||||
|
? "https://account.nextgraph.eu/#/create" |
||||||
|
: "http://account-dev.nextgraph.eu:5173/#/create"; |
||||||
|
|
||||||
|
export const NG_NET_BSP = "https://nextgraph.net"; |
||||||
|
export const NG_NET_BSP_REGISTER = import.meta.env.PROD |
||||||
|
? "https://account.nextgraph.net/#/create" |
||||||
|
: "http://account-dev.nextgraph.net:5173/#/create"; |
||||||
|
|
||||||
|
export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/user/registered"; |
||||||
|
export const APP_WALLET_CREATE_SUFFIX = "/#/wallet/create"; |
||||||
|
|
||||||
|
export const LINK_NG_BOX = "https://nextgraph.org/ng-box/"; |
||||||
|
export const LINK_SELF_HOST = "https://nextgraph.org/self-host/"; |
||||||
|
|
||||||
|
export const init_api = function (a) { |
||||||
|
proxy = a; |
||||||
|
} |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 786 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.3 KiB |