AutomergeEditor and Viewer and JsonSource

master
Niko PLP 5 months ago
parent ea38ac7d2a
commit 456ddf7e30
  1. 4
      Cargo.lock
  2. 1
      ng-app/package.json
  3. 130
      ng-app/src/apps/AutomergeEditor.svelte
  4. 74
      ng-app/src/apps/AutomergeJsonSource.svelte
  5. 19
      ng-app/src/apps/AutomergeViewer.svelte
  6. 1
      ng-app/src/apps/ContainerView.svelte
  7. 0
      ng-app/src/apps/JsonSource.svelte
  8. 6
      ng-app/src/apps/MilkDownEditor.svelte
  9. 0
      ng-app/src/apps/YArrayJsonSource.svelte
  10. 36
      ng-app/src/apps/automerge/ABoolean.svelte
  11. 65
      ng-app/src/apps/automerge/ACounter.svelte
  12. 73
      ng-app/src/apps/automerge/ADate.svelte
  13. 118
      ng-app/src/apps/automerge/AList.svelte
  14. 131
      ng-app/src/apps/automerge/AMap.svelte
  15. 42
      ng-app/src/apps/automerge/ANumber.svelte
  16. 32
      ng-app/src/apps/automerge/AString.svelte
  17. 87
      ng-app/src/apps/automerge/AValue.svelte
  18. 81
      ng-app/src/apps/automerge/utils.ts
  19. 14
      ng-app/src/classes.ts
  20. 2
      ng-app/src/lib/icons/DataClassIcon.svelte
  21. 3
      ng-app/src/locales/en.json
  22. 4
      ng-app/src/zeras.ts
  23. 2
      ng-app/vite.config.ts
  24. 4
      ng-net/src/app_protocol.rs
  25. 1
      ng-repo/src/errors.rs
  26. 2
      ng-verifier/Cargo.toml
  27. 9
      ng-verifier/src/commits/transaction.rs
  28. 9
      ng-verifier/src/types.rs
  29. 8
      pnpm-lock.yaml

4
Cargo.lock generated

@ -473,9 +473,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "automerge" name = "automerge"
version = "0.5.9" version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93b5e6ed2097a1e55cce3128d64c909cdb42c800d4880411c7382f3dfa2c808d" checksum = "b0b670b68c38e4042ea4826415f0f8101428810bce821d215e271966b24abac4"
dependencies = [ dependencies = [
"flate2", "flate2",
"fxhash", "fxhash",

@ -16,6 +16,7 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@automerge/automerge": "^2.2.8",
"@codemirror/autocomplete": "^6.17.0", "@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0", "@codemirror/commands": "^6.6.0",
"@codemirror/lang-css": "^6.0.1", "@codemirror/lang-css": "^6.0.1",

@ -0,0 +1,130 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { onMount, tick, onDestroy } from "svelte";
import { Button, Progressbar, Spinner, Alert } from "flowbite-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
} from "../tab";
import { t } from "svelte-i18n";
import wasmUrl from "@automerge/automerge/automerge.wasm?url";
import { next as A } from "@automerge/automerge/slim";
import AMap from "./automerge/AMap.svelte";
export let commits = {};
export let readonly = false;
let doc = {};
let safari_error = false;
function concatenate(uint8arrays) {
const totalLength = uint8arrays.reduce(
(total, uint8array) => total + uint8array.byteLength,
0
);
const result = new Uint8Array(totalLength);
let offset = 0;
uint8arrays.forEach((uint8array) => {
result.set(uint8array, offset);
offset += uint8array.byteLength;
});
return result;
}
let root_proxy;
onMount(async ()=>{
try {
await A.initializeWasm(wasmUrl);
} catch (e) {
toast_error($t("errors.no_wasm_on_old_safari"));
safari_error = true;
return;
}
doc = A.init();
if (!readonly) {
cur_tab_register_on_save(async (updates)=>{
let update = concatenate(updates);
await live_discrete_update(update, "Automerge", commits.heads);
});
}
let history = commits.discrete?.registerOnUpdate((update) => {
doc = A.loadIncremental(doc, update.Automerge);
});
for (const h of history) {
doc = A.loadIncremental(doc, h.Automerge);
}
A.change(doc, (d) => {
root_proxy = d;
});
});
async function update(event) {
//console.log("got update", event)
doc = event.detail.d;
try {
await discrete_update(event.detail.u, "Automerge", commits.heads);
} catch (e){
toast_error(display_error(e));
}
}
onDestroy(async ()=>{
commits.discrete?.deregisterOnUpdate();
if (!readonly) {
await cur_tab_deregister_on_save();
}
});
async function updateText(event) {
doc = A.change(doc, (d) => {
A.updateText(d, event.detail.p, event.detail.s)
});
let update = A.getLastLocalChange(doc);
try {
await discrete_update(update, "Automerge", commits.heads);
} catch (e){
toast_error(display_error(e));
}
}
</script>
{#if safari_error}
<Alert class="m-2" color="red">{$t("errors.no_wasm_on_old_safari")}</Alert>
{:else}
<div class="grow mb-20" style="min-height:300px;">
<AMap {readonly} value={doc} {doc} on:update={update} on:updateText={updateText} proxy={root_proxy}/>
</div>
{/if}
<style>
</style>

@ -0,0 +1,74 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { onMount, tick, onDestroy } from "svelte";
import { Button, Progressbar, Spinner, Alert } from "flowbite-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
} from "../tab";
import { t } from "svelte-i18n";
import wasmUrl from "@automerge/automerge/automerge.wasm?url";
import { next as A } from "@automerge/automerge/slim";
import Highlight, { LineNumbers } from "svelte-highlight";
import json from "svelte-highlight/languages/json";
import "svelte-highlight/styles/github.css";
export let commits = {};
let doc = {};
let source = "";
let safari_error = false;
onMount(async ()=>{
try {
await A.initializeWasm(wasmUrl);
} catch (e) {
toast_error($t("errors.no_wasm_on_old_safari"));
safari_error = true;
return;
}
doc = A.init();
let history = commits.discrete?.registerOnUpdate((update) => {
doc = A.loadIncremental(doc, update.Automerge);
source = JSON.stringify(doc,null , 4 );
});
for (const h of history) {
doc = A.loadIncremental(doc, h.Automerge);
}
source = JSON.stringify(doc,null , 4 );
});
onDestroy(async ()=>{
commits.discrete?.deregisterOnUpdate();
});
</script>
{#if safari_error}
<Alert class="m-2" color="red">{$t("errors.no_wasm_on_old_safari")}</Alert>
{:else if source}
<Highlight language={json} code={source} class="mb-10" let:highlighted >
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{/if}

@ -0,0 +1,19 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import AutomergeEditor from "./AutomergeEditor.svelte";
export let commits = {};
</script>
<AutomergeEditor {commits} readonly={true}/>

@ -34,6 +34,7 @@
ret.push({nuri,hash}); ret.push({nuri,hash});
} }
} }
ret.sort((a, b) => a.hash.localeCompare(b.hash));
return ret; return ret;
} }

@ -83,11 +83,11 @@
return { return {
update: (updatedView, prevState) => { update: (updatedView, prevState) => {
provider.update(updatedView, prevState); provider.update(updatedView, prevState);
}, },
destroy: () => { destroy: () => {
provider.destroy(); provider.destroy();
content.remove(); content.remove();
} }
} }
} }

@ -0,0 +1,36 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { Toggle } from 'flowbite-svelte';
const dispatch = createEventDispatcher();
export let value;
function update() {
temp_val = value;
}
let temp_val;
$: value, update();
const change = (event) => {
dispatch('updateScalar', {
v: temp_val,
});
}
</script>
<Toggle bind:checked={temp_val} on:change={change} />

@ -0,0 +1,65 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
import { next as A } from "@automerge/automerge/slim";
export let value;
export let proxy;
export let doc;
async function increment(val) {
doc = A.change(doc, (d) => {
proxy.increment(val);
});
let update = A.getLastLocalChange(doc);
dispatch('update', {
u: update,
d: doc,
});
}
function update() {
temp_val = value.value;
previous_val = value.value;
}
let temp_val;
let previous_val;
$: value, update();
const inc = async () => { temp_val+=1; await increment(1) }
const dec = async () => { temp_val-=1; await increment(-1) }
const change = async () => { let diff = Math.round(temp_val - previous_val); if (diff !==0) await increment(diff); else temp_val = previous_val; }
</script>
<div class="relative flex items-center max-w-[8rem]">
<button type="button" on:click={dec} class="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-s-lg p-2 h-7 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none">
<svg class="w-3 h-3 text-gray-900 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/>
</svg>
</button>
<input bind:value={temp_val} on:change={change} type="text" id="quantity-input" data-input-counter aria-describedby="helper-text-explanation" style="max-width:70px;" class="bg-gray-50 border-x-0 border-gray-300 h-7 text-center text-gray-900 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full py-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" placeholder="999" required />
<button type="button" on:click={inc} class="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-e-lg p-2 h-7 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none">
<svg class="w-3 h-3 text-gray-900 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/>
</svg>
</button>
</div>

@ -0,0 +1,73 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { onMount } from "svelte";
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
import "flowbite/dist/flowbite.min.js"
export let value;
function update() {
time = value.toLocaleTimeString([],{
hour: "2-digit",
minute: "2-digit"
});
date = value.toLocaleDateString([],{
year: "numeric",
month: "numeric",
day: "numeric",
});
}
let time;
let date;
$: value, update();
const change = (event) => {
let newval = new Date(date.split('/').reverse().join('/')+" "+time);
console.log(time, date, newval)
dispatch('updateScalar', {
v: newval,
});
}
</script>
<div class="flex flex-wrap">
<div class="relative" style="max-width: 129px;">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 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 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"/>
</svg>
</div>
<input type="text" style="max-width: 129px;cursor:pointer;" on:change={change} bind:value={date} datepicker-format="dd/mm/yyyy"
class="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 ps-10 p-2.5 px-2 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" placeholder="Select date">
</div>
<div class="relative" style="max-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"/>
</svg>
</div>
<input bind:value={time} on:change={change} type="time" class="bg-gray-50 border leading-none 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" />
</div>
</div>

@ -0,0 +1,118 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { new_value, find_type, new_prop_types } from "./utils";
import AValue from "./AValue.svelte";
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
import { next as A } from "@automerge/automerge/slim";
export let value;
export let proxy;
export let doc;
export let path = undefined;
export let readonly = false;
let props = [];
$: props = value.map((v,i)=> {
let ar = [i];
ar.push(v);
let type = find_type(v);
ar.push(type);
const with_proxy = type == "counter" || type == "map" || type == "list" ;
if (with_proxy) {
ar.push(proxy[i]);
}
return ar;
});
function add_prop() {
doc = A.change(doc, (d) => {
proxy.push(new_value(new_prop_type_selected))
});
let update = A.getLastLocalChange(doc);
dispatch('update', {
u: update,
d: doc,
});
}
let new_prop_type_selected = 'text';
function updateText(event) {
if (path !== undefined) event.detail.p.unshift(path);
dispatch('updateText', {
s: event.detail.s,
p: event.detail.p,
});
}
function updateScalar(prop, event) {
doc = A.change(doc, (d) => {
proxy[prop] = event.detail.v;
});
let update = A.getLastLocalChange(doc);
dispatch('update', {
u: update,
d: doc,
});
}
</script>
<table class="border-collapse border border-slate-400">
<thead>
<tr class="bg-slate-100">
<th>List</th>
<th class="text-sm">
{#if !readonly}
<span class="ml-2">Push entry at the end of list:</span>
<select bind:value={new_prop_type_selected}>
{#each new_prop_types as value}<option value={value.value}>{value.name}</option>{/each}
</select>
<button on:click={add_prop}>Add</button>
{/if}
</th>
</tr>
</thead>
<tbody>
{#each props as prop}
<tr>
<td>{prop[0]}</td>
<!-- <td>{prop[2]}</td> -->
<td>
<AValue {readonly} type={prop[2]} value={prop[1]} {doc} on:updateText={updateText} on:update proxy={prop[3]} path={prop[0]} on:updateScalar={(event)=>updateScalar(prop[0],event)} />
</td>
<!-- <td>{prop[3]?.constructor.name || ""}</td> -->
</tr>
{/each}
</tbody>
</table>
<style>
td {
padding:5px;
}
tr {
border-bottom: 1px;
border-style: dashed;
border-top: none;
}
</style>

@ -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.
-->
<script lang="ts">
import AValue from "./AValue.svelte";
import { createEventDispatcher } from 'svelte';
import { new_value, find_type, new_prop_types } from "./utils";
const dispatch = createEventDispatcher();
import { next as A } from "@automerge/automerge/slim";
export let value;
export let proxy;
export let doc;
export let path = undefined;
export let readonly = false;
let props = [];
$: props = Object.entries(value).map((ar)=> {
let type = find_type(ar[1]);
ar.push(type);
const with_proxy = type == "counter" || type == "map" || type == "list" ;
if (with_proxy) {
ar.push(proxy[ar[0]]);
}
return ar;
});
let new_prop = "";
function add_prop() {
if (new_prop.trim().length > 0) {
doc = A.change(doc, (d) => {
proxy[new_prop] = new_value(new_prop_type_selected);
});
let update = A.getLastLocalChange(doc);
dispatch('update', {
u: update,
d: doc,
});
new_prop = "";
}
}
let new_prop_type_selected = 'text';
function updateText(event) {
if (path!== undefined) event.detail.p.unshift(path);
dispatch('updateText', {
s: event.detail.s,
p: event.detail.p,
});
}
function updateScalar(prop, event) {
doc = A.change(doc, (d) => {
proxy[prop] = event.detail.v;
});
let update = A.getLastLocalChange(doc);
dispatch('update', {
u: update,
d: doc,
});
}
</script>
<table class="border-collapse border border-slate-400">
<thead>
<tr class="bg-slate-100">
<th>Map</th>
<th class="text-sm">
{#if !readonly}
<span>Add property:</span>
<input placeholder="Enter the name of property" bind:value={new_prop} class="prop-input"/>
<select bind:value={new_prop_type_selected}>
{#each new_prop_types as value}<option value={value.value}>{value.name}</option>{/each}
</select>
<button on:click={add_prop}>Add</button>
{/if}
</th>
</tr>
</thead>
<tbody>
{#each props as prop}
<tr>
<td>{prop[0]}</td>
<!-- <td>{prop[2]}</td> -->
<td>
<AValue {readonly} type={prop[2]} value={prop[1]} {doc} on:updateText={updateText} on:update proxy={prop[3]} path={prop[0]} on:updateScalar={(event)=>updateScalar(prop[0],event)} />
</td>
<!-- <td>{prop[3]?.constructor.name || ""}</td> -->
</tr>
{/each}
</tbody>
</table>
<style>
td {
padding:5px;
min-width:80px;
}
tr {
border-bottom: 1px;
border-style: dashed;
border-top: none;
}
@screen xs {
.prop-input {
min-width: 250px;
}
}
</style>

@ -0,0 +1,42 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { Input } from 'flowbite-svelte';
const dispatch = createEventDispatcher();
export let value;
function update() {
temp_val = value;
previous_val = value;
}
let temp_val;
let previous_val;
$: value, update();
const change = (event) => {
let newval = parseFloat(event.target.value.replace(",", "."));
//console.log(previous_val, temp_val, newval)
if (isNaN(newval) || previous_val === newval) return;
dispatch('updateScalar', {
v: newval,
});
}
</script>
<Input style="max-width: 129px;" bind:value={temp_val} on:change={change} on:keyup={change} type="number" />

@ -0,0 +1,32 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { Input } from 'flowbite-svelte';
const dispatch = createEventDispatcher();
export let value;
export let path;
const change = (event) => {
dispatch('updateText', {
s: event.target.value,
p: [path]
});
}
</script>
<Input bind:value={value} on:keyup={change} type="text" placeholder="Enter some text" />

@ -0,0 +1,87 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import AMap from "./AMap.svelte";
import AList from "./AList.svelte";
import ACounter from "./ACounter.svelte";
import AString from "./AString.svelte";
import ABoolean from "./ABoolean.svelte";
import ANumber from "./ANumber.svelte";
import ADate from "./ADate.svelte";
export let value;
export let type;
export let doc;
export let proxy;
export let path;
export let readonly = false;
function render_date(value) {
let time = value.toLocaleTimeString([],{
hour: "2-digit",
minute: "2-digit"
});
let date = value.toLocaleDateString([],{
year: "numeric",
month: "numeric",
day: "numeric",
});
return `${date} ${time}`;
}
</script>
{#if type==="map"}
<AMap {readonly} {value} {doc} on:updateText on:update {proxy} {path}/>
{:else if type==="list"}
<AList {readonly} {value} {doc} on:updateText on:update {proxy} {path}/>
{:else if type==="counter"}
{#if !readonly}
<ACounter {value} {doc} on:update {proxy} />
{:else}
: {value}
{/if}
{:else if type==="text"}
{#if !readonly}
<AString {value} on:updateText {path}/>
{:else}
: {value}
{/if}
{:else if type==="boolean"}
{#if !readonly}
<ABoolean {value} on:updateScalar/>
{:else}
: {value}
{/if}
{:else if type==="number"}
{#if !readonly}
<ANumber {value} on:updateScalar/>
{:else}
: {value}
{/if}
{:else if value?.toDateString || type==="timestamp"}
{#if !readonly}
<ADate {value} on:updateScalar/>
{:else}
: {render_date(value)}
{/if}
{:else}
: {value}
{/if}

@ -0,0 +1,81 @@
// 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.
import { next as A } from "@automerge/automerge/slim";
const UINT = Symbol.for("_am_uint")
const INT = Symbol.for("_am_int")
const F64 = Symbol.for("_am_f64")
const COUNTER = Symbol.for("_am_counter")
const TEXT = Symbol.for("_am_text")
export function find_type(value) {
switch (typeof value) {
case "object":
if (value == null) {
return "null"
} else if (value[UINT]) {
return "uint"
} else if (value[INT]) {
return "number"
} else if (value[F64]) {
return "number"
} else if (value[COUNTER]) {
return "counter"
} else if (value instanceof Date) {
return "timestamp"
} else if (value instanceof A.RawString) {
return "str"
} else if (value instanceof Text) {
return "text"
} else if (value instanceof Uint8Array) {
return "bytes"
} else if (value instanceof Array) {
return "list"
} else if (Object.getPrototypeOf(value) === Object.getPrototypeOf({})) {
return "map"
}
case "boolean":
return "boolean"
case "number":
if (Number.isInteger(value)) {
return "number"
} else {
return "number"
}
case "string":
return "text"
}
}
export const new_prop_types = [
{value:'text',name:"text"},
{value:'number',name:"number"},
{value:'counter',name:"counter"},
{value:'boolean',name:"boolean"},
{value:'null',name:"null"},
{value:'timestamp',name:"timestamp"},
{value:'map',name:"map"},
{value:'list',name:"list"},
{value:'bytes',name:"bytes"}
];
export function new_value(new_prop_type_selected) {
switch (new_prop_type_selected) {
case 'text': return '';
case 'map': return {};
case 'list': return [];
case 'counter': return new A.Counter();
case 'number': return 0;
case 'boolean': return false;
case 'null': return null;
case 'timestamp': return new Date();
case 'bytes': return new Uint8Array(0);
}
}

@ -286,15 +286,15 @@ export const official_classes = {
}, },
"data:json": { "data:json": {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "JSON", "ng:n": "JSON (Automerge)",
"ng:a": "JSON Data CRDT", "ng:a": "Automerge JSON Data CRDT",
"ng:o": "n:g:z:json_automerge_viewer", // default viewer "ng:o": "n:g:z:json_automerge_viewer", // default viewer
"ng:w": "n:g:z:json_automerge_editor", // default editor "ng:w": "n:g:z:json_automerge_editor", // default editor
"ng:compat": ["file:iana:application:json", "code:json"], "ng:compat": ["file:iana:application:json", "code:json"],
}, },
"data:array": { "data:array": {
"ng:crdt": "YArray", "ng:crdt": "YArray",
"ng:n": "Array", "ng:n": "Array (Yjs)",
"ng:a": "Yjs Array CRDT", "ng:a": "Yjs Array CRDT",
"ng:o": "n:g:z:json_yarray_viewer", // default viewer "ng:o": "n:g:z:json_yarray_viewer", // default viewer
"ng:w": "n:g:z:json_yarray_editor", // default editor "ng:w": "n:g:z:json_yarray_editor", // default editor
@ -302,16 +302,16 @@ export const official_classes = {
}, },
"data:map": { "data:map": {
"ng:crdt": "YMap", "ng:crdt": "YMap",
"ng:n": "Object", "ng:n": "Object (Yjs)",
"ng:a": "Yjs Map CRDT", "ng:a": "Yjs Map CRDT (JSON Object)",
"ng:o": "n:g:z:json_ymap_viewer", // default viewer "ng:o": "n:g:z:json_ymap_viewer", // default viewer
"ng:w": "n:g:z:json_ymap_editor", // default editor "ng:w": "n:g:z:json_ymap_editor", // default editor
"ng:compat": ["file:iana:application:json", "code:json"], "ng:compat": ["file:iana:application:json", "code:json"],
}, },
"data:xml": { "data:xml": {
"ng:crdt": "YXml", "ng:crdt": "YXml",
"ng:n": "XML", "ng:n": "XML (Yjs)",
"ng:a": "XML Data CRDT", "ng:a": "Yjs XML CRDT",
"ng:compat": ["file:iana:text:xml","file:iana:application:xml", "code:xml"], "ng:compat": ["file:iana:text:xml","file:iana:application:xml", "code:xml"],
}, },
"data:table": { "data:table": {

@ -104,6 +104,8 @@
"query:graphql": GraphQLIcon, "query:graphql": GraphQLIcon,
"data:graph": Sun, "data:graph": Sun,
"data:json": JsonIcon, "data:json": JsonIcon,
"data:map": JsonIcon,
"data:array": JsonIcon,
"data:table": TableCells, "data:table": TableCells,
"data:collection": ListBullet, "data:collection": ListBullet,
"data:container": Square3Stack3d, "data:container": Square3Stack3d,

@ -596,7 +596,8 @@
"camera_unavailable": "Camera is unavailable.", "camera_unavailable": "Camera is unavailable.",
"ServerAlreadyRunningInOtherProcess": "App is already running on this device. Check it and close it.", "ServerAlreadyRunningInOtherProcess": "App is already running on this device. Check it and close it.",
"cannot_load_this_file": "Cannot load this file", "cannot_load_this_file": "Cannot load this file",
"graph_not_found": "Graph not found" "graph_not_found": "Graph not found",
"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"
}, },
"connectivity": { "connectivity": {
"stopped": "Stopped", "stopped": "Stopped",

@ -57,6 +57,7 @@ export const official_apps = {
"ng:g": "n:g:z:json_automerge_editor", "ng:g": "n:g:z:json_automerge_editor",
"ng:b": "AutomergeEditor", "ng:b": "AutomergeEditor",
"ng:w": ["data:json"], "ng:w": ["data:json"],
"full_width": true,
}, },
"n:g:z:json_ymap_editor": { "n:g:z:json_ymap_editor": {
"ng:n": "JSON Editor", "ng:n": "JSON Editor",
@ -141,6 +142,7 @@ export const official_apps = {
"ng:g": "n:g:z:json_automerge_viewer", "ng:g": "n:g:z:json_automerge_viewer",
"ng:b": "AutomergeViewer", "ng:b": "AutomergeViewer",
"ng:o": ["data:json"], "ng:o": ["data:json"],
"full_width": true,
}, },
"n:g:z:triple_viewer": { "n:g:z:triple_viewer": {
"ng:n": "Graph Triples", "ng:n": "Graph Triples",
@ -318,7 +320,7 @@ export const official_apps = {
"ng:c": "app", "ng:c": "app",
"ng:u": "source",//favicon. can be a did:ng:j "ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:json", "ng:g": "n:g:z:crdt_source_viewer:json",
"ng:b": "JsonSource", // displayed with highlight.js , with option to download "ng:b": "AutomergeJsonSource", // displayed with highlight.js , with option to download
"ng:o": ["data:json", "data:table", "doc:diagram:jsmind", "doc:diagram:gantt", "doc:diagram:excalidraw", "doc:viz:*", "doc:chart:*", "prod:cad"], "ng:o": ["data:json", "data:table", "doc:diagram:jsmind", "doc:diagram:gantt", "doc:diagram:excalidraw", "doc:viz:*", "doc:chart:*", "prod:cad"],
"ng:w": [], "ng:w": [],
}, },

@ -18,7 +18,7 @@ export default defineConfig(async () => {
"@milkdown/core", "@milkdown/ctx", "@milkdown/prose", "@milkdown/transformer", "@milkdown/preset-commonmark", "@milkdown/theme-nord", "@milkdown/plugin-collab", "@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/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", "@milkdown-lab/plugin-split-editing", "@milkdown/plugin-slash", "@milkdown/utils", "@milkdown/plugin-prism", "@milkdown/plugin-emoji", "@milkdown/plugin-math", "@milkdown/plugin-indent",
"svelte-jsoneditor"], "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", include: ["debug","extend","highlight.js","highlight.js/lib/core","lodash.debounce","@sindresorhus/is","char-regex","emojilib","skin-tone",
'immutable-json-patch', ] 'immutable-json-patch', ]

@ -636,7 +636,7 @@ pub enum DiscreteUpdate {
YXml(Vec<u8>), YXml(Vec<u8>),
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
YText(Vec<u8>), YText(Vec<u8>),
/// An automerge::Patch /// An automerge::Change.raw_bytes()
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
Automerge(Vec<u8>), Automerge(Vec<u8>),
} }
@ -752,7 +752,7 @@ pub enum DiscretePatch {
YXml(Vec<u8>), YXml(Vec<u8>),
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
YText(Vec<u8>), YText(Vec<u8>),
/// An automerge::Patch /// An automerge::Change.raw_bytes() or a concatenation of several.
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
Automerge(Vec<u8>), Automerge(Vec<u8>),
} }

@ -369,6 +369,7 @@ pub enum VerifierError {
CannotRemoveTriplesWhenNewBranch, CannotRemoveTriplesWhenNewBranch,
PermissionDenied, PermissionDenied,
YrsError(String), YrsError(String),
AutomergeError(String),
InvalidNuri, InvalidNuri,
} }

@ -29,7 +29,7 @@ either = "1.8.1"
futures = "0.3.24" futures = "0.3.24"
async-trait = "0.1.64" async-trait = "0.1.64"
async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] } async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] }
automerge = "0.5.9" automerge = "0.5.11"
yrs = "0.19.2" yrs = "0.19.2"
sbbf-rs-safe = "0.3.2" sbbf-rs-safe = "0.3.2"
ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" } ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" }

@ -183,7 +183,12 @@ impl Verifier {
{ {
match crdt { match crdt {
BranchCrdt::Automerge(_) => { BranchCrdt::Automerge(_) => {
unimplemented!(); let mut doc = automerge::Automerge::load(&state)
.map_err(|e| VerifierError::AutomergeError(e.to_string()))?;
let _ = doc
.load_incremental(patch.as_slice())
.map_err(|e| VerifierError::AutomergeError(e.to_string()))?;
doc.save()
} }
BranchCrdt::YArray(_) BranchCrdt::YArray(_)
| BranchCrdt::YMap(_) | BranchCrdt::YMap(_)
@ -195,7 +200,7 @@ impl Verifier {
let update = yrs::Update::decode_v1(&state) let update = yrs::Update::decode_v1(&state)
.map_err(|e| VerifierError::YrsError(e.to_string()))?; .map_err(|e| VerifierError::YrsError(e.to_string()))?;
txn.apply_update(update); txn.apply_update(update);
let update = yrs::Update::decode_v1(&patch.to_vec()) let update = yrs::Update::decode_v1(patch.as_slice())
.map_err(|e| VerifierError::YrsError(e.to_string()))?; .map_err(|e| VerifierError::YrsError(e.to_string()))?;
txn.apply_update(update); txn.apply_update(update);
txn.commit(); txn.commit();

@ -75,6 +75,15 @@ impl DiscreteTransaction {
| Self::Automerge(v) => v.to_vec(), | Self::Automerge(v) => v.to_vec(),
} }
} }
pub fn as_slice(&self) -> &[u8] {
match self {
Self::YMap(v)
| Self::YArray(v)
| Self::YXml(v)
| Self::YText(v)
| Self::Automerge(v) => v,
}
}
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]

@ -7,6 +7,7 @@ importers:
ng-app: ng-app:
specifiers: specifiers:
'@automerge/automerge': ^2.2.8
'@codemirror/autocomplete': ^6.17.0 '@codemirror/autocomplete': ^6.17.0
'@codemirror/commands': ^6.6.0 '@codemirror/commands': ^6.6.0
'@codemirror/lang-css': ^6.0.1 '@codemirror/lang-css': ^6.0.1
@ -105,6 +106,7 @@ importers:
y-protocols: ^1.0.1 y-protocols: ^1.0.1
yjs: ^13.6.18 yjs: ^13.6.18
dependencies: dependencies:
'@automerge/automerge': 2.2.8
'@codemirror/autocomplete': 6.17.0_y4udqqf6vpekb2i4jypui2pd5e '@codemirror/autocomplete': 6.17.0_y4udqqf6vpekb2i4jypui2pd5e
'@codemirror/commands': 6.6.0 '@codemirror/commands': 6.6.0
'@codemirror/lang-css': 6.2.1_@codemirror+view@6.29.1 '@codemirror/lang-css': 6.2.1_@codemirror+view@6.29.1
@ -250,6 +252,12 @@ packages:
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
dev: false dev: false
/@automerge/automerge/2.2.8:
resolution: {integrity: sha512-kP6Z9lIkNeIGe/jhF8SUQAgUMwVjtXCBL0eZEgQGCoWvPNTPcg8fnQco28XkYBrfkkuqiEUAbdHhJmSp0y79ug==}
dependencies:
uuid: 9.0.0
dev: false
/@codemirror/autocomplete/6.17.0_auqt24wwbhvfpe3kty7jtmzbdy: /@codemirror/autocomplete/6.17.0_auqt24wwbhvfpe3kty7jtmzbdy:
resolution: {integrity: sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==} resolution: {integrity: sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==}
peerDependencies: peerDependencies:

Loading…
Cancel
Save