fix splash screen, milkdown on safaai, link preview/opengraph

master
Niko PLP 3 months ago
parent 248916c561
commit b8474bc2ce
  1. 17
      ng-app/index-native.html
  2. 43
      ng-app/index-web.html
  3. BIN
      ng-app/public/favicon.ico
  4. 1
      ng-app/src-tauri/Cargo.toml
  5. 33
      ng-app/src-tauri/src/lib.rs
  6. 7
      ng-app/src/App.svelte
  7. 6
      ng-app/src/api.ts
  8. 1
      ng-app/src/apps/AutomergeEditor.svelte
  9. 64
      ng-app/src/apps/MilkDownEditor.svelte
  10. 52
      ng-app/src/apps/PostMdViewer.svelte
  11. 2
      ng-app/src/apps/automerge/ADate.svelte
  12. 4
      ng-app/src/styles.css
  13. 17
      ng-app/vite.config.ts
  14. 13
      ng-broker/src/server_ws.rs

@ -58,14 +58,21 @@
<title>NextGraph</title>
<style>
.splashing {
height: 95vh;display: grid;
height: 95vh;
width:100%;
display: flex;
justify-content: center;
align-items: center;
}
.noshow {
display: none !important;
}
</style>
</head>
<body>
<div id="splash" class="splashing">
<div style="margin: auto; display: grid;">
<div style="flex-direction: column;justify-content: center;color:#4972a5;width:100%;text-align:center;font-family: Inter, Avenir, Helvetica, Arial, sans-serif;">
<svg
style="width:100px;height:100px;margin: 0 auto 20px ;"
xmlns="http://www.w3.org/2000/svg"
@ -82,9 +89,13 @@
style="fill:#4972a5;fill-opacity:1;stroke:#4972a5;stroke-width:0.377976;stroke-opacity:1" />
</g>
</svg>
<div>&nbsp;&nbsp;&nbsp;Loading ...</div>
</div>
</div>
<div id="app"></div>
<div id="app" class="noshow"></div>
<script type="module" src="/src/main.ts"></script>
<script>
window.supported = true;
</script>
</body>
</html>

@ -56,10 +56,23 @@
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NextGraph</title>
<meta property="og:title" content="NextGraph App - The New Internet Platform" />
<meta property="og:image" content="https://nextgraph.org/card.png"/>
<meta name="twitter:image" content="https://nextgraph.org/card-twitter.png"/>
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:description" content="Decentralized, encrypted and local-first platform and framework, towards the better internet we all deserve! Features a social network, shared documents, productivity tools, an app store, and more! You can use NextGraph for free, and it works online and offline. With NextGraph you own your data and software, having your privacy respected, while enjoying high-quality apps for your daily use. Try it now! Developers can build new web3.0 local-first apps with our open source framework, based on open standards, with CRDTs, E2EE, Semantic Web, RDF, SPARQL, JSON, Markdown, Svelte, React, JavaScript, Rust, etc... ActivityPub and Solid compatible." />
<meta name="description" content="Decentralized, encrypted and local-first platform and framework, towards the better internet we all deserve! Features a social network, shared documents, productivity tools, an app store, and more! You can use NextGraph for free, and it works online and offline. With NextGraph you own your data and software, having your privacy respected, while enjoying high-quality apps for your daily use. Try it now! Developers can build new web3.0 local-first apps with our open source framework, based on open standards, with CRDTs, E2EE, Semantic Web, RDF, SPARQL, JSON, Markdown, Svelte, React, JavaScript, Rust, etc... ActivityPub and Solid compatible.">
</head>
<style>
.splashing {
height: 95vh;display: grid;
height: 95vh;
width:100%;
display: flex;
justify-content: center;
align-items: center;
}
.noshow {
display: none !important;
}
.error-no-wasm-hidden {
display:none;
@ -68,9 +81,9 @@
<body>
<div id="splash" class="splashing">
<div style="margin: auto; display: grid;">
<div style="flex-direction: column;justify-content: center;color:#4972a5;width:100%;text-align:center;font-family: Inter, Avenir, Helvetica, Arial, sans-serif;">
<svg
style="width:100px;height:100px;margin: 0 auto 20px ;"
style="width:100px;height:100px;margin: 0 auto 20px ;display:flex;"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 225 225"
>
@ -85,19 +98,25 @@
style="fill:#4972a5;fill-opacity:1;stroke:#4972a5;stroke-width:0.377976;stroke-opacity:1" />
</g>
</svg>
<br/>
<div id="error-no-wasm" class="error-no-wasm-hidden" style="text-align: center;">
Your browser is too old and does not support NextGraph. <br/>Please upgrade to a newer version of this browser,<br/> try with another browser, <br/>or <a href="https://nextgraph.org/download">install our native app</a>.
<div class="noshow" id="app-loading">&nbsp;&nbsp;&nbsp;Loading ...</div>
<div id="error-no-wasm" class="error-no-wasm-hidden">
Your browser is too old and does not support NextGraph. <br/>Please upgrade to a newer version of this browser,<br/> try with another browser,<br/> <br/>or <a href="https://nextgraph.org/download">install our native apps on <br/>
Linux, macOS, Windows desktops and laptops,<br/> and iOS, Android mobiles.</a>
</div>
<noscript style="text-align: center;">
NextGraph cannot load as Javascript is deactivated
<noscript style="display:grid;">
NextGraph cannot load as Javascript is deactivated.<br/>
You can use the CLI ngcli to access the Documents in the terminal<br/>
Or use the native apps for Linux, macOS, Windows, Android, iOS<br/>
Or setup an SSR static generator like Astro for read-only access.
</noscript>
</div>
</div>
<div id="app">
<div id="app" class="noshow">
</div>
<script>
const supported = (() => {
if (RegExp().hasIndices === undefined) return false;
try {
if (typeof WebAssembly === "object"
&& typeof WebAssembly.instantiate === "function") {
@ -109,10 +128,14 @@
}
return false;
})();
if (!supported || RegExp().hasIndices === undefined) {
if (!supported ) {
window.document.getElementById("error-no-wasm").className="";
}
window.supported = supported;
if (supported) window.document.getElementById("app-loading").className="";
</script>
<!-- # INSERT SCRIPT HERE -->
<script type="module" src="/src/main-web.ts"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

@ -32,6 +32,7 @@ async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
sys-locale = { version = "0.3.1" }
ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] }
tauri = { version = "2.0.0-alpha.14", features = [] }
# add the "devtools" feature if devtools in the production build should be activated
tauri-plugin-window = "2.0.0-alpha.1"
tauri-plugin-barcode-scanner = "=2.0.0-alpha.0"
# tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" }

@ -401,6 +401,36 @@ async fn app_request_stream(
Ok(())
}
#[tauri::command(rename_all = "snake_case")]
async fn discrete_update(
session_id: u64,
update: serde_bytes::ByteBuf,
heads: Vec<String>,
crdt: String,
nuri: String,
) -> Result<(), String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_update(),
nuri,
payload: Some(
AppRequestPayload::new_discrete_update(heads, crdt, update.into_vec())
.map_err(|e| format!("Deserialization error of heads: {e}"))?,
),
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Error(e)) = res {
Err(e)
} else {
Ok(())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn file_save_to_downloads(
session_id: u64,
@ -417,7 +447,7 @@ async fn file_save_to_downloads(
let mut request = AppRequest::new(AppRequestCommandV0::FileGet, nuri, None);
request.set_session_id(session_id);
let (mut reader, cancel) = nextgraph::local_broker::app_request_stream(request)
let (mut reader, _cancel) = nextgraph::local_broker::app_request_stream(request)
.await
.map_err(|e| e.to_string())?;
@ -864,6 +894,7 @@ impl AppBuilder {
doc_fetch_repo_subscribe,
doc_create,
cancel_stream,
discrete_update,
app_request_stream,
file_get,
file_save_to_downloads,

@ -74,6 +74,12 @@
// };
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();
@ -273,6 +279,7 @@
}
});
}
});
onDestroy(() => {

@ -38,6 +38,7 @@ const mapping = {
"decode_invitation": ["invite"],
"user_connect": ["info","user_id","location"],
"user_disconnect": ["user_id"],
"discrete_update": ["session_id", "update", "heads", "crdt", "nuri"],
"app_request": ["request"],
"app_request_with_nuri_command": ["nuri", "command", "session_id", "payload"],
"sparql_query": ["session_id","sparql","nuri"],
@ -200,6 +201,11 @@ const handler = {
tauri.invoke("cancel_stream", {stream_id});
}
} else if (path[0] === "discrete_update") {
let arg = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
arg.update = Array.from(new Uint8Array(arg.update));
return await tauri.invoke(path[0],arg)
} else if (path[0] === "app_request_stream") {
let stream_id = (lastStreamId += 1).toString();
//console.log("stream_id",stream_id);

@ -64,7 +64,6 @@
try {
await A.initializeWasm(wasmUrl);
} catch (e) {
toast_error($t("errors.no_wasm_on_old_safari"));
safari_error = true;
return;
}

@ -44,13 +44,12 @@
import { collab, collabServiceCtx } from '@milkdown/plugin-collab';
import { placeholder, placeholderCtx } from './milkdown-placeholder'
import { splitEditing, toggleSplitEditing } from '@milkdown-lab/plugin-split-editing'
import { SlashProvider, slashFactory } from '@milkdown/plugin-slash'
//import { SlashProvider, slashFactory } from '@milkdown/plugin-slash'
import { callCommand } from '@milkdown/utils';
import { emoji } from '@milkdown/plugin-emoji';
import { math } from '@milkdown/plugin-math';
import 'katex/dist/katex.min.css';
import { indent } from '@milkdown/plugin-indent';
import { prism } from '@milkdown/plugin-prism';
import 'prism-themes/themes/prism-nord.css'
export let commits = {};
@ -74,38 +73,59 @@
$: width, width_changed();
function slashPluginView(view) {
const content = document.createElement('div');
// function slashPluginView(view) {
// const content = document.createElement('div');
const provider = new SlashProvider({
content,
});
// const provider = new SlashProvider({
// content,
// });
// return {
// update: (updatedView, prevState) => {
// provider.update(updatedView, prevState);
// },
// destroy: () => {
// provider.destroy();
// content.remove();
// }
// }
// }
// const slash = slashFactory('my-slash');
async function setup() {
if (!Array.prototype.at) {
Array.prototype.at = function at(n) {
let i = Math.trunc(n) || 0
i = i < 0 ? this.length + i : i
if (i < 0 || i >= this.length) return undefined
return {
update: (updatedView, prevState) => {
provider.update(updatedView, prevState);
},
destroy: () => {
provider.destroy();
content.remove();
return this[i]
}
}
if (!Element.prototype.replaceChildren) {
Element.prototype.replaceChildren = function replaceChildren(...new_children) {
const { childNodes } = this;
while (childNodes.length) {
childNodes[0].remove();
}
this.append(...new_children);
}
}
let prism = await import("@milkdown/plugin-prism");
const slash = slashFactory('my-slash');
async function setup() {
editor = await Editor.make().config((ctx) => {
ctx.set(rootCtx, '#mdeditor')
ctx.set(placeholderCtx, $t("doc.type_your_text_here"))
ctx.set(slash.key, {
view: slashPluginView
})
}).use(slash)
// ctx.set(slash.key, {
// view: slashPluginView
// })
})//.use(slash)
.config(nord)
.use(commonmark)
.use(gfm)
.use(prism)
.use(prism.prism)
.use(indent)
.use(math)
.use(emoji)

@ -45,18 +45,18 @@
import { math } from '@milkdown/plugin-math';
import 'katex/dist/katex.min.css';
import { indent } from '@milkdown/plugin-indent';
import { prism } from '@milkdown/plugin-prism';
import 'prism-themes/themes/prism-nord.css'
import "prism-themes/themes/prism-nord.css";
export let commits = {};
const ydoc = new Y.Doc()
let editor;
let has_content = false;
let has_content = true;
async function setup() {
editor = await Editor.make().config((ctx) => {
try {
editor = Editor.make().config((ctx) => {
ctx.set(rootCtx, '#mdeditor')
ctx.update(editorViewOptionsCtx, (prev) => ({
...prev,
@ -64,8 +64,33 @@
}))
}).config(nord)
.use(commonmark)
.use(gfm)
.use(prism)
.use(gfm);
// do not load prism if Safari < 15.4
if (!Array.prototype.at) {
Array.prototype.at = function at(n) {
let i = Math.trunc(n) || 0
i = i < 0 ? this.length + i : i
if (i < 0 || i >= this.length) return undefined
return this[i]
}
}
if (!Element.prototype.replaceChildren) {
Element.prototype.replaceChildren = function replaceChildren(...new_children) {
const { childNodes } = this;
while (childNodes.length) {
childNodes[0].remove();
}
this.append(...new_children);
}
}
if ([].at) {
let prism = await import("@milkdown/plugin-prism");
editor = editor.use(prism.prism);
}
editor = await editor
.use(indent)
.use(math)
.use(emoji)
@ -86,6 +111,7 @@
.connect();
});
has_content = false;
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YXml, {local:true})
has_content = true;
@ -95,6 +121,11 @@
has_content = true;
}
}
catch (e){
console.log("in setup ")
console.log(e)
}
}
onMount(async ()=>{
await setup();
@ -103,7 +134,12 @@
onDestroy(async ()=>{
ydoc.destroy();
await editor.destroy();
try {
if (editor) await editor.destroy();
} catch(e) {
console.log(e);
}
});
const edit = () => {
@ -116,7 +152,7 @@
<button
on:click={edit}
on:keypress={edit}
class="select-none mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
class="select-none ml-5 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PencilSquare tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.start_editing")}

@ -60,7 +60,7 @@
</div>
<div class="relative" style="max-width: 129px;">
<div class="relative" style="width: 129px;">
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
<svg class="w-4 h-4 text-primary-700 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm11-4a1 1 0 1 0-2 0v4a1 1 0 0 0 .293.707l3 3a1 1 0 0 0 1.414-1.414L13 11.586V8Z" clip-rule="evenodd"/>

@ -23,10 +23,6 @@ td.hljs {
display: none;
} */
.splashing {
display: none !important;
}
.split-editor {
display: grid;
grid-template-columns: repeat(2, 1fr);

@ -7,6 +7,20 @@ import svelteSVG from "vite-plugin-svelte-svg";
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
const jsToBottom = () => {
return {
name: "script-at-end-of-body",
transformIndexHtml(html) {
let scriptTag = html.match(/<script type[^>]*>(.*?)<\/script[^>]*>/)[0]
//console.log("\n SCRIPT TAG", scriptTag, "\n")
html = html.replace(scriptTag, "")
html = html.replace("<!-- # INSERT SCRIPT HERE -->", scriptTag)
return html;
}
}
}
// https://vitejs.dev/config/
export default defineConfig(async () => {
const host = await internalIpV4()
@ -105,5 +119,8 @@ if (process.env.NG_APP_FILE) {
config.plugins.push(viteSingleFile());
config.worker.plugins.push(viteSingleFile());
}
if (process.env.NG_APP_WEB) {
config.plugins.push(jsToBottom());
}
return config
})

@ -196,6 +196,10 @@ fn prepare_urls_from_private_addrs(addrs: &Vec<BindAddress>, port: u16) -> Vec<S
#[include = "*.gzip"]
struct App;
#[derive(RustEmbed)]
#[folder = "../ng-app/public/"]
struct AppPublic;
static ROBOTS: &str = "User-agent: *\r\nDisallow: /";
fn upgrade_ws_or_serve_app(
@ -258,6 +262,15 @@ fn upgrade_ws_or_serve_app(
.body(Some(BOOTSTRAP_STRING.get().unwrap().as_bytes().to_vec()))
.unwrap();
return Err(res);
} else if uri == "/favicon.ico" {
let file = AppPublic::get("favicon.ico").unwrap();
let res = Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "image/x-icon")
.header("Cache-Control", "max-age=432000, must-revalidate")
.body(Some(file.data.to_vec()))
.unwrap();
return Err(res);
} else if uri == "/robots.txt" {
let res = Response::builder()
.status(StatusCode::OK)

Loading…
Cancel
Save