diff --git a/Cargo.lock b/Cargo.lock
index a327cd0..232990f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -473,9 +473,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "automerge"
-version = "0.5.9"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93b5e6ed2097a1e55cce3128d64c909cdb42c800d4880411c7382f3dfa2c808d"
+checksum = "b0b670b68c38e4042ea4826415f0f8101428810bce821d215e271966b24abac4"
dependencies = [
"flate2",
"fxhash",
diff --git a/ng-app/package.json b/ng-app/package.json
index 35da6ff..bebf180 100644
--- a/ng-app/package.json
+++ b/ng-app/package.json
@@ -16,6 +16,7 @@
"tauri": "tauri"
},
"dependencies": {
+ "@automerge/automerge": "^2.2.8",
"@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0",
"@codemirror/lang-css": "^6.0.1",
diff --git a/ng-app/src/apps/AutomergeEditor.svelte b/ng-app/src/apps/AutomergeEditor.svelte
new file mode 100644
index 0000000..074d2a0
--- /dev/null
+++ b/ng-app/src/apps/AutomergeEditor.svelte
@@ -0,0 +1,130 @@
+
+
+
+ {#if safari_error}
+ {$t("errors.no_wasm_on_old_safari")}
+ {:else}
+
+ {/if}
+
\ No newline at end of file
diff --git a/ng-app/src/apps/AutomergeJsonSource.svelte b/ng-app/src/apps/AutomergeJsonSource.svelte
new file mode 100644
index 0000000..67d3171
--- /dev/null
+++ b/ng-app/src/apps/AutomergeJsonSource.svelte
@@ -0,0 +1,74 @@
+
+
+
+
+{#if safari_error}
+ {$t("errors.no_wasm_on_old_safari")}
+{:else if source}
+
+
+
+{/if}
diff --git a/ng-app/src/apps/AutomergeViewer.svelte b/ng-app/src/apps/AutomergeViewer.svelte
new file mode 100644
index 0000000..ec620c6
--- /dev/null
+++ b/ng-app/src/apps/AutomergeViewer.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/ContainerView.svelte b/ng-app/src/apps/ContainerView.svelte
index a25bcef..eea99d8 100644
--- a/ng-app/src/apps/ContainerView.svelte
+++ b/ng-app/src/apps/ContainerView.svelte
@@ -34,6 +34,7 @@
ret.push({nuri,hash});
}
}
+ ret.sort((a, b) => a.hash.localeCompare(b.hash));
return ret;
}
diff --git a/ng-app/src/apps/JsonSource.svelte b/ng-app/src/apps/JsonSource.svelte
deleted file mode 100644
index e69de29..0000000
diff --git a/ng-app/src/apps/MilkDownEditor.svelte b/ng-app/src/apps/MilkDownEditor.svelte
index a506a9d..1edba40 100644
--- a/ng-app/src/apps/MilkDownEditor.svelte
+++ b/ng-app/src/apps/MilkDownEditor.svelte
@@ -83,11 +83,11 @@
return {
update: (updatedView, prevState) => {
- provider.update(updatedView, prevState);
+ provider.update(updatedView, prevState);
},
destroy: () => {
- provider.destroy();
- content.remove();
+ provider.destroy();
+ content.remove();
}
}
}
diff --git a/ng-app/src/apps/YArrayJsonSource.svelte b/ng-app/src/apps/YArrayJsonSource.svelte
deleted file mode 100644
index e69de29..0000000
diff --git a/ng-app/src/apps/automerge/ABoolean.svelte b/ng-app/src/apps/automerge/ABoolean.svelte
new file mode 100644
index 0000000..172f56e
--- /dev/null
+++ b/ng-app/src/apps/automerge/ABoolean.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/ACounter.svelte b/ng-app/src/apps/automerge/ACounter.svelte
new file mode 100644
index 0000000..e27ba69
--- /dev/null
+++ b/ng-app/src/apps/automerge/ACounter.svelte
@@ -0,0 +1,65 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/ADate.svelte b/ng-app/src/apps/automerge/ADate.svelte
new file mode 100644
index 0000000..b888df2
--- /dev/null
+++ b/ng-app/src/apps/automerge/ADate.svelte
@@ -0,0 +1,73 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/AList.svelte b/ng-app/src/apps/automerge/AList.svelte
new file mode 100644
index 0000000..77674ff
--- /dev/null
+++ b/ng-app/src/apps/automerge/AList.svelte
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+ List |
+
+ {#if !readonly}
+ Push entry at the end of list:
+
+
+ {/if}
+ |
+
+
+
+ {#each props as prop}
+
+ {prop[0]} |
+
+
+ updateScalar(prop[0],event)} />
+ |
+
+
+ {/each}
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/AMap.svelte b/ng-app/src/apps/automerge/AMap.svelte
new file mode 100644
index 0000000..6fea690
--- /dev/null
+++ b/ng-app/src/apps/automerge/AMap.svelte
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/ANumber.svelte b/ng-app/src/apps/automerge/ANumber.svelte
new file mode 100644
index 0000000..0fbe7b3
--- /dev/null
+++ b/ng-app/src/apps/automerge/ANumber.svelte
@@ -0,0 +1,42 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/AString.svelte b/ng-app/src/apps/automerge/AString.svelte
new file mode 100644
index 0000000..c40ae16
--- /dev/null
+++ b/ng-app/src/apps/automerge/AString.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/AValue.svelte b/ng-app/src/apps/automerge/AValue.svelte
new file mode 100644
index 0000000..da2d764
--- /dev/null
+++ b/ng-app/src/apps/automerge/AValue.svelte
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+{#if type==="map"}
+
+{:else if type==="list"}
+
+{:else if type==="counter"}
+ {#if !readonly}
+
+ {:else}
+ : {value}
+ {/if}
+{:else if type==="text"}
+ {#if !readonly}
+
+ {:else}
+ : {value}
+ {/if}
+{:else if type==="boolean"}
+ {#if !readonly}
+
+ {:else}
+ : {value}
+ {/if}
+{:else if type==="number"}
+ {#if !readonly}
+
+ {:else}
+ : {value}
+ {/if}
+{:else if value?.toDateString || type==="timestamp"}
+ {#if !readonly}
+
+ {:else}
+ : {render_date(value)}
+ {/if}
+{:else}
+ : {value}
+{/if}
\ No newline at end of file
diff --git a/ng-app/src/apps/automerge/utils.ts b/ng-app/src/apps/automerge/utils.ts
new file mode 100644
index 0000000..582d433
--- /dev/null
+++ b/ng-app/src/apps/automerge/utils.ts
@@ -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
+//
+// or the MIT license ,
+// 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);
+ }
+}
\ No newline at end of file
diff --git a/ng-app/src/classes.ts b/ng-app/src/classes.ts
index f42ab99..c05d0d8 100644
--- a/ng-app/src/classes.ts
+++ b/ng-app/src/classes.ts
@@ -286,15 +286,15 @@ export const official_classes = {
},
"data:json": {
"ng:crdt": "Automerge",
- "ng:n": "JSON",
- "ng:a": "JSON Data CRDT",
+ "ng:n": "JSON (Automerge)",
+ "ng:a": "Automerge JSON Data CRDT",
"ng:o": "n:g:z:json_automerge_viewer", // default viewer
"ng:w": "n:g:z:json_automerge_editor", // default editor
"ng:compat": ["file:iana:application:json", "code:json"],
},
"data:array": {
"ng:crdt": "YArray",
- "ng:n": "Array",
+ "ng:n": "Array (Yjs)",
"ng:a": "Yjs Array CRDT",
"ng:o": "n:g:z:json_yarray_viewer", // default viewer
"ng:w": "n:g:z:json_yarray_editor", // default editor
@@ -302,16 +302,16 @@ export const official_classes = {
},
"data:map": {
"ng:crdt": "YMap",
- "ng:n": "Object",
- "ng:a": "Yjs Map CRDT",
+ "ng:n": "Object (Yjs)",
+ "ng:a": "Yjs Map CRDT (JSON Object)",
"ng:o": "n:g:z:json_ymap_viewer", // default viewer
"ng:w": "n:g:z:json_ymap_editor", // default editor
"ng:compat": ["file:iana:application:json", "code:json"],
},
"data:xml": {
"ng:crdt": "YXml",
- "ng:n": "XML",
- "ng:a": "XML Data CRDT",
+ "ng:n": "XML (Yjs)",
+ "ng:a": "Yjs XML CRDT",
"ng:compat": ["file:iana:text:xml","file:iana:application:xml", "code:xml"],
},
"data:table": {
diff --git a/ng-app/src/lib/icons/DataClassIcon.svelte b/ng-app/src/lib/icons/DataClassIcon.svelte
index d2643c4..58cbfb8 100644
--- a/ng-app/src/lib/icons/DataClassIcon.svelte
+++ b/ng-app/src/lib/icons/DataClassIcon.svelte
@@ -104,6 +104,8 @@
"query:graphql": GraphQLIcon,
"data:graph": Sun,
"data:json": JsonIcon,
+ "data:map": JsonIcon,
+ "data:array": JsonIcon,
"data:table": TableCells,
"data:collection": ListBullet,
"data:container": Square3Stack3d,
diff --git a/ng-app/src/locales/en.json b/ng-app/src/locales/en.json
index 6ae836a..3413c66 100644
--- a/ng-app/src/locales/en.json
+++ b/ng-app/src/locales/en.json
@@ -596,7 +596,8 @@
"camera_unavailable": "Camera is unavailable.",
"ServerAlreadyRunningInOtherProcess": "App is already running on this device. Check it and close it.",
"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": {
"stopped": "Stopped",
diff --git a/ng-app/src/zeras.ts b/ng-app/src/zeras.ts
index 8d115d0..0d73669 100644
--- a/ng-app/src/zeras.ts
+++ b/ng-app/src/zeras.ts
@@ -57,6 +57,7 @@ export const official_apps = {
"ng:g": "n:g:z:json_automerge_editor",
"ng:b": "AutomergeEditor",
"ng:w": ["data:json"],
+ "full_width": true,
},
"n:g:z:json_ymap_editor": {
"ng:n": "JSON Editor",
@@ -141,6 +142,7 @@ export const official_apps = {
"ng:g": "n:g:z:json_automerge_viewer",
"ng:b": "AutomergeViewer",
"ng:o": ["data:json"],
+ "full_width": true,
},
"n:g:z:triple_viewer": {
"ng:n": "Graph Triples",
@@ -318,7 +320,7 @@ 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: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:w": [],
},
diff --git a/ng-app/vite.config.ts b/ng-app/vite.config.ts
index 22c4c8a..e81e595 100644
--- a/ng-app/vite.config.ts
+++ b/ng-app/vite.config.ts
@@ -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",
"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",
- "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",
'immutable-json-patch', ]
diff --git a/ng-net/src/app_protocol.rs b/ng-net/src/app_protocol.rs
index b9f61d8..a2a6aaf 100644
--- a/ng-net/src/app_protocol.rs
+++ b/ng-net/src/app_protocol.rs
@@ -636,7 +636,7 @@ pub enum DiscreteUpdate {
YXml(Vec),
#[serde(with = "serde_bytes")]
YText(Vec),
- /// An automerge::Patch
+ /// An automerge::Change.raw_bytes()
#[serde(with = "serde_bytes")]
Automerge(Vec),
}
@@ -752,7 +752,7 @@ pub enum DiscretePatch {
YXml(Vec),
#[serde(with = "serde_bytes")]
YText(Vec),
- /// An automerge::Patch
+ /// An automerge::Change.raw_bytes() or a concatenation of several.
#[serde(with = "serde_bytes")]
Automerge(Vec),
}
diff --git a/ng-repo/src/errors.rs b/ng-repo/src/errors.rs
index 9f4743e..2e3b990 100644
--- a/ng-repo/src/errors.rs
+++ b/ng-repo/src/errors.rs
@@ -369,6 +369,7 @@ pub enum VerifierError {
CannotRemoveTriplesWhenNewBranch,
PermissionDenied,
YrsError(String),
+ AutomergeError(String),
InvalidNuri,
}
diff --git a/ng-verifier/Cargo.toml b/ng-verifier/Cargo.toml
index e3452f0..fb2901e 100644
--- a/ng-verifier/Cargo.toml
+++ b/ng-verifier/Cargo.toml
@@ -29,7 +29,7 @@ either = "1.8.1"
futures = "0.3.24"
async-trait = "0.1.64"
async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] }
-automerge = "0.5.9"
+automerge = "0.5.11"
yrs = "0.19.2"
sbbf-rs-safe = "0.3.2"
ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" }
diff --git a/ng-verifier/src/commits/transaction.rs b/ng-verifier/src/commits/transaction.rs
index c0d42bf..17b2a39 100644
--- a/ng-verifier/src/commits/transaction.rs
+++ b/ng-verifier/src/commits/transaction.rs
@@ -183,7 +183,12 @@ impl Verifier {
{
match crdt {
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::YMap(_)
@@ -195,7 +200,7 @@ impl Verifier {
let update = yrs::Update::decode_v1(&state)
.map_err(|e| VerifierError::YrsError(e.to_string()))?;
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()))?;
txn.apply_update(update);
txn.commit();
diff --git a/ng-verifier/src/types.rs b/ng-verifier/src/types.rs
index 2e1f6a4..17704d9 100644
--- a/ng-verifier/src/types.rs
+++ b/ng-verifier/src/types.rs
@@ -75,6 +75,15 @@ impl DiscreteTransaction {
| 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)]
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e2ae34a..79664c9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,6 +7,7 @@ importers:
ng-app:
specifiers:
+ '@automerge/automerge': ^2.2.8
'@codemirror/autocomplete': ^6.17.0
'@codemirror/commands': ^6.6.0
'@codemirror/lang-css': ^6.0.1
@@ -105,6 +106,7 @@ importers:
y-protocols: ^1.0.1
yjs: ^13.6.18
dependencies:
+ '@automerge/automerge': 2.2.8
'@codemirror/autocomplete': 6.17.0_y4udqqf6vpekb2i4jypui2pd5e
'@codemirror/commands': 6.6.0
'@codemirror/lang-css': 6.2.1_@codemirror+view@6.29.1
@@ -250,6 +252,12 @@ packages:
'@jridgewell/trace-mapping': 0.3.25
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:
resolution: {integrity: sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==}
peerDependencies: