MD and XML source viewer, and hide unimplemented Graph editors and viewers

master
Niko PLP 4 months ago
parent 6f3e7b16eb
commit 2f799ed937
  1. 1
      ng-app/package.json
  2. 2
      ng-app/src/App.svelte
  3. 157
      ng-app/src/apps/MdSource.svelte
  4. 2
      ng-app/src/apps/ProseMirrorEditor.svelte
  5. 4
      ng-app/src/apps/ProseMirrorViewer.svelte
  6. 131
      ng-app/src/apps/XmlSource.svelte
  7. 3
      ng-app/src/apps/YMapEditor.svelte
  8. 25
      ng-app/src/apps/YMapViewer.svelte
  9. 10
      ng-app/src/classes.ts
  10. 29
      ng-app/src/lib/Document.svelte
  11. 97
      ng-app/src/lib/FullLayout.svelte
  12. 9
      ng-app/src/lib/Pane.svelte
  13. 25
      ng-app/src/lib/components/MenuItem.svelte
  14. 4
      ng-app/src/styles.css
  15. 1
      ng-app/src/tab.ts
  16. 48
      ng-app/src/zeras.ts
  17. 4
      ng-app/vite.config.ts
  18. 13
      pnpm-lock.yaml

@ -82,6 +82,7 @@
"svelte-jsoneditor": "^0.23.8",
"svelte-spa-router": "^3.3.0",
"vite-plugin-top-level-await": "^1.3.1",
"xml-beautifier": "^0.5.0",
"y-codemirror.next": "^0.3.5",
"y-prosemirror": "^1.2.10",
"y-protocols": "^1.0.1",

@ -74,7 +74,7 @@
// };
onMount(async () => {
console.log("hide splash", window.supported);
//console.log("hide splash", window.supported);
if (window.supported) {
window.document.getElementById("splash").className="noshow";
window.document.getElementById("app").className="";

@ -0,0 +1,157 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<!--
We could maybe also use https://ssssota.github.io/svelte-exmarkdown/ for rendering the MD (but to obtain the MD, we need to instantiate Milkdown anyway)
-->
<script lang="ts">
import { onMount, tick, onDestroy } from "svelte";
import {
toast_error,
toast_success,
reset_toasts,
display_error,
live_discrete_update,
discrete_update
} from "../store";
import {
cur_tab_register_on_save,
cur_tab_deregister_on_save,
cur_tab_branch_class,
set_view_or_edit
} from "../tab";
import { t } from "svelte-i18n";
import{ PencilSquare } from "svelte-heros-v2";
import * as Y from 'yjs'
import { Editor, editorCtx, rootCtx } from '@milkdown/core';
import { collab, collabServiceCtx } from '@milkdown/plugin-collab';
import { commonmark } from '@milkdown/preset-commonmark';
import { gfm } from '@milkdown/preset-gfm';
import markdown from "svelte-highlight/languages/markdown";
import Highlight, { LineNumbers } from "svelte-highlight";
import "svelte-highlight/styles/github.css";
import { getMarkdown } from "@milkdown/utils";
export let commits = {};
const ydoc = new Y.Doc()
let has_content = true;
let loading = true;
let source = "";
let editor;
function process_doc() {
editor.action((ctx) => {
const editor = ctx.get(editorCtx);
source = editor.action(getMarkdown());
});
}
async function setup() {
try {
editor = await Editor.make().config((ctx) => {
ctx.set(rootCtx, '#mdhiddeneditor')
})
.use(commonmark)
.use(gfm)
.use(collab).create();
ydoc.on('destroy', async () => {
})
editor.action((ctx) => {
const collabService = ctx.get(collabServiceCtx);
collabService
// bind doc
.bindDoc(ydoc)
// connect yjs with milkdown
.connect();
});
has_content = false;
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YXml, {local:true})
has_content = true;
process_doc();
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YXml, {local:true})
has_content = true;
}
if (has_content) process_doc();
loading = false;
}
catch (e){
console.log(e)
}
}
onMount(async ()=>{
await setup();
});
onDestroy(async ()=>{
ydoc.destroy();
commits.discrete?.deregisterOnUpdate();
if (editor) await editor.destroy();
});
</script>
{#if !has_content}
<p class="ml-5">{$t("doc.empty")}</p>
{/if}
{#if loading}
<div class="grow flex flex-col justify-center text-primary-700">
<svg
class="animate-spin mt-4 h-10 w-10 mb-4 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>
<p class="text-center">{$t("connectivity.loading")}...</p>
</div>
{/if}
{#if source}
<Highlight language={markdown} code={source} class="mb-10" let:highlighted>
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{/if}
<div id="mdhiddeneditor" style="display:none;"></div>

@ -37,7 +37,7 @@
export let commits = {};
const ydoc = new Y.Doc()
const yxml = ydoc.getXmlFragment('ng')
const yxml = ydoc.getXmlFragment('prosemirror')
let view;

@ -31,7 +31,7 @@
let source = "";
const ydoc = new Y.Doc()
const yxml = ydoc.getXmlFragment('ng')
const yxml = ydoc.getXmlFragment('prosemirror')
let loading = true;
@ -101,7 +101,7 @@
{#if source}
<div class="post-rich-text prose">
<div class="post-rich-text prose" style="margin-top: 1.25em;">
{@html source}
</div>

@ -0,0 +1,131 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<!--
We could maybe also use https://ssssota.github.io/svelte-exmarkdown/ for rendering the MD (but to obtain the MD, we need to instantiate Milkdown anyway)
-->
<script lang="ts">
import { onMount, tick, onDestroy } from "svelte";
import {
toast_error,
toast_success,
reset_toasts,
display_error,
live_discrete_update,
discrete_update
} from "../store";
import {
cur_tab_register_on_save,
cur_tab_deregister_on_save,
cur_tab_branch_class,
set_view_or_edit
} from "../tab";
import { t } from "svelte-i18n";
import{ PencilSquare } from "svelte-heros-v2";
import * as Y from 'yjs'
import xml from "svelte-highlight/languages/xml";
import Highlight, { LineNumbers } from "svelte-highlight";
import "svelte-highlight/styles/github.css";
import beautify from "xml-beautifier";
export let commits = {};
const ydoc = new Y.Doc();
const yxml = ydoc.getXmlFragment('prosemirror');
let has_content = true;
let loading = true;
let source = "";
function process_doc() {
source = beautify("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+yxml.toString());
//console.log(source);
}
async function setup() {
try {
ydoc.on('destroy', async () => {
})
has_content = false;
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YXml, {local:true})
has_content = true;
process_doc();
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YXml, {local:true})
has_content = true;
}
if (has_content) process_doc();
loading = false;
}
catch (e){
console.log(e)
}
}
onMount(async ()=>{
await setup();
});
onDestroy(async ()=>{
ydoc.destroy();
commits.discrete?.deregisterOnUpdate();
});
</script>
{#if !has_content}
<p class="ml-5">{$t("doc.empty")}</p>
{/if}
{#if loading}
<div class="grow flex flex-col justify-center text-primary-700">
<svg
class="animate-spin mt-4 h-10 w-10 mb-4 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>
<p class="text-center">{$t("connectivity.loading")}...</p>
</div>
{/if}
{#if source}
<Highlight language={xml} code={source} class="mb-10" let:highlighted>
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{/if}

@ -266,7 +266,7 @@
function onRenderContextMenu(items, context) {
if (items[4].items[1].items[0].text == "Convert to:") items[4].items.pop();
if (Array.isArray(context.selection.path) && context.selection.path.length == 0 && context.selection.type === "value") {
if (Array.isArray(context.selection?.path) && context.selection.path.length == 0 && context.selection.type === "value") {
items[2].items.shift();
items[2].items.pop();
items[4].items[0].items.pop();
@ -275,6 +275,7 @@
}
</script>
<div class="grow ng-json-editor" style="min-height:300px;">
<JSONEditor bind:this={editor} {content} onChange={handleChange} {onRenderMenu} {onRenderContextMenu}/>

@ -20,11 +20,11 @@
discrete_update
} from "../store";
import {
cur_tab_register_on_save,
cur_tab_deregister_on_save,
cur_tab_branch_class
cur_tab_doc_can_edit,
set_view_or_edit
} from "../tab";
import { t } from "svelte-i18n";
import{ PencilSquare } from "svelte-heros-v2";
import * as Y from 'yjs'
import { JSONEditor } from 'svelte-jsoneditor'
@ -119,12 +119,17 @@
for (const h of history) {
Y.applyUpdate(ydoc, h[crdt], {local:true})
}
loading = false;
});
onDestroy(async ()=>{
ydoc.destroy();
await editor.destroy();
});
const edit = () => {
set_view_or_edit(false);
}
</script>
@ -155,6 +160,20 @@
</div>
{/if}
{#if $cur_tab_doc_can_edit && ( crdt=="YMap" && Object.keys(content.json).length == 0 || crdt=="YArray" && Array.isArray(content.json) && content.json.length == 0 ) }
<div class="flex-row">
<button
on:click={edit}
on:keypress={edit}
class="shrink select-none ml-4 mt-2 mb-4 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 class="mr-2 focus:outline-none" tabindex="-1" />
{$t("doc.start_editing")}
</button>
</div>
{/if}
<div class="grow ng-json-editor" style="min-height:300px;">
<JSONEditor bind:this={editor} {content} readOnly={true} />

@ -105,6 +105,7 @@ export const official_classes = {
"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",
@ -113,6 +114,7 @@ export const official_classes = {
"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",
@ -121,6 +123,7 @@ export const official_classes = {
"ng:o": "n:g:z:pre",
"ng:w": "n:g:z:code_editor",
"ng:compat": [],
"implemented": true
},
"code:svelte": {
"ng:crdt": "YText",
@ -129,6 +132,7 @@ export const official_classes = {
"ng:o": "n:g:z:pre",
"ng:w": "n:g:z:code_editor",
"ng:compat": [],
"implemented": true
},
"code:react": {
"ng:crdt": "YText",
@ -137,6 +141,7 @@ export const official_classes = {
"ng:o": "n:g:z:pre",
"ng:w": "n:g:z:code_editor",
"ng:compat": [],
"implemented": true
},
"app": {
"ng:n": "Official App",
@ -283,6 +288,7 @@ export const official_classes = {
},
"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",
@ -291,6 +297,7 @@ export const official_classes = {
"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",
@ -299,6 +306,7 @@ export const official_classes = {
"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",
@ -307,6 +315,7 @@ export const official_classes = {
"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",
@ -343,6 +352,7 @@ export const official_classes = {
"ldp": true,
},
"ng:compat": ["rdfs:member","ldp:contains","rdf:Bag","rdf:Alt"],
"implemented": true
},
"data:plato": {
"ng:crdt": "Graph",

@ -118,7 +118,34 @@
</div>
{:then}
{#if $cur_app}
{#await load_official_app($cur_app) then app}
{#await load_official_app($cur_app)}
<div class="flex flex-col justify-center text-primary-700">
<div class:max-w-screen-lg={center} class="p-4" class:w-[1024px]={center}>
<svg
class="animate-spin mt-4 h-10 w-10 mb-4 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>
<p class="text-center">{$t("connectivity.loading")}...</p>
</div>
</div>
{:then app}
<div class:max-w-screen-lg={center} class="flex flex-col" style="overflow-wrap: anywhere;" class:w-[1024px]={center} >
<svelte:component this={app} commits={$commits}/>
</div>

@ -103,6 +103,8 @@
Camera,
VideoCamera,
Microphone,
ChevronUp,
ChevronDown,
} from "svelte-heros-v2";
import NavBar from "./components/NavBar.svelte";
@ -340,8 +342,8 @@
"data:board",
"data:grid",
"data:json",
"data:array",
"data:map",
"data:array",
"data:xml",
];
@ -643,7 +645,7 @@
</li>
{/if}
{#if $cur_viewer}
<MenuItem selected={$cur_tab_view_or_edit} title={$cur_viewer["ng:a"]} clickable={($available_viewers.length > 1 || !$cur_tab_view_or_edit) && function () { if ($available_viewers.length > 1) { open_view_as = !open_view_as; } else { set_view_or_edit(true); hideMenu(); } open_edit_with=false;} }>
<MenuItem selected={$cur_tab_view_or_edit} title={$cur_viewer["ng:a"]} dropdown={$available_viewers.length > 1 ? open_view_as : undefined} clickable={($available_viewers.length > 1 || !$cur_tab_view_or_edit) && function () { if ($available_viewers.length > 1) { open_view_as = !open_view_as; } else { set_view_or_edit(true); hideMenu(); } open_edit_with=false;} }>
<Eye
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white "
@ -652,7 +654,7 @@
</MenuItem>
{#if open_view_as && $available_viewers.length > 1 }
{#each $available_viewers as viewer}
<MenuItem title={viewer["ng:a"]} extraClass="submenu" clickable={(viewer["ng:g"] !== $cur_viewer["ng:g"] || !$cur_tab_view_or_edit) && function () { set_view_or_edit(true); set_viewer(viewer["ng:g"]); hideMenu(); open_view_as = false} }>
<MenuItem title={viewer["ng:a"]} extraClass="submenu" clickable={viewer["implemented"] ? (viewer["ng:g"] !== $cur_viewer["ng:g"] || !$cur_tab_view_or_edit) && function () { set_view_or_edit(true); set_viewer(viewer["ng:g"]); hideMenu(); open_view_as = false} : undefined }>
<ZeraIcon
zera={viewer["ng:u"]}
config={{
@ -667,7 +669,9 @@
{/if}
{#if $cur_tab_doc_can_edit}
{#if $cur_editor}
<MenuItem title={$cur_editor["ng:a"]} selected={!$cur_tab_view_or_edit} clickable={ ($available_editors.length > 1 || $cur_tab_view_or_edit) && function () { if ($available_editors.length > 1) { open_edit_with = !open_edit_with; } else { set_view_or_edit(false); hideMenu(); } open_view_as=false;} }>
<MenuItem title={$cur_editor["ng:a"]} selected={!$cur_tab_view_or_edit}
dropdown={$available_editors.length > 1 ? open_edit_with : undefined}
clickable={ ($available_editors.length > 1 || $cur_tab_view_or_edit) && function () { if ($available_editors.length > 1) { open_edit_with = !open_edit_with; } else { set_view_or_edit(false); hideMenu(); } open_view_as=false;} }>
<PencilSquare
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white "
@ -676,7 +680,8 @@
</MenuItem>
{#if open_edit_with && $available_editors.length > 1 }
{#each $available_editors as editor}
<MenuItem title={editor["ng:a"]} extraClass="submenu" clickable={(editor["ng:g"] !== $cur_editor["ng:g"] || $cur_tab_view_or_edit) && function () { set_view_or_edit(false); set_editor(editor["ng:g"]); hideMenu(); open_edit_with = false} }>
<MenuItem title={editor["ng:a"]} extraClass="submenu"
clickable={editor["implemented"] ? (editor["ng:g"] !== $cur_editor["ng:g"] || $cur_tab_view_or_edit) && function () { set_view_or_edit(false); set_editor(editor["ng:g"]); hideMenu(); open_edit_with = false} : undefined }>
<ZeraIcon
zera={editor["ng:u"]}
config={{
@ -752,7 +757,7 @@
<span class="ml-3">{$t("doc.menu.items.files.label")} {$all_files_count}</span>
</MenuItem>
<div style="padding:0;" bind:this={shareMenu}></div>
<MenuItem title={$t("doc.menu.items.share.desc")} clickable={ () => { open_share = !open_share; scrollToMenuShare(); } }>
<MenuItem title={$t("doc.menu.items.share.desc")} dropdown={open_share} clickable={ () => { open_share = !open_share; scrollToMenuShare(); } }>
<Share
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -838,7 +843,7 @@
<span class="ml-3">{$t("doc.menu.items.settings.label")}</span>
</MenuItem>
<div style="padding:0;" bind:this={toolsMenu}></div>
<MenuItem title={$t("doc.menu.items.tools.desc")} clickable={ () => {open_tools = !open_tools; scrollToMenuTools();} } >
<MenuItem title={$t("doc.menu.items.tools.desc")} dropdown={open_tools} clickable={ () => {open_tools = !open_tools; scrollToMenuTools();} } >
<WrenchScrewdriver
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -858,7 +863,7 @@
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["mc"]} />
<span class="ml-3">{$t("doc.menu.items.mc.label")}</span>
</MenuItem>
<MenuItem title={$t("doc.menu.items.archive.desc")} selected={$cur_tab_right_pane == "mc"} clickable={ ()=> openArchive() }>
<MenuItem title={$t("doc.menu.items.archive.desc")} clickable={ ()=> openArchive() }>
<ArchiveBox
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -934,17 +939,19 @@
<TxtIcon class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" />
<span class="ml-3">{$t("doc.text")}</span>
</MenuItem>
<MenuItem title={$t("doc.group")} clickable={ ()=> new_group() }>
<!--new_group-->
<MenuItem title={$t("doc.group")} clickable={ undefined }>
<UserGroup tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("doc.group")}</span>
</MenuItem>
<MenuItem title={get_class("doc:compose")["ng:a"]} clickable={ ()=> new_document("doc:compose") }>
<!-- ()=> new_document("doc:compose") -->
<MenuItem title={get_class("doc:compose")["ng:a"]} clickable={ undefined }>
<DataClassIcon dataClass="doc:compose" {config}/>
<span class="ml-3">{get_class("doc:compose")["ng:n"]}</span>
</MenuItem>
<div style="padding:0;" bind:this={createMenu.social}></div>
<MenuItem title={$t("doc.social")} clickable={ () => { createMenuOpened.social = !createMenuOpened.social; scrollToCreateMenu("social"); } }>
<MenuItem title={$t("doc.social")} dropdown={createMenuOpened.social} clickable={ () => { createMenuOpened.social = !createMenuOpened.social; scrollToCreateMenu("social"); } }>
<Users
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -953,7 +960,8 @@
</MenuItem>
{#if createMenuOpened.social }
{#each create_social_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -961,18 +969,20 @@
{/if}
<div style="padding:0;" bind:this={createMenu.apps}></div>
<MenuItem title={$t("doc.apps")} clickable={ () => { createMenuOpened.apps = !createMenuOpened.apps; scrollToCreateMenu("apps"); } }>
<MenuItem title={$t("doc.apps")} dropdown={createMenuOpened.apps} clickable={ () => { createMenuOpened.apps = !createMenuOpened.apps; scrollToCreateMenu("apps"); } }>
<DataClassIcon dataClass="app:z" {config}/>
<span class="ml-3">{$t("doc.apps")}</span>
</MenuItem>
{#if createMenuOpened.apps }
<MenuItem title={$t("doc.new_app")} extraClass="submenu" clickable={ () => new_app() }>
<!-- () => new_app() -->
<MenuItem title={$t("doc.new_app")} extraClass="submenu" clickable={ undefined }>
<Beaker tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("doc.new_app")}</span>
</MenuItem>
{#each create_apps_items as item}
<MenuItem title="" extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title="" extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">3rd party app Class</span>
</MenuItem>
@ -980,7 +990,7 @@
{/if}
<div style="padding:0;" bind:this={createMenu.pro}></div>
<MenuItem title={$t("doc.pro")} clickable={ () => { createMenuOpened.pro = !createMenuOpened.pro; scrollToCreateMenu("pro"); } }>
<MenuItem title={$t("doc.pro")} dropdown={createMenuOpened.pro} clickable={ () => { createMenuOpened.pro = !createMenuOpened.pro; scrollToCreateMenu("pro"); } }>
<Briefcase
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -989,7 +999,8 @@
</MenuItem>
{#if createMenuOpened.pro }
{#each create_pro_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -998,25 +1009,26 @@
{#await check_has_camera() then has_camera}
{#if has_camera}
<MenuItem title={$t("buttons.scan_qr")} clickable={ ()=> scan_qr() }>
<!-- ()=> scan_qr() -->
<MenuItem title={$t("buttons.scan_qr")} clickable={ undefined }>
<QrCode tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("buttons.scan_qr")}</span>
</MenuItem>
<MenuItem title={$t("doc.take_picture")} clickable={ ()=> take_picture() }>
<!-- ()=> take_picture() -->
<MenuItem title={$t("doc.take_picture")} clickable={ undefined }>
<Camera tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("doc.take_picture")}</span>
</MenuItem>
<MenuItem title={$t("doc.record_reel")} clickable={ ()=> record_reel() }>
<!-- ()=> record_reel() -->
<MenuItem title={$t("doc.record_reel")} clickable={ undefined }>
<VideoCamera tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("doc.record_reel")}</span>
</MenuItem>
<MenuItem title={$t("doc.record_voice")} clickable={ ()=> record_voice() }>
<!-- ()=> record_voice() -->
<MenuItem title={$t("doc.record_voice")} clickable={ undefined }>
<Microphone tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("doc.record_voice")}</span>
@ -1024,7 +1036,7 @@
{/if}
<div style="padding:0;" bind:this={createMenu.media}></div>
<MenuItem title={$t("doc.media")} clickable={ () => { createMenuOpened.media = !createMenuOpened.media; scrollToCreateMenu("media"); } }>
<MenuItem title={$t("doc.media")} dropdown={createMenuOpened.media} clickable={ () => { createMenuOpened.media = !createMenuOpened.media; scrollToCreateMenu("media"); } }>
<DocumentArrowUp
tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -1033,13 +1045,15 @@
</MenuItem>
{#if createMenuOpened.media }
{#each create_media_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
{/each}
{#if !has_camera}
<MenuItem title={$t("doc.record_voice")} extraClass="submenu" clickable={ ()=> record_voice() }>
<!-- ()=> record_voice() -->
<MenuItem title={$t("doc.record_voice")} extraClass="submenu" clickable={ undefined }>
<Microphone tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"/>
<span class="ml-3">{$t("doc.record_voice")}</span>
@ -1049,13 +1063,14 @@
{/await}
<div style="padding:0;" bind:this={createMenu.chart}></div>
<MenuItem title={$t("doc.chart")} clickable={ () => { createMenuOpened.chart = !createMenuOpened.chart; scrollToCreateMenu("chart"); } }>
<MenuItem title={$t("doc.chart")} dropdown={createMenuOpened.chart} clickable={ () => { createMenuOpened.chart = !createMenuOpened.chart; scrollToCreateMenu("chart"); } }>
<DataClassIcon dataClass="doc:chart" {config}/>
<span class="ml-3">{$t("doc.chart")}</span>
</MenuItem>
{#if createMenuOpened.chart }
{#each create_chart_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -1063,13 +1078,14 @@
{/if}
<div style="padding:0;" bind:this={createMenu.viz}></div>
<MenuItem title={$t("doc.viz")} clickable={ () => { createMenuOpened.viz = !createMenuOpened.viz; scrollToCreateMenu("viz"); } }>
<MenuItem title={$t("doc.viz")} dropdown={createMenuOpened.viz} clickable={ () => { createMenuOpened.viz = !createMenuOpened.viz; scrollToCreateMenu("viz"); } }>
<DataClassIcon dataClass="doc:viz" {config}/>
<span class="ml-3">{$t("doc.viz")}</span>
</MenuItem>
{#if createMenuOpened.viz }
{#each create_viz_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -1077,13 +1093,14 @@
{/if}
<div style="padding:0;" bind:this={createMenu.diagram}></div>
<MenuItem title={$t("doc.diagram")} clickable={ () => { createMenuOpened.diagram = !createMenuOpened.diagram; scrollToCreateMenu("diagram"); } }>
<MenuItem title={$t("doc.diagram")} dropdown={createMenuOpened.diagram} clickable={ () => { createMenuOpened.diagram = !createMenuOpened.diagram; scrollToCreateMenu("diagram"); } }>
<DataClassIcon dataClass="doc:diagram" {config}/>
<span class="ml-3">{$t("doc.diagram")}</span>
</MenuItem>
{#if createMenuOpened.diagram }
{#each create_diagram_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -1091,13 +1108,14 @@
{/if}
<div style="padding:0;" bind:this={createMenu.doc}></div>
<MenuItem title={$t("doc.other")} clickable={ () => { createMenuOpened.doc = !createMenuOpened.doc; scrollToCreateMenu("doc"); } }>
<MenuItem title={$t("doc.other")} dropdown={createMenuOpened.doc} clickable={ () => { createMenuOpened.doc = !createMenuOpened.doc; scrollToCreateMenu("doc"); } }>
<DataClassIcon dataClass="doc:" {config}/>
<span class="ml-3">{$t("doc.other")}</span>
</MenuItem>
{#if createMenuOpened.doc }
{#each create_doc_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<!-- () => new_document(item) -->
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -1105,13 +1123,13 @@
{/if}
<div style="padding:0;" bind:this={createMenu.data}></div>
<MenuItem title={$t("doc.data")} clickable={ () => { createMenuOpened.data = !createMenuOpened.data; scrollToCreateMenu("data"); } }>
<MenuItem title={$t("doc.data")} dropdown={createMenuOpened.data} clickable={ () => { createMenuOpened.data = !createMenuOpened.data; scrollToCreateMenu("data"); } }>
<DataClassIcon dataClass="data:" {config}/>
<span class="ml-3">{$t("doc.data")}</span>
</MenuItem>
{#if createMenuOpened.data }
{#each create_data_items as item}
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ () => new_document(item) }>
<MenuItem title={get_class(item)["ng:a"]} extraClass="submenu" clickable={ get_class(item)["implemented"] ? () => new_document(item) : undefined }>
<DataClassIcon dataClass={item} {config}/>
<span class="ml-3">{get_class(item)["ng:n"]}</span>
</MenuItem>
@ -1119,7 +1137,7 @@
{/if}
<div style="padding:0;" bind:this={createMenu.code}></div>
<MenuItem title={$t("doc.code")} clickable={ () => { createMenuOpened.code = !createMenuOpened.code; scrollToCreateMenu("code"); } }>
<MenuItem title={$t("doc.code")} dropdown={createMenuOpened.code} clickable={ () => { createMenuOpened.code = !createMenuOpened.code; scrollToCreateMenu("code"); } }>
<DataClassIcon dataClass="code" {config}/>
<span class="ml-3">{$t("doc.code")}</span>
</MenuItem>
@ -1132,7 +1150,8 @@
{/each}
{/if}
<MenuItem title={get_class("e:link")["ng:a"]} clickable={ ()=> new_document("e:link") }>
<!-- ()=> new_document("e:link") -->
<MenuItem title={get_class("e:link")["ng:a"]} clickable={ undefined }>
<DataClassIcon dataClass="e:link" {config}/>
<span class="ml-3">{get_class("e:link")["ng:n"]}</span>
</MenuItem>

@ -19,6 +19,7 @@
};
const load_pane = async (pane_name) => {
if (!panes[pane_name]) return false;
let component = await import(`./panes/${panes[pane_name]}.svelte`);
return component.default;
};
@ -29,9 +30,11 @@
{#if pane_name}
{#await load_pane(pane_name) then pane}
<div class="flex w-full" style="overflow-wrap: anywhere;">
<svelte:component this={pane}/>
</div>
{#if pane}
<div class="flex w-full" style="overflow-wrap: anywhere;">
<svelte:component this={pane}/>
</div>
{/if}
{/await}
{/if}

@ -16,17 +16,36 @@
export let extraClass = "";
export let selected = false;
export let title = "";
export let dropdown = undefined;
import {
ChevronUp,
ChevronDown,
} from "svelte-heros-v2";
</script>
{#if clickable}
<li {title} role="menuitem" tabindex="0" class:text-primary-600={selected} class:text-gray-800={!selected} class:dark:text-white={!selected} class:dark:text-primary-300={selected}
class="{extraClass} select-none clickable focus:outline-2 focus:outline flex items-center px-2 py-1 text-base font-normal rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 mt-1"
class="{extraClass} select-none clickable focus:outline-2 focus:outline flex items-center pl-2 py-1 text-base font-normal rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 mt-1 pr-0"
on:click={(e) => { e.currentTarget.blur(); clickable();}} on:keypress={clickable} on:keydown={(e) => {if (e.code=='Space') { e.preventDefault(); clickable();} }}>
<slot />
{#if dropdown!==undefined}
<div class="grow"></div>
{#if dropdown}
<ChevronUp/>
{:else}
<ChevronDown/>
{/if}
{/if}
</li>
{:else if clickable === false}
<li {title} class="{extraClass} select-none flex items-center px-2 py-1 text-base font-normal text-primary-600 rounded-lg dark:text-primary-300 mt-1">
<slot />
</li>
{:else}
<li class="{extraClass} select-none flex items-center px-2 py-1 text-base font-normal text-primary-600 rounded-lg dark:text-primary-300 mt-1">
<li {title} class="{extraClass} select-none flex items-center px-2 py-1 text-base font-normal deactivated-menu text-gray-400 rounded-lg dark:text-gray-400 mt-1" >
<slot />
</li>
{/if}
{/if}

@ -238,6 +238,10 @@ div[role="alert"] div {
justify-content: center;
}
.deactivated-menu > svg {
color: rgb(156 163 175) !important;
}
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;

@ -535,6 +535,7 @@ export const change_nav_bar = (icon, title, back) => {
}
return old;
});
live_editing.set(get(cur_tab).doc.live_edit);
};
export const persistent_error = (nuri, pe) => {

@ -30,6 +30,7 @@ export const official_apps = {
"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",
@ -48,6 +49,7 @@ export const official_apps = {
"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",
@ -58,6 +60,7 @@ export const official_apps = {
"ng:b": "AutomergeEditor",
"ng:w": ["data:json"],
"full_width": true,
implemented: true,
},
"n:g:z:json_ymap_editor": {
"ng:n": "JSON Editor",
@ -67,6 +70,7 @@ export const official_apps = {
"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",
@ -86,6 +90,7 @@ export const official_apps = {
"ng:b": "TurtleViewer",
"ng:o": ["data:graph"],
"ng:w": [],
implemented: true,
},
"n:g:z:sparql_query": {
"ng:n": "SPARQL Query",
@ -96,6 +101,7 @@ export const official_apps = {
"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",
@ -124,6 +130,7 @@ export const official_apps = {
"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",
@ -133,6 +140,7 @@ export const official_apps = {
"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",
@ -143,6 +151,7 @@ export const official_apps = {
"ng:b": "AutomergeViewer",
"ng:o": ["data:json"],
"full_width": true,
implemented: true,
},
"n:g:z:triple_viewer": {
"ng:n": "Graph Triples",
@ -252,6 +261,7 @@ export const official_apps = {
"ng:b": "ProseMirrorEditor",
"ng:o": [],
"ng:w": ["post:rich"],
implemented: true,
},
"n:g:z:post_md_editor": {
"ng:n": "Post MD Editor",
@ -263,6 +273,7 @@ export const official_apps = {
"ng:o": [],
"ng:w": ["post:md"],
"full_width": true,
implemented: true,
},
"n:g:z:code_editor": {
"ng:n": "Text Editor",
@ -273,6 +284,7 @@ export const official_apps = {
"ng:b": "CodeMirrorEditor",
"ng:o": [],
"ng:w": ["code*","post:text"],
implemented: true,
},
"n:g:z:file_viewer": {
"ng:n": "File details",
@ -303,6 +315,7 @@ export const official_apps = {
"ng:b": "XmlSource", // displayed with highlight.js , with option to download
"ng:o": ["post:rich","post:md","post:html","page","data:xml", "doc:diagram:drawio"],
"ng:w": [],
implemented: true,
},
"n:g:z:viewer:md": {
"ng:n": "MarkDown source",
@ -313,6 +326,7 @@ export const official_apps = {
"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",
@ -324,6 +338,7 @@ export const official_apps = {
"ng:o": ["data:json", "data:table", "doc:diagram:jsmind", "doc:diagram:gantt", "doc:diagram:excalidraw", "doc:viz:*", "doc:chart:*", "prod:cad"],
"ng:w": [],
"full_width": true,
implemented: true,
},
"n:g:z:crdt_source_viewer:ymap": {
"ng:n": "JSON Source",
@ -334,6 +349,7 @@ export const official_apps = {
"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",
@ -344,6 +360,7 @@ export const official_apps = {
"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",
@ -351,21 +368,23 @@ export const official_apps = {
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:text",
"ng:b": "TextViewer", // displayed with highlight.js , with option to download
"ng:b": "TextViewer", // displayed with highlight.js , with option to download and copy paste
"ng:o": ["post:asciidoc", "service*", "contract", "query:sparql*","query:graphql","doc:diagram:mermaid","doc:diagram:graphviz","doc:diagram:flowchart",
"doc:diagram:sequence","doc:diagram:markmap","doc:diagram:mymind","doc:music*", "doc:maths", "doc:chemistry", "doc:ancientscript", "doc:braille", "media:subtitle"],
"ng:w": [],
},
"n:g:z:crdt_source_viewer:rdf": {
"ng:n": "RDF source",
"ng:a": "See the source graph of this document, in RDF (turtle)",
"ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:rdf",
"ng:b": "TurtleViewer", //, with option to download
"ng:o": ["data:graph"],
"ng:w": [],
},
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",
@ -375,6 +394,7 @@ export const official_apps = {
"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",
@ -385,6 +405,7 @@ export const official_apps = {
"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",
@ -413,6 +434,7 @@ export const official_apps = {
"ng:b": "TextViewer",
"ng:o": ["post:text"],
"ng:w": [],
implemented: true,
},
"n:g:z:pre": {
"ng:n": "Source Code",
@ -423,6 +445,7 @@ export const official_apps = {
"ng:b": "TextViewer", // displayed with highlight.js
"ng:o": ["code*"],
"ng:w": [],
implemented: true,
},
"n:g:z:pad": {
"ng:n": "Pad",
@ -481,6 +504,7 @@ export const official_apps = {
"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"],
},

@ -30,12 +30,12 @@ export default defineConfig(async () => {
"@codemirror/lang-javascript", "@codemirror/lang-rust", "@replit/codemirror-lang-svelte", "yjs", "y-codemirror.next", "svelte-codemirror-editor",
"prosemirror-svelte", "prosemirror-svelte/state", "prosemirror-svelte/helpers", "y-prosemirror", "prosemirror-state", "prosemirror-model", "prosemirror-view", "y-protocols",
"@milkdown/core", "@milkdown/ctx", "@milkdown/prose", "@milkdown/transformer", "@milkdown/preset-commonmark", "@milkdown/theme-nord", "@milkdown/plugin-collab",
"svelte-highlight", "svelte-highlight/languages/typescript", "svelte-highlight/languages/javascript", "svelte-highlight/languages/rust", "@milkdown/preset-gfm",
"svelte-highlight", "svelte-highlight/languages/typescript", "svelte-highlight/languages/markdown", "svelte-highlight/languages/xml", "svelte-highlight/languages/javascript", "svelte-highlight/languages/rust", "@milkdown/preset-gfm",
"@milkdown-lab/plugin-split-editing", "@milkdown/plugin-slash", "@milkdown/utils", "@milkdown/plugin-prism", "@milkdown/plugin-emoji", "@milkdown/plugin-math", "@milkdown/plugin-indent",
"svelte-jsoneditor", "@automerge/automerge/next", "@automerge/automerge/slim"],
include: ["debug","extend","highlight.js","highlight.js/lib/core","lodash.debounce","@sindresorhus/is","char-regex","emojilib","skin-tone",
'immutable-json-patch', ]
'immutable-json-patch', "xml-beautifier" ]
},
worker: {

@ -101,6 +101,7 @@ importers:
vite-plugin-svelte-svg: ^2.2.1
vite-plugin-top-level-await: ^1.3.1
vite-plugin-wasm: ^3.2.2
xml-beautifier: ^0.5.0
y-codemirror.next: ^0.3.5
y-prosemirror: ^1.2.10
y-protocols: ^1.0.1
@ -172,6 +173,7 @@ importers:
svelte-jsoneditor: 0.23.8_@lezer+common@1.2.1
svelte-spa-router: 3.3.0
vite-plugin-top-level-await: 1.3.1_vite@4.3.9
xml-beautifier: 0.5.0
y-codemirror.next: 0.3.5_2derscuhaavtzv2sogf3enfvaa
y-prosemirror: 1.2.10_t5dsb3fc2figoeqqliqhb3exne
y-protocols: 1.0.6_yjs@13.6.18
@ -3720,6 +3722,11 @@ packages:
- supports-color