Compare commits

...

125 Commits

Author SHA1 Message Date
Niko PLP ecf9b766d8 fix error message for security img too short 2 months ago
Niko PLP 9b239229b5 remove viva sponsorship 2 months ago
Niko PLP fcba2e867d remove log 2 months ago
Niko PLP bb518792eb alpha 0.1.1 2 months ago
Niko PLP 254ad56e5b edit title and intro 2 months ago
Niko PLP 711fa42621 small fix and doc changes 2 months ago
Niko PLP 513d7742f1 server locaction 2 months ago
Niko PLP 30f93e0797 fix bug save when nagivating away 2 months ago
Niko PLP 86a64d7ef5 new peer id for nextgraph.eu 2 months ago
Niko PLP b4ea570146 fix redirect_after_login 3 months ago
Niko PLP e4b96d96e5 enable TLS support in CLI 3 months ago
Niko PLP b7c5aa7cc4 v0.1.0-preview.8 3 months ago
Niko PLP 856d713a73 add ngcli get :j:k and fix bug in stores in tauri 3 months ago
Niko PLP 48c63ead2f signature verification in CLI with ExtRequest. Display snapshot 3 months ago
Niko PLP 17e1eb95e3 snapshot and async signature 3 months ago
Niko PLP 495340dabe updated error message in case of jshelter use 3 months ago
Niko PLP 4ef7e2730e force bright mode 3 months ago
Niko PLP b16e649d53 added error message in case web worker cannot be started on web-app 3 months ago
Niko PLP fa7cd603ee fix typo 3 months ago
Niko PLP f781f446fb adding native apps to changelog 3 months ago
Niko PLP de6d053961 update changelog links 3 months ago
Niko PLP 2b9b9107a5 new README, changelog, release note 3 months ago
Niko PLP dc4cf5ae13 fix file upload progress bar 3 months ago
Niko PLP 44fd445f4d fix creation of new doc 3 months ago
Niko PLP 09a0e83a21 fix sparql update on tauri. had wrong base 3 months ago
Niko PLP 465b43d88d reconnecting attempts every 5 secs 3 months ago
Niko PLP 3295c685d6 fix transaltion 3 months ago
Niko PLP 6110ebf733 fix tauri API 3 months ago
Niko PLP 8f053793f0 spinner when creating document 3 months ago
Niko PLP f2245c3b8c fix ToS 3 months ago
Niko PLP b76dc2be9b fix style on WalletCreate 3 months ago
Niko PLP d819fd4618 new PeerId of EU BSP 3 months ago
Niko PLP 8b9c3ea409 version 0.1.0-preview7 3 months ago
Niko PLP 76ef3eee4d download recovery PDF 3 months ago
Niko PLP 81eeae8947 added edit button on viewer header 3 months ago
Niko PLP e453c8998f RDF resource identifier is without overlay id 3 months ago
Niko PLP cc89bb1808 blank node skolemization should work without BASE 3 months ago
Niko PLP 077ba6265e skolemization of blank nodes and BASE in SPARQl query and update 3 months ago
Niko PLP d03d15f9b7 update GDPR/DSA 3 months ago
Niko PLP 6852d56bbc derive key of UserStorage 3 months ago
Niko PLP 3926f1f2e4 offset for viewer menu if no discrete 3 months ago
Niko PLP 2f799ed937 MD and XML source viewer, and hide unimplemented Graph editors and viewers 3 months ago
Niko PLP 6f3e7b16eb spinners for data loading 3 months ago
Niko PLP a127efffa6 download links 3 months ago
Niko PLP b8474bc2ce fix splash screen, milkdown on safaai, link preview/opengraph 3 months ago
Niko PLP 248916c561 removed unused wallet emojis 3 months ago
Niko PLP a983205baa removed recursion in load_causal_past 3 months ago
Niko PLP 5155dc208b fix spurious input change in AString automerge component 3 months ago
Niko PLP c38585f87d GUI adjustments on empty JSON doc 3 months ago
Niko PLP 8209fff45b fix import and css 3 months ago
Niko PLP 456ddf7e30 AutomergeEditor and Viewer and JsonSource 3 months ago
Niko PLP ea38ac7d2a upgrade plugin-split-editing to latest release with fix 3 months ago
Niko PLP a29c225216 JsonEditor for Yjs 3 months ago
Niko PLP c1945d7349 JsonEditor for Yjs 3 months ago
Niko PLP ded846f2fc upgrade plugin-split-editing to latest after bugfix 3 months ago
Niko PLP fa0102f1f4 more plugins for MilkDown and CSS for all 3 months ago
Niko PLP f289170e74 MilkDownEditor and PostMdViewer 3 months ago
Niko PLP 0a5c03ebd1 focus and placeholder for ProseMirrorEditor 3 months ago
Niko PLP 74137c4bdd ProseMirrorEditor and ProseMirrorViewer 3 months ago
Niko PLP 4ae372a860 TextViewer for post:text and code: classes 3 months ago
Niko PLP c7eda5198b toggle live edit 3 months ago
Niko PLP 434201c657 discrete doc: Codemirror+Yjs 3 months ago
Niko PLP 9e6c004fc4 Merge branch 'fix/wallet-create-pazzle-display' 3 months ago
Niko PLP 9f3efec14c ignore pnpm lock file, fix typo 3 months ago
Niko PLP 28bf09f24d minor adjustment 3 months ago
Niko PLP fdac0dfa68 container 3 months ago
Niko PLP 8396f30975 doc_create 3 months ago
Laurin Weger 2cb65a4d16
fix: wallet create pazzle list layout 3 months ago
Niko PLP 01816d000b added web link class 3 months ago
Niko PLP 947afbdceb change display of media when has_camera 3 months ago
Niko PLP 68ad4ac632 upload binary file in edit opens the pane 3 months ago
Niko PLP 00e39f0cc4 create document GUI 3 months ago
Niko PLP 5e7ba770cd fix toasts 3 months ago
Niko PLP 148838aada error mng improvement 3 months ago
Niko PLP 00398272bd files and attachments pane 3 months ago
Niko PLP b6680d7cab splash screen and check minimal browser version 3 months ago
Niko PLP c956cfcb35 history pane in Tauri + fixes GUI 3 months ago
Niko PLP f7ee5457ef history pane 3 months ago
Niko PLP 9659f1bbd2 sparql update and query for Tauri 3 months ago
Niko PLP 029276e205 SPARQL Query viewer 4 months ago
Niko PLP 95ed77e414 error and success toasts in SPARQL Update Editor 4 months ago
Niko PLP 263df00e93 fix bloomfilter, due to hash function issue on wasm32 4 months ago
Niko PLP b6db6b0d76 dont propagate removes of triples in AppPatch when SU-Set keeps them (some observed are not removed) 4 months ago
Niko PLP 9fbeccc3f5 remove log 4 months ago
Niko PLP 7c621509d2 navigation between docs and tabs, sparql update editor 4 months ago
Niko PLP a8ec95a583 3 panes windowing system 4 months ago
Niko PLP ff379ccf65 menu and NavBar 4 months ago
Niko PLP 3f635248be document side menu pane 4 months ago
Niko PLP 1bc2d46ba8 small fixes css 4 months ago
Niko PLP b3a00f3963 copyright header 4 months ago
Niko PLP 8c9147712d more copyright headers 4 months ago
Niko PLP 707d105091 fix emoji 4 months ago
Niko PLP 5b7cb80142 fix on mobile tauri 4 months ago
Niko PLP 947e5f5de4 error handling and sanitizing of string input for mnemonic and TextCode 4 months ago
Niko PLP c9630401f5 error handling 4 months ago
Niko PLP 81167ed5f3 error handling 4 months ago
Niko PLP 50153b2018 no cmaera on tauri desktop/laptop native apps 4 months ago
Niko PLP b02375ff47 error handling on wallet/scanqr 4 months ago
Niko PLP abfe68b4ac fix Tauri API error handling 4 months ago
Niko PLP aa3b1321bd try again if no rear camera 4 months ago
Niko PLP 700413d31a try again if no rear camera 4 months ago
Niko PLP a403ea31e7 use API for qrscan on html, force rear camera 4 months ago
Niko PLP 4860a943bb more fixes 4 months ago
Niko PLP dfa9c99615 fixes and improvements of QR scanning 4 months ago
Niko PLP 2dca4c0567 finish recovery PDF and few fixes 4 months ago
Laurin Weger 6362fb09f1
ng-app: add German translation 4 months ago
Laurin Weger 18101e4fae
final frontend parts 4 months ago
Niko PLP bb6d118f34 wallet_recovery_pdf working 4 months ago
Niko PLP 8b2558cd9c wallet_recovery_pdf 4 months ago
Laurin Weger 46c6228de2
user panel part 4 months ago
Laurin Weger a62c0cc342
wip: login pages finished 4 months ago
Niko PLP 26e67a0bc4 merge feat/ng-app/sync-wallets with master 4 months ago
Niko PLP 41cba12377 merge ask-device-name 4 months ago
Niko PLP ee385cbdfe get_device_name 4 months ago
Niko PLP b06b31916e fix and more examples 4 months ago
Niko PLP fb0c161f7d merge 4 months ago
Laurin Weger e68bc0c4d6
wip login part 4 months ago
Niko PLP 65b91ffc3f wallet import/export API and QRcode scanning from Tauri plugin 4 months ago
Laurin Weger 6f818448b3
use Logo component in full layout instead of svg 4 months ago
Laurin Weger 4edb821e5e
ng-app: wallet info panel qr scanner and generator frontend 4 months ago
Laurin Weger 9882667604
ng-app: mnemonic confirm button same hover color 4 months ago
Laurin Weger a29db952e2
feat/ng-app: ask for device name svelte part 4 months ago
Niko PLP f8a2f6ebb4 Merge pull request 'Upload / Download Progress' (#30) from feat/ng-app/progress-indicators into master 4 months ago
Niko PLP 104c796f68 fix mimetypes 4 months ago
Niko PLP e2c2596e1e handle error about rocksdb lock already taken. concurrent start of ngd in same working directory 4 months ago
  1. 6
      .github/FUNDING.yml
  2. 189
      CHANGELOG.md
  3. 423
      Cargo.lock
  4. 2
      Cargo.toml
  5. 187
      DEV.md
  6. 190
      README.md
  7. 40
      RELEASE-NOTE.md
  8. 1
      nextgraph/.gitignore
  9. 20
      nextgraph/Cargo.toml
  10. 2
      nextgraph/README.md
  11. 2
      nextgraph/examples/in_memory.rs
  12. 2
      nextgraph/examples/persistent.rs
  13. 38
      nextgraph/src/lib.rs
  14. 896
      nextgraph/src/local_broker.rs
  15. 1
      nextgraph/src/local_broker_dev_env.rs
  16. 38
      ng-app/index-native.html
  17. 86
      ng-app/index-web.html
  18. 70
      ng-app/package.json
  19. 7
      ng-app/src-tauri/Cargo.toml
  20. 1
      ng-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml
  21. 529
      ng-app/src-tauri/src/lib.rs
  22. 2
      ng-app/src-tauri/tauri.conf.json
  23. 26
      ng-app/src/App.svelte
  24. 145
      ng-app/src/api.ts
  25. 179
      ng-app/src/apps/AutomergeEditor.svelte
  26. 74
      ng-app/src/apps/AutomergeJsonSource.svelte
  27. 19
      ng-app/src/apps/AutomergeViewer.svelte
  28. 101
      ng-app/src/apps/CodeMirrorEditor.svelte
  29. 88
      ng-app/src/apps/ContainerView.svelte
  30. 0
      ng-app/src/apps/JsonLdEditor.svelte
  31. 159
      ng-app/src/apps/MdSource.svelte
  32. 200
      ng-app/src/apps/MilkDownEditor.svelte
  33. 197
      ng-app/src/apps/PostMdViewer.svelte
  34. 105
      ng-app/src/apps/ProseMirrorEditor.svelte
  35. 120
      ng-app/src/apps/ProseMirrorViewer.svelte
  36. 126
      ng-app/src/apps/SparqlQueryEditor.svelte
  37. 78
      ng-app/src/apps/SparqlUpdateEditor.svelte
  38. 129
      ng-app/src/apps/TextViewer.svelte
  39. 84
      ng-app/src/apps/TurtleViewer.svelte
  40. 131
      ng-app/src/apps/XmlSource.svelte
  41. 19
      ng-app/src/apps/YArrayEditor.svelte
  42. 19
      ng-app/src/apps/YArraySource.svelte
  43. 19
      ng-app/src/apps/YArrayViewer.svelte
  44. 291
      ng-app/src/apps/YMapEditor.svelte
  45. 79
      ng-app/src/apps/YMapSource.svelte
  46. 189
      ng-app/src/apps/YMapViewer.svelte
  47. 36
      ng-app/src/apps/automerge/ABoolean.svelte
  48. 65
      ng-app/src/apps/automerge/ACounter.svelte
  49. 71
      ng-app/src/apps/automerge/ADate.svelte
  50. 118
      ng-app/src/apps/automerge/AList.svelte
  51. 131
      ng-app/src/apps/automerge/AMap.svelte
  52. 42
      ng-app/src/apps/automerge/ANumber.svelte
  53. 43
      ng-app/src/apps/automerge/AString.svelte
  54. 87
      ng-app/src/apps/automerge/AValue.svelte
  55. 81
      ng-app/src/apps/automerge/utils.ts
  56. 102
      ng-app/src/apps/milkdown-placeholder.ts
  57. 59
      ng-app/src/base64url.js
  58. 519
      ng-app/src/classes.ts
  59. 8
      ng-app/src/lib/CenteredLayout.svelte
  60. 187
      ng-app/src/lib/DataClassIcon.svelte
  61. 224
      ng-app/src/lib/Document.svelte
  62. 1306
      ng-app/src/lib/FullLayout.svelte
  63. 67
      ng-app/src/lib/Home.svelte
  64. 18
      ng-app/src/lib/Install.svelte
  65. 73
      ng-app/src/lib/Login.svelte
  66. 2
      ng-app/src/lib/MobileBottomBar.svelte
  67. 11
      ng-app/src/lib/MobileBottomBarItem.svelte
  68. 4
      ng-app/src/lib/NoWallet.svelte
  69. 41
      ng-app/src/lib/Pane.svelte
  70. 719
      ng-app/src/lib/Test.svelte
  71. 8
      ng-app/src/lib/components/CopyToClipboard.svelte
  72. 2
      ng-app/src/lib/components/Logo.svelte
  73. 57
      ng-app/src/lib/components/MenuItem.svelte
  74. 64
      ng-app/src/lib/components/Message.svelte
  75. 94
      ng-app/src/lib/components/NavBar.svelte
  76. 49
      ng-app/src/lib/components/PaneHeader.svelte
  77. 2
      ng-app/src/lib/components/PasswordInput.svelte
  78. 3
      ng-app/src/lib/icons/BrailleIcon.svelte
  79. 13
      ng-app/src/lib/icons/BranchIcon.svelte
  80. 8
      ng-app/src/lib/icons/ChemistryIcon.svelte
  81. 191
      ng-app/src/lib/icons/DataClassIcon.svelte
  82. 0
      ng-app/src/lib/icons/DeviceIcon.svelte
  83. 3
      ng-app/src/lib/icons/GraphQLIcon.svelte
  84. 4
      ng-app/src/lib/icons/GuitarIcon.svelte
  85. 3
      ng-app/src/lib/icons/JsIcon.svelte
  86. 4
      ng-app/src/lib/icons/JsonIcon.svelte
  87. 10
      ng-app/src/lib/icons/JsonLdIcon.svelte
  88. 6
      ng-app/src/lib/icons/MarkdownIcon.svelte
  89. 64
      ng-app/src/lib/icons/NavIcon.svelte
  90. 3
      ng-app/src/lib/icons/PdfIcon.svelte
  91. 9
      ng-app/src/lib/icons/RdfIcon.svelte
  92. 3
      ng-app/src/lib/icons/ReactIcon.svelte
  93. 3
      ng-app/src/lib/icons/RustIcon.svelte
  94. 3
      ng-app/src/lib/icons/SvelteIcon.svelte
  95. 3
      ng-app/src/lib/icons/TsIcon.svelte
  96. 35
      ng-app/src/lib/icons/TurtleIcon.svelte
  97. 5
      ng-app/src/lib/icons/TxtIcon.svelte
  98. 22
      ng-app/src/lib/icons/ZeraIcon.svelte
  99. 236
      ng-app/src/lib/panes/Files.svelte
  100. 166
      ng-app/src/lib/panes/History.svelte
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,3 +1,7 @@
liberapay: nextgraph liberapay: nextgraph
ko_fi: nextgraph ko_fi: nextgraph
custom: ["https://donate.stripe.com/8wMcOE3NI2B69NKeUU", "https://pay.vivawallet.com/par-le-peuple", "https://nextgraph.org/donate"] custom:
[
"https://donate.stripe.com/8wMcOE3NI2B69NKeUU",
"https://nextgraph.org/donate",
]

@ -0,0 +1,189 @@
# Changelog
Access the sub-sections directly :
[App](#app) - [SDK](#sdk) - [Broker](#broker) - [CLI](#cli)
## App
### App [0.1.1-alpha] - 2024-09-02
#### Added
- edit title and intro
#### Fixed
- bug doc not saved when back navigation
### App [0.1.0-preview.8] - 2024-08-21
#### Added
- signature tool: signs HEADS or a snapshot
#### Fixed
- bug in synchronization of stores content (container) on tauri native apps
- removed dark theme (that wasn't implemented properly)
- on web-app, detects jshelter and ask user to deactivate it
### App [0.1.0-preview.7] - 2024-08-15
#### Added
- Wallet Creation : Download Recovery PDF
- Wallet Creation : Download wallet file
- Wallet Login : with pazzle
- Wallet Login : correct errors while entering pazzle
- Wallet Login : with mnemonic
- Wallet Login : in-memory session (save nothing locally)
- Wallet Import : from file
- Wallet Import : from QR code
- Wallet Import : from TextCode
- User Panel : Online / Offline status
- User Panel : Toggle Personal Connection
- User Panel : Logout
- User Panel / Wallet : Export by scanning QRCode
- User Panel / Wallet : Export by generating QRCode
- User Panel / Wallet : Export by generating TextCode
- User Panel / Wallet : Download file
- User Panel / Accounts Info : basic info (not accurate)
- Document Menu : switch Viewer / Editor
- Document Menu : switch Graph / Document
- Document Menu : Live editing
- Document Menu : Upload binary file + Attachements and Files pane
- Document Menu : History pane
- Add Document : Save in current Store
- Document class: Source Code: Rust, JS, TS, Svelte, React
- Document class: Data : Graph, Container, JSON, Array, Object
- Document class: Post (rich text)
- Document class: Markdown (rich text)
- Document class: Plain Text
- A11Y : limited ARIA and tabulation navigation on all pages. not tested with screen-reader.
- I18N : english
- I18N : german (partial)
- Native app: macOS
- Native app: android
- Native app: linux and Ubuntu
- Native app: Windows
## SDK
### SDK [unreleased]
#### Added
- js & nodejs : fetch_header
- js & nodejs : update_header
- js & nodejs : signature_status
- js & nodejs : signed_snapshot_request
- js & nodejs : signature_request
- rust : app_request: Fetch : SignatureStatus , SignatureRequest SignedSnapshotRequest
### SDK [0.1.0-preview.6] - 2024-08-15
#### Added
- js : session_start
- js : session_start_remote
- js : session_stop
- js : user_connect
- js : user_disconnect
- js : discrete_update
- js : sparql_update
- js : sparql_query (returns SPARQL Query Results JSON Format, a list of turtle triples, or a boolean )
- js : branch_history
- js : app_request_stream (fetch and subscribe)
- js : app_request
- js : doc_create
- js : file_get
- js : upload_start
- js : upload_done
- js : upload_chunk
- nodejs : init_headless
- nodejs : session_headless_start
- nodejs : session_headless_stop
- nodejs : sparql_query (returns SPARQL Query Results JSON Format, RDF-JS data model, or a boolean)
- nodejs : discrete_update
- nodejs : sparql_update
- nodejs : rdf_dump
- nodejs : admin_create_user
- nodejs : doc_create
- nodejs : file_get
- nodejs : file_put
- rust : session_start
- rust : session_stop
- rust : app_request_stream, gives access to:
- fetch and subscribe
- file_get
- rust : app_request, gives access to:
- create_doc
- sparql_query
- sparql_update
- discrete_update
- rdf_dump
- history
- file_put
## Broker
### Broker [0.1.1-alpha] - 2024-09-02
### Broker [0.1.0-preview.8] - 2024-08-21
#### Added
- ExtProtocol : ObjectGet
### Broker [0.1.0-preview.7] - 2024-08-15
#### Added
- listen on localhost
- listen on domain
- listen on private LAN
- listen on public IP
- invite-admin
- broker service provider : add invitation for user
- serve web app
- ExtProtocol : WalletGetExport
- ClientProtocol : BlocksExist
- ClientProtocol : BlocksGet
- ClientProtocol : BlocksPut
- ClientProtocol : CommitGet
- ClientProtocol : Event
- ClientProtocol : PinRepo
- ClientProtocol : RepoPinStatus
- ClientProtocol : TopicSub
- ClientProtocol : TopicSyncReq
- ClientProtocol : WalletPutExport
- AppProtocol : AppRequest
- AppProtocol : AppSessionStart
- AppProtocol : AppSessionStop
- AdminProtocol : AddInvitation
- AdminProtocol : AddUser
- AdminProtocol : CreateUser
- AdminProtocol : DelUser
- AdminProtocol : ListInvitations
- AdminProtocol : ListUsers
## CLI
### CLI [0.1.1-alpha] - 2024-09-02
### CLI [0.1.0-preview.8] - 2024-08-21
#### Added
- get : download binary files, snapshots, and head commits, and verify signature
### CLI [0.1.0-preview.7] - 2024-08-15
#### Added
- gen-key
- admin : add/remove admin user
- admin : add invitation
- admin : list users
- admin : list invitations

423
Cargo.lock generated

@ -12,15 +12,6 @@ dependencies = [
"psl-types", "psl-types",
] ]
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]] [[package]]
name = "adler" name = "adler"
version = "1.0.2" version = "1.0.2"
@ -473,9 +464,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",
@ -493,21 +484,6 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.13.1" version = "0.13.1"
@ -520,6 +496,12 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "base64-url" name = "base64-url"
version = "2.0.0" version = "2.0.0"
@ -556,15 +538,6 @@ dependencies = [
"syn 2.0.58", "syn 2.0.58",
] ]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bit_field" name = "bit_field"
version = "0.10.2" version = "0.10.2"
@ -664,17 +637,6 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "bloomfilter"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64d54e47a7f4fd723f082e8f11429f3df6ba8adaeca355a76556f9f0602bbcf"
dependencies = [
"bit-vec",
"getrandom 0.2.10",
"siphasher 1.0.1",
]
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "3.3.4" version = "3.3.4"
@ -1421,6 +1383,12 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]] [[package]]
name = "debug_print" name = "debug_print"
version = "1.0.0" version = "1.0.0"
@ -1691,28 +1659,6 @@ dependencies = [
"zune-inflate", "zune-inflate",
] ]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.9.0" version = "1.9.0"
@ -1731,6 +1677,12 @@ dependencies = [
"getrandom 0.2.10", "getrandom 0.2.10",
] ]
[[package]]
name = "fastrange-rs"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e90a1392cd6ec5ebe42ccaf251f2b7ba6be654c377f05c913f3898bfb2172512"
[[package]] [[package]]
name = "fdeflate" name = "fdeflate"
version = "0.3.0" version = "0.3.0"
@ -1791,6 +1743,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.10.14" version = "0.10.14"
@ -2139,12 +2097,6 @@ dependencies = [
"weezl", "weezl",
] ]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.16.7" version = "0.16.7"
@ -2620,6 +2572,12 @@ dependencies = [
"tiff", "tiff",
] ]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "1.9.3"
@ -2797,14 +2755,13 @@ checksum = "f850fafca79ebacd70eab9d80cb75a33aeda38bde8f3dd784c1837cdf0bde631"
[[package]] [[package]]
name = "json-patch" name = "json-patch"
version = "1.0.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658" checksum = "ec9ad60d674508f3ca8f380a928cfe7b096bc729c4e2dbfe3852bc45da3ab30b"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
"treediff",
] ]
[[package]] [[package]]
@ -2831,6 +2788,16 @@ dependencies = [
"selectors", "selectors",
] ]
[[package]]
name = "kurbo"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c"
dependencies = [
"arrayvec",
"smallvec",
]
[[package]] [[package]]
name = "kv-log-macro" name = "kv-log-macro"
version = "1.0.7" version = "1.0.7"
@ -2942,9 +2909,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.19" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
dependencies = [ dependencies = [
"value-bag", "value-bag",
] ]
@ -3257,13 +3224,14 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]] [[package]]
name = "nextgraph" name = "nextgraph"
version = "0.1.0-preview.6" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-once-cell", "async-once-cell",
"async-std", "async-std",
"async-trait", "async-trait",
"base64-url", "base64-url",
"futures", "futures",
"lazy_static",
"ng-client-ws", "ng-client-ws",
"ng-net", "ng-net",
"ng-repo", "ng-repo",
@ -3271,15 +3239,20 @@ dependencies = [
"ng-verifier", "ng-verifier",
"ng-wallet", "ng-wallet",
"once_cell", "once_cell",
"pdf-writer",
"qrcode",
"serde_bare", "serde_bare",
"serde_bytes",
"serde_json", "serde_json",
"svg2pdf",
"web-time", "web-time",
"whoami",
"zeroize", "zeroize",
] ]
[[package]] [[package]]
name = "ng-app" name = "ng-app"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-std", "async-std",
"nextgraph", "nextgraph",
@ -3287,13 +3260,18 @@ dependencies = [
"ng-net", "ng-net",
"ng-repo", "ng-repo",
"ng-wallet", "ng-wallet",
"oxrdf",
"serde", "serde",
"serde_bare",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"sys-locale", "sys-locale",
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-barcode-scanner",
"tauri-plugin-window", "tauri-plugin-window",
"tauri-utils",
"zeroize",
] ]
[[package]] [[package]]
@ -3312,7 +3290,7 @@ dependencies = [
[[package]] [[package]]
name = "ng-broker" name = "ng-broker"
version = "0.1.0-preview.6" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
@ -3337,7 +3315,7 @@ dependencies = [
[[package]] [[package]]
name = "ng-client-ws" name = "ng-client-ws"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
@ -3356,7 +3334,7 @@ dependencies = [
[[package]] [[package]]
name = "ng-net" name = "ng-net"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"async-std", "async-std",
@ -3386,7 +3364,7 @@ dependencies = [
[[package]] [[package]]
name = "ng-oxigraph" name = "ng-oxigraph"
version = "0.4.0-alpha.7-ngpreview6" version = "0.4.0-alpha.7-ngalpha"
dependencies = [ dependencies = [
"base64-url", "base64-url",
"codspeed-criterion-compat", "codspeed-criterion-compat",
@ -3399,6 +3377,7 @@ dependencies = [
"libc", "libc",
"md-5", "md-5",
"memchr", "memchr",
"ng-repo",
"ng-rocksdb", "ng-rocksdb",
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
@ -3409,18 +3388,17 @@ dependencies = [
"serde", "serde",
"sha1", "sha1",
"sha2 0.10.8", "sha2 0.10.8",
"siphasher 1.0.1", "siphasher 0.3.10",
"thiserror", "thiserror",
"zstd", "zstd",
] ]
[[package]] [[package]]
name = "ng-repo" name = "ng-repo"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"base64-url", "base64-url",
"blake3", "blake3",
"bloomfilter",
"chacha20", "chacha20",
"crypto_box", "crypto_box",
"current_platform", "current_platform",
@ -3430,11 +3408,13 @@ dependencies = [
"futures", "futures",
"getrandom 0.2.10", "getrandom 0.2.10",
"gloo-timers", "gloo-timers",
"lazy_static",
"log", "log",
"num_enum", "num_enum",
"once_cell", "once_cell",
"os_info", "os_info",
"rand 0.7.3", "rand 0.7.3",
"sbbf-rs-safe",
"serde", "serde",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
@ -3464,9 +3444,10 @@ dependencies = [
[[package]] [[package]]
name = "ng-sdk-js" name = "ng-sdk-js"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-std", "async-std",
"futures",
"getrandom 0.1.16", "getrandom 0.1.16",
"gloo-timers", "gloo-timers",
"js-sys", "js-sys",
@ -3482,7 +3463,6 @@ dependencies = [
"serde-wasm-bindgen", "serde-wasm-bindgen",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"serde_json",
"sys-locale", "sys-locale",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@ -3491,7 +3471,7 @@ dependencies = [
[[package]] [[package]]
name = "ng-storage-rocksdb" name = "ng-storage-rocksdb"
version = "0.1.0-preview.6" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"ng-repo", "ng-repo",
"ng-rocksdb", "ng-rocksdb",
@ -3519,34 +3499,37 @@ dependencies = [
[[package]] [[package]]
name = "ng-verifier" name = "ng-verifier"
version = "0.1.0-preview.6" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
"automerge", "automerge",
"bloomfilter",
"either", "either",
"futures", "futures",
"getrandom 0.2.10", "getrandom 0.2.10",
"lazy_static",
"ng-net", "ng-net",
"ng-oxigraph", "ng-oxigraph",
"ng-repo", "ng-repo",
"ng-storage-rocksdb", "ng-storage-rocksdb",
"rand 0.7.3", "rand 0.7.3",
"sbbf-rs-safe",
"serde", "serde",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"serde_json",
"web-time", "web-time",
"yrs", "yrs",
] ]
[[package]] [[package]]
name = "ng-wallet" name = "ng-wallet"
version = "0.1.0-preview.6" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"aes-gcm-siv", "aes-gcm-siv",
"argon2", "argon2",
"async-std", "async-std",
"base64-url",
"blake3", "blake3",
"chacha20poly1305", "chacha20poly1305",
"crypto_box", "crypto_box",
@ -3568,7 +3551,7 @@ dependencies = [
[[package]] [[package]]
name = "ngaccount" name = "ngaccount"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"duration-str", "duration-str",
@ -3586,7 +3569,7 @@ dependencies = [
[[package]] [[package]]
name = "ngcli" name = "ngcli"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"async-std", "async-std",
"blake3", "blake3",
@ -3595,6 +3578,7 @@ dependencies = [
"env_logger", "env_logger",
"getrandom 0.2.10", "getrandom 0.2.10",
"log", "log",
"ng-async-tungstenite",
"ng-client-ws", "ng-client-ws",
"ng-net", "ng-net",
"ng-repo", "ng-repo",
@ -3605,7 +3589,7 @@ dependencies = [
[[package]] [[package]]
name = "ngd" name = "ngd"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"addr", "addr",
"async-std", "async-std",
@ -3624,7 +3608,7 @@ dependencies = [
[[package]] [[package]]
name = "ngone" name = "ngone"
version = "0.1.0-preview.1" version = "0.1.1-alpha"
dependencies = [ dependencies = [
"base64-url", "base64-url",
"bytes", "bytes",
@ -3783,6 +3767,15 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "objc" name = "objc"
version = "0.2.7" version = "0.2.7"
@ -3811,15 +3804,6 @@ dependencies = [
"objc", "objc",
] ]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.18.0"
@ -4040,6 +4024,18 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pdf-writer"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af6a7882fda7808481d43c51cadfc3ec934c6af72612a1fe6985ce329a2f0469"
dependencies = [
"bitflags 2.5.0",
"itoa 1.0.6",
"memchr",
"ryu",
]
[[package]] [[package]]
name = "peeking_take_while" name = "peeking_take_while"
version = "0.1.2" version = "0.1.2"
@ -4197,6 +4193,12 @@ dependencies = [
"siphasher 0.3.10", "siphasher 0.3.10",
] ]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.0" version = "1.1.0"
@ -4436,6 +4438,12 @@ dependencies = [
"bytemuck", "bytemuck",
] ]
[[package]]
name = "qrcode"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.28.2" version = "0.28.2"
@ -4608,6 +4616,15 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.3" version = "0.4.3"
@ -4690,6 +4707,12 @@ dependencies = [
"winreg 0.10.1", "winreg 0.10.1",
] ]
[[package]]
name = "roxmltree"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]] [[package]]
name = "rust-embed" name = "rust-embed"
version = "6.7.0" version = "6.7.0"
@ -4735,12 +4758,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@ -4821,6 +4838,25 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "sbbf-rs"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5525db49c7719816ac719ea8ffd0d0b4586db1a3f5d3e7751593230dacc642fd"
dependencies = [
"cpufeatures",
"fastrange-rs",
]
[[package]]
name = "sbbf-rs-safe"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9902ffeb2cff3f8c072c60c7d526ac9560fc9a66fe1dfc3c240eba5e2151ba3c"
dependencies = [
"sbbf-rs",
]
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.22" version = "0.1.22"
@ -5139,6 +5175,15 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
[[package]]
name = "simplecss"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
dependencies = [
"log",
]
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.10" version = "0.3.10"
@ -5150,9 +5195,6 @@ name = "siphasher"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "sized-chunks" name = "sized-chunks"
@ -5271,6 +5313,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]] [[package]]
name = "string_cache" name = "string_cache"
version = "0.8.7" version = "0.8.7"
@ -5309,6 +5360,29 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "svg2pdf"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31565956eb1dc398c0d9776ee1d1bac4e34759af63dcbe0520df32313a5b53b"
dependencies = [
"log",
"miniz_oxide",
"once_cell",
"pdf-writer",
"usvg",
]
[[package]]
name = "svgtypes"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c"
dependencies = [
"kurbo",
"siphasher 1.0.1",
]
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
version = "1.0.6" version = "1.0.6"
@ -5342,18 +5416,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]] [[package]]
name = "sys-locale" name = "sys-locale"
version = "0.3.1" version = "0.3.1"
@ -5575,6 +5637,20 @@ dependencies = [
"tauri-utils", "tauri-utils",
] ]
[[package]]
name = "tauri-plugin-barcode-scanner"
version = "2.0.0-alpha.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058922dd9cafc89a865593c99507177c4cdbdad9d22a911ac41872ed7dbf0348"
dependencies = [
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"thiserror",
]
[[package]] [[package]]
name = "tauri-plugin-window" name = "tauri-plugin-window"
version = "2.0.0-alpha.1" version = "2.0.0-alpha.1"
@ -5740,11 +5816,9 @@ dependencies = [
[[package]] [[package]]
name = "threshold_crypto" name = "threshold_crypto"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/nextgraph-org/threshold_crypto.git?branch=master#b60552e4d42f67058455779eed476a76986b5478"
checksum = "7f708705bce37e765c37a95a8e0221a327c880d5a5a148d522552e8daa85787a"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"failure",
"ff", "ff",
"group", "group",
"hex_fmt", "hex_fmt",
@ -5753,6 +5827,7 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
"rand_chacha 0.2.2", "rand_chacha 0.2.2",
"serde", "serde",
"thiserror",
"tiny-keccak", "tiny-keccak",
"zeroize", "zeroize",
] ]
@ -5787,7 +5862,10 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa 1.0.6", "itoa 1.0.6",
"js-sys",
"libc",
"num-conv", "num-conv",
"num_threads",
"powerfmt", "powerfmt",
"serde", "serde",
"time-core", "time-core",
@ -5819,6 +5897,17 @@ dependencies = [
"crunchy", "crunchy",
] ]
[[package]]
name = "tiny-skia-path"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]] [[package]]
name = "tinytemplate" name = "tinytemplate"
version = "1.2.1" version = "1.2.1"
@ -6043,15 +6132,6 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "treediff"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303"
dependencies = [
"serde_json",
]
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.4" version = "0.2.4"
@ -6129,12 +6209,6 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]] [[package]]
name = "unique_id" name = "unique_id"
version = "0.1.5" version = "0.1.5"
@ -6168,6 +6242,28 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "usvg"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
dependencies = [
"base64 0.22.1",
"data-url",
"flate2",
"imagesize",
"kurbo",
"log",
"pico-args",
"roxmltree",
"simplecss",
"siphasher 1.0.1",
"strict-num",
"svgtypes",
"tiny-skia-path",
"xmlwriter",
]
[[package]] [[package]]
name = "utf-8" name = "utf-8"
version = "0.7.6" version = "0.7.6"
@ -6198,9 +6294,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]] [[package]]
name = "value-bag" name = "value-bag"
version = "1.4.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
@ -6325,6 +6421,12 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.87" version = "0.2.87"
@ -6536,9 +6638,20 @@ dependencies = [
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.7" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "whoami"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
dependencies = [
"redox_syscall 0.4.1",
"wasite",
"web-sys",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
@ -6999,6 +7112,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "xsalsa20poly1305" name = "xsalsa20poly1305"
version = "0.9.1" version = "0.9.1"
@ -7014,9 +7133,9 @@ dependencies = [
[[package]] [[package]]
name = "yrs" name = "yrs"
version = "0.18.2" version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4058d69bbbc97181d53d9d093a4b892001b84601f2fc4e27f48c8862bc8b369" checksum = "a8ca5126331b9a5ef5bb10f3f1c3d01b05f298d348c66f8fb15497d83ee73176"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"atomic_refcell", "atomic_refcell",

@ -19,7 +19,7 @@ members = [
default-members = [ "nextgraph", "ngcli", "ngd" ] default-members = [ "nextgraph", "ngcli", "ngd" ]
[workspace.package] [workspace.package]
version = "0.1.0-preview.1" version = "0.1.1-alpha"
edition = "2021" edition = "2021"
rust-version = "1.74.0" rust-version = "1.74.0"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"

187
DEV.md

@ -0,0 +1,187 @@
# Contributors or compilation guide
- [Install Rust](https://www.rust-lang.org/tools/install) minimum required MSRV 1.74.0
- [Install Nodejs](https://nodejs.org/en/download/)
- [Install LLVM](https://rust-lang.github.io/rust-bindgen/requirements.html)
On openbsd, for LLVM you need to choose llvm-17.
until this [PR](https://github.com/rustwasm/wasm-pack/pull/1271) is accepted, will have to install wasm-pack this way:
```
cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2
```
then :
create a file called `nextgraph/src/local_broker_dev_env.rs` with the content :
```
pub const PEER_ID: &str = "FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA";
```
once your ngd server will run in your dev env, replace the above string with the actual PEER ID of your ngd server.
```
cargo install cargo-watch
// optionally, if you want a Rust REPL: cargo install evcxr_repl
git clone git@git.nextgraph.org:NextGraph/nextgraph-rs.git
// or if you don't have a git account: git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git
cd nextgraph-rs
cargo build
```
### Packages
The crates are organized as follow :
- [nextgraph](nextgraph/README.md) : Client library. Use this crate to embed NextGraph client in your Rust application
- [ngcli](ngcli/README.md) : CLI tool to manipulate the local documents and repos and administrate the server
- [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services)
- [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the official web app.
- [ng-sdk-js](ng-sdk-js/DEV.md) : contains the JS SDK, with example for: web app, react app, or node service.
- ng-repo : Repositories common library
- ng-net : Network common library
- ng-oxigraph : Fork of OxiGraph. contains our CRDT of RDF
- ng-verifier : Verifier library, that exposes the document API to the app
- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet
- ng-broker : Core and Server Broker library
- ng-client-ws : Websocket client library
- ng-storage-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb)
- ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency
- ngaccount : server for nextgraph's Broker Service Provider account manager. Not useful to you. Published here for transparency
### Run
Build & run debug executables:
```
// runs the daemon
cargo run --bin ngd
// runs the client
cargo run --bin ngcli
```
For the apps, see the [README](ng-app/README.md)
### Test
Please test by following this order (as we need to generate some files locally)
```
cargo test --package nextgraph -r --lib -- local_broker::test::gen_wallet_for_test --show-output --nocapture
cargo test -r
cargo test --package nextgraph -r --lib -- local_broker::test::import_session_for_test_to_disk --show-output --nocapture --ignored
```
Test a single crate:
```
cargo test --package ng-repo --lib -- --show-output --nocapture
cargo test --package ng-wallet --lib -- --show-output --nocapture
cargo test --package ng-verifier --lib -- --show-output --nocapture
cargo test --package ng-sdk-js --lib -- --show-output --nocapture
cargo test --package ng-broker --lib -- --show-output --nocapture
cargo test --package ng-client-ws --lib -- --show-output --nocapture
```
Test WASM websocket
First you need to install the `chromedriver` that matches your version of Chrome
https://googlechromelabs.github.io/chrome-for-testing/
then:
```
cd ng-sdk-js
wasm-pack test --chrome --headless
```
Test Rust websocket
```
cargo test --package ng-client-ws --lib -- remote_ws::test::test_ws --show-output --nocapture
```
### Build release binaries
First you will need to have the production build of the frontend.
If you do not want to setup a whole development environment for the frontend, you can use the precompiled release of the frontend available in `dist-file.tar.gz` that you can download from the release page.
```
cd ng-app
tar -xzf dist-file.tar.gz
cd ..
```
Otherwise, build from source the single-file release of ng-app
```
npm install -g pnpm
cd ng-sdk-js
wasm-pack build --target bundler
cd ../ng-app
pnpm install
pnpm webfilebuild
cd ..
```
then build the ngd daemon
```
cargo build -r -p ngd
```
you can then find the binary `ngd` in `target/release`
The CLI tool can be obtained with :
```
cargo build -r -p ngcli
```
you can then use the binary `target/release/ngcli`
For usage, see the documentation [here](ngd/README.md).
For building the apps, see this [documentation](ng-app/README.md).
#### OpenBSD
On OpenBSD, a conflict between the installed LibreSSL library and the reqwest crate, needs a bit of attention.
Before compiling the daemon for OpenBSD, please comment out lines 41-42 of `ng-net/Cargo.toml`. This will be solved soon by using `resolver = "2"`.
```
#[target.'cfg(target_arch = "wasm32")'.dependencies]
#reqwest = { version = "0.11.18", features = ["json","native-tls-vendored"] }
```
to use the app on OpenBSD, you need to run the daemon locally.
```
ngd -l 14400 --save-key
```
then open chrome (previously installed with `doas pkg_add chrome`)
```
env ENABLE_WASM=1 chrome --enable-wasm --process-per-site --new-window --app=http://localhost:14400
```
### Generate documentation
Generate documentation for all packages without their dependencies:
```
cargo doc --no-deps
```
The generated documentation can be found in `target/doc/nextgraph`.
### Contributions license
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed as below, without any
additional terms or conditions.

@ -30,195 +30,23 @@ Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgr
And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org) And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org)
[![Mastodon](https://img.shields.io/badge/-MASTODON-%232B90D9?style=for-the-badge&logo=mastodon&logoColor=white)](https://mastodon.lescommuns.org/@nextgraph) [![Mastodon](https://img.shields.io/badge/-MASTODON-%232B90D9?style=for-the-badge&logo=mastodon&logoColor=white)](https://fosstodon.org/@nextgraph)
[![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fnextgraph)](https://twitter.com/nextgraph)
## How to use NextGraph ## How to use NextGraph App & Platform
NextGraph is not ready yet. You can subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/). NextGraph is in alpha release!
## For developers You can try it online or by installing the apps. Please follow our [Getting started](https://docs.nextgraph.org/en/getting-started/) guide .
Read our [getting started guide](https://docs.nextgraph.org/en/getting-started/). You can also subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/).
## For contributors ## NextGraph is also a Framework for App developers
- [Install Rust](https://www.rust-lang.org/tools/install) minimum required MSRV 1.74.0 Read our [getting started guide for developers](https://docs.nextgraph.org/en/framework/getting-started/).
- [Install Nodejs](https://nodejs.org/en/download/)
- [Install LLVM](https://rust-lang.github.io/rust-bindgen/requirements.html)
On openbsd, for LLVM you need to choose llvm-17. ## For contributors or self compilation
until this [PR](https://github.com/rustwasm/wasm-pack/pull/1271) is accepted, will have to install wasm-pack this way: See our [contributor's guide](DEV.md)
```
cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2
```
then :
```
cargo install cargo-watch
// optionally, if you want a Rust REPL: cargo install evcxr_repl
git clone git@git.nextgraph.org:NextGraph/nextgraph-rs.git
// or if you don't have a git account: git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git
cd nextgraph-rs
cargo build
```
### Packages
The crates are organized as follow :
- [nextgraph](nextgraph/README.md) : Client library. Use this crate to embed NextGraph client in your Rust application
- [ngcli](ngcli/README.md) : CLI tool to manipulate the local documents and repos and administrate the server
- [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services)
- [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the official web app.
- [ng-sdk-js](ng-sdk-js/DEV.md) : contains the JS SDK, with example for: web app, react app, or node service.
- ng-repo : Repositories common library
- ng-net : Network common library
- ng-verifier : Verifier library, that exposes the document API to the app
- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet
- ng-broker : Core and Server Broker library
- ng-client-ws : Websocket client library
- ng-storage-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb)
- ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency
- ngaccount : server for nextgraph's Broker Service Provider account manager. Not useful to you. Published here for transparency
### Run
Build & run debug executables:
```
// runs the daemon
cargo run --bin ngd
// runs the client
cargo run --bin ngcli
```
For the apps, see the [README](ng-app/README.md)
### Test
Please test by following this order (as we need to generate some files locally)
```
cargo test --package nextgraph -r --lib -- local_broker::test::gen_wallet_for_test --show-output --nocapture
cargo test -r
cargo test --package nextgraph -r --lib -- local_broker::test::import_session_for_test_to_disk --show-output --nocapture --ignored
```
Test a single crate:
```
cargo test --package ng-repo --lib -- --show-output --nocapture
cargo test --package ng-wallet --lib -- --show-output --nocapture
cargo test --package ng-verifier --lib -- --show-output --nocapture
cargo test --package ng-sdk-js --lib -- --show-output --nocapture
cargo test --package ng-broker --lib -- --show-output --nocapture
cargo test --package ng-client-ws --lib -- --show-output --nocapture
```
Test WASM websocket
First you need to install the `chromedriver` that matches your version of Chrome
https://googlechromelabs.github.io/chrome-for-testing/
then:
```
cd ng-sdk-js
wasm-pack test --chrome --headless
```
Test Rust websocket
```
cargo test --package ng-client-ws --lib -- remote_ws::test::test_ws --show-output --nocapture
```
### Build release binaries
First you will need to have the production build of the frontend.
If you do not want to setup a whole development environment for the frontend, you can use the precompiled release of the frontend available in `dist-file.tar.gz` that you can download from the release page.
```
cd ng-app
tar -xzf dist-file.tar.gz
cd ..
```
Otherwise, build from source the single-file release of ng-app
```
npm install -g pnpm
cd ng-sdk-js
wasm-pack build --target bundler
cd ../ng-app
pnpm install
pnpm webfilebuild
cd ..
```
then build the ngd daemon
```
cargo build -r -p ngd
```
you can then find the binary `ngd` in `target/release`
The CLI tool can be obtained with :
```
cargo build -r -p ngcli
```
you can then use the binary `target/release/ngcli`
For usage, see the documentation [here](ngd/README.md).
For building the apps, see this [documentation](ng-app/README.md).
#### OpenBSD
On OpenBSD, a conflict between the installed LibreSSL library and the reqwest crate, needs a bit of attention.
Before compiling the daemon for OpenBSD, please comment out lines 41-42 of `ng-net/Cargo.toml`. This will be solved soon by using `resolver = "2"`.
```
#[target.'cfg(target_arch = "wasm32")'.dependencies]
#reqwest = { version = "0.11.18", features = ["json","native-tls-vendored"] }
```
to use the app on OpenBSD, you need to run the daemon locally.
```
ngd -l 14400 --save-key
```
then open chrome (previously installed with `doas pkg_add chrome`)
```
env ENABLE_WASM=1 chrome --enable-wasm --process-per-site --new-window --app=http://localhost:14400
```
### Generate documentation
Generate documentation for all packages without their dependencies:
```
cargo doc --no-deps
```
The generated documentation can be found in `target/doc/nextgraph`.
### Contributions license
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed as below, without any
additional terms or conditions.
## License ## License

@ -0,0 +1,40 @@
# Release 0.1.1-alpha
_02 September 2024_
This release is not stable and should not be used for any productive work or to store personal documents. This release is meant as a **preview** of what NextGraph can do as of today and hints at its future potential.
**Please note: The binary format of the Documents or Wallet might change, that might result in a complete loss of data. We will not provide migration scripts as the APIs and formats aren't stable yet.**
If you previously installed any NextGraph app on your device, please uninstall it first, by following the normal uninstall procedure specific to your OS. If you have previously created a Wallet, it will not work with this new release. Please create a new one now.
## App
Please read the [Getting started](https://docs.nextgraph.org/en/getting-started) guide.
[changelog](CHANGELOG.md#app-0-1-1-alpha-2024-09-02)
## SDK
The SDK for is not documented yet.
[changelog](CHANGELOG.md#sdk-0-1-0-preview-6-2024-08-15)
## Broker
The `ngd` daemon is release with the basic features listed in `ngd --help`. More documentation will come soon
[changelog](CHANGELOG.md#broker-0-1-1-alpha-2024-09-02)
## CLI
The `ngcli` daemon is release with the basic features listed in `ngcli --help`. More documentation will come soon.
[changelog](CHANGELOG.md#cli-0-1-1-alpha-2024-09-02)
## Limitations of this release
- you cannot share documents with other users. Everything is ready for this internally, but there is still some wiring to do that will take some more time.
- the Rich text editors (both for normal Post/Article and in Markdown) do not let you insert images nor links to other documents.
- The webapp has some limitation for now when it is offline, because it doesn't have a UserStorage. it works differently than the native apps, as it has to replay all the commits at every load. This will stay like that for now, as the feature "Web UserStorage" based on IndexedDB will take some time to be coded.
- JSON-LD isn't ready yet as we need the "Context branch" feature in order to enter the list of ontologies each document is based on.

@ -1 +1,2 @@
tests tests
local_broker_dev_env_peer_id.rs

@ -2,7 +2,7 @@
name = "nextgraph" name = "nextgraph"
description = "NextGraph client library. Nextgraph is a decentralized, secure and local-first web 3.0 ecosystem based on Semantic Web and CRDTs" description = "NextGraph client library. Nextgraph is a decentralized, secure and local-first web 3.0 ecosystem based on Semantic Web and CRDTs"
categories = ["asynchronous","text-editors","web-programming","development-tools","database-implementations"] categories = ["asynchronous","text-editors","web-programming","development-tools","database-implementations"]
version = "0.1.0-preview.6" version = "0.1.1-alpha"
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true
@ -18,6 +18,7 @@ maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
serde_bare = "0.5.0" serde_bare = "0.5.0"
serde_json = "1.0" serde_json = "1.0"
serde_bytes = "0.11.7"
base64-url = "2.0.0" base64-url = "2.0.0"
once_cell = "1.17.1" once_cell = "1.17.1"
zeroize = { version = "1.7.0", features = ["zeroize_derive"] } zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
@ -25,15 +26,20 @@ futures = "0.3.24"
async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] } async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] }
async-trait = "0.1.64" async-trait = "0.1.64"
async-once-cell = "0.5.3" async-once-cell = "0.5.3"
lazy_static = "1.4.0"
web-time = "0.2.0" web-time = "0.2.0"
ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" } whoami = "1.5.1"
ng-net = { path = "../ng-net", version = "0.1.0-preview.1" } qrcode = { version = "0.14.1", default-features = false, features = ["svg"] }
ng-wallet = { path = "../ng-wallet", version = "0.1.0-preview.5" } svg2pdf = { version = "0.11.0", default-features = false }
ng-client-ws = { path = "../ng-client-ws", version = "0.1.0-preview.1" } pdf-writer = "0.10.0"
ng-verifier = { path = "../ng-verifier", version = "0.1.0-preview.5" } ng-repo = { path = "../ng-repo", version = "0.1.1-alpha" }
ng-net = { path = "../ng-net", version = "0.1.1-alpha" }
ng-wallet = { path = "../ng-wallet", version = "0.1.1-alpha" }
ng-client-ws = { path = "../ng-client-ws", version = "0.1.1-alpha" }
ng-verifier = { path = "../ng-verifier", version = "0.1.1-alpha" }
[target.'cfg(all(not(target_family = "wasm"),not(docsrs)))'.dependencies] [target.'cfg(all(not(target_family = "wasm"),not(docsrs)))'.dependencies]
ng-storage-rocksdb = { path = "../ng-storage-rocksdb", version = "0.1.0-preview.6" } ng-storage-rocksdb = { path = "../ng-storage-rocksdb", version = "0.1.1-alpha" }
[[example]] [[example]]
name = "in_memory" name = "in_memory"

@ -43,7 +43,7 @@ A tokio-based version (as a feature) might be available in the future.
```toml ```toml
[dependencies] [dependencies]
nextgraph = "0.1.0-preview.6" nextgraph = "0.1.1-alpha"
async-std = "1.12.0" async-std = "1.12.0"
``` ```

@ -55,6 +55,8 @@ async fn main() -> std::io::Result<()> {
core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker), core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker),
core_registration: None, core_registration: None,
additional_bootstrap: None, additional_bootstrap: None,
pdf: false,
device_name: "test".to_string(),
}) })
.await?; .await?;

@ -63,6 +63,8 @@ async fn main() -> std::io::Result<()> {
core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker), core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker),
core_registration: None, core_registration: None,
additional_bootstrap: None, additional_bootstrap: None,
pdf: false,
device_name: "test".to_string(),
}) })
.await?; .await?;

@ -1,4 +1,4 @@
#![doc(html_logo_url = "https://file.nextgraph.org/download/1fd175bb6d7d832156bd5ad4abcdee7e")] #![doc(html_logo_url = "https://nextgraph.org/nextgraph-logo-192.png")]
#![doc(issue_tracker_base_url = "https://git.nextgraph.org/NextGraph/nextgraph-rs/issues")] #![doc(issue_tracker_base_url = "https://git.nextgraph.org/NextGraph/nextgraph-rs/issues")]
#![doc(html_favicon_url = "https://nextgraph.org/favicon.svg")] #![doc(html_favicon_url = "https://nextgraph.org/favicon.svg")]
//! # NextGraph framework client library //! # NextGraph framework client library
@ -93,8 +93,44 @@ pub mod verifier {
pub mod protocol { pub mod protocol {
pub use ng_net::app_protocol::*; pub use ng_net::app_protocol::*;
} }
pub use ng_verifier::prepare_app_response_for_js;
pub use ng_verifier::triples_ser_to_json_string;
} }
pub mod wallet { pub mod wallet {
pub use ng_wallet::*; pub use ng_wallet::*;
} }
pub fn get_device_name() -> String {
let mut list: Vec<String> = Vec::with_capacity(3);
#[cfg(not(target_arch = "wasm32"))]
if let Ok(realname) = whoami::fallible::realname() {
list.push(realname);
} else {
#[cfg(not(target_arch = "wasm32"))]
if let Ok(username) = whoami::fallible::username() {
list.push(username);
}
}
if let Ok(devicename) = whoami::fallible::devicename() {
list.push(devicename);
} else {
#[cfg(not(target_arch = "wasm32"))]
if let Ok(hostname) = whoami::fallible::hostname() {
list.push(hostname);
} else {
if let Ok(distro) = whoami::fallible::distro() {
list.push(distro);
}
}
}
#[cfg(target_arch = "wasm32")]
if let Ok(distro) = whoami::fallible::distro() {
list.push(distro);
}
list.join(" ")
}
#[cfg(debug_assertions)]
mod local_broker_dev_env;

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
pub const PEER_ID: &str = "FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA";

@ -56,10 +56,46 @@
/> />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NextGraph</title> <title>NextGraph</title>
<style>
.splashing {
height: 95vh;
width:100%;
display: flex;
justify-content: center;
align-items: center;
}
.noshow {
display: none !important;
}
</style>
</head> </head>
<body> <body>
<div id="app"></div> <div id="splash" class="splashing">
<div style="flex-direction: column;justify-content: center;color:#4972a5;width:100%;text-align:center;font-family: Inter, Avenir, Helvetica, Arial, sans-serif;">
<svg
style="width:100px;height:100px;margin: 0 auto 20px ;"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 225 225"
>
<g>
<circle
r="106.98013"
cy="112.90476"
cx="109.88096"
style="fill:#ffffff;stroke:none;stroke-width:0.268375" />
<path
d="M 98.343352,190.26108 C 80.403778,187.53354 65.011938,179.57839 52.608228,166.62327 38.602093,151.99448 31.178059,133.41381 31.178059,112.98841 c 0,-10.21889 1.700058,-19.44396 5.221234,-28.332119 4.28678,-10.820699 10.037295,-19.39063 18.535095,-27.62263 4.72982,-4.58187 6.60687,-6.10643 11.28099,-9.16256 11.89869,-7.779841 24.173884,-11.879991 38.095802,-12.724761 19.80437,-1.2017 39.11165,5.11306 54.60284,17.858751 1.50718,1.24006 2.72951,2.35934 2.71628,2.48729 -0.0132,0.12795 -3.85821,3.63443 -8.54442,7.79217 -4.6862,4.157729 -10.04724,8.96276 -11.91342,10.677819 -1.86617,1.715071 -3.54094,3.11831 -3.7217,3.11831 -0.18075,0 -1.39985,-0.745188 -2.70911,-1.655969 -7.53011,-5.23834 -15.99428,-7.82188 -25.62597,-7.82188 -12.731628,0 -23.249192,4.3379 -32.143882,13.257541 -6.39594,6.413868 -10.70387,14.555268 -12.50018,23.623578 -0.69099,3.48832 -0.68968,13.53072 0.002,17.00893 3.70508,18.62577 18.31886,33.10194 36.642322,36.29729 4.16439,0.72621 11.98099,0.71223 15.98975,-0.0286 14.03187,-2.59311 25.86047,-11.36806 32.26533,-23.93578 0.77379,-1.51834 1.26018,-2.88461 1.08086,-3.03616 -0.17934,-0.15156 -6.87448,-1.1779 -14.87813,-2.28078 -9.7795,-1.34758 -14.92353,-2.21379 -15.68471,-2.64117 -1.52067,-0.85379 -2.83611,-2.88806 -2.83611,-4.3859 0,-1.1732 2.02687,-15.86876 2.49085,-18.05962 0.29676,-1.40127 2.42559,-3.4934 3.84317,-3.77691 0.62227,-0.12445 8.82712,0.85555 18.28065,2.18348 9.43343,1.32511 17.26269,2.29453 17.39833,2.15427 0.13566,-0.14026 1.11808,-6.54833 2.18313,-14.24014 1.10778,-8.000208 2.20407,-14.60184 2.56177,-15.426229 0.34392,-0.792599 1.11019,-1.849131 1.70287,-2.34782 2.06321,-1.736079 3.1433,-1.785011 12.20439,-0.55291 9.63637,1.310309 10.70873,1.56224 12.28077,2.88503 1.64359,1.382979 2.2732,2.810909 2.25906,5.123309 -0.007,1.10173 -0.92172,8.29645 -2.03332,15.98826 -1.11158,7.69182 -1.97159,14.04091 -1.91113,14.1091 0.0605,0.0682 7.16644,1.11143 15.79109,2.31832 11.10566,1.55407 16.00827,2.38757 16.80223,2.85657 1.53015,0.90389 2.48023,2.64785 2.45017,4.49756 -0.0462,2.84349 -2.41252,18.12279 -2.97521,19.21089 -0.66164,1.27949 -2.60244,2.54696 -3.92109,2.56074 -0.51973,0.005 -7.87449,-0.95937 -16.34391,-2.144 -8.46944,-1.18464 -15.47588,-2.077 -15.56986,-1.98301 -0.094,0.094 -1.18792,7.34163 -2.43097,16.10589 -1.44004,10.15311 -2.49792,16.43621 -2.91556,17.31631 -0.72531,1.52848 -2.76261,3.06291 -4.53817,3.41802 -0.95688,0.19138 -10.90014,-0.92798 -13.59859,-1.53084 -0.5471,-0.12223 -1.89146,0.67252 -4.50941,2.66588 -11.2627,8.57562 -24.34195,13.90917 -38.35741,15.64164 -4.40038,0.54395 -15.72658,0.43298 -19.853658,-0.19451 z"
style="fill:#4972a5;fill-opacity:1;stroke:#4972a5;stroke-width:0.377976;stroke-opacity:1" />
</g>
</svg>
<div>&nbsp;&nbsp;&nbsp;Loading ...</div>
</div>
</div>
<div id="app" class="noshow"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
<script>
window.supported = true;
</script>
</body> </body>
</html> </html>

@ -56,10 +56,94 @@
/> />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NextGraph</title> <title>NextGraph</title>
<meta property="og:title" content="NextGraph App - The New Internet Platform" />
<meta property="og:image" content="https://nextgraph.org/card.png"/>
<meta name="twitter:image" content="https://nextgraph.org/card-twitter.png"/>
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:description" content="Decentralized, encrypted and local-first platform and framework, towards the better internet we all deserve! Features a social network, shared documents, productivity tools, an app store, and more! You can use NextGraph for free, and it works online and offline. With NextGraph you own your data and software, having your privacy respected, while enjoying high-quality apps for your daily use. Try it now! Developers can build new web3.0 local-first apps with our open source framework, based on open standards, with CRDTs, E2EE, Semantic Web, RDF, SPARQL, JSON, Markdown, Svelte, React, JavaScript, Rust, etc... ActivityPub and Solid compatible." />
<meta name="description" content="Decentralized, encrypted and local-first platform and framework, towards the better internet we all deserve! Features a social network, shared documents, productivity tools, an app store, and more! You can use NextGraph for free, and it works online and offline. With NextGraph you own your data and software, having your privacy respected, while enjoying high-quality apps for your daily use. Try it now! Developers can build new web3.0 local-first apps with our open source framework, based on open standards, with CRDTs, E2EE, Semantic Web, RDF, SPARQL, JSON, Markdown, Svelte, React, JavaScript, Rust, etc... ActivityPub and Solid compatible.">
</head> </head>
<style>
.splashing {
height: 95vh;
width:100%;
display: flex;
justify-content: center;
align-items: center;
}
.noshow {
display: none !important;
}
.error-no-wasm-hidden {
display:none;
}
</style>
<body> <body>
<div id="app"></div> <div id="splash" class="splashing">
<div style="flex-direction: column;justify-content: center;color:#4972a5;width:100%;text-align:center;font-family: Inter, Avenir, Helvetica, Arial, sans-serif;">
<svg
style="width:100px;height:100px;margin: 0 auto 20px ;display:flex;"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 225 225"
>
<g>
<circle
r="106.98013"
cy="112.90476"
cx="109.88096"
style="fill:#ffffff;stroke:none;stroke-width:0.268375" />
<path
d="M 98.343352,190.26108 C 80.403778,187.53354 65.011938,179.57839 52.608228,166.62327 38.602093,151.99448 31.178059,133.41381 31.178059,112.98841 c 0,-10.21889 1.700058,-19.44396 5.221234,-28.332119 4.28678,-10.820699 10.037295,-19.39063 18.535095,-27.62263 4.72982,-4.58187 6.60687,-6.10643 11.28099,-9.16256 11.89869,-7.779841 24.173884,-11.879991 38.095802,-12.724761 19.80437,-1.2017 39.11165,5.11306 54.60284,17.858751 1.50718,1.24006 2.72951,2.35934 2.71628,2.48729 -0.0132,0.12795 -3.85821,3.63443 -8.54442,7.79217 -4.6862,4.157729 -10.04724,8.96276 -11.91342,10.677819 -1.86617,1.715071 -3.54094,3.11831 -3.7217,3.11831 -0.18075,0 -1.39985,-0.745188 -2.70911,-1.655969 -7.53011,-5.23834 -15.99428,-7.82188 -25.62597,-7.82188 -12.731628,0 -23.249192,4.3379 -32.143882,13.257541 -6.39594,6.413868 -10.70387,14.555268 -12.50018,23.623578 -0.69099,3.48832 -0.68968,13.53072 0.002,17.00893 3.70508,18.62577 18.31886,33.10194 36.642322,36.29729 4.16439,0.72621 11.98099,0.71223 15.98975,-0.0286 14.03187,-2.59311 25.86047,-11.36806 32.26533,-23.93578 0.77379,-1.51834 1.26018,-2.88461 1.08086,-3.03616 -0.17934,-0.15156 -6.87448,-1.1779 -14.87813,-2.28078 -9.7795,-1.34758 -14.92353,-2.21379 -15.68471,-2.64117 -1.52067,-0.85379 -2.83611,-2.88806 -2.83611,-4.3859 0,-1.1732 2.02687,-15.86876 2.49085,-18.05962 0.29676,-1.40127 2.42559,-3.4934 3.84317,-3.77691 0.62227,-0.12445 8.82712,0.85555 18.28065,2.18348 9.43343,1.32511 17.26269,2.29453 17.39833,2.15427 0.13566,-0.14026 1.11808,-6.54833 2.18313,-14.24014 1.10778,-8.000208 2.20407,-14.60184 2.56177,-15.426229 0.34392,-0.792599 1.11019,-1.849131 1.70287,-2.34782 2.06321,-1.736079 3.1433,-1.785011 12.20439,-0.55291 9.63637,1.310309 10.70873,1.56224 12.28077,2.88503 1.64359,1.382979 2.2732,2.810909 2.25906,5.123309 -0.007,1.10173 -0.92172,8.29645 -2.03332,15.98826 -1.11158,7.69182 -1.97159,14.04091 -1.91113,14.1091 0.0605,0.0682 7.16644,1.11143 15.79109,2.31832 11.10566,1.55407 16.00827,2.38757 16.80223,2.85657 1.53015,0.90389 2.48023,2.64785 2.45017,4.49756 -0.0462,2.84349 -2.41252,18.12279 -2.97521,19.21089 -0.66164,1.27949 -2.60244,2.54696 -3.92109,2.56074 -0.51973,0.005 -7.87449,-0.95937 -16.34391,-2.144 -8.46944,-1.18464 -15.47588,-2.077 -15.56986,-1.98301 -0.094,0.094 -1.18792,7.34163 -2.43097,16.10589 -1.44004,10.15311 -2.49792,16.43621 -2.91556,17.31631 -0.72531,1.52848 -2.76261,3.06291 -4.53817,3.41802 -0.95688,0.19138 -10.90014,-0.92798 -13.59859,-1.53084 -0.5471,-0.12223 -1.89146,0.67252 -4.50941,2.66588 -11.2627,8.57562 -24.34195,13.90917 -38.35741,15.64164 -4.40038,0.54395 -15.72658,0.43298 -19.853658,-0.19451 z"
style="fill:#4972a5;fill-opacity:1;stroke:#4972a5;stroke-width:0.377976;stroke-opacity:1" />
</g>
</svg>
<div class="noshow" id="app-loading">&nbsp;&nbsp;&nbsp;Loading ...</div>
<div id="error-no-wasm" class="error-no-wasm-hidden">
Your browser is too old and does not support NextGraph. <br/>Please upgrade to a newer version of this browser,<br/> try with another browser,<br/> <br/>or <a href="https://nextgraph.org/download">install our native apps on <br/>
Linux, macOS, Windows desktops and laptops,<br/> and iOS, Android mobiles.</a><br/><br/>If you are using jshelter or another javascript protection mechanism, please deactivate it as we need access to the WebWorker facility of your browser.
</div>
<noscript style="display:grid;">
NextGraph cannot load as Javascript is deactivated.<br/>
You can use the CLI ngcli to access the Documents in the terminal<br/>
Or use the native apps for Linux, macOS, Windows, Android, iOS<br/>
Or setup an SSR static generator like Astro for read-only access.
</noscript>
</div>
</div>
<div id="app" class="noshow">
</div>
<script>
const supported = (() => {
if (RegExp().hasIndices === undefined) {console.log("no RegExp().hasIndices");return false;}
try {
if (Worker === undefined) {console.log("no Worker");return false;}
new Worker(URL.createObjectURL(new Blob([';'], {type: 'application/javascript'})));
if (typeof WebAssembly === "object"
&& typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
if (module instanceof WebAssembly.Module)
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
else {
console.log("no WebAssembly module");
}
}
} catch (e) {
{console.log(e);return false;}
}
console.log("no WebAssembly");
return false;
})();
if (!supported ) {
window.document.getElementById("error-no-wasm").className="";
}
window.supported = supported;
if (supported) window.document.getElementById("app-loading").className="";
</script>
<!-- # INSERT SCRIPT HERE -->
<script type="module" src="/src/main-web.ts"></script> <script type="module" src="/src/main-web.ts"></script>
</body> </body>
</html> </html>

@ -1,7 +1,7 @@
{ {
"name": "ng-app", "name": "ng-app",
"private": true, "private": true,
"version": "0.1.0", "version": "0.1.1-alpha",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "shx cp index-native.html index.html && vite", "dev": "shx cp index-native.html index.html && vite",
@ -16,17 +16,77 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@automerge/automerge": "^2.2.8",
"@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0",
"@codemirror/lang-css": "^6.0.1",
"@codemirror/lang-html": "^6.2.0",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-rust": "^6.0.1",
"@codemirror/language": "^6.10.2",
"@codemirror/legacy-modes": "^6.4.0",
"@codemirror/lint": "^6.8.1",
"@codemirror/search": "^6.5.6",
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.29.1",
"@lezer/common": "^1.0.0",
"@lezer/highlight": "^1.0.0",
"@lezer/javascript": "^1.2.0",
"@lezer/lr": "^1.0.0",
"@milkdown-lab/plugin-split-editing": "^1.3.1",
"@milkdown/core": "^7.4.0",
"@milkdown/ctx": "^7.2.0",
"@milkdown/plugin-collab": "^7.4.0",
"@milkdown/plugin-emoji": "^7.4.0",
"@milkdown/plugin-indent": "^7.4.0",
"@milkdown/plugin-math": "^7.4.0",
"@milkdown/plugin-prism": "^7.4.0",
"@milkdown/plugin-slash": "^7.4.0",
"@milkdown/preset-commonmark": "^7.4.0",
"@milkdown/preset-gfm": "^7.4.0",
"@milkdown/prose": "^7.2.0",
"@milkdown/theme-nord": "^7.4.0",
"@milkdown/transformer": "^7.2.0",
"@milkdown/utils": "^7.4.0",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@replit/codemirror-lang-svelte": "^6.0.0",
"@sindresorhus/is": "4.6.0",
"@tailwindcss/typography": "^0.5.13",
"@tauri-apps/api": "2.0.0-alpha.8", "@tauri-apps/api": "2.0.0-alpha.8",
"@tauri-apps/plugin-barcode-scanner": "2.0.0-alpha.0",
"@tauri-apps/plugin-window": "2.0.0-alpha.1", "@tauri-apps/plugin-window": "2.0.0-alpha.1",
"async-proxy": "^0.4.1", "async-proxy": "^0.4.1",
"char-regex": "1.0.2",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"codemirror": "^6.0.1",
"debug": "^4.3.6",
"emojilib": "2.4.0",
"extend": "3.0.2",
"flowbite": "^1.6.5", "flowbite": "^1.6.5",
"flowbite-svelte": "^0.43.3", "flowbite-svelte": "^0.43.3",
"ng-sdk-js": "workspace:^0.1.0-preview.1", "html5-qrcode": "^2.3.8",
"immutable-json-patch": "^6.0.1",
"katex": "^0.16.11",
"lodash.debounce": "4.0.8",
"ng-sdk-js": "workspace:^0.1.1-alpha",
"prism-themes": "^1.9.0",
"prosemirror-model": "^1.7.1",
"prosemirror-state": "^1.2.3",
"prosemirror-svelte": "^0.2.4",
"prosemirror-view": "^1.9.10",
"skin-tone": "2.0.0",
"style-mod": "^4.1.2",
"svelte-codemirror-editor": "^1.4.0",
"svelte-i18n": "^4.0.0", "svelte-i18n": "^4.0.0",
"svelte-inview": "^4.0.2",
"svelte-jsoneditor": "^0.23.8",
"svelte-spa-router": "^3.3.0", "svelte-spa-router": "^3.3.0",
"vite-plugin-top-level-await": "^1.3.1" "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",
"yjs": "^13.6.18"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^2.0.0",
@ -36,14 +96,18 @@
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"highlight.js": "^11.10.0",
"internal-ip": "^7.0.0", "internal-ip": "^7.0.0",
"node-gzip": "^1.1.2", "node-gzip": "^1.1.2",
"postcss": "^8.4.23", "postcss": "^8.4.23",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.5",
"shx": "^0.3.4", "shx": "^0.3.4",
"svelte": "^3.54.0", "svelte": "^3.54.0",
"svelte-check": "^3.0.0", "svelte-check": "^3.0.0",
"svelte-heros-v2": "^0.10.12", "svelte-heros-v2": "^0.10.12",
"svelte-highlight": "^7.7.0",
"svelte-preprocess": "^5.0.3", "svelte-preprocess": "^5.0.3",
"svelte-time": "^0.8.0", "svelte-time": "^0.8.0",
"tailwindcss": "^3.3.1", "tailwindcss": "^3.3.1",

@ -21,17 +21,21 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2.0.0-alpha.8", features = [] } tauri-build = { version = "2.0.0-alpha.8", features = [] }
# tauri-macros = { version = "=2.0.0-alpha.6" } # tauri-macros = { version = "=2.0.0-alpha.6" }
# tauri-codegen = { version = "=2.0.0-alpha.6" } # tauri-codegen = { version = "=2.0.0-alpha.6" }
# tauri-utils = { version = "=2.0.0-alpha.6" } tauri-utils = { version = "=2.0.0-alpha.7" }
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_bare = "0.5.0"
serde_json = "1.0" serde_json = "1.0"
serde_bytes = "0.11.7" serde_bytes = "0.11.7"
async-std = { version = "1.12.0", features = ["attributes", "unstable"] } async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
sys-locale = { version = "0.3.1" } sys-locale = { version = "0.3.1" }
zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] } ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] }
tauri = { version = "2.0.0-alpha.14", features = [] } tauri = { version = "2.0.0-alpha.14", features = [] }
# add the "devtools" feature if devtools in the production build should be activated
tauri-plugin-window = "2.0.0-alpha.1" tauri-plugin-window = "2.0.0-alpha.1"
tauri-plugin-barcode-scanner = "=2.0.0-alpha.0"
# tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" } # tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" }
# tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] } # tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] }
# tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup", features = [] } # tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup", features = [] }
@ -39,6 +43,7 @@ ng-repo = { path = "../../ng-repo" }
ng-net = { path = "../../ng-net" } ng-net = { path = "../../ng-net" }
ng-wallet = { path = "../../ng-wallet" } ng-wallet = { path = "../../ng-wallet" }
nextgraph = { path = "../../nextgraph" } nextgraph = { path = "../../nextgraph" }
oxrdf = { git = "https://git.nextgraph.org/NextGraph/oxigraph.git", branch="main", features = ["rdf-star", "oxsdatatypes"] }
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem # this feature is used for production builds or when `devPath` points to the filesystem

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<application <application
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"

@ -11,12 +11,14 @@ use std::collections::HashMap;
use std::fs::write; use std::fs::write;
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use oxrdf::Triple;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use sys_locale::get_locales; use sys_locale::get_locales;
use tauri::scope::ipc::RemoteDomainAccessScope; use tauri::scope::ipc::RemoteDomainAccessScope;
use tauri::utils::config::WindowConfig; use tauri::utils::config::WindowConfig;
use tauri::{path::BaseDirectory, App, Manager}; use tauri::{path::BaseDirectory, App, Manager};
use zeroize::Zeroize;
use ng_repo::errors::NgError; use ng_repo::errors::NgError;
use ng_repo::log::*; use ng_repo::log::*;
@ -111,12 +113,12 @@ async fn wallet_open_with_pazzle(
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn wallet_open_with_mnemonic( async fn wallet_open_with_mnemonic(
wallet: Wallet, wallet: Wallet,
mnemonic: Vec<u16>, mnemonic: [u16; 12],
pin: [u8; 4], pin: [u8; 4],
_app: tauri::AppHandle, _app: tauri::AppHandle,
) -> Result<SensitiveWallet, String> { ) -> Result<SensitiveWallet, String> {
let wallet = nextgraph::local_broker::wallet_open_with_mnemonic(&wallet, mnemonic, pin) let wallet =
.map_err(|e| e.to_string())?; ng_wallet::open_wallet_with_mnemonic(&wallet, mnemonic, pin).map_err(|e| e.to_string())?;
Ok(wallet) Ok(wallet)
} }
@ -127,7 +129,8 @@ async fn wallet_open_with_mnemonic_words(
pin: [u8; 4], pin: [u8; 4],
_app: tauri::AppHandle, _app: tauri::AppHandle,
) -> Result<SensitiveWallet, String> { ) -> Result<SensitiveWallet, String> {
let wallet = nextgraph::local_broker::wallet_open_with_mnemonic_words(&wallet, &mnemonic_words, pin) let wallet =
nextgraph::local_broker::wallet_open_with_mnemonic_words(&wallet, &mnemonic_words, pin)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
Ok(wallet) Ok(wallet)
} }
@ -158,6 +161,7 @@ async fn wallet_create(
//log_debug!("wallet_create from rust {:?}", params); //log_debug!("wallet_create from rust {:?}", params);
params.result_with_wallet_file = !params.local_save; params.result_with_wallet_file = !params.local_save;
let local_save = params.local_save; let local_save = params.local_save;
let pdf = params.pdf;
let mut cwr = nextgraph::local_broker::wallet_create_v0(params) let mut cwr = nextgraph::local_broker::wallet_create_v0(params)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@ -171,8 +175,22 @@ async fn wallet_create(
) )
.unwrap(); .unwrap();
let _r = write(path, &cwr.wallet_file); let _r = write(path, &cwr.wallet_file);
cwr.wallet_file.zeroize();
cwr.wallet_file = vec![]; cwr.wallet_file = vec![];
} }
if pdf {
// save pdf file to Downloads folder
let path = app
.path()
.resolve(
format!("wallet-{}.pdf", cwr.wallet_name),
BaseDirectory::Download,
)
.unwrap();
let _r = write(path, &cwr.pdf_file);
cwr.pdf_file.zeroize();
cwr.pdf_file = vec![];
}
Ok(cwr) Ok(cwr)
} }
@ -205,6 +223,55 @@ async fn wallet_import(
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
#[tauri::command(rename_all = "snake_case")]
async fn wallet_export_rendezvous(
session_id: u64,
code: String,
_app: tauri::AppHandle,
) -> Result<(), String> {
nextgraph::local_broker::wallet_export_rendezvous(session_id, code)
.await
.map_err(|e: NgError| e.to_string())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_export_get_qrcode(
session_id: u64,
size: u32,
_app: tauri::AppHandle,
) -> Result<String, String> {
nextgraph::local_broker::wallet_export_get_qrcode(session_id, size)
.await
.map_err(|e: NgError| e.to_string())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_export_get_textcode(
session_id: u64,
_app: tauri::AppHandle,
) -> Result<String, String> {
nextgraph::local_broker::wallet_export_get_textcode(session_id)
.await
.map_err(|e: NgError| e.to_string())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_import_rendezvous(
size: u32,
_app: tauri::AppHandle,
) -> Result<(String, String), String> {
nextgraph::local_broker::wallet_import_rendezvous(size)
.await
.map_err(|e: NgError| e.to_string())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_import_from_code(code: String, _app: tauri::AppHandle) -> Result<Wallet, String> {
nextgraph::local_broker::wallet_import_from_code(code)
.await
.map_err(|e: NgError| e.to_string())
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn get_wallets( async fn get_wallets(
app: tauri::AppHandle, app: tauri::AppHandle,
@ -287,6 +354,25 @@ async fn decode_invitation(invite: String) -> Option<Invitation> {
decode_invitation_string(invite) decode_invitation_string(invite)
} }
#[tauri::command(rename_all = "snake_case")]
async fn file_get(
session_id: u64,
stream_id: &str,
reference: BlockRef,
branch_nuri: String,
app: tauri::AppHandle,
) -> Result<(), String> {
let branch_nuri =
NuriV0::new_from(&branch_nuri).map_err(|e| format!("branch_nuri: {}", e.to_string()))?;
let mut nuri = NuriV0::new_from_obj_ref(&reference);
nuri.copy_target_from(&branch_nuri);
let mut request = AppRequest::new(AppRequestCommandV0::FileGet, nuri, None);
request.set_session_id(session_id);
app_request_stream(request, stream_id, app).await
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn app_request_stream( async fn app_request_stream(
request: AppRequest, request: AppRequest,
@ -314,6 +400,7 @@ async fn app_request_stream(
main_window: tauri::Window, main_window: tauri::Window,
) -> ResultSend<()> { ) -> ResultSend<()> {
while let Some(app_response) = reader.next().await { while let Some(app_response) = reader.next().await {
let app_response = nextgraph::verifier::prepare_app_response_for_js(app_response)?;
main_window.emit(&stream_id, app_response).unwrap(); main_window.emit(&stream_id, app_response).unwrap();
} }
@ -330,6 +417,99 @@ async fn app_request_stream(
Ok(()) Ok(())
} }
#[tauri::command(rename_all = "snake_case")]
async fn discrete_update(
session_id: u64,
update: serde_bytes::ByteBuf,
heads: Vec<String>,
crdt: String,
nuri: String,
) -> Result<(), String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_update(),
nuri,
payload: Some(
AppRequestPayload::new_discrete_update(heads, crdt, update.into_vec())
.map_err(|e| format!("Deserialization error of heads: {e}"))?,
),
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Error(e)) = res {
Err(e)
} else {
Ok(())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn file_save_to_downloads(
session_id: u64,
reference: ObjectRef,
filename: String,
branch_nuri: String,
app: tauri::AppHandle,
) -> Result<(), String> {
let branch_nuri =
NuriV0::new_from(&branch_nuri).map_err(|e| format!("branch_nuri: {}", e.to_string()))?;
let mut nuri = NuriV0::new_from_obj_ref(&reference);
nuri.copy_target_from(&branch_nuri);
let mut request = AppRequest::new(AppRequestCommandV0::FileGet, nuri, None);
request.set_session_id(session_id);
let (mut reader, _cancel) = nextgraph::local_broker::app_request_stream(request)
.await
.map_err(|e| e.to_string())?;
let mut file_vec: Vec<u8> = vec![];
while let Some(app_response) = reader.next().await {
match app_response {
AppResponse::V0(AppResponseV0::FileMeta(filemeta)) => {
file_vec = Vec::with_capacity(filemeta.size as usize);
}
AppResponse::V0(AppResponseV0::FileBinary(mut bin)) => {
if !bin.is_empty() {
file_vec.append(&mut bin);
}
}
AppResponse::V0(AppResponseV0::EndOfStream) => break,
_ => return Err("invalid response".to_string()),
}
}
let mut i: usize = 0;
loop {
let dest_filename = if i == 0 {
filename.clone()
} else {
filename
.rsplit_once(".")
.map(|(l, r)| format!("{l} ({}).{r}", i.to_string()))
.or_else(|| Some(format!("{filename} ({})", i.to_string())))
.unwrap()
};
let path = app
.path()
.resolve(dest_filename, BaseDirectory::Download)
.unwrap();
if path.exists() {
i = i + 1;
} else {
write(path, &file_vec).map_err(|e| e.to_string())?;
break;
}
}
Ok(())
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn doc_fetch_private_subscribe() -> Result<AppRequest, String> { async fn doc_fetch_private_subscribe() -> Result<AppRequest, String> {
let request = AppRequest::new( let request = AppRequest::new(
@ -341,17 +521,166 @@ async fn doc_fetch_private_subscribe() -> Result<AppRequest, String> {
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn doc_fetch_repo_subscribe(repo_id: String) -> Result<AppRequest, String> { async fn doc_fetch_repo_subscribe(repo_o: String) -> Result<AppRequest, String> {
let request = AppRequest::new( let request = AppRequest::new(
AppRequestCommandV0::Fetch(AppFetchContentV0::get_or_subscribe(true)), AppRequestCommandV0::Fetch(AppFetchContentV0::get_or_subscribe(true)),
NuriV0::new_repo_target_from_string(repo_id).map_err(|e| e.to_string())?, NuriV0::new_from(&repo_o).map_err(|e| e.to_string())?,
None, None,
); );
Ok(request) Ok(request)
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn app_request(request: AppRequest, _app: tauri::AppHandle) -> Result<AppResponse, String> { async fn branch_history(session_id: u64, nuri: String) -> Result<AppHistoryJs, String> {
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_history(),
nuri: NuriV0::new_from(&nuri).map_err(|e| e.to_string())?,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
let AppResponse::V0(res) = res;
//log_debug!("{:?}", res);
match res {
AppResponseV0::History(s) => Ok(s.to_js()),
_ => Err("invalid response".to_string()),
}
}
#[tauri::command(rename_all = "snake_case")]
async fn update_header(
session_id: u64,
nuri: String,
title: Option<String>,
about: Option<String>,
) -> Result<(), String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_header(),
nuri,
payload: Some(AppRequestPayload::new_header(title, about)),
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Error(e)) = res {
Err(e)
} else {
Ok(())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn fetch_header(session_id: u64, nuri: String) -> Result<AppHeader, String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_fetch_header(),
nuri,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
match res {
AppResponse::V0(AppResponseV0::Error(e)) => Err(e),
AppResponse::V0(AppResponseV0::Header(h)) => Ok(h),
_ => Err("invalid response".to_string()),
}
}
#[tauri::command(rename_all = "snake_case")]
async fn sparql_update(
session_id: u64,
sparql: String,
nuri: Option<String>,
) -> Result<(), String> {
let (nuri, base) = if let Some(n) = nuri {
let nuri = NuriV0::new_from(&n).map_err(|e| e.to_string())?;
let b = nuri.repo();
(nuri, Some(b))
} else {
(NuriV0::new_private_store_target(), None)
};
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_write_query(),
nuri,
payload: Some(AppRequestPayload::new_sparql_query(sparql, base)),
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Error(e)) = res {
Err(e)
} else {
Ok(())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn sparql_query(
session_id: u64,
sparql: String,
base: Option<String>,
nuri: Option<String>,
) -> Result<Value, String> {
let nuri = if nuri.is_some() {
NuriV0::new_from(&nuri.unwrap()).map_err(|e| e.to_string())?
} else {
NuriV0::new_entire_user_site()
};
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_read_query(),
nuri,
payload: Some(AppRequestPayload::new_sparql_query(sparql, base)),
session_id,
});
let response = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
let AppResponse::V0(res) = response;
match res {
AppResponseV0::False => return Ok(Value::Bool(false)),
AppResponseV0::True => return Ok(Value::Bool(true)),
AppResponseV0::Graph(graph) => {
let triples: Vec<Triple> = serde_bare::from_slice(&graph)
.map_err(|_| "Deserialization error of graph".to_string())?;
Ok(Value::Array(
triples
.into_iter()
.map(|t| Value::String(t.to_string()))
.collect(),
))
}
AppResponseV0::QueryResult(buf) => {
let string = String::from_utf8(buf)
.map_err(|_| "Deserialization error of JSON QueryResult String".to_string())?;
Ok(serde_json::from_str(&string)
.map_err(|_| "Parsing error of JSON QueryResult String".to_string())?)
}
AppResponseV0::Error(e) => Err(e.to_string().into()),
_ => Err("invalid AppResponse".to_string().into()),
}
}
#[tauri::command(rename_all = "snake_case")]
async fn app_request(request: AppRequest) -> Result<AppResponse, String> {
//log_debug!("app request {:?}", request); //log_debug!("app request {:?}", request);
nextgraph::local_broker::app_request(request) nextgraph::local_broker::app_request(request)
@ -359,19 +688,164 @@ async fn app_request(request: AppRequest, _app: tauri::AppHandle) -> Result<AppR
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
} }
#[tauri::command(rename_all = "snake_case")]
async fn signature_status(
session_id: u64,
nuri: Option<String>,
) -> Result<Vec<(String, Option<String>, bool)>, String> {
let nuri = if nuri.is_some() {
NuriV0::new_from(&nuri.unwrap()).map_err(|e| e.to_string())?
} else {
NuriV0::new_private_store_target()
};
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_signature_status(),
nuri,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
let AppResponse::V0(res) = res;
//log_debug!("{:?}", res);
match res {
AppResponseV0::SignatureStatus(s) => Ok(s),
_ => Err("invalid response".to_string()),
}
}
#[tauri::command(rename_all = "snake_case")]
async fn signed_snapshot_request(session_id: u64, nuri: Option<String>) -> Result<bool, String> {
let nuri = if nuri.is_some() {
NuriV0::new_from(&nuri.unwrap()).map_err(|e| e.to_string())?
} else {
NuriV0::new_private_store_target()
};
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_signed_snapshot_request(),
nuri,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
let AppResponse::V0(res) = res;
//log_debug!("{:?}", res);
match res {
AppResponseV0::True => Ok(true),
AppResponseV0::False => Ok(false),
AppResponseV0::Error(e) => Err(e),
_ => Err("invalid response".to_string()),
}
}
#[tauri::command(rename_all = "snake_case")]
async fn signature_request(session_id: u64, nuri: Option<String>) -> Result<bool, String> {
let nuri = if nuri.is_some() {
NuriV0::new_from(&nuri.unwrap()).map_err(|e| e.to_string())?
} else {
NuriV0::new_private_store_target()
};
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_signature_request(),
nuri,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
let AppResponse::V0(res) = res;
//log_debug!("{:?}", res);
match res {
AppResponseV0::True => Ok(true),
AppResponseV0::False => Ok(false),
AppResponseV0::Error(e) => Err(e),
_ => Err("invalid response".to_string()),
}
}
#[tauri::command(rename_all = "snake_case")]
async fn doc_create(
session_id: u64,
crdt: String,
class_name: String,
store_repo: StoreRepo,
destination: String,
) -> Result<String, String> {
let class = BranchCrdt::from(crdt, class_name).map_err(|e| e.to_string())?;
let destination = DocCreateDestination::from(destination).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
session_id,
command: AppRequestCommandV0::new_create(),
nuri: NuriV0::new_empty(),
payload: Some(AppRequestPayload::V0(AppRequestPayloadV0::Create(
DocCreate {
store: store_repo,
class,
destination,
},
))),
});
let response = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Nuri(nuri)) = response {
Ok(nuri)
} else {
Err("invalid response".to_string())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn app_request_with_nuri_command(
nuri: String,
command: AppRequestCommandV0,
session_id: u64,
payload: Option<AppRequestPayloadV0>,
) -> Result<AppResponse, String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let payload = payload.map(|p| AppRequestPayload::V0(p));
let request = AppRequest::V0(AppRequestV0 {
session_id,
command,
nuri,
payload,
});
app_request(request).await
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn upload_chunk( async fn upload_chunk(
session_id: u64, session_id: u64,
upload_id: u32, upload_id: u32,
chunk: serde_bytes::ByteBuf, chunk: serde_bytes::ByteBuf,
nuri: NuriV0, nuri: String,
_app: tauri::AppHandle, _app: tauri::AppHandle,
) -> Result<AppResponse, String> { ) -> Result<AppResponse, String> {
//log_debug!("upload_chunk {:?}", chunk); //log_debug!("upload_chunk {:?}", chunk);
let mut request = AppRequest::new( let mut request = AppRequest::new(
AppRequestCommandV0::FilePut, AppRequestCommandV0::FilePut,
nuri, NuriV0::new_from(&nuri).map_err(|e| e.to_string())?,
Some(AppRequestPayload::V0( Some(AppRequestPayload::V0(
AppRequestPayloadV0::RandomAccessFilePutChunk((upload_id, chunk)), AppRequestPayloadV0::RandomAccessFilePutChunk((upload_id, chunk)),
)), )),
@ -491,6 +965,11 @@ fn client_info_rust() -> Result<Value, String> {
Ok(ng_repo::os_info::get_os_info()) Ok(ng_repo::os_info::get_os_info())
} }
#[tauri::command(rename_all = "snake_case")]
fn get_device_name() -> Result<String, String> {
Ok(nextgraph::get_device_name())
}
#[derive(Default)] #[derive(Default)]
pub struct AppBuilder { pub struct AppBuilder {
setup: Option<SetupHook>, setup: Option<SetupHook>,
@ -517,7 +996,9 @@ impl AppBuilder {
pub fn run(self) { pub fn run(self) {
let setup = self.setup; let setup = self.setup;
tauri::Builder::default()
#[allow(unused_mut)]
let mut builder = tauri::Builder::default()
.setup(move |app| { .setup(move |app| {
if let Some(setup) = setup { if let Some(setup) = setup {
(setup)(app)?; (setup)(app)?;
@ -533,7 +1014,14 @@ impl AppBuilder {
} }
Ok(()) Ok(())
}) })
.plugin(tauri_plugin_window::init()) .plugin(tauri_plugin_window::init());
#[cfg(mobile)]
{
builder = builder.plugin(tauri_plugin_barcode_scanner::init());
}
builder
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
test, test,
locales, locales,
@ -547,6 +1035,11 @@ impl AppBuilder {
wallet_read_file, wallet_read_file,
wallet_get_file, wallet_get_file,
wallet_import, wallet_import,
wallet_export_rendezvous,
wallet_export_get_qrcode,
wallet_export_get_textcode,
wallet_import_rendezvous,
wallet_import_from_code,
wallet_close, wallet_close,
encode_create_account, encode_create_account,
session_start, session_start,
@ -561,10 +1054,24 @@ impl AppBuilder {
client_info_rust, client_info_rust,
doc_fetch_private_subscribe, doc_fetch_private_subscribe,
doc_fetch_repo_subscribe, doc_fetch_repo_subscribe,
doc_create,
cancel_stream, cancel_stream,
discrete_update,
app_request_stream, app_request_stream,
file_get,
file_save_to_downloads,
app_request, app_request,
app_request_with_nuri_command,
upload_chunk, upload_chunk,
get_device_name,
sparql_query,
sparql_update,
branch_history,
signature_status,
signature_request,
signed_snapshot_request,
update_header,
fetch_header,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "Nextgraph", "productName": "Nextgraph",
"version": "0.1.0" "version": "0.1.1-alpha"
}, },
"tauri": { "tauri": {
"bundle": { "bundle": {

@ -35,22 +35,32 @@
import User from "./routes/User.svelte"; import User from "./routes/User.svelte";
import UserRegistered from "./routes/UserRegistered.svelte"; import UserRegistered from "./routes/UserRegistered.svelte";
import Install from "./routes/Install.svelte"; import Install from "./routes/Install.svelte";
import ScanQR from "./routes/ScanQR.svelte";
import Shared from "./routes/Shared.svelte";
import Site from "./routes/Site.svelte";
import ng from "./api"; import ng from "./api";
import AccountInfo from "./routes/AccountInfo.svelte"; import AccountInfo from "./routes/AccountInfo.svelte";
import WalletLoginQr from "./routes/WalletLoginQr.svelte";
import WalletLoginTextCode from "./routes/WalletLoginTextCode.svelte";
const routes = new Map(); const routes = new Map();
routes.set("/", Home); routes.set("/", Home);
routes.set("/test", Test); routes.set("/test", Test);
routes.set("/wallet/login", WalletLogin); routes.set("/wallet/login", WalletLogin);
routes.set("/wallet/login-qr", WalletLoginQr);
routes.set("/wallet/login-text-code", WalletLoginTextCode);
routes.set("/wallet/create", WalletCreate); routes.set("/wallet/create", WalletCreate);
routes.set("/i/:invitation", Invitation); routes.set("/i/:invitation", Invitation);
routes.set("/user", User); routes.set("/user", User);
routes.set("/user/registered", UserRegistered); routes.set("/user/registered", UserRegistered);
routes.set("/wallet", WalletInfo); routes.set("/wallet", WalletInfo);
routes.set("/user/accounts", AccountInfo); routes.set("/user/accounts", AccountInfo);
routes.set("/wallet/scanqr", ScanQR);
if (import.meta.env.NG_APP_WEB) routes.set("/install", Install); if (import.meta.env.NG_APP_WEB) routes.set("/install", Install);
routes.set(/^\/did:ng(.*)/i, NURI); routes.set("/shared", Shared);
routes.set("/site", Site);
routes.set(/^\/did:ng:(.*)/i, NURI);
routes.set("*", NotFound); routes.set("*", NotFound);
let unsubscribe = () => {}; let unsubscribe = () => {};
@ -64,11 +74,18 @@
// }; // };
onMount(async () => { onMount(async () => {
//console.log("hide splash", window.supported);
if (window.supported) {
window.document.getElementById("splash").className="noshow";
window.document.getElementById("app").className="";
}
//window.document.getElementById("splash").className="splash-loaded";
try { try {
await disconnections_subscribe(); await disconnections_subscribe();
await select_default_lang(); await select_default_lang();
} catch (e) { } catch (e) {
console.error(e); console.warn(e);
//console.log("called disconnections_subscribe twice"); //console.log("called disconnections_subscribe twice");
} }
let tauri_platform = import.meta.env.TAURI_PLATFORM; let tauri_platform = import.meta.env.TAURI_PLATFORM;
@ -134,7 +151,7 @@
window.wallet_channel = wallet_channel; window.wallet_channel = wallet_channel;
wallet_channel.postMessage({ cmd: "startup" }, location.href); wallet_channel.postMessage({ cmd: "startup" }, location.href);
wallet_channel.onmessage = async (event) => { wallet_channel.onmessage = async (event) => {
//console.log(event.data.cmd, event.data); // console.log(event.data.cmd, event.data);
if (!location.href.startsWith(event.origin)) return; if (!location.href.startsWith(event.origin)) return;
switch (event.data.cmd) { switch (event.data.cmd) {
case "startup": case "startup":
@ -262,6 +279,7 @@
} }
}); });
} }
}); });
onDestroy(() => { onDestroy(() => {
@ -286,5 +304,3 @@
{:else} {:else}
<Router {routes} /> <Router {routes} />
{/if} {/if}

@ -17,11 +17,17 @@ const mapping = {
"wallet_gen_shuffle_for_pin": [], "wallet_gen_shuffle_for_pin": [],
"wallet_open_with_pazzle": ["wallet","pazzle","pin"], "wallet_open_with_pazzle": ["wallet","pazzle","pin"],
"wallet_open_with_mnemonic_words": ["wallet","mnemonic_words","pin"], "wallet_open_with_mnemonic_words": ["wallet","mnemonic_words","pin"],
"wallet_open_with_mnemonic": ["wallet","mnemonic","pin"],
"wallet_was_opened": ["opened_wallet"], "wallet_was_opened": ["opened_wallet"],
"wallet_create": ["params"], "wallet_create": ["params"],
"wallet_read_file": ["file"], "wallet_read_file": ["file"],
"wallet_get_file": ["wallet_name"], "wallet_get_file": ["wallet_name"],
"wallet_import": ["encrypted_wallet","opened_wallet","in_memory"], "wallet_import": ["encrypted_wallet","opened_wallet","in_memory"],
"wallet_export_rendezvous": ["session_id", "code"],
"wallet_export_get_qrcode": ["session_id", "size"],
"wallet_export_get_textcode": ["session_id"],
"wallet_import_rendezvous": ["size"],
"wallet_import_from_code": ["code"],
"wallet_close": ["wallet_name"], "wallet_close": ["wallet_name"],
"encode_create_account": ["payload"], "encode_create_account": ["payload"],
"session_start": ["wallet_name","user"], "session_start": ["wallet_name","user"],
@ -32,10 +38,23 @@ const mapping = {
"decode_invitation": ["invite"], "decode_invitation": ["invite"],
"user_connect": ["info","user_id","location"], "user_connect": ["info","user_id","location"],
"user_disconnect": ["user_id"], "user_disconnect": ["user_id"],
"discrete_update": ["session_id", "update", "heads", "crdt", "nuri"],
"app_request": ["request"], "app_request": ["request"],
"app_request_with_nuri_command": ["nuri", "command", "session_id", "payload"],
"sparql_query": ["session_id","sparql","base","nuri"],
"sparql_update": ["session_id","sparql","nuri"],
"test": [ ], "test": [ ],
"get_device_name": [],
"doc_create": [ "session_id", "crdt", "class_name", "store_repo", "destination" ],
"doc_fetch_private_subscribe": [], "doc_fetch_private_subscribe": [],
"doc_fetch_repo_subscribe": ["repo_id"], "doc_fetch_repo_subscribe": ["repo_o"],
"branch_history": ["session_id", "nuri"],
"file_save_to_downloads": ["session_id", "reference", "filename", "branch_nuri"],
"signature_status": ["session_id", "nuri"],
"signed_snapshot_request": ["session_id", "nuri"],
"signature_request": ["session_id", "nuri"],
"update_header": ["session_id","nuri","title","about"],
"fetch_header": ["session_id", "nuri"]
} }
@ -60,11 +79,28 @@ const handler = {
// } else if (path[0] === "wallet_create") { // } else if (path[0] === "wallet_create") {
// let res = await Reflect.apply(sdk[path], caller, args); // let res = await Reflect.apply(sdk[path], caller, args);
// return res; // return res;
} else if (path[0] === "app_request_stream") {
let callback = args[1];
let new_callback = (event) => {
if (event.V0.State?.graph?.triples) {
let json_str = new TextDecoder().decode(event.V0.State.graph.triples);
event.V0.State.graph.triples = JSON.parse(json_str);
} else if (event.V0.Patch?.graph) {
let inserts_json_str = new TextDecoder().decode(event.V0.Patch.graph.inserts);
event.V0.Patch.graph.inserts = JSON.parse(inserts_json_str);
let removes_json_str = new TextDecoder().decode(event.V0.Patch.graph.removes);
event.V0.Patch.graph.removes = JSON.parse(removes_json_str);
}
callback(event).then(()=> {})
};
args[1] = new_callback;
return Reflect.apply(sdk[path], caller, args)
} else { } else {
return Reflect.apply(sdk[path], caller, args) return Reflect.apply(sdk[path], caller, args)
} }
} else { } else {
let tauri = await import("@tauri-apps/api/tauri"); let tauri = await import("@tauri-apps/api/tauri");
try {
if (path[0] === "client_info") { if (path[0] === "client_info") {
let from_rust = await tauri.invoke("client_info_rust",{}); let from_rust = await tauri.invoke("client_info_rust",{});
@ -98,6 +134,11 @@ const handler = {
}; };
//console.log(info,res); //console.log(info,res);
return res; return res;
} else if (path[0] === "get_device_name") {
let tauri_platform = import.meta.env.TAURI_PLATFORM;
if (tauri_platform == 'android') return "Android Phone";
else if (tauri_platform == 'ios') return "iPhone";
else return await tauri.invoke(path[0],{});
} else if (path[0] === "locales") { } else if (path[0] === "locales") {
let from_rust = await tauri.invoke("locales",{}); let from_rust = await tauri.invoke("locales",{});
let from_js = window.navigator.languages; let from_js = window.navigator.languages;
@ -130,7 +171,47 @@ const handler = {
} }
return ret; return ret;
} }
else if (path[0] === "app_request_stream") { else if (path[0] === "file_get") {
let stream_id = (lastStreamId += 1).toString();
//console.log("stream_id",stream_id);
let { getCurrent } = await import("@tauri-apps/plugin-window");
//let session_id = args[0];
let callback = args[3];
let unlisten = await getCurrent().listen(stream_id, async (event) => {
//console.log(event.payload);
if (event.payload.V0.FileBinary) {
event.payload.V0.FileBinary = Uint8Array.from(event.payload.V0.FileBinary);
}
let ret = callback(event.payload);
if (ret === true) {
await tauri.invoke("cancel_stream", {stream_id});
} else if (ret.then) {
ret.then(async (val)=> {
if (val === true) {
await tauri.invoke("cancel_stream", {stream_id});
}
});
}
})
try {
await tauri.invoke("file_get",{stream_id, session_id:args[0], reference: args[1], branch_nuri:args[2]});
} catch (e) {
unlisten();
await tauri.invoke("cancel_stream", {stream_id});
throw e;
}
return () => {
unlisten();
tauri.invoke("cancel_stream", {stream_id});
}
} else if (path[0] === "discrete_update") {
let arg = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
arg.update = Array.from(new Uint8Array(arg.update));
return await tauri.invoke(path[0],arg)
} else if (path[0] === "app_request_stream") {
let stream_id = (lastStreamId += 1).toString(); let stream_id = (lastStreamId += 1).toString();
//console.log("stream_id",stream_id); //console.log("stream_id",stream_id);
let { getCurrent } = await import("@tauri-apps/plugin-window"); let { getCurrent } = await import("@tauri-apps/plugin-window");
@ -138,15 +219,45 @@ const handler = {
let request = args[0]; let request = args[0];
let callback = args[1]; let callback = args[1];
let unlisten = await getCurrent().listen(stream_id, (event) => { let unlisten = await getCurrent().listen(stream_id, async (event) => {
//console.log(event.payload); //console.log(event.payload);
if (event.payload.V0.FileBinary) { if (event.payload.V0.FileBinary) {
event.payload.V0.FileBinary = Uint8Array.from(event.payload.V0.FileBinary); event.payload.V0.FileBinary = Uint8Array.from(event.payload.V0.FileBinary);
} }
callback(event.payload).then(()=> {}) if (event.payload.V0.State?.graph?.triples) {
let json_str = new TextDecoder().decode(Uint8Array.from(event.payload.V0.State.graph.triples));
event.payload.V0.State.graph.triples = JSON.parse(json_str);
} else if (event.payload.V0.Patch?.graph) {
let inserts_json_str = new TextDecoder().decode(Uint8Array.from(event.payload.V0.Patch.graph.inserts));
event.payload.V0.Patch.graph.inserts = JSON.parse(inserts_json_str);
let removes_json_str = new TextDecoder().decode(Uint8Array.from(event.payload.V0.Patch.graph.removes));
event.payload.V0.Patch.graph.removes = JSON.parse(removes_json_str);
}
if (event.payload.V0.State?.discrete) {
let crdt = Object.getOwnPropertyNames(event.payload.V0.State.discrete)[0];
event.payload.V0.State.discrete[crdt] = Uint8Array.from(event.payload.V0.State.discrete[crdt]);
} else if (event.payload.V0.Patch?.discrete) {
let crdt = Object.getOwnPropertyNames(event.payload.V0.Patch.discrete)[0];
event.payload.V0.Patch.discrete[crdt] = Uint8Array.from(event.payload.V0.Patch.discrete[crdt]);
}
let ret = callback(event.payload);
if (ret === true) {
await tauri.invoke("cancel_stream", {stream_id});
} else if (ret.then) {
ret.then(async (val)=> {
if (val === true) {
await tauri.invoke("cancel_stream", {stream_id});
}
});
}
}) })
try {
await tauri.invoke("app_request_stream",{stream_id, request}); await tauri.invoke("app_request_stream",{stream_id, request});
} catch (e) {
unlisten();
await tauri.invoke("cancel_stream", {stream_id});
throw e;
}
return () => { return () => {
unlisten(); unlisten();
tauri.invoke("cancel_stream", {stream_id}); tauri.invoke("cancel_stream", {stream_id});
@ -159,6 +270,15 @@ const handler = {
} }
return res || {}; return res || {};
} else if (path[0] === "wallet_import_from_code") {
let arg = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el);
let res = await tauri.invoke(path[0],arg);
if (res) {
res.V0.content.security_img = Uint8Array.from(res.V0.content.security_img);
}
return res || {};
} else if (path[0] === "upload_chunk") { } else if (path[0] === "upload_chunk") {
let session_id = args[0]; let session_id = args[0];
let upload_id = args[1]; let upload_id = args[1];
@ -183,7 +303,7 @@ const handler = {
return false; return false;
} else if (path[0] === "get_local_url") { } else if (path[0] === "get_local_url") {
return false; return false;
} else if (path[0] === "wallet_open_with_pazzle" || path[0] === "wallet_open_with_mnemonic_words") { } else if (path[0] === "wallet_open_with_pazzle" || path[0] === "wallet_open_with_mnemonic_words" || path[0] === "wallet_open_with_mnemonic") {
let arg:any = {}; let arg:any = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el) args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img)); let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img));
@ -191,11 +311,20 @@ const handler = {
arg.wallet = {V0:{id:arg.wallet.V0.id, sig:arg.wallet.V0.sig, content:{}}}; arg.wallet = {V0:{id:arg.wallet.V0.id, sig:arg.wallet.V0.sig, content:{}}};
Object.assign(arg.wallet.V0.content,old_content); Object.assign(arg.wallet.V0.content,old_content);
arg.wallet.V0.content.security_img = img; arg.wallet.V0.content.security_img = img;
return tauri.invoke(path[0],arg); return await tauri.invoke(path[0],arg);
} else { } else {
let arg = {}; let arg = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el) args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
return tauri.invoke(path[0],arg) return await tauri.invoke(path[0],arg)
}
} catch (e) {
let error;
try {
error = JSON.parse(e);
} catch (f) {
error = e;
}
throw error;
} }
} }
}, },

@ -0,0 +1,179 @@
<!--
// 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_doc_can_edit,
set_view_or_edit
} from "../tab";
import { t } from "svelte-i18n";
import wasmUrl from "@automerge/automerge/automerge.wasm?url";
import { next as A } from "@automerge/automerge/slim";
import{ PencilSquare } from "svelte-heros-v2";
import AMap from "./automerge/AMap.svelte";
export let commits = {};
export let readonly = false;
let doc = {};
let loading = true;
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) {
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;
});
loading = false;
});
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));
}
}
const edit = () => {
set_view_or_edit(false);
}
</script>
{#if safari_error}
<Alert class="m-2" color="red">{$t("errors.no_wasm_on_old_safari")}</Alert>
{:else}
{#if loading}
<div class="mb-4 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 Object.keys(doc).length !== 0 || !readonly}
<div class="grow mb-20" style="min-height:300px;">
<AMap {readonly} value={doc} {doc} on:update={update} on:updateText={updateText} proxy={root_proxy}/>
</div>
{:else if $cur_tab_doc_can_edit}
<div class="flex-row">
<button
on:click={edit}
on:keypress={edit}
class="shrink select-none ml-4 mt-2 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PencilSquare class="mr-2 focus:outline-none" tabindex="-1" />
{$t("doc.start_editing")}
</button>
</div>
{:else}
<p class="ml-5">{$t("doc.empty")}</p>
{/if}
{/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}/>

@ -0,0 +1,101 @@
<!--
// 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 {
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 * as Y from 'yjs'
// @ts-ignore
import { yCollab } from 'y-codemirror.next'
import CodeMirror from "svelte-codemirror-editor";
import { javascript } from '@codemirror/lang-javascript'
import { rust } from '@codemirror/lang-rust'
import { svelte } from "@replit/codemirror-lang-svelte";
import {basicSetup} from "codemirror"
export let commits = {};
const class_to_lang = {
"code:js" : javascript(),
"code:ts" : javascript({"typescript":true}),
"code:rust" : rust(),
"code:svelte" : svelte(),
"code:react" : javascript({"jsx":true, "typescript":true}),
}
let lang;
$: lang = $cur_tab_branch_class && class_to_lang[$cur_tab_branch_class]
const ydoc = new Y.Doc()
const ytext = ydoc.getText('ng')
ydoc.on('update', async (update, origin) => {
if (!origin.local) {
try {
await discrete_update(update, "YText", commits.heads);
} catch (e){
toast_error(display_error(e));
}
}
})
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
await cur_tab_deregister_on_save();
})
let view;
onMount(()=>{
cur_tab_register_on_save(async (updates)=>{
let update = Y.mergeUpdates(updates);
await live_discrete_update(update, "YText", commits.heads);
});
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YText, {local:true})
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YText, {local:true})
}
});
onDestroy(()=>{
ydoc.destroy();
});
</script>
<div class="flex-col">
<CodeMirror {lang} on:ready={(e) => { view = e.detail; view.focus(); }} lineWrapping extensions={[basicSetup, yCollab(ytext, false, { undoManager: false })]} styles={{
"&": {
maxWidth: "100%",
},
}}/>
</div>

@ -0,0 +1,88 @@
<!--
// 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 ng from "../api";
import { link } from "svelte-spa-router";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import{ PlusCircle } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import {
in_memory_discrete, open_viewer, set_viewer, set_editor, set_view_or_edit, cur_tab_branch_class, cur_tab_doc_can_edit, cur_tab
} from "../tab";
import DataClassIcon from "../lib/icons/DataClassIcon.svelte";
import {
openModalCreate,
sparql_query,
active_session
} from "../store";
import {
Clipboard
} from "svelte-heros-v2";
export let commits;
function contained(graph) {
let ret = [];
for (const g of graph) {
if (g.substring(57,90) === "http://www.w3.org/ns/ldp#contains") {
let nuri = g.substring(93,146);
let repo = nuri;
nuri = nuri + ":" + $cur_tab.store.overlay;
let hash = nuri.substring(9,16);
ret.push({nuri,hash,repo});
}
}
ret.sort((a, b) => a.hash.localeCompare(b.hash));
return ret;
}
async function fetch_header(repo) {
try {
let res = await ng.fetch_header($active_session.session_id, repo);
return res;
}catch(e){
console.error(e);
return {};
}
}
const create = () => {
openModalCreate();
}
const config = {
class: "mr-2 w-6 h-6 shrink-0 focus:outline-none"
}
</script>
<div class="flex-col p-5">
{#each contained(commits.graph) as doc}
{#await fetch_header(doc.repo)}
<div class="flex"> <Clipboard tabindex="-1" class="mr-2 w-6 h-6 shrink-0 focus:outline-none"/><div class="flex font-mono mb-3"> <a use:link href="/{doc.nuri}">{doc.hash}</a> </div> </div>
{:then header}
<div class="flex" title="{header.about || ''}"> {#if header.class}<DataClassIcon {config} dataClass={header.class}/>{:else}<Clipboard tabindex="-1" class="mr-2 w-6 h-6 shrink-0 focus:outline-none"/>{/if}<div class="flex font-mono mb-3"> <a use:link href="/{doc.nuri}">{header.title || doc.hash}</a> </div></div>
{/await}
{/each}
{#if commits.graph.length == 0 || contained(commits.graph).length == 0}
<p>{$t("doc.empty_container")}</p>
{#if $cur_tab_doc_can_edit}
<button
on:click={create}
on:keypress={create}
class="select-none ml-0 mt-2 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PlusCircle tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.create")}
</button>
{/if}
{/if}
</div>

@ -0,0 +1,159 @@
<!--
// 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 ()=>{
if (editor) await editor.destroy();
await setup();
});
onDestroy(async ()=>{
ydoc.destroy();
commits.discrete?.deregisterOnUpdate();
if (editor) await editor.destroy();
editor = undefined;
});
</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>

@ -0,0 +1,200 @@
<!--
// 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.
-->
<!--
TODO:
https://github.com/Milkdown/milkdown/tree/main/packages/components/src
https://milkdown-storybook.vercel.app/?path=/story/components-image-block--empty
https://github.com/Milkdown/milkdown/tree/main/packages/crepe
https://milkdown-storybook.vercel.app/?path=/story/crepe-crepe--empty
-->
<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
} from "../tab";
import { t } from "svelte-i18n";
import * as Y from 'yjs'
import { Editor, rootCtx, editorViewCtx } from '@milkdown/core';
import { commonmark } from '@milkdown/preset-commonmark';
import { gfm } from '@milkdown/preset-gfm'
import { nord } from '@milkdown/theme-nord';
import '@milkdown/theme-nord/style.css';
import { collab, collabServiceCtx } from '@milkdown/plugin-collab';
import { placeholder, placeholderCtx } from './milkdown-placeholder'
import { splitEditing, toggleSplitEditing } from '@milkdown-lab/plugin-split-editing'
//import { SlashProvider, slashFactory } from '@milkdown/plugin-slash'
import { callCommand } from '@milkdown/utils';
import { emoji } from '@milkdown/plugin-emoji';
import { math } from '@milkdown/plugin-math';
import 'katex/dist/katex.min.css';
import { indent } from '@milkdown/plugin-indent';
import 'prism-themes/themes/prism-nord.css'
export let commits = {};
const ydoc = new Y.Doc()
let editor;
let width;
let split = true;
function width_changed() {
if (!editor) return;
if (width < 768 && split) {
split = false;
editor.action(callCommand(toggleSplitEditing.key, true));
} else if (width >= 768 && !split) {
split = true;
editor.action(callCommand(toggleSplitEditing.key, false));
}
}
$: width, width_changed();
// function slashPluginView(view) {
// const content = document.createElement('div');
// const provider = new SlashProvider({
// content,
// });
// return {
// update: (updatedView, prevState) => {
// provider.update(updatedView, prevState);
// },
// destroy: () => {
// provider.destroy();
// content.remove();
// }
// }
// }
// const slash = slashFactory('my-slash');
async function setup() {
if (!Array.prototype.at) {
Array.prototype.at = function at(n) {
let i = Math.trunc(n) || 0
i = i < 0 ? this.length + i : i
if (i < 0 || i >= this.length) return undefined
return this[i]
}
}
if (!Element.prototype.replaceChildren) {
Element.prototype.replaceChildren = function replaceChildren(...new_children) {
const { childNodes } = this;
while (childNodes.length) {
childNodes[0].remove();
}
this.append(...new_children);
}
}
let prism = await import("@milkdown/plugin-prism");
editor = await Editor.make().config((ctx) => {
ctx.set(rootCtx, '#mdeditor')
ctx.set(placeholderCtx, $t("doc.type_your_text_here"))
// ctx.set(slash.key, {
// view: slashPluginView
// })
})//.use(slash)
.config(nord)
.use(commonmark)
.use(gfm)
.use(prism.prism)
.use(indent)
.use(math)
.use(emoji)
.use(placeholder)
.use(splitEditing)
.use(collab).create();
ydoc.on('update', async (update, origin) => {
//console.log(update,origin);
if (!origin.local) {
try {
await discrete_update(update, "YXml", commits.heads);
} catch (e){
toast_error(display_error(e));
}
}
})
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
await cur_tab_deregister_on_save();
})
editor.action((ctx) => {
const collabService = ctx.get(collabServiceCtx);
collabService
// bind doc
.bindDoc(ydoc)
// connect yjs with milkdown
.connect();
});
cur_tab_register_on_save(async (updates)=>{
let update = Y.mergeUpdates(updates);
await live_discrete_update(update, "YXml", commits.heads);
});
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YXml, {local:true})
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YXml, {local:true})
}
await tick();
editor.action((ctx) => {
const editorView = ctx.get(editorViewCtx)
editorView.focus();
});
width_changed();
}
onMount(async ()=>{
if (editor) await editor.destroy();
await setup();
});
onDestroy(async ()=>{
ydoc.destroy();
if (editor) await editor.destroy();
editor = undefined;
});
</script>
<div class="grow p-5 post-rich-text" style="min-height:300px;" bind:clientWidth={width}>
<div id="mdeditor" class="prosemirror-editor"></div>
</div>

@ -0,0 +1,197 @@
<!--
// 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, rootCtx, editorViewOptionsCtx } from '@milkdown/core';
import { commonmark } from '@milkdown/preset-commonmark';
import { gfm } from '@milkdown/preset-gfm'
import { nord } from '@milkdown/theme-nord';
import '@milkdown/theme-nord/style.css';
import { collab, collabServiceCtx } from '@milkdown/plugin-collab';
import "svelte-highlight/styles/github.css";
import { emoji } from '@milkdown/plugin-emoji';
import { math } from '@milkdown/plugin-math';
import 'katex/dist/katex.min.css';
import { indent } from '@milkdown/plugin-indent';
import "prism-themes/themes/prism-nord.css";
export let commits = {};
const ydoc = new Y.Doc()
let editor;
let has_content = true;
let loading = true;
async function setup() {
try {
editor = Editor.make().config((ctx) => {
ctx.set(rootCtx, '#mdeditor')
ctx.update(editorViewOptionsCtx, (prev) => ({
...prev,
editable:() => false,
}))
}).config(nord)
.use(commonmark)
.use(gfm);
// polyfill if Safari < 15.4
if (!Array.prototype.at) {
Array.prototype.at = function at(n) {
let i = Math.trunc(n) || 0
i = i < 0 ? this.length + i : i
if (i < 0 || i >= this.length) return undefined
return this[i]
}
}
if (!Element.prototype.replaceChildren) {
Element.prototype.replaceChildren = function replaceChildren(...new_children) {
const { childNodes } = this;
while (childNodes.length) {
childNodes[0].remove();
}
this.append(...new_children);
}
}
if ([].at) {
let prism = await import("@milkdown/plugin-prism");
editor = editor.use(prism.prism);
}
editor = await editor
.use(indent)
.use(math)
.use(emoji)
.use(collab)
.create();
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
})
editor.action((ctx) => {
const collabService = ctx.get(collabServiceCtx);
collabService
// bind doc and awareness
.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;
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YXml, {local:true})
has_content = true;
}
loading = false;
}
catch (e){
console.log(e)
}
}
onMount(async ()=>{
if (editor) await editor.destroy();
await setup();
});
onDestroy(async ()=>{
ydoc.destroy();
try {
if (editor) await editor.destroy();
editor = undefined;
} catch(e) {
console.log(e);
}
});
const edit = () => {
set_view_or_edit(false);
}
</script>
{#if !has_content}
<div class="flex-row">
<button
on:click={edit}
on:keypress={edit}
class="select-none ml-5 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PencilSquare tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.start_editing")}
</button>
</div>
{/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}
<div class="grow p-5 post-rich-text prose">
<div id="mdeditor" class="prosemirror-editor"></div>
</div>

@ -0,0 +1,105 @@
<!--
// 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 {
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 * as Y from 'yjs'
// @ts-ignore
import { ySyncPlugin, initProseMirrorDoc } from 'y-prosemirror';
import ProsemirrorEditor from 'prosemirror-svelte';
import { richTextSchema } from 'prosemirror-svelte/state';
import { richTextPlugins, corePlugins } from 'prosemirror-svelte/helpers';
import { EditorState } from "prosemirror-state";
export let commits = {};
const ydoc = new Y.Doc()
const yxml = ydoc.getXmlFragment('prosemirror')
let view;
ydoc.on('update', async (update, origin) => {
if (!origin.local) {
try {
await discrete_update(update, "YXml", commits.heads);
} catch (e){
toast_error(display_error(e));
}
}
})
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
await cur_tab_deregister_on_save();
})
const { doc, mapping } = initProseMirrorDoc(yxml, richTextSchema)
let selection;
let editorState = EditorState.create({
schema: richTextSchema,
doc,
selection,
plugins: [
...corePlugins,
...richTextPlugins,
ySyncPlugin(yxml, { mapping })
]
});
onMount(()=>{
cur_tab_register_on_save(async (updates)=>{
let update = Y.mergeUpdates(updates);
await live_discrete_update(update, "YXml", commits.heads);
});
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YXml, {local:true})
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YXml, {local:true})
}
view.focus()
});
onDestroy(()=>{
ydoc.destroy();
});
</script>
<div class="grow p-5 post-rich-text prose" style="min-height:300px;">
<ProsemirrorEditor
className="prosemirror-editor"
{editorState}
debounceChangeEventsInterval=2000
placeholder={$t("doc.type_your_text_here")}
bind:view={view}
/>
</div>

@ -0,0 +1,120 @@
<!--
// 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 {
sparql_update,
toast_error,
toast_success
} from "../store";
import {
set_view_or_edit, cur_tab_doc_can_edit
} from "../tab";
import{ PencilSquare, RocketLaunch } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import * as Y from 'yjs'
import { yXmlFragmentToProseMirrorRootNode } from 'y-prosemirror';
import { richTextSchema } from 'prosemirror-svelte/state';
import { DOMSerializer } from "prosemirror-model";
export let commits = {};
let source = "";
const ydoc = new Y.Doc()
const yxml = ydoc.getXmlFragment('prosemirror')
let loading = true;
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
})
const toHTML = () => {
const serializer = DOMSerializer.fromSchema(richTextSchema);
const fragment = serializer.serializeFragment(yXmlFragmentToProseMirrorRootNode(yxml, richTextSchema));
const node = document.createElement('div');
node.append(fragment);
return node.innerHTML;
}
onMount(()=>{
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YXml, {local:true})
source = toHTML()
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YXml, {local:true})
}
source = toHTML();
loading = false;
});
onDestroy(()=>{
ydoc.destroy();
});
const edit = () => {
set_view_or_edit(false);
}
</script>
<div class="grow p-5">
{#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}
<div class="post-rich-text prose" style="margin-top: 1.25em;">
{@html source}
</div>
{:else if $cur_tab_doc_can_edit}
<button
on:click={edit}
on:keypress={edit}
class="select-none mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PencilSquare tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.start_editing")}
</button>
{/if}
</div>

@ -0,0 +1,126 @@
<!--
// 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 {
sparql_query,
toast_error,
toast_success,
reset_toasts,
display_error,
} from "../store";
import {
in_memory_discrete, open_viewer, set_viewer, reset_in_memory
} from "../tab";
import{ Sun, RocketLaunch } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import { Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell, Toggle } from 'flowbite-svelte';
import CodeMirror from "svelte-codemirror-editor";
import {StreamLanguage} from "@codemirror/language"
import { sparql } from "@codemirror/legacy-modes/mode/sparql";
import {basicSetup} from "codemirror"
import Highlight, { LineNumbers } from "svelte-highlight";
import hljs from "highlight.js";
import { definer } from "../turtle";
import "svelte-highlight/styles/github.css";
import { each } from "svelte/internal";
const language = {
name: "turtle",
register: (hljs) => {
return definer(hljs);
},
};
onMount(()=>{
reset_in_memory();
if (!$in_memory_discrete){
$in_memory_discrete = "SELECT ?subject ?predicate ?object WHERE {\n ?subject ?predicate ?object .\n} LIMIT 10";
}
});
let union = false;
const run = async () => {
try{
await reset_toasts();
results = await sparql_query($in_memory_discrete, union);
} catch(e) {
console.error(e)
toast_error(display_error(e));
}
}
const openTurtle = () => {
reset_toasts();
set_viewer("n:g:z:rdf_viewer:turtle");
}
let results = undefined;
</script>
<div class="flex-col">
<CodeMirror bind:value={$in_memory_discrete} lineWrapping useTab={false} extensions={[basicSetup,StreamLanguage.define(sparql)]} styles={{
"&": {
maxWidth: "100%",
},
}}/>
<Toggle class="mt-1 ml-2" bind:checked={union}>{$t("doc.query_all_docs")}</Toggle>
<button
on:click={run}
on:keypress={run}
class="select-none ml-2 mt-2 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<RocketLaunch tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.run_query")}
</button>
<button
on:click={openTurtle}
on:keypress={openTurtle}
class="select-none ml-2 mt-2 mb-10 text-gray-600 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"
>
<Sun class="mr-2 focus:outline-none" tabindex="-1" />
{$t("doc.view_as_turtle")}
</button>
{#if results!==undefined}
<div>
<span class="ml-2 font-bold">{$t("doc.results")}: <br/></span>
{#if Array.isArray(results)}
{#if results.length}
<Highlight {language} code={results.join(" .\r\n") + (results.length ? " .":"")} class="mb-10" let:highlighted >
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{:else}
<span class="ml-2">{$t("doc.empty")}</span>
{/if}
{:else if results?.head}
<Table>
<TableHead>
{#each results.head.vars as variable}
<TableHeadCell>{variable}</TableHeadCell>
{/each}
</TableHead>
<TableBody tableBodyClass="divide-y">
{#each results.results.bindings as row}
<TableBodyRow>
{#each results.head.vars as variable}
<TableBodyCell class="px-6 py-4 whitespace-break-spaces font-medium">{#if row[variable]} {row[variable].value }{/if}</TableBodyCell>
{/each}
</TableBodyRow>
{/each}
</TableBody>
</Table>
{:else}
<span class="ml-2">{results}</span>
{/if}
</div>
{/if}
</div>

@ -0,0 +1,78 @@
<!--
// 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 {
sparql_update,
toast_error,
toast_success,
reset_toasts,
display_error,
} from "../store";
import {
in_memory_discrete, open_viewer, reset_in_memory
} from "../tab";
import{ Sun, RocketLaunch } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import CodeMirror from "svelte-codemirror-editor";
import {StreamLanguage} from "@codemirror/language"
import { sparql } from "@codemirror/legacy-modes/mode/sparql";
import {basicSetup} from "codemirror"
onMount(()=>{
reset_in_memory();
if (!$in_memory_discrete){
$in_memory_discrete = "INSERT DATA { \n <> <example:predicate> \"An example value\".\r}";
}
});
const run = async () => {
try{
await reset_toasts();
await sparql_update($in_memory_discrete);
toast_success($t("app.sparql_update_editor.success"));
} catch(e) {
toast_error(display_error(e));
}
}
const openViewer = () => {
reset_toasts();
open_viewer();
}
</script>
<div class="flex-col">
<CodeMirror bind:value={$in_memory_discrete} lineWrapping useTab={false} extensions={[basicSetup,StreamLanguage.define(sparql)]} styles={{
"&": {
maxWidth: "100%",
},
}}/>
<button
on:click={run}
on:keypress={run}
class="select-none ml-2 mt-2 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<RocketLaunch tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.run_update")}
</button>
<button
on:click={openViewer}
on:keypress={openViewer}
class="select-none ml-2 mt-2 mb-10 text-gray-600 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"
>
<Sun class="mr-2 focus:outline-none" tabindex="-1" />
{$t("doc.view_graph")}
</button>
</div>

@ -0,0 +1,129 @@
<!--
// 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 {
sparql_update,
toast_error,
toast_success
} from "../store";
import {
in_memory_discrete, open_viewer, set_viewer, set_editor, set_view_or_edit, cur_tab_branch_class, cur_tab_doc_can_edit
} from "../tab";
import{ PencilSquare, RocketLaunch } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import * as Y from 'yjs'
import Highlight, { LineNumbers, HighlightSvelte } from "svelte-highlight";
import typescript from "svelte-highlight/languages/typescript";
import javascript from "svelte-highlight/languages/javascript";
import rust from "svelte-highlight/languages/rust";
import "svelte-highlight/styles/github.css";
const class_to_lang = {
"code:js" : javascript,
"code:ts" : typescript,
"code:rust" : rust,
"code:react" : javascript,
}
let language;
$: language = $cur_tab_branch_class && class_to_lang[$cur_tab_branch_class]
export let commits = {};
let source = "";
const ydoc = new Y.Doc()
const ytext = ydoc.getText('ng');
let loading = true;
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
})
onMount(()=>{
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update.YText, {local:true})
source = ytext.toString()
});
for (const h of history) {
Y.applyUpdate(ydoc, h.YText, {local:true})
}
source = ytext.toString()
loading = false;
});
onDestroy(()=>{
ydoc.destroy();
});
const edit = () => {
set_view_or_edit(false);
}
</script>
<div class="flex-col">
{#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}
{#if $cur_tab_branch_class === "code:svelte"}
<HighlightSvelte code={source} class="mb-10" let:highlighted>
<LineNumbers {highlighted} wrapLines hideBorder />
</HighlightSvelte>
{:else if language}
<Highlight {language} code={source} class="mb-10" let:highlighted>
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{:else}
<p class="font-mono whitespace-pre-wrap p-5">
{source}
</p>
{/if}
{:else if $cur_tab_doc_can_edit}
<button
on:click={edit}
on:keypress={edit}
class="select-none ml-5 mt-2 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PencilSquare tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.start_editing")}
</button>
{/if}
</div>

@ -0,0 +1,84 @@
<!--
// 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 {
sparql_update,
toast_error,
toast_success
} from "../store";
import {
in_memory_discrete, open_viewer, set_viewer, set_editor, set_view_or_edit, cur_tab_doc_can_edit
} from "../tab";
import{ PencilSquare, RocketLaunch } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import Highlight, { LineNumbers } from "svelte-highlight";
import hljs from "highlight.js";
import { definer } from "../turtle";
import "svelte-highlight/styles/github.css";
const language = {
name: "turtle",
register: (hljs) => {
return definer(hljs);
},
};
export let commits = {graph:[]};
let source = "";
$: source = commits.graph.join(" .\r\n") + (commits.graph.length ? " .":"");
const openQuery = () => {
set_viewer("n:g:z:sparql_query");
}
const openUpdate = () => {
set_editor("n:g:z:sparql_update");
set_view_or_edit(false);
}
onMount(()=>{
});
</script>
<div class="flex-col">
{#if !source}
<p class="p-3">{$t("doc.no_triples")}</p>
{/if}
<button
on:click={openQuery}
on:keypress={openQuery}
class="select-none ml-2 mt-2 mb-2 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"
>
<RocketLaunch tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.sparql_query")}
</button>
{#if $cur_tab_doc_can_edit}
<button
on:click={openUpdate}
on:keypress={openUpdate}
class="select-none ml-2 mt-2 text-gray-600 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.sparql_update")}
</button>
{/if}
{#if source}
<Highlight {language} code={source} class="mb-10" let:highlighted >
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{/if}
</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}

@ -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 YMapEditor from "./YMapEditor.svelte";
export let commits = {};
</script>
<YMapEditor {commits} crdt="YArray"/>

@ -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 YMapSource from "./YMapSource.svelte";
export let commits = {};
</script>
<YMapSource {commits} crdt="YArray"/>

@ -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 YMapViewer from "./YMapViewer.svelte";
export let commits = {};
</script>
<YMapViewer {commits} crdt="YArray"/>

@ -0,0 +1,291 @@
<!--
// 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 {
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 * as Y from 'yjs'
import { JSONEditor } from 'svelte-jsoneditor'
export let commits = {};
export let crdt = "YMap";
const ydoc = new Y.Doc()
const ymap = ydoc.get('ng', crdt == "YMap" ? Y.Map : Y.Array)
let editor;
let content = {
text: undefined,
json: crdt=="YMap"? {
} : []
}
ymap.observeDeep((events, transaction) => {
if (transaction.origin.local) {
let operations = [];
events.forEach((event) => {
let target = ymap;
let path = "";
event.path.forEach((p)=> { target = target.get(p); path += `/${p}`;});
event.changes.keys.forEach((change, key) => {
if (change.action === 'add') {
let newval = target.get(key);
if ( newval instanceof Y.Array) newval = newval.toJSON();
else if ( newval instanceof Y.Map) newval = newval.toJSON();
//console.log(`Property "${key}" was added in path "${path}". Initial value: "`,newval)
let p = path + `/${key}`;
operations.push({ op: 'add', path:p, value: newval });
} else if (change.action === 'update') {
let newval = target.get(key);
if ( newval instanceof Y.Array) newval = newval.toJSON();
else if ( newval instanceof Y.Map) newval = newval.toJSON();
//console.log(`Property "${key}" was updated in path "${path}". Previous value: "${change.oldValue}". New value: `, newval)
let p = path + `/${key}`;
operations.push({ op: 'replace', path:p, value: newval });
} else if (change.action === 'delete') {
//console.log(`Property "${key}" was deleted in path "${path}". Previous value: "${change.oldValue}".`)
let p = path + `/${key}`;
operations.push({ op: 'remove', path:p });
}
});
let pos = 0;
event.changes.delta.forEach((delta) => {
if (delta.retain) pos += delta.retain;
else if (delta.insert && Array.isArray(delta.insert)) {
delta.insert.forEach((newval) => {
let p = path + `/${pos}`;
if ( newval instanceof Y.Array) newval = newval.toJSON();
else if ( newval instanceof Y.Map) newval = newval.toJSON();
//console.log(`Adding array element to path "${p}". New value: `, newval)
operations.push({ op: 'add', path:p, value: newval });
pos += 1;
});
} else if (delta.delete) {
let p = path + `/${pos}`;
for (let i=0; i< delta.delete; i++) {
//console.log(`removing array element in path "${p}"`)
operations.push({ op: 'remove', path:p });
}
}
});
});
editor.patch(operations);
content.json = ymap.toJSON();
}
});
ydoc.on('update', async (update, origin) => {
//console.log(update, origin)
if (!origin.local) {
try {
await discrete_update(update, crdt, commits.heads);
} catch (e){
toast_error(display_error(e));
}
}
})
function process_value(val) {
let value;
if (Array.isArray(val)) {
const subArray = new Y.Array();
for (let i=0; i<val.length; i++) {
let r = process_value(val[i]);
subArray.insert(i, [r]);
}
value = subArray;
} else if (typeof val === 'object' && val !== null) {
const ymapNested = new Y.Map();
for (const [key, value] of Object.entries(val)) {
ymapNested.set(key, process_value(value));
}
value = ymapNested;
} else {
value = val;
}
return value;
}
function handleChange(updatedContent, previousContent, { contentErrors, patchResult }) {
// content is an object { json: unknown } | { text: string }
//console.log('onChange: ', patchResult?.redo, patchResult, updatedContent)
if (patchResult) {
ydoc.transact((transac) => {
patchResult.redo.forEach((op)=>{
let path = op.path.split("/");
path.shift();
let key = path.pop();
let target = ymap;
path.forEach((p)=> { target = target.get(p);});
if (op.op == "add") {
//console.log("adding", op.value,key, op.path)
let value = process_value(op.value);
if (target instanceof Y.Map) {
target.set(key, value);
} else {
target.insert(Number(key), [value]);
}
} else if (op.op == "remove") {
//console.log("removing", key, op.path)
if (target instanceof Y.Map) {
target.delete(key);
} else {
target.delete(Number(key), 1);
}
} else if (op.op == "replace") {
//console.log("replacing", op.value, key, op.path)
if (key === undefined) {
if (crdt === "YArray" && Array.isArray(op.value)) {
if (target.length) target.delete(0,target.length);
op.value.forEach((v)=> {
target.push([process_value(v)]);
});
} else if (crdt === "YMap" && (typeof op.value === 'object' && op.value !== null)) {
target.clear();
for (const [key, value] of Object.entries(op.value)) {
target.set(key, process_value(value));
}
}
return;
}
if (target instanceof Y.Map) {
target.set(key, process_value(op.value));
} else {
let idx = Number(key);
target.delete(idx, 1);
target.insert(idx, [process_value(op.value)]);
}
} else if (op.op == "move" || op.op == "copy") {
let move = op.op == "move";
if (op.from === op.path) return;
//console.log("moving or copying", op.from, op.path)
let from = op.from.split("/");
from.shift();
let from_key = from.pop();
let origin = ymap;
from.forEach((p)=> { origin = origin.get(p);});
from_key = (origin instanceof Y.Map) ? from_key : Number(from_key);
let value_to_move = origin.get(from_key);
if ( value_to_move instanceof Y.Array) value_to_move = value_to_move.clone();
else if ( value_to_move instanceof Y.Map) value_to_move = value_to_move.clone();
if (target instanceof Y.Map) {
target.set(key, value_to_move);
} else {
let idx = Number(key);
target.insert(idx, [value_to_move]);
}
if (move) {
if (typeof from_key === "number") {
origin.delete(from_key, 1);
} else {
origin.delete(from_key);
}
}
}
});
} , {local:false});
}
content = updatedContent
}
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
await cur_tab_deregister_on_save();
})
onMount(async ()=>{
cur_tab_register_on_save(async (updates)=>{
let update = Y.mergeUpdates(updates);
await live_discrete_update(update, crdt, commits.heads);
});
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update[crdt], {local:true})
});
for (const h of history) {
Y.applyUpdate(ydoc, h[crdt], {local:true})
}
await editor.focus()
});
onDestroy(async ()=>{
ydoc.destroy();
await editor.destroy();
editor = undefined;
});
function onRenderMenu(items, context) {
items.shift();
items.pop();
items.pop();
items.pop();
return items;
}
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") {
items[2].items.shift();
items[2].items.pop();
items[4].items[0].items.pop();
}
return items;
}
</script>
<div class="grow ng-json-editor" style="min-height:300px;">
<JSONEditor bind:this={editor} {content} onChange={handleChange} {onRenderMenu} {onRenderContextMenu}/>
</div>
<style>
.ng-json-editor {
/* define a custom theme color */
--jse-theme-color: rgb(73, 114, 165);
--jse-theme-color-highlight: rgb(30 136 229);
}
</style>

@ -0,0 +1,79 @@
<!--
// 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 {
sparql_update,
toast_error,
toast_success
} from "../store";
import {
set_view_or_edit, cur_tab_doc_can_edit
} from "../tab";
import{ PencilSquare, } from "svelte-heros-v2";
import { t } from "svelte-i18n";
import Highlight, { LineNumbers } from "svelte-highlight";
import json from "svelte-highlight/languages/json";
import "svelte-highlight/styles/github.css";
import * as Y from 'yjs'
let source = "";
const edit = () => {
set_view_or_edit(false);
}
export let commits = {};
export let crdt = "YMap";
const ydoc = new Y.Doc()
const ymap = ydoc.get('ng', crdt == "YMap" ? Y.Map : Y.Array)
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
})
onMount(()=>{
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update[crdt], {local:true})
source = JSON.stringify(ymap.toJSON(),null , 4 );
});
for (const h of history) {
if (h[crdt]) Y.applyUpdate(ydoc, h[crdt], {local:true})
}
source = JSON.stringify(ymap.toJSON(), null , 4 );
});
</script>
<div class="flex-col">
{#if source}
<Highlight language={json} code={source} class="mb-10" let:highlighted >
<LineNumbers {highlighted} wrapLines hideBorder />
</Highlight>
{:else if $cur_tab_doc_can_edit}
<button
on:click={edit}
on:keypress={edit}
class="select-none ml-2 mt-2 mb-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<PencilSquare class="mr-2 focus:outline-none" tabindex="-1" />
{$t("doc.start_editing")}
</button>
{/if}
</div>

@ -0,0 +1,189 @@
<!--
// 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 {
toast_error,
toast_success,
reset_toasts,
display_error,
live_discrete_update,
discrete_update
} from "../store";
import {
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'
export let commits = {};
export let crdt = "YMap";
const ydoc = new Y.Doc()
const ymap = ydoc.get('ng', crdt == "YMap" ? Y.Map : Y.Array)
let editor;
let loading = true;
let content = {
text: undefined,
json: crdt=="YMap"? {
} : []
}
ymap.observeDeep((events, transaction) => {
if (transaction.origin.local) {
let operations = [];
events.forEach((event) => {
let target = ymap;
let path = "";
event.path.forEach((p)=> { target = target.get(p); path += `/${p}`;});
event.changes.keys.forEach((change, key) => {
if (change.action === 'add') {
let newval = target.get(key);
if ( newval instanceof Y.Array) newval = newval.toJSON();
else if ( newval instanceof Y.Map) newval = newval.toJSON();
//console.log(`Property "${key}" was added in path "${path}". Initial value: "`,newval)
let p = path + `/${key}`;
operations.push({ op: 'add', path:p, value: newval });
} else if (change.action === 'update') {
let newval = target.get(key);
if ( newval instanceof Y.Array) newval = newval.toJSON();
else if ( newval instanceof Y.Map) newval = newval.toJSON();
//console.log(`Property "${key}" was updated in path "${path}". Previous value: "${change.oldValue}". New value: `, newval)
let p = path + `/${key}`;
operations.push({ op: 'replace', path:p, value: newval });
} else if (change.action === 'delete') {
//console.log(`Property "${key}" was deleted in path "${path}". Previous value: "${change.oldValue}".`)
let p = path + `/${key}`;
operations.push({ op: 'remove', path:p });
}
});
let pos = 0;
event.changes.delta.forEach((delta) => {
if (delta.retain) pos += delta.retain;
else if (delta.insert && Array.isArray(delta.insert)) {
delta.insert.forEach((newval) => {
let p = path + `/${pos}`;
if ( newval instanceof Y.Array) newval = newval.toJSON();
else if ( newval instanceof Y.Map) newval = newval.toJSON();
//console.log(`Adding array element to path "${p}". New value: `, newval)
operations.push({ op: 'add', path:p, value: newval });
pos += 1;
});
} else if (delta.delete) {
let p = path + `/${pos}`;
for (let i=0; i< delta.delete; i++) {
//console.log(`removing array element in path "${p}"`)
operations.push({ op: 'remove', path:p });
}
}
});
});
editor.patch(operations);
content.json = ymap.toJSON();
loading = false;
}
});
ydoc.on('destroy', async () => {
commits.discrete?.deregisterOnUpdate();
})
onMount(async ()=>{
let history = commits.discrete?.registerOnUpdate((update) => {
Y.applyUpdate(ydoc, update[crdt], {local:true})
});
for (const h of history) {
Y.applyUpdate(ydoc, h[crdt], {local:true})
}
loading = false;
});
onDestroy(async ()=>{
ydoc.destroy();
await editor.destroy();
editor = undefined;
});
const edit = () => {
set_view_or_edit(false);
}
</script>
{#if loading}
<div class="grow mb-4 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 $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} />
</div>
<style>
.ng-json-editor {
/* define a custom theme color */
--jse-theme-color: rgb(73, 114, 165);
--jse-theme-color-highlight: rgb(30 136 229);
}
</style>

@ -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,71 @@
<!--
// 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();
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="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,43 @@
<!--
// 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;
function update() {
temp_val = value;
previous_val = value;
}
let temp_val;
let previous_val;
$: value, update();
const change = (event) => {
if (previous_val!=temp_val)
dispatch('updateText', {
s: event.target.value,
p: [path]
});
}
</script>
<Input bind:value={temp_val} 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);
}
}

@ -0,0 +1,102 @@
/**
MIT License
Copyright (c) 2022 Mox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Source: https://github.com/HexMox/milkdown-plugin-placeholder
*/
import type { MilkdownPlugin, TimerType } from '@milkdown/ctx'
import type { EditorView } from '@milkdown/prose/view'
import { createSlice, createTimer } from '@milkdown/ctx'
import { InitReady, prosePluginsCtx } from '@milkdown/core'
import { Plugin, PluginKey } from '@milkdown/prose/state'
export const placeholderCtx = createSlice('Please input here...', 'placeholder')
export const placeholderTimerCtx = createSlice([] as TimerType[], 'editorStateTimer')
export const PlaceholderReady = createTimer('PlaceholderReady')
const key = new PluginKey('MILKDOWN_PLACEHOLDER')
export const placeholder: MilkdownPlugin = (ctx) => {
ctx.inject(placeholderCtx).inject(placeholderTimerCtx, [InitReady]).record(PlaceholderReady)
return async () => {
await ctx.waitTimers(placeholderTimerCtx)
const prosePlugins = ctx.get(prosePluginsCtx)
const update = (view: EditorView) => {
const placeholder = ctx.get(placeholderCtx)
const doc = view.state.doc
if (
view.editable &&
doc.childCount === 1 &&
doc.firstChild?.isTextblock &&
doc.firstChild?.content.size === 0 &&
doc.firstChild?.type.name === 'paragraph'
) {
view.dom.classList.add('editor_empty');
view.dom.setAttribute('data-placeholder', placeholder);
} else {
view.dom.classList.remove('editor_empty');
}
}
const plugins = [
...prosePlugins,
new Plugin({
key,
// props: {
// decorations(state) {
// const doc = state.doc
// if (
// doc.childCount === 1 &&
// doc.firstChild?.isTextblock &&
// doc.firstChild?.content.size === 0
// ) {
// return DecorationSet.create(doc, [
// Decoration.widget(1, (view) => {
// if (view.editable) {
// const span = document.createElement('span')
// span.classList.add('placeholder')
// span.textContent = placeholder
// return span
// }
// }),
// ])
// }
// },
// },
view(view) {
update(view)
return { update }
},
}),
]
ctx.set(prosePluginsCtx, plugins)
ctx.done(PlaceholderReady)
}
}

@ -0,0 +1,59 @@
/*
* Base64URL-ArrayBuffer
* https://github.com/herrjemand/Base64URL-ArrayBuffer
*
* Copyright (c) 2017 Yuriy Ackermann <ackermann.yuriy@gmail.com>
* Copyright (c) 2012 Niklas von Hertzen
* Licensed under the MIT license.
*
*/
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// Use a lookup table to find the index.
var lookup = new Uint8Array(256);
for (var i = 0; i < chars.length; i++) {
lookup[chars.charCodeAt(i)] = i;
}
export const encode = function(arraybuffer) {
var bytes = new Uint8Array(arraybuffer),
i, len = bytes.length, base64 = "";
for (i = 0; i < len; i+=3) {
base64 += chars[bytes[i] >> 2];
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
base64 += chars[bytes[i + 2] & 63];
}
if ((len % 3) === 2) {
base64 = base64.substring(0, base64.length - 1);
} else if (len % 3 === 1) {
base64 = base64.substring(0, base64.length - 2);
}
return base64;
};
export const decode = function(base64) {
var bufferLength = base64.length * 0.75,
len = base64.length, i, p = 0,
encoded1, encoded2, encoded3, encoded4;
var arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);
for (i = 0; i < len; i+=4) {
encoded1 = lookup[base64.charCodeAt(i)];
encoded2 = lookup[base64.charCodeAt(i+1)];
encoded3 = lookup[base64.charCodeAt(i+2)];
encoded4 = lookup[base64.charCodeAt(i+3)];
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}
return arraybuffer;
};

File diff suppressed because it is too large Load Diff

@ -62,7 +62,7 @@
<div class="mb-20 mt-10"> <div class="mb-20 mt-10">
<button <button
on:click={changeLang} on:click={changeLang}
class="text-primary-700 bg-[#f6f6f6] bg-none ring-0 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55" class="text-primary-700 bg-white bg-none ring-0 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55"
> >
<Language <Language
tabindex="-1" tabindex="-1"
@ -72,7 +72,7 @@
<br /> <br />
<button <button
on:click={displayNextgraphOrg} on:click={displayNextgraphOrg}
class="text-primary-700 bg-[#f6f6f6] bg-none ring-0 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="text-primary-700 bg-white bg-none ring-0 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
> >
{$t("common.about_nextgraph")} {$t("common.about_nextgraph")}
</button> </button>
@ -106,7 +106,5 @@
text-align: center; text-align: center;
width: fit-content; width: fit-content;
} }
li.clickable {
cursor: pointer;
}
</style> </style>

@ -1,187 +0,0 @@
<!--
// 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 {
Icon,
BugAnt,
DocumentText,
Window,
CodeBracket,
SquaresPlus,
ViewfinderCircle,
ArrowsPointingOut,
Cube,
Briefcase,
MagnifyingGlass,
RocketLaunch,
Sun,
TableCells,
ListBullet,
RectangleGroup,
Squares2x2,
MapPin,
CircleStack,
Envelope,
GlobeAlt,
DocumentChartBar,
Document,
ClipboardDocumentList,
Photo,
Film,
RectangleStack,
Microphone,
MusicalNote,
Ticket,
CursorArrowRays,
Megaphone,
User,
Clock,
CalendarDays,
Calendar,
Stop,
Flag,
HandRaised,
Newspaper,
PencilSquare,
CubeTransparent,
PresentationChartBar,
QuestionMarkCircle,
CheckCircle,
ChartPie,
Bars3BottomLeft,
Link,
Square2Stack,
Clipboard,
StopCircle,
Bolt,
Heart,
} from "svelte-heros-v2";
export let config = {};
export let dataClass: string;
const exact_mapping = {
page: Window,
"app/z": SquaresPlus,
class: ViewfinderCircle,
contract: Briefcase,
"query/text": MagnifyingGlass,
"query/web": MagnifyingGlass,
"data/graph": Sun,
"data/table": TableCells,
"data/collection": ListBullet,
"data/board": RectangleGroup,
"data/grid": Squares2x2,
"data/geomap": MapPin,
"e/email": Envelope,
"mc/text": Bars3BottomLeft,
"mc/link": Link,
"plato/card": Clipboard,
"plato/pad": Square2Stack,
"media/image": Photo,
"media/reel": Film,
"media/video": Film,
"media/album": RectangleStack,
"media/audio": Microphone,
"media/song": MusicalNote,
"media/subtitle": Ticket,
"media/overlay": CursorArrowRays,
"social/channel": Megaphone,
"social/stream": Bolt,
"social/contact": User,
"social/event": Clock,
"social/calendar": CalendarDays,
"social/scheduler": Calendar,
"social/reaction": Heart,
"prod/task": Stop,
"prod/project": Flag,
"prod/issue": HandRaised,
"prod/form": Newspaper,
"prod/filling": PencilSquare,
"prod/cad": CubeTransparent,
"prod/slides": PresentationChartBar,
"prod/question": QuestionMarkCircle,
"prod/answer": CheckCircle,
"prod/poll": QuestionMarkCircle,
"prod/vote": CheckCircle,
};
const prefix_mapping = {
"post/": DocumentText,
code: CodeBracket,
schema: ArrowsPointingOut,
service: Cube,
"e/": GlobeAlt,
"app/": StopCircle,
"query/": RocketLaunch,
"data/": CircleStack,
"doc/diagram": DocumentChartBar,
"doc/chart": ChartPie,
"doc/viz": ChartPie,
"doc/": ClipboardDocumentList,
file: Document,
};
const find = (t) => {
let e = exact_mapping[t];
if (e) return e;
for (let prefix of Object.entries(prefix_mapping)) {
if (t.startsWith(prefix[0])) return prefix[1];
}
return BugAnt;
};
</script>
<!--
did:ng:n:g:z:[official apps]
did:ng:n:g:ns
did:ng:n:g:x list of context used by nextgraph
rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
rdfs: http://www.w3.org/2000/01/rdf-schema#
schema: https://schema.org/
skos: http://www.w3.org/2004/02/skos/core#
owl: http://www.w3.org/2002/07/owl#
foaf: http://xmlns.com/foaf/0.1/
relationship: http://purl.org/vocab/relationship/
dcterms: http://purl.org/dc/terms/
dcmitype: http://purl.org/dc/dcmitype/
sh: http://www.w3.org/ns/shacl#
shex: http://www.w3.org/ns/shex#
xsd: http://www.w3.org/2001/XMLSchema#
as: https://www.w3.org/ns/activitystreams#
ldp: http://www.w3.org/ns/ldp#
vcard: http://www.w3.org/2006/vcard/ns#
sec: https://w3id.org/security#
wgs: http://www.w3.org/2003/01/geo/wgs84_pos#
cc: http://creativecommons.org/ns#
gn: https://www.geonames.org/ontology#
geo: http://www.opengis.net/ont/geosparql#
time: http://www.w3.org/2006/time#
ng: did:ng:n:g:ns# or http://nextgraph.org/ns#
did:ng:n:g:ns#post/rich
ng:class => shortcut for did:ng:n:g:ns#class
a rdfs:Class
a ng:class
did:ng:o:xxxx:yy:yy
did:ng:n:xx.xx#name
did:ng:n:x: curated list of ontologies
did:ng:k common list of things (keyword)
did:ng:n:c common data
did:ng:n:z: curated list of external apps and services
http://nextgraph.org/ns# => the ng: ontology (did:ng:n:g:ns#)
ng:compat -> owl:unionOf rdf:List (alphabetical order, including itself as first element)
-->
<Icon {...config} variation="outline" color="black" icon={find(dataClass)} />

@ -0,0 +1,224 @@
<!--
// 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 {
branch_subscribe,
active_session,
cannot_load_offline,
open_doc_popup
} from "../store";
import {
Pencil,
PencilSquare
} from "svelte-heros-v2";
import { t } from "svelte-i18n";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import { inview } from 'svelte-inview';
import { cur_tab, cur_tab_view_or_edit, cur_tab_doc_can_edit, can_have_header, header_icon, header_title, header_description, cur_branch, set_header_in_view, edit_header_button, cur_app, load_official_app, nav_bar_reset_newest, set_view_or_edit } from "../tab";
import NavIcon from "./icons/NavIcon.svelte";
export let nuri = "";
let width;
let center;
$: center = width > 1024 && !$cur_app?.full_width
let commits;
// TODO deals with cases when nuri has :r :w :l (remove them from nuri that should only have :o:v format , and add them in cur_tab)
$: commits = $active_session && nuri && branch_subscribe(nuri, true);
const inview_options = {};//{rootMargin: "-44px"};
function openEditHeader() {
open_doc_popup("header");
}
</script>
<div bind:clientWidth={width}>
{#if $cannot_load_offline}
<div class="row p-4">
<Alert color="yellow">
{@html $t("doc.cannot_load_offline")}
<a href="#/user">{$t("pages.user_panel.title")}</a>.
</Alert>
</div>
{:else}
<div class="flex justify-left" class:justify-center={center} use:inview={inview_options} on:inview_change={(event) => {
const { inView, entry, scrollDirection, observer, node} = event.detail;
if ($cur_branch) { set_header_in_view(inView); }
if (inView) nav_bar_reset_newest();
}}>
<div class="flex flex-col" class:grow={width<=1024 || $cur_app?.full_width}>
{#if $can_have_header}
<div class:max-w-screen-lg={center} class="flex p-4 justify-start flex-wrap" class:w-[1024px]={center} >
{#if $header_icon}
<NavIcon img={$header_icon} config={{
tabindex:"-1",
class:"w-8 h-8 mr-2 mb-2 flex-none focus:outline-none"
}}/>
{/if}
{#if $cur_tab_view_or_edit && $cur_tab_doc_can_edit}
<button class="p-1 mr-2 mb-2 w-8 h-8 flex-none" on:click={()=>{set_view_or_edit(false);}} title={$t("doc.header.buttons.edit")}>
<PencilSquare tabindex=-1 class="w-5 h-5 focus:outline-none" />
</button>
{/if}
{#if !$header_title} <span class="font-mono h-8 py-1 inline-block align-middle mr-2"> {$cur_tab.doc.nuri.substring(2,9)} </span> {/if}
{#if !$cur_tab_view_or_edit}
<button class="p-1 mr-2 mb-2 w-8 h-8 flex-none" on:click={openEditHeader} title={$t($edit_header_button)}>
<Pencil tabindex=-1 class="w-5 h-5 focus:outline-none" />
</button>{#if !$header_title}<span role="button" on:click={openEditHeader} on:keypress={openEditHeader} tabindex="-1" class="h-8 py-1 inline-block align-middle ">{$t($edit_header_button)}</span> {/if}
{/if}
{#if $header_title}
<h1 class="grow text-left text-2xl">{$header_title}</h1>
{/if}
</div>
{#if $header_description}
<div class:max-w-screen-lg={center} class="flex p-4 text-left text-gray-600 dark:text-white" class:w-[1024px]={center}>
{$header_description}
</div>
{/if}
{/if}
{#if commits}
{#await commits.load()}
<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}
{#if $cur_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 break-all" style="overflow-wrap: anywhere;" class:w-[1024px]={center} >
<svelte:component this={app} commits={$commits}/>
</div>
{/await}
{:else}
<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>
{/if}
{/await}
{:else}
<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>
{/if}
</div>
</div>
{/if}
</div>

File diff suppressed because it is too large Load Diff

@ -10,16 +10,30 @@
--> -->
<script lang="ts"> <script lang="ts">
import { onMount, tick } from "svelte";
import { t } from "svelte-i18n";
import FullLayout from "./FullLayout.svelte"; import FullLayout from "./FullLayout.svelte";
import Test from "./Test.svelte"; import Document from "./Document.svelte";
import {
active_session,
} from "../store";
import {
change_nav_bar,reset_in_memory
} from "../tab";
import { import {
PaperAirplane, PaperAirplane,
Bell, Bell,
ArrowRightOnRectangle, ArrowRightOnRectangle,
Users, User,
Bookmark,
Sparkles,
Square3Stack3d,
ArchiveBox,
} from "svelte-heros-v2"; } from "svelte-heros-v2";
import Logo from "./components/Logo.svelte"; import Logo from "./components/Logo.svelte";
import NavBar from "./components/NavBar.svelte";
let top;
let width: number; let width: number;
let breakPoint: number = 662; let breakPoint: number = 662;
let mobile = false; let mobile = false;
@ -28,31 +42,41 @@
} else { } else {
mobile = true; mobile = true;
} }
function scrollToTop() {
top.scrollIntoView();
}
onMount(() => {
change_nav_bar("nav:private",$t("doc.private_store"), false);
reset_in_memory();
});
let nuri = $active_session && $active_session.private_store_id;
</script> </script>
<FullLayout> <FullLayout withoutNavBar={true}>
{#if mobile} {#if mobile}
<nav <nav bind:this={top}
class="border-t border-solid border-gray-200 bg-white dark:bg-gray-900 text-gray-700 dark:text-gray-200 dark:border-gray-700 divide-gray-100 dark:divide-gray-700 px-2 sm:px-4 py-2.5 w-full" style="background-color: #f6f6f6;" class="border-t border-solid border-gray-200 text-gray-700 dark:text-gray-200 dark:border-gray-700 divide-gray-100 dark:divide-gray-700 px-2 sm:px-4 py-2.5 w-full"
> >
<div <div
class="mx-auto flex flex-wrap justify-between items-center w-full px-2 xxs:px-8 xs:px-10" class="mx-auto flex flex-wrap justify-between items-center w-full xxs:px-8 xs:px-10"
> >
<a href="#/user" class="flex items-center" on:click> <a href="#/user" class="flex items-center" >
<Logo className="w-7 h-7 tall:w-10 tall:h-10" /> <Logo className="w-7 h-7 tall:w-10 tall:h-10" />
<span <span
class="ml-2 self-center text-lg font-normal text-gray-900 rounded-lg dark:text-white whitespace-nowrap" class="ml-2 self-center text-base font-normal text-gray-900 rounded-lg dark:text-white whitespace-nowrap"
>NextGraph</span >NextGraph</span
> >
</a> </a>
<div class="w-auto flex row"> <div class="w-auto flex row">
<a href="#/shared" class="row items-center" on:click> <a href="#/site" class="row items-center" on:click={scrollToTop}>
<Users <User
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none" class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none"
/> />
</a> </a>
<a href="#/messages" class="ml-4 row items-center" on:click> <a href="#/messages" class="ml-4 row items-center" >
<PaperAirplane <PaperAirplane
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none" class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none"
@ -64,7 +88,7 @@
</span> </span>
</a> </a>
<a href="#/notifications" class="ml-4 row items-center" on:click> <a href="#/notifications" class="ml-4 row items-center" >
<Bell <Bell
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none" class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white focus:outline-none"
@ -78,9 +102,24 @@
</div> </div>
</div> </div>
</nav> </nav>
<div class="sticky top-0 w-full" style="z-index:39;">
<NavBar {scrollToTop}/>
</div>
{/if} {/if}
<div /> <div class="bg-gray-100 flex p-1 justify-around md:justify-start h-11 gap-0 xs:gap-3 text-gray-500">
<div class="overflow-hidden w-24 sm:ml-3 flex justify-start mr-1" role="button" tabindex="0">
<Bookmark tabindex="-1" class="mt-1 flex-none w-7 h-7 mr-1 focus:outline-none "/><div class="text-xs xs:text-sm flex items-center"><div style="overflow-wrap: anywhere;" class="max-h-8 xs:max-h-10">{$t("doc.header.buttons.bookmarked")}</div></div>
</div>
<div class="overflow-hidden w-32 sm:ml-3 flex justify-start mr-1" role="button" tabindex="0" title={$t("doc.menu.items.mc.desc")}>
<Sparkles tabindex="-1" class="mt-1 flex-none w-7 h-7 mr-1 focus:outline-none "/><div class="text-xs xs:text-sm flex items-center"><div style="overflow-wrap: anywhere;" class="max-h-8 xs:max-h-10">{$t("doc.menu.items.mc.label")}</div></div>
</div>
<div class="overflow-hidden w-28 sm:ml-3 flex justify-start" role="button" tabindex="0">
<Square3Stack3d tabindex="-1" class="mt-1 flex-none w-7 h-7 mr-1 focus:outline-none "/><div class="text-xs xs:text-sm flex items-center"><div style="overflow-wrap: anywhere;" class="max-h-8 xs:max-h-10">{$t("doc.header.buttons.all_docs")}</div></div>
</div>
</div>
<Test /> <Document {nuri}/>
</FullLayout> </FullLayout>
<svelte:window bind:innerWidth={width} /> <svelte:window bind:innerWidth={width} />

@ -51,7 +51,7 @@
</Alert> </Alert>
{/if} {/if}
<div class="row mt-5"> <div class="row mt-5">
<a href="#/"> <a href="https://nextgraph.org/download/#android" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -71,7 +71,7 @@
</a> </a>
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<a href="https://nextgraph.org/download/#android"> <a href="https://nextgraph.org/download/#android" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -91,7 +91,7 @@
</a> </a>
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<a href="#/"> <a href="https://nextgraph.org/download/#ios" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -112,7 +112,7 @@
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<a href="https://nextgraph.org/download/#macos"> <a href="https://nextgraph.org/download/#macos" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -133,7 +133,7 @@
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<a href="https://nextgraph.org/download/#linux"> <a href="https://nextgraph.org/download/#linux" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -154,7 +154,7 @@
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<a href="https://nextgraph.org/download/#windows"> <a href="https://nextgraph.org/download/#windows" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -174,8 +174,8 @@
</a> </a>
</div> </div>
<div class="row mt-5 mb-12"> <!-- <div class="row mt-5 mb-12">
<a href="https://nextgraph.org/self-host"> <a href="https://nextgraph.org/self-host" target="_blank">
<button <button
tabindex="-1" tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2" class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
@ -193,5 +193,5 @@
{$t("pages.install.other_platforms")} {$t("pages.install.other_platforms")}
</button> </button>
</a> </a>
</div> </div> -->
</main> </main>

@ -27,12 +27,12 @@
Backspace, Backspace,
ArrowPath, ArrowPath,
LockOpen, LockOpen,
Key,
CheckCircle, CheckCircle,
ArrowLeft, ArrowLeft,
} from "svelte-heros-v2"; } from "svelte-heros-v2";
import PasswordInput from "./components/PasswordInput.svelte"; import PasswordInput from "./components/PasswordInput.svelte";
import Spinner from "./components/Spinner.svelte"; import Spinner from "./components/Spinner.svelte";
import { display_error } from "../store";
//import Worker from "../worker.js?worker&inline"; //import Worker from "../worker.js?worker&inline";
export let wallet; export let wallet;
export let for_import = false; export let for_import = false;
@ -48,9 +48,13 @@
onMount(async () => { onMount(async () => {
loaded = false; loaded = false;
if (for_import) {
device_name = await ng.get_device_name();
}
load_svg(); load_svg();
//console.log(wallet); //console.log(wallet);
await init(); await init();
}); });
async function init() { async function init() {
@ -85,7 +89,7 @@
unlockWith = "pazzle"; unlockWith = "pazzle";
scrollToTop(); scrollToTop();
} }
function start_with_mnemonic() { async function start_with_mnemonic() {
loaded = false; loaded = false;
step = "mnemonic"; step = "mnemonic";
unlockWith = "mnemonic"; unlockWith = "mnemonic";
@ -122,6 +126,8 @@
let unlockWith: "pazzle" | "mnemonic" | undefined; let unlockWith: "pazzle" | "mnemonic" | undefined;
let device_name;
function order() { function order() {
step = "order"; step = "order";
ordered = []; ordered = [];
@ -163,11 +169,12 @@
pazzle.push((emoji.cat << 4) + emoji.index); pazzle.push((emoji.cat << 4) + emoji.index);
} }
const mnemonic_words = mnemonic.split(" "); const mnemonic_words = mnemonic.split(" ").filter((t) => t !== "");
// open the wallet // open the wallet
try { try {
if (tauri_platform) { if (tauri_platform) {
// TODO @niko: Add device_name as param to open_with_* APIs
let opened_wallet = let opened_wallet =
unlockWith === "pazzle" unlockWith === "pazzle"
? await ng.wallet_open_with_pazzle(wallet, pazzle, pin_code) ? await ng.wallet_open_with_pazzle(wallet, pazzle, pin_code)
@ -191,6 +198,7 @@
wallet: opened_wallet, wallet: opened_wallet,
id: opened_wallet.V0.wallet_id, id: opened_wallet.V0.wallet_id,
trusted, trusted,
device_name,
}); });
} else { } else {
let worker_import = await import("../worker.js?worker&inline"); let worker_import = await import("../worker.js?worker&inline");
@ -211,9 +219,14 @@
//console.log("Message received from worker", msg.data); //console.log("Message received from worker", msg.data);
if (msg.data.loaded) { if (msg.data.loaded) {
if (unlockWith === "pazzle") { if (unlockWith === "pazzle") {
myWorker.postMessage({ wallet, pazzle, pin_code }); myWorker.postMessage({ wallet, pazzle, pin_code, device_name });
} else { } else {
myWorker.postMessage({ wallet, mnemonic_words, pin_code }); myWorker.postMessage({
wallet,
mnemonic_words,
pin_code,
device_name,
});
} }
//console.log("postMessage"); //console.log("postMessage");
} else if (msg.data.success) { } else if (msg.data.success) {
@ -233,6 +246,7 @@
wallet: msg.data.success, wallet: msg.data.success,
id: msg.data.success.V0.wallet_id, id: msg.data.success.V0.wallet_id,
trusted, trusted,
device_name,
}); });
} else { } else {
console.error(msg.data.error); console.error(msg.data.error);
@ -244,6 +258,7 @@
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
if (e.message && e.message.includes("constructor") || (typeof e === "string" && e.includes("constructor") )) e = "BrowserTooOld";
error = e; error = e;
step = "end"; step = "end";
dispatch("error", { error: e }); dispatch("error", { error: e });
@ -258,6 +273,9 @@
async function on_pin_key(val) { async function on_pin_key(val) {
pin_code = [...pin_code, val]; pin_code = [...pin_code, val];
if (pin_code.length == 4) {
setTimeout(()=>window.document.getElementById("confirm_pin_btn").focus(),50);
}
} }
async function select_order(val) { async function select_order(val) {
@ -329,8 +347,8 @@
<div <div
class="flex-col justify-center md:max-w-2xl py-4 sm:px-8" class="flex-col justify-center md:max-w-2xl py-4 sm:px-8"
class:h-screen={step !== "load" && height > 660} class:h-screen={step !== "load" && height > 640}
class:flex={height > 660} class:flex={height > 640}
bind:this={top} bind:this={top}
> >
{#if step == "load"} {#if step == "load"}
@ -387,13 +405,26 @@
<Toggle class="" bind:checked={trusted} <Toggle class="" bind:checked={trusted}
>{$t("pages.login.trust_device_yes")}</Toggle >{$t("pages.login.trust_device_yes")}</Toggle
> >
<!-- Ask for Device Name -->
</div> </div>
</div> </div>
{/if} {/if}
<div class=" max-w-xl lg:px-8 mx-auto px-4 text-primary-700"> <div class="max-w-xl lg:px-8 mx-auto px-4 text-primary-700">
<div class="flex flex-col justify-centerspace-x-12 mt-4 mb-4"> <div class="flex flex-col justify-centerspace-x-12 mt-4 mb-4">
<!-- Device Name, if trusted-->
{#if for_import && trusted}
<label for="device-name-input" class="text-sm text-black">
{$t("pages.login.device_name_label")}
</label>
<input
id="device-name-input"
bind:value={device_name}
placeholder={$t("pages.login.device_name_placeholder")}
type="text"
class="w-full mb-10 lg:px-8 mx-auto px-4 bg-gray-50 border border-gray-300 text-xs rounded-lg focus:ring-blue-500 focus:border-blue-500 block 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"
/>
{/if}
{#if !loaded} {#if !loaded}
{$t("pages.login.loading_pazzle")}... {$t("pages.login.loading_pazzle")}...
<Spinner className="my-4 h-14 w-14 mx-auto" /> <Spinner className="my-4 h-14 w-14 mx-auto" />
@ -422,7 +453,7 @@
on:keypress={start_with_mnemonic} on:keypress={start_with_mnemonic}
role="link" role="link"
tabindex="0" tabindex="0"
class="mt-1 text-lg px-5 py-2.5 text-center inline-flex items-center mb-2 underline cursor-pointer" class="mt-1 text-lg px-5 py-2.5 text-center inline-flex items-center underline cursor-pointer"
> >
{$t("pages.login.open_with_mnemonic")} {$t("pages.login.open_with_mnemonic")}
</span> </span>
@ -433,8 +464,8 @@
{:else if step == "pazzle" || step == "order" || step == "pin" || step == "mnemonic"} {:else if step == "pazzle" || step == "order" || step == "pin" || step == "mnemonic"}
<div <div
class="flex-col justify-center h-screen" class="flex-col justify-center h-screen"
class:flex={height > 660} class:flex={height > 640}
class:min-w-[310px]={mobile} class:min-w-[300px]={mobile}
class:min-w-[500px]={!mobile} class:min-w-[500px]={!mobile}
class:max-w-[370px]={mobile} class:max-w-[370px]={mobile}
class:max-w-[600px]={!mobile} class:max-w-[600px]={!mobile}
@ -459,7 +490,7 @@
<div class="flex"> <div class="flex">
<Button <Button
type="submit" type="submit"
class="mt-3 mb-2 ml-auto text-white bg-primary-700 disabled:opacity-65 focus:ring-4 focus:ring-blue-500 focus:border-blue-500 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-blue-500 dark:focus:border-blue-500" class="mt-3 mb-2 ml-auto text-white bg-primary-700 hover:bg-primary-700/90 disabled:opacity-65 focus:ring-4 focus:ring-blue-500 focus:border-blue-500 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-blue-500 dark:focus:border-blue-500"
on:click={start_pin} on:click={start_pin}
disabled={mnemonic.split(" ").length !== 12} disabled={mnemonic.split(" ").length !== 12}
><CheckCircle ><CheckCircle
@ -558,9 +589,9 @@
tabindex="0" tabindex="0"
class="pindigit m-1 disabled:opacity-15 disabled:text-gray-200 select-none align-bottom text-7xl p-0 w-full aspect-square border-0" class="pindigit m-1 disabled:opacity-15 disabled:text-gray-200 select-none align-bottom text-7xl p-0 w-full aspect-square border-0"
class:h-[160px]={!mobile} class:h-[160px]={!mobile}
class:h-[100px]={mobile} class:h-[93px]={mobile}
class:text-8xl={!mobile} class:text-8xl={!mobile}
on:click={async () => await on_pin_key(num)} on:click={async () => {window.document.activeElement.blur(); await on_pin_key(num)}}
disabled={pin_code.length >= 4} disabled={pin_code.length >= 4}
> >
<span>{num}</span> <span>{num}</span>
@ -574,17 +605,19 @@
tabindex="0" tabindex="0"
class="pindigit disabled:opacity-15 m-1 disabled:text-gray-200 select-none align-bottom text-7xl p-0 w-full aspect-square border-0" class="pindigit disabled:opacity-15 m-1 disabled:text-gray-200 select-none align-bottom text-7xl p-0 w-full aspect-square border-0"
class:h-[160px]={!mobile} class:h-[160px]={!mobile}
class:h-[100px]={mobile} class:h-[93px]={mobile}
class:text-8xl={!mobile} class:text-8xl={!mobile}
on:click={async () => await on_pin_key(shuffle_pin[9])} on:click={async () => {window.document.activeElement.blur();await on_pin_key(shuffle_pin[9])}}
disabled={pin_code.length >= 4} disabled={pin_code.length >= 4}
> >
<span>{shuffle_pin[9]}</span> <span>{shuffle_pin[9]}</span>
</button> </button>
<Button <Button
tabindex="0" tabindex="0"
id="confirm_pin_btn"
class="w-full bg-green-300 hover:bg-green-300/90 enabled:animate-bounce disabled:bg-gray-200 disabled:opacity-15 m-1 select-none align-bottom text-7xl p-0 aspect-square border-0" class="w-full bg-green-300 hover:bg-green-300/90 enabled:animate-bounce disabled:bg-gray-200 disabled:opacity-15 m-1 select-none align-bottom text-7xl p-0 aspect-square border-0"
on:click={async () => await finish()} on:click={async () => await finish()}
on:keypress={async () => await finish()}
disabled={pin_code.length < 4} disabled={pin_code.length < 4}
> >
<LockOpen <LockOpen
@ -593,7 +626,7 @@
/> />
</Button> </Button>
</div> </div>
<span class="mt-3 text-9xl min-h-[8rem] text-center" <span class="select-none text-9xl h-[4rem] text-center"
>{#each pin_code as pin_key}*{/each}</span >{#each pin_code as pin_key}*{/each}</span
> >
{/if} {/if}
@ -649,7 +682,7 @@
/> />
</svg> </svg>
<Alert color="red" class="mt-5"> <Alert color="red" class="mt-5">
{$t("errors." + error)} {display_error(error)}
</Alert> </Alert>
</div> </div>
<div class="flex justify-between mt-auto gap-4"> <div class="flex justify-between mt-auto gap-4">
@ -700,7 +733,7 @@
<style> <style>
.pindigit { .pindigit {
min-height: 99px; min-height: 93px;
} }
/* .pazzleline { /* .pazzleline {

@ -24,7 +24,7 @@
</script> </script>
<nav <nav
class="border-t border-solid border-gray-200 bg-white dark:bg-gray-900 text-gray-700 dark:text-gray-200 dark:border-gray-700 divide-gray-100 dark:divide-gray-700 px-2 sm:px-4 py-2.5 w-full fixed bottom-0 left-0 z-20" style="background-color: #f6f6f6;" class="border-t border-solid border-gray-200 text-gray-700 dark:text-gray-200 dark:border-gray-700 divide-gray-100 dark:divide-gray-700 px-2 sm:px-4 py-2.5 w-full fixed bottom-0 left-0 z-20"
> >
<div <div
class="mx-auto flex flex-wrap justify-between items-center w-full px-2 xxs:px-8 xs:px-10" class="mx-auto flex flex-wrap justify-between items-center w-full px-2 xxs:px-8 xs:px-10"

@ -13,6 +13,7 @@
import { getContext } from "svelte"; import { getContext } from "svelte";
import type { ComponentType } from "svelte"; import type { ComponentType } from "svelte";
import { Icon } from "svelte-heros-v2"; import { Icon } from "svelte-heros-v2";
import { onMount, onDestroy, tick } from "svelte";
export let href: string = ""; export let href: string = "";
export let icon: ComponentType; export let icon: ComponentType;
@ -22,14 +23,20 @@
}; };
let sidebarUrl = ""; let sidebarUrl = "";
activeUrlStore.subscribe((value) => { let unsub;
onMount( () => {
unsub = activeUrlStore.subscribe((value) => {
sidebarUrl = value; sidebarUrl = value;
}); });
});
onDestroy( () => {
if (unsub) unsub();
});
$: active = sidebarUrl ? href === sidebarUrl : false; $: active = sidebarUrl ? href === sidebarUrl : false;
</script> </script>
<a {href} class="flex items-center" on:click> <a {href} class="flex items-center">
<Icon <Icon
tabindex="-1" tabindex="-1"
color="black" color="black"

@ -27,7 +27,7 @@
<div class="row"> <div class="row">
<Logo class="logo block h-40" alt={$t("common.logo")} /> <Logo class="logo block h-40" alt={$t("common.logo")} />
</div> </div>
<h1 class="text-2xl mb-10">{$t("pages.no_wallet.welcome")}</h1> <h1 class="text-2xl text-center mb-10">{$t("pages.no_wallet.welcome")}</h1>
<p class="max-w-sm"> <p class="max-w-sm">
{@html $t("pages.no_wallet.description")} {@html $t("pages.no_wallet.description")}
@ -39,7 +39,7 @@
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
> >
<svg <svg
class="w-8 h-8 -ml-1" class="w-8 h-8 -ml-1 mr-2"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"

@ -0,0 +1,41 @@
<!--
// 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">
export let pane_name = "";
const panes = {
"history": "History",
"files": "Files",
};
const load_pane = async (pane_name) => {
if (!panes[pane_name]) return false;
let component = await import(`./panes/${panes[pane_name]}.svelte`);
return component.default;
};
</script>
<div>
{#if pane_name}
{#await load_pane(pane_name) then pane}
{#if pane}
<div class="flex w-full" style="overflow-wrap: anywhere;">
<svelte:component this={pane}/>
</div>
{/if}
{/await}
{/if}
</div>

@ -10,737 +10,24 @@
--> -->
<script lang="ts"> <script lang="ts">
import {
createGitgraph,
templateExtend,
TemplateName,
} from "../history/gitgraph-js/gitgraph";
import ng from "../api"; import ng from "../api";
import { import {
branch_subs, branch_subscribe,
active_session, active_session,
cannot_load_offline,
online, online,
get_blob,
} from "../store"; } from "../store";
import { link } from "svelte-spa-router";
import { onMount, onDestroy, tick } from "svelte"; import { onMount, onDestroy, tick } from "svelte";
import { Button, Progressbar, Spinner } from "flowbite-svelte"; import { Button, Progressbar, Spinner } from "flowbite-svelte";
import DataClassIcon from "./DataClassIcon.svelte";
import { t } from "svelte-i18n"; import { t } from "svelte-i18n";
let is_tauri = import.meta.env.TAURI_PLATFORM; let is_tauri = import.meta.env.TAURI_PLATFORM;
let upload_progress: null | { total: number; current: number; error?: any } =
null;
let files = $active_session && branch_subs($active_session.private_store_id);
let gitgraph;
let next = [
{
hash: "I",
subject: "niko2",
author: "",
parents: ["G"],
},
{
hash: "T",
subject: "niko2",
author: "",
parents: ["D", "H"],
},
{
hash: "Z",
subject: "niko2",
author: "",
parents: ["E"],
},
{
hash: "L",
subject: "niko2",
author: "",
parents: ["H"],
},
{
hash: "J",
subject: "niko2",
author: "",
parents: ["L", "Z", "I"],
},
{
hash: "K",
subject: "niko2",
author: "",
parents: ["G", "E"],
},
{
hash: "X",
subject: "niko2",
author: "",
parents: ["I"],
},
{
hash: "Q",
subject: "niko2",
author: "",
parents: ["L", "X"],
},
];
function add() {
let n = next.shift();
if (n) gitgraph.commit(n);
}
onMount(async () => { onMount(async () => {
const graphContainer = document.getElementById("graph-container");
gitgraph = createGitgraph(graphContainer, {
template: templateExtend(TemplateName.Metro, {
branch: { label: { display: false } },
commit: { message: { displayAuthor: false } },
}),
});
gitgraph.swimlanes(["A", "F", "C"]);
gitgraph.import([
{
hash: "A",
subject: "niko2",
branch: "A",
parents: [],
author: "",
x: 0,
y: 0,
},
{
hash: "B",
subject: "niko2",
branch: "A",
author: "",
parents: ["A"],
x: 0,
y: 1,
},
{
hash: "D",
subject: "niko2",
branch: "A",
author: "",
parents: ["B"],
x: 0,
y: 2,
},
{
hash: "C",
subject: "niko2",
branch: "C",
author: "",
parents: ["A"],
x: 2,
y: 3,
},
{
hash: "F",
subject: "niko2",
branch: "F",
author: "",
parents: ["B", "C"],
x: 1,
y: 4,
},
{
hash: "G",
subject: "niko2",
branch: "F",
parents: ["F"],
author: "",
x: 1,
y: 5,
},
{
hash: "E",
subject: "niko2",
branch: "C",
author: "",
parents: ["C"],
x: 2,
y: 6,
},
// {
// hash: "H",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["D", "G"],
// x: 0,
// y: 7,
// },
// {
// hash: "I",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["D", "H"],
// x: 0,
// y: 8,
// },
// {
// hash: "H",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["D", "G", "E"],
// x: 0,
// y: 7,
// },
]);
// window.setTimeout(() => {
// gitgraph.commit({
// hash: "H",
// subject: "niko2",
// author: "",
// parents: ["D", "G", "E"],
// });
// }, 0);
window.setTimeout(() => {
gitgraph.commit({
hash: "H",
subject: "niko2",
author: "",
parents: ["G", "E"],
});
}, 0);
// window.setTimeout(() => {
// gitgraph.commit({
// hash: "H",
// subject: "niko2",
// author: "",
// parents: ["G"],
// });
// }, 0);
// gitgraph.swimlanes(["A", "B", false, "D"]);
// gitgraph.import([
// {
// hash: "A",
// subject: "niko2",
// branch: "A",
// parents: [],
// author: "",
// x: 0,
// y: 0,
// },
// {
// hash: "C",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["A"],
// x: 0,
// y: 1,
// },
// {
// hash: "D",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["C"],
// x: 0,
// y: 2,
// },
// {
// hash: "E",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["D"],
// x: 0,
// y: 3,
// },
// {
// hash: "B",
// subject: "niko2",
// branch: "C",
// author: "",
// parents: ["A"],
// x: 2,
// y: 4,
// },
// {
// hash: "G",
// subject: "niko2",
// branch: "C",
// parents: ["B"],
// author: "",
// x: 2,
// y: 5,
// },
// {
// hash: "F",
// subject: "niko2",
// branch: "B",
// author: "",
// parents: ["D", "G"],
// x: 1,
// y: 6,
// },
// {
// hash: "H",
// subject: "niko2",
// branch: "D",
// author: "",
// parents: ["G"],
// x: 3,
// y: 7,
// },
// // {
// // hash: "I",
// // subject: "niko2",
// // branch: "A",
// // author: "",
// // parents: ["E", "F", "H"],
// // x: 0,
// // y: 8,
// // },
// ]);
// gitgraph.swimlanes([
// "A",
// false,
// false,
// false,
// false,
// false,
// false,
// false,
// false,
// false,
// false,
// false,
// false,
// ]);
// gitgraph.import([
// {
// hash: "A",
// subject: "niko2",
// branch: "A",
// parents: [],
// author: "",
// x: 0,
// y: 0,
// },
// {
// hash: "B",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["A"],
// x: 0,
// y: 1,
// },
// {
// hash: "C",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["B"],
// x: 0,
// y: 2,
// },
// {
// hash: "D",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["C"],
// x: 0,
// y: 3,
// },
// {
// hash: "E",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["D"],
// x: 0,
// y: 4,
// },
// {
// hash: "J",
// subject: "niko2",
// branch: "J",
// parents: ["A"],
// author: "",
// x: 2,
// y: 5,
// },
// {
// hash: "K",
// subject: "niko2",
// branch: "J",
// author: "",
// parents: ["J"],
// x: 2,
// y: 6,
// },
// {
// hash: "L",
// subject: "niko2",
// branch: "L",
// author: "",
// parents: ["A"],
// x: 3,
// y: 7,
// },
// {
// hash: "M",
// subject: "niko2",
// branch: "L",
// author: "",
// parents: ["L"],
// x: 3,
// y: 8,
// },
// {
// hash: "G",
// subject: "niko2",
// branch: "G",
// author: "",
// parents: ["C", "K", "M"],
// x: 1,
// y: 9,
// },
// {
// hash: "H",
// subject: "niko2",
// branch: "G",
// author: "",
// parents: ["G"],
// x: 1,
// y: 10,
// },
// {
// hash: "I",
// subject: "niko2",
// branch: "G",
// author: "",
// parents: ["H"],
// x: 1,
// y: 11,
// },
// {
// hash: "F",
// subject: "niko2",
// branch: "A",
// author: "",
// parents: ["E", "I"],
// x: 0,
// y: 12,
// },
// {
// hash: "1",
// subject: "niko2",
// branch: "1",
// author: "",
// parents: ["A"],
// x: 4,
// y: 13,
// },
// {
// hash: "2",
// subject: "niko2",
// branch: "2",
// author: "",
// parents: ["A"],
// x: 5,
// y: 14,
// },
// {
// hash: "3",
// subject: "niko2",
// branch: "3",
// author: "",
// parents: ["A"],
// x: 6,
// y: 15,
// },
// {
// hash: "4",
// subject: "niko2",
// branch: "4",
// author: "",
// parents: ["A"],
// x: 7,
// y: 16,
// },
// {
// hash: "5",
// subject: "niko2",
// branch: "5",
// author: "",
// parents: ["A"],
// x: 8,
// y: 17,
// },
// {
// hash: "6",
// subject: "niko2",
// branch: "6",
// author: "",
// parents: ["A"],
// x: 9,
// y: 18,
// },
// {
// hash: "7",
// subject: "niko2",
// branch: "7",
// author: "",
// parents: ["A"],
// x: 10,
// y: 19,
// },
// {
// hash: "8",
// subject: "niko2",
// branch: "8",
// author: "",
// parents: ["A"],
// x: 11,
// y: 20,
// },
// {
// hash: "9",
// subject: "niko2",
// branch: "9",
// author: "",
// parents: ["A"],
// x: 12,
// y: 21,
// },
// ]);
}); });
let fileinput;
function uploadFile(upload_id, nuri, file, success) {
let chunkSize = 1_048_564;
let fileSize = file.size;
let offset = 0;
let readBlock = null;
upload_progress = { total: fileSize, current: offset };
let onLoadHandler = async function (event) {
let result = event.target.result;
if (event.target.error == null) {
offset += result.byteLength;
upload_progress = { total: fileSize, current: offset };
// console.log("chunk", result);
let res = await ng.upload_chunk(
$active_session.session_id,
upload_id,
result,
nuri
);
//console.log("chunk upload res", res);
// if (onChunkRead) {
// onChunkRead(result);
// }
} else {
// if (onChunkError) {
// onChunkError(event.target.error);
// }
return;
}
// If finished:
if (offset >= fileSize) {
//console.log("file uploaded");
let res = await ng.upload_chunk(
$active_session.session_id,
upload_id,
[],
nuri
);
//console.log("end upload res", res);
if (success) {
upload_progress = { total: fileSize, current: fileSize };
success(res);
} else {
upload_progress = { total: fileSize, current: fileSize, error: true };
}
// Make progress bar disappear
setTimeout(() => {
upload_progress = null;
}, 2_500);
return;
}
readBlock(offset, chunkSize, file);
};
readBlock = function (offset, length, file) {
let fileReader = new FileReader();
let blob = file.slice(offset, length + offset);
fileReader.onload = onLoadHandler;
fileReader.readAsArrayBuffer(blob);
};
readBlock(offset, chunkSize, file);
return;
}
const onFileSelected = async (e) => {
let image = e.target.files[0];
if (!image) return;
//console.log(image);
let nuri = {
target: "PrivateStore",
entire_store: false,
access: [],
locator: [],
};
let start_request = {
V0: {
command: "FilePut",
nuri,
payload: {
V0: {
RandomAccessFilePut: image.type,
},
},
session_id: $active_session.session_id,
},
};
let start_res = await ng.app_request(start_request);
let upload_id = start_res.V0.FileUploading;
uploadFile(upload_id, nuri, image, async (reference) => {
if (reference) {
let request = {
V0: {
command: "FilePut",
nuri,
payload: {
V0: {
AddFile: {
filename: image.name,
object: reference.V0.FileUploaded,
},
},
},
session_id: $active_session.session_id,
},
};
await ng.app_request(request);
}
});
fileinput.value = "";
};
</script> </script>
<div>
<div id="graph-container"></div>
{#if $cannot_load_offline}
<div class="row p-4">
<p>
{@html $t("pages.test.cannot_load_offline")}
<a href="#/user">{$t("pages.user_panel.title")}</a>.
</p>
</div>
{:else}
<div class="row pt-2">
<!-- <a use:link href="/">
<button tabindex="-1" class=" mr-5 select-none"> Back home </button>
</a> -->
<Button
type="button"
on:click={() => {
add();
}}
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mr-2 mb-2"
>
g
</Button>
<Button
disabled={!$online && !is_tauri}
type="button"
on:click={() => {
fileinput.click();
}}
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
/>
</svg>
{$t("pages.test.add_image")}
</Button>
<input
style="display:none"
type="file"
accept=".jpg, .jpeg, .png"
on:change={(e) => onFileSelected(e)}
bind:this={fileinput}
/>
</div>
{#if upload_progress !== null}
<div class="mx-6 mt-2">
<Progressbar
progress={(
(100 * upload_progress.current) /
upload_progress.total
).toFixed(0)}
labelOutside={$t("pages.test.upload_progress")}
/>
</div>
{/if}
{#if files}
{#await files.load()}
<p>{$t("connectivity.loading")}...</p>
{:then}
{#each $files as file}
<p>
{file.name}
{#await get_blob(file)}
<div class="ml-2">
<Spinner />
</div>
{:then url}
{#if url}
<img src={url} title={file.nuri} alt={file.name} />
{/if}
{/await}
</p>
{/each}
{/await}
{/if}
{/if}
</div>

@ -11,7 +11,8 @@
<script lang="ts"> <script lang="ts">
export let value: string = ""; export let value: string = "";
export let id: string; export let id: string | undefined = undefined;
export let rows: number = 3;
let has_success: boolean = false; let has_success: boolean = false;
@ -37,10 +38,11 @@
<div class="relative"> <div class="relative">
<textarea <textarea
{id} {id}
rows="3" {rows}
style="resize: none;" style="resize: none;"
{value} {value}
class="col-span-6 pr-11 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 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500" class="col-span-6 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 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
class:pr-11={!tauri_platform}
disabled disabled
readonly readonly
/> />

@ -28,7 +28,7 @@ Provide classes using the `className` prop.
export let className: string = ""; export let className: string = "";
let connection_status_class = "logo-blue"; let connection_status_class = "logo-blue";
// Color is adjusted to connection status. // Color is adjusted to connection status.
$: if ($connection_status === "connecting") { $: if ($connection_status === "connecting" || $connection_status === "starting") {
connection_status_class = "logo-pulse"; connection_status_class = "logo-pulse";
} else if ($connection_status === "disconnected") { } else if ($connection_status === "disconnected") {
connection_status_class = "logo-gray"; connection_status_class = "logo-gray";

@ -0,0 +1,57 @@
<!--
// 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">
export let clickable;
export let extraClass = "";
export let selected = false;
export let title = "";
export let dropdown = undefined;
export let offset = false;
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 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 offset}
<div style="width:35px;">
</div>
{/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 {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}

@ -0,0 +1,64 @@
<!--
// 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 {
Toast,
} from "flowbite-svelte";
import {
remove_toast
} from "../../store";
import { onMount, onDestroy, tick } from "svelte";
const toast_color = {
"error":"red",
"warning":"orange",
"success":"green",
"info":"blue"
};
const toast_icon = {
"error": XCircle,
"warning": ExclamationCircle,
"success": CheckCircle,
"info": InformationCircle,
}
import {
CheckCircle,
XCircle,
ExclamationCircle,
InformationCircle,
Icon,
} from "svelte-heros-v2";
export let toast;
export let i;
onMount(()=>{
toast.i = i;
if (toast.level=="success")
{
toast.timer = setTimeout(()=>{remove_toast(i);}, toast.timeout || 10000);
}
});
</script>
<div class="toast fixed flex w-full max-w-xs" style="top:{16+i*74}px;"
on:click|capture|stopPropagation={()=>{remove_toast(toast.i);}}
on:keypress={()=>{}}
>
<Toast color="{toast_color[toast.level]}" >
<Icon tabindex="-1" slot="icon" class="w-8 h-8 p-1 focus:outline-none" variation="outline" color="currentColor" icon={toast_icon[toast.level]} />
{toast.text}
</Toast>
</div>

@ -0,0 +1,94 @@
<!--
// 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 {
ArrowLeft,
ChevronDoubleUp,
CheckCircle,
CheckBadge,
EllipsisVertical,
ExclamationTriangle,
} from "svelte-heros-v2";
import NavIcon from "../icons/NavIcon.svelte";
import {
Popover,
} from "flowbite-svelte";
import {nav_bar_newest, save, nav_bar, showMenu, cur_tab_header_in_view, cur_tab_store_name_override, cur_tab_store_icon_override, cur_tab_persistent_error, nav_bar_save,
cur_tab_view_or_edit, cur_tab_graph_or_discrete, nav_bar_back, nav_bar_title, nav_bar_icon} from "../../tab";
export let scrollToTop = () => {};
const back = () => {
// going back
window.history.go(-1);
}
const closeErrorPopup = () => {
window.document.getElementById("error_popover_btn").click();
}
</script>
<div style="background-color: #fbfbfb;" class="h-11 pb-1 flex text-center text-gray-700 dark:text-white">
{#if $nav_bar_back}
<div role="button" tabindex="0" on:click={back} on:keypress={back} class="flex-none w-10 flex justify-center items-center">
<ArrowLeft tabindex="-1" class="w-8 h-8 focus:outline-none"/>
</div>
{/if}
{#if $cur_tab_store_icon_override || $nav_bar_icon}
<div style="cursor:pointer;" class:w-10={!$nav_bar_back} class:ml-3={!$nav_bar_back} class="flex-none w-8 m-1 " on:click={scrollToTop} on:keypress={scrollToTop}>
<NavIcon img={$cur_tab_store_icon_override || $nav_bar_icon} config={{
tabindex:"-1",
class:"w-8 h-8 focus:outline-none"
}}/>
</div>
{/if}
<div style="cursor:pointer;" class:pl-3={!$nav_bar_back && !$nav_bar_icon} class="grow w-10 items-center flex px-1" on:click={scrollToTop} on:keypress={scrollToTop}>
<span class="inline-block truncate" > {$cur_tab_store_name_override || $nav_bar_title} </span>
</div>
{#if $nav_bar_newest && !$cur_tab_header_in_view}
<div role="button" tabindex="0" class="flex-none m-1 rounded-full bg-primary-700 text-white dark:bg-primary-700" on:click={scrollToTop} on:keypress={scrollToTop}>
<div class="flex items-center grow pr-2">
<ChevronDoubleUp tabindex="-1" class="w-6 h-6 m-1 focus:outline-none"/>
<span class="inline-block">{@html $nav_bar_newest < 100 ? "+"+$nav_bar_newest : "<span class=\"text-xl\">&infin;</span>"}</span>
</div>
</div>
{/if}
{#if $cur_tab_persistent_error}
<div id="error_popover_btn" tabindex="0" class="flex-none w-10" role="button" title={$cur_tab_persistent_error.title + ". Click for more details"}>
<ExclamationTriangle variation="outline" tabindex="-1" strokeWidth="2" class="w-9 h-9 mt-1 text-red-700 focus:outline-none"/>
</div>
<Popover class="text-left text-black w-[300px] text-sm error-popover" title={$cur_tab_persistent_error.title} triggeredBy="#error_popover_btn" trigger="click" placement = 'bottom'
open={true}
>{@html $cur_tab_persistent_error.desc}
<br/><br/><span class="text-primary-700" on:click={closeErrorPopup} on:keypress={closeErrorPopup} role="button" tabindex="0">Dismiss</span>
</Popover>
{:else if $nav_bar_save !== undefined && !$cur_tab_view_or_edit && !$cur_tab_graph_or_discrete}
{#if $nav_bar_save }
<div tabindex="0" class="flex-none w-10" role="button" on:click={save} on:keypress={save} title="Save">
<CheckCircle variation="solid" tabindex="-1" strokeWidth="3" class="w-10 h-10 text-primary-400 focus:outline-none"/>
</div>
{:else}
<div class="flex-none w-10" title="Saved!">
<CheckBadge tabindex="-1" class="w-8 h-8 m-1 text-green-500 focus:outline-none"/>
</div>
{/if}
{/if}
<div tabindex="0" class="flex-none w-10 " role="button" title="Open Menu" on:click={showMenu} on:keypress={showMenu}>
<EllipsisVertical tabindex="-1" class="w-8 h-8 my-1 mr-2"/>
</div>
</div>

@ -0,0 +1,49 @@
<!--
// 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 {
XMark,
Icon,
CheckCircle,
CheckBadge,
EllipsisVertical,
} from "svelte-heros-v2";
import { t } from "svelte-i18n";
import {cur_tab_update} from "../../tab";
export let pane_name = "";
export let pane_items = {};
const closePane = (pane:string|boolean) => {
cur_tab_update(($cur_tab) => {
if (pane=="folders") {
$cur_tab.folders_pane = false;
} else if (pane=="toc") {
$cur_tab.toc_pane = false;
} else {
$cur_tab.right_pane = "";
}
return $cur_tab;
});
}
</script>
<div style="height:44px; background-color: rgb(251, 251, 251);" class={`${$$props.class || ''} fixed top-0 w-10 h-10 p-1 bg-white dark:bg-black; rounded-bl-xl`} role="button" aria-label="Close pane" title="Close pane"
on:click={()=>closePane(pane_name)}
on:keypress={()=>closePane(pane_name)}
tabindex="0">
<XMark class="w-8 h-8 p-1 text-gray-400 focus:outline-none dark:text-white"/>
</div>
<div style="height:44px; background-color: rgb(251, 251, 251);" class="p-1 flex items-center">
<Icon tabindex="-1" class="w-8 h-8 text-gray-400 dark:text-white focus:outline-none " variation="outline" color="currentColor" icon={pane_items[pane_name]} />
<span class="ml-2 inline-block text-gray-500 select-none dark:text-white">{$t(`doc.menu.items.${pane_name}.label`)}</span>
</div>

@ -40,12 +40,14 @@
</script> </script>
<div class="relative"> <div class="relative">
<!-- svelte-ignore a11y-autofocus -->
<input <input
bind:this={input} bind:this={input}
{value} {value}
{placeholder} {placeholder}
{id} {id}
{type} {type}
autofocus
on:input={handleInput} on:input={handleInput}
class={`${className} pr-12 text-md block`} class={`${className} pr-12 text-md block`}
autocomplete={auto_complete} autocomplete={auto_complete}

@ -0,0 +1,3 @@
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''}>
<path d="M4 21.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM4 25.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0zM20 21.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM20 25.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0zM12 13.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM12 17.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0zM20 13.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM20 17.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0zM28 5.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM28 9.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0zM4 5.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM4 9.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0zM20 5.25c-1.519 0-2.75 1.231-2.75 2.75s1.231 2.75 2.75 2.75c1.519 0 2.75-1.231 2.75-2.75v0c-0.002-1.518-1.232-2.748-2.75-2.75h-0zM20 9.25c-0.69 0-1.25-0.56-1.25-1.25s0.56-1.25 1.25-1.25c0.69 0 1.25 0.56 1.25 1.25v0c-0.001 0.69-0.56 1.249-1.25 1.25h-0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''}>
<path d="M365.1,74.6c-43.8,0-80.2,36.4-80.2,80.2c0,38.2,27,70.9,64.3,78.3c-0.9,21.4-12.1,33.6-30.8,48.5
c-23.3,17.7-53.2,23.3-74.6,27c-46.6,8.4-71.8,30.8-83,45.7V159.5c16.8-2.8,32.6-12.1,44.8-25.2c13.1-14.9,20.5-33.6,20.5-54.1
C226.2,36.4,189.8,0,146,0S65.7,36.4,65.7,80.2c0,19.6,7.5,38.2,19.6,53.2c11.2,13.1,26.1,21.4,42.9,25.2v195.8
c-16.8,3.7-31.7,13.1-42.9,25.2c-13.1,14.9-19.6,33.6-19.6,52.2c0,43.8,36.4,80.2,80.2,80.2s80.2-36.4,80.2-80.2
c0-27-13.1-51.3-35.4-66.2c10.3-11.2,28-22.4,58.8-28c25.2-4.7,60.6-11.2,88.6-32.6c27-20.5,42-42,43.8-73.7
c37.3-7.5,64.3-40.1,64.3-78.3C445.3,110,408.9,74.6,365.1,74.6L365.1,74.6z M97.5,81.1c0-26.1,21.4-48.5,48.5-48.5
c26.1,0,48.5,21.4,48.5,48.5S173,129.6,146,129.6C118.9,129.6,97.5,107.2,97.5,81.1z M193.5,433.7c0,26.1-21.4,48.5-48.5,48.5
c-26.1,0-48.5-21.4-48.5-48.5s21.4-48.5,48.5-48.5C172.1,386.1,193.5,407.5,193.5,433.7z M365.1,202.4c-26.1,0-48.5-21.4-48.5-48.5
c0-26.1,21.4-48.5,48.5-48.5c26.1,0,48.5,21.4,48.5,48.5C412.7,180.9,391.2,202.4,365.1,202.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,8 @@
<svg viewBox="0 0 360.99 360.99" xml:space="preserve" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path d="M360.99,189.609l-9.322-11.75l-43.003,34.118l-56.418-32.573v-64.916l45.639-36.209l-9.322-11.75l-44.455,35.27
l-57.419-33.151V22.754h-15v48.673l-52.578,30.356L74.676,66.529l-9.322,11.75l45.592,36.171v63.564l-65.558,37.851v73.894
L0,325.768l9.322,11.75l44.426-35.246l62.29,35.964l64.355-37.156l64.354,37.156l70.651-40.791v-71.664L360.99,189.609z
M125.945,115.158l55.651-32.13l55.65,32.13v64.246l-55.662,32.137l-55.639-32.123V115.158z M171.689,288.785l-55.651,32.131
l-55.65-32.131v-64.26l55.65-32.131l55.651,32.131V288.785z M300.398,288.785l-55.651,32.131l-55.65-32.131v-64.246l55.663-32.137
l55.639,32.124V288.785z"/>
</svg>

After

Width:  |  Height:  |  Size: 841 B

@ -0,0 +1,191 @@
<!--
// 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 {
Icon,
BugAnt,
DocumentText,
Window,
CodeBracket,
SquaresPlus,
ViewfinderCircle,
ArrowsPointingOut,
Cube,
ClipboardDocumentCheck,
MagnifyingGlass,
RocketLaunch,
Sun,
TableCells,
ListBullet,
RectangleGroup,
Squares2x2,
MapPin,
CircleStack,
Envelope,
GlobeAlt,
DocumentChartBar,
Document,
ClipboardDocumentList,
Photo,
Film,
RectangleStack,
SpeakerWave,
MusicalNote,
Ticket,
CursorArrowRays,
Megaphone,
User,
Clock,
CalendarDays,
Calendar,
Stop,
Flag,
HandRaised,
Newspaper,
PencilSquare,
CubeTransparent,
PresentationChartBar,
QuestionMarkCircle,
CheckCircle,
ChartPie,
Bars3BottomLeft,
Link,
Square2Stack,
Clipboard,
StopCircle,
Bolt,
Heart,
Cog,
Square3Stack3d,
ChatBubbleLeftRight,
Fire,
ReceiptPercent,
ArrowTrendingUp,
CursorArrowRipple,
VideoCamera,
Variable,
Language,
QueueList,
} from "svelte-heros-v2";
import PdfIcon from "./PdfIcon.svelte";
import BrailleIcon from "./BrailleIcon.svelte";
import ChemistryIcon from "./ChemistryIcon.svelte";
import GuitarIcon from "./GuitarIcon.svelte";
import JsonIcon from "./JsonIcon.svelte";
import JsIcon from "./JsIcon.svelte";
import TsIcon from "./TsIcon.svelte";
import RustIcon from "./RustIcon.svelte";
import SvelteIcon from "./SvelteIcon.svelte";
import ReactIcon from "./ReactIcon.svelte";
import GraphQLIcon from "./GraphQLIcon.svelte";
export let config = {};
export let dataClass: string;
export let color = "currentColor";
const exact_mapping = {
page: Window,
"app": Cog,
"app:z": SquaresPlus,
class: ViewfinderCircle,
contract: ClipboardDocumentCheck,
"query:text": MagnifyingGlass,
"query:web": MagnifyingGlass,
"query:graphql": GraphQLIcon,
"data:graph": Sun,
"data:json": JsonIcon,
"data:map": JsonIcon,
"data:array": JsonIcon,
"data:table": TableCells,
"data:collection": ListBullet,
"data:container": Square3Stack3d,
"data:board": RectangleGroup,
"data:grid": Squares2x2,
"data:geomap": MapPin,
"e:email": Envelope,
"e:link": Link,
"mc:text": Bars3BottomLeft,
"mc:link": Link,
"plato:card": Clipboard,
"plato:pad": Square2Stack,
"media:image": Photo,
"media:reel": VideoCamera,
"media:video": Film,
"media:album": RectangleStack,
"media:audio": SpeakerWave,
"media:song": MusicalNote,
"media:subtitle": Ticket,
"media:overlay": CursorArrowRays,
"social:channel": Megaphone,
"social:stream": Bolt,
"social:contact": User,
"social:event": Clock,
"social:calendar": CalendarDays,
"social:scheduler": Calendar,
"social:reaction": Heart,
"social:chatroom": ChatBubbleLeftRight,
"social:live": Fire,
"prod:task": Stop,
"prod:project": Flag,
"prod:issue": HandRaised,
"prod:form": Newspaper,
"prod:filling": PencilSquare,
"prod:cad": CubeTransparent,
"prod:slides": PresentationChartBar,
"prod:question": QuestionMarkCircle,
"prod:answer": CheckCircle,
"prod:poll": CursorArrowRipple,
"prod:vote": CheckCircle,
"prod:spreadsheet": ReceiptPercent,
"doc:compose": QueueList,
"doc:maths": Variable,
"doc:music:abc": MusicalNote,
"doc:pdf": PdfIcon,
"doc:braille": BrailleIcon,
"doc:ancientscript": Language,
"doc:chemistry": ChemistryIcon,
"doc:music:guitar": GuitarIcon,
"code:js": JsIcon,
"code:ts": TsIcon,
"code:rust": RustIcon,
"code:svelte": SvelteIcon,
"code:react": ReactIcon,
};
const prefix_mapping = {
"post:": DocumentText,
code: CodeBracket,
schema: ArrowsPointingOut,
service: Cube,
"e:": GlobeAlt,
"app:": StopCircle,
"query:": RocketLaunch,
"data:": CircleStack,
"diagram": DocumentChartBar,
"chart": ChartPie,
"viz": ArrowTrendingUp,
"doc:": ClipboardDocumentList,
file: Document,
};
const find = (t) => {
let e = exact_mapping[t];
if (e) return e;
for (let prefix of Object.entries(prefix_mapping)) {
if (t.startsWith(prefix[0])) return prefix[1];
}
return BugAnt;
};
</script>
<Icon {...config} variation="outline" {color} icon={find(dataClass)} />

@ -0,0 +1,3 @@
<svg viewBox="0 0 32 32" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path d="M18.734 3.667l6.578 3.802c1.089-1.146 2.901-1.193 4.047-0.104 0.193 0.188 0.365 0.401 0.5 0.635 0.786 1.37 0.313 3.12-1.063 3.906-0.229 0.13-0.479 0.234-0.745 0.297v7.599c1.531 0.365 2.474 1.896 2.109 3.427-0.063 0.271-0.172 0.531-0.307 0.771-0.792 1.365-2.536 1.833-3.906 1.042-0.26-0.146-0.5-0.344-0.698-0.568l-6.542 3.776c0.495 1.495-0.318 3.109-1.813 3.604-0.292 0.099-0.594 0.146-0.896 0.146-1.573 0-2.854-1.271-2.854-2.849 0-0.271 0.042-0.547 0.12-0.813l-6.583-3.797c-1.089 1.141-2.896 1.188-4.036 0.094-1.135-1.089-1.177-2.891-0.094-4.031 0.38-0.396 0.865-0.677 1.396-0.807v-7.599c-1.531-0.365-2.479-1.906-2.109-3.443 0.063-0.266 0.167-0.521 0.302-0.755 0.786-1.365 2.536-1.833 3.901-1.042 0.234 0.135 0.453 0.302 0.641 0.5l6.583-3.797c-0.448-1.51 0.417-3.099 1.922-3.542 0.26-0.083 0.536-0.12 0.813-0.12 1.573 0 2.854 1.271 2.854 2.844 0 0.281-0.042 0.557-0.12 0.823zM18.047 4.839c-0.026 0.026-0.047 0.052-0.078 0.078l8.615 14.917c0.036-0.010 0.078-0.021 0.109-0.031v-7.609c-1.526-0.375-2.453-1.922-2.073-3.448 0.005-0.031 0.016-0.068 0.021-0.099zM14.026 4.917l-0.078-0.078-6.594 3.802c0.438 1.51-0.438 3.089-1.948 3.526-0.036 0.010-0.068 0.016-0.104 0.026v7.609l0.115 0.031 8.615-14.917zM16.797 5.594c-0.521 0.146-1.073 0.146-1.589 0l-8.615 14.917c0.391 0.375 0.667 0.859 0.802 1.391h17.214c0.13-0.531 0.406-1.016 0.802-1.396zM18.109 27.229l6.552-3.786c-0.021-0.063-0.036-0.125-0.052-0.188h-17.219l-0.031 0.109 6.589 3.802c0.516-0.536 1.245-0.87 2.052-0.87 0.839 0 1.589 0.359 2.109 0.932z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1,4 @@
<svg viewBox="0 0 32 32" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path d="M18.448,26.655c0.001,-0.001 0.001,-0.001 0.001,-0.002c2.407,-3.406 6.792,-9.888 8.717,-14.401c0.535,-1.252 0.882,-2.367 0.983,-3.246c0.088,-0.778 -0.016,-1.414 -0.263,-1.881c-0.359,-0.676 -1.158,-1.309 -2.339,-1.783c-2.201,-0.881 -5.872,-1.342 -9.545,-1.342c-3.673,-0 -7.344,0.461 -9.545,1.342c-1.181,0.474 -1.98,1.107 -2.338,1.783c-0.248,0.467 -0.352,1.103 -0.263,1.881c0.1,0.879 0.447,1.994 0.982,3.246c1.925,4.513 6.31,10.995 8.716,14.402c0.001,0 0.001,0.001 0.001,0.002c0.563,0.792 1.475,1.263 2.447,1.263c0.972,-0 1.884,-0.471 2.446,-1.264Zm-1.631,-1.157c-0.188,0.264 -0.491,0.421 -0.815,0.421c-0.324,0 -0.628,-0.157 -0.815,-0.421c-2.346,-3.321 -6.633,-9.632 -8.51,-14.03c-0.375,-0.88 -0.652,-1.675 -0.781,-2.345c-0.086,-0.449 -0.142,-0.813 -0.01,-1.061c0.109,-0.207 0.339,-0.37 0.627,-0.536c0.422,-0.242 0.961,-0.45 1.585,-0.632c2.061,-0.601 4.983,-0.894 7.904,-0.894c2.921,-0 5.843,0.293 7.904,0.894c0.624,0.182 1.163,0.39 1.585,0.632c0.288,0.166 0.518,0.329 0.628,0.536c0.131,0.248 0.075,0.612 -0.011,1.061c-0.129,0.67 -0.406,1.465 -0.781,2.345c-1.877,4.398 -6.164,10.709 -8.51,14.03l-0,-0Z"/>
<path d="M15.993,8.956l0,0.001c-0.001,0.018 -0.001,0.036 -0.001,0.054l-0,4.601c-0.236,-0.066 -0.485,-0.101 -0.742,-0.101c-1.518,-0 -2.75,1.232 -2.75,2.75c0,1.517 1.232,2.75 2.75,2.75c1.518,-0 2.75,-1.233 2.75,-2.75c0,-0.073 -0.008,-4.835 -0.008,-4.835l1.301,1.301c0.39,0.39 1.024,0.39 1.414,-0c0.39,-0.391 0.39,-1.024 0,-1.414l-3.008,-3.009c-0.39,-0.39 -1.024,-0.39 -1.414,0c-0.182,0.181 -0.279,0.415 -0.292,0.652Zm-0.743,6.555c0.414,-0 0.75,0.336 0.75,0.75c0,0.414 -0.336,0.75 -0.75,0.75c-0.414,-0 -0.75,-0.336 -0.75,-0.75c0,-0.414 0.336,-0.75 0.75,-0.75Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path fill-rule="nonzero" clip-rule="nonzero" d="M0 1.75C0 0.783501 0.783502 0 1.75 0H14.25C15.2165 0 16 0.783502 16 1.75V3.75C16 4.16421 15.6642 4.5 15.25 4.5C14.8358 4.5 14.5 4.16421 14.5 3.75V1.75C14.5 1.61193 14.3881 1.5 14.25 1.5H1.75C1.61193 1.5 1.5 1.61193 1.5 1.75V14.25C1.5 14.3881 1.61193 14.5 1.75 14.5H15.25C15.6642 14.5 16 14.8358 16 15.25C16 15.6642 15.6642 16 15.25 16H1.75C0.783501 16 0 15.2165 0 14.25V1.75ZM8.25 5.75C8.66421 5.75 9 6.08579 9 6.5V10.5C9 11.5048 8.72399 12.2584 8.15514 12.7324C7.61223 13.1848 6.95384 13.25 6.5 13.25C6.08579 13.25 5.75 12.9142 5.75 12.5C5.75 12.0858 6.08579 11.75 6.5 11.75C6.84617 11.75 7.06277 11.6902 7.19486 11.5801C7.301 11.4916 7.5 11.2452 7.5 10.5V6.5C7.5 6.08579 7.83578 5.75 8.25 5.75ZM11.2757 6.58011C11.6944 6.08164 12.3507 5.75 13.25 5.75C14.0849 5.75 14.7148 6.03567 15.1394 6.48481C15.4239 6.78583 15.4105 7.26052 15.1095 7.54505C14.8085 7.82958 14.3338 7.81621 14.0493 7.51519C13.9394 7.39898 13.7204 7.25 13.25 7.25C12.7493 7.25 12.5306 7.41836 12.4243 7.54489C12.2934 7.70065 12.25 7.896 12.25 8C12.25 8.104 12.2934 8.29935 12.4243 8.45511C12.5306 8.58164 12.7493 8.75 13.25 8.75C13.3257 8.75 13.3988 8.76121 13.4676 8.78207C14.1307 8.87646 14.6319 9.17251 14.9743 9.58011C15.3684 10.0493 15.5 10.604 15.5 11C15.5 11.396 15.3684 11.9507 14.9743 12.4199C14.5556 12.9184 13.8993 13.25 13 13.25C12.1651 13.25 11.5352 12.9643 11.1106 12.5152C10.8261 12.2142 10.8395 11.7395 11.1405 11.4549C11.4415 11.1704 11.9162 11.1838 12.2007 11.4848C12.3106 11.601 12.5296 11.75 13 11.75C13.5007 11.75 13.7194 11.5816 13.8257 11.4551C13.9566 11.2993 14 11.104 14 11C14 10.896 13.9566 10.7007 13.8257 10.5449C13.7194 10.4184 13.5007 10.25 13 10.25C12.9243 10.25 12.8512 10.2388 12.7824 10.2179C12.1193 10.1235 11.6181 9.82749 11.2757 9.41989C10.8816 8.95065 10.75 8.396 10.75 8C10.75 7.604 10.8816 7.04935 11.2757 6.58011Z" fill="currentColor" fill-opacity="0.80"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path d="M14 19H16C17.1046 19 18 18.1046 18 17V14.5616C18 13.6438 18.6246 12.8439 19.5149 12.6213L21.0299 12.2425C21.2823 12.1794 21.2823 11.8206 21.0299 11.7575L19.5149 11.3787C18.6246 11.1561 18 10.3562 18 9.43845V5H14" stroke="currentColor"/>
<path d="M10 5H8C6.89543 5 6 5.89543 6 7V9.43845C6 10.3562 5.37541 11.1561 4.48507 11.3787L2.97014 11.7575C2.71765 11.8206 2.71765 12.1794 2.97014 12.2425L4.48507 12.6213C5.37541 12.8439 6 13.6438 6 14.5616V19H10" stroke="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 665 B

@ -0,0 +1,10 @@
<svg fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} viewBox="-10 255 1034 734" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M278 256q-95 0 -127.5 28t-32.5 113v92q0 57 -20 79t-74 22h-24v55h24q53 0 73.5 22t20.5 80v92q0 84 32.5 112.5t127.5 28.5h25v-55h-27q-54 0 -70 -16.5t-16 -71.5v-95q0 -59 -17.5 -86.5t-59.5 -37.5q42 -10 59.5 -37t17.5 -87v-95q0 -55 16 -71.5t70 -16.5h27v-55
h-25zM697 256v55h27q53 0 69.5 16.5t16.5 71.5v95q0 60 17 87t60 37q-42 10 -59.5 37t-17.5 87v95q0 55 -16.5 71.5t-69.5 16.5h-27v55h24q95 0 127 -28.5t32 -112.5l1 -92q-1 -58 20 -80t75 -22h24v-55h-24q-54 0 -75 -22t-20 -79v-92q-1 -85 -33 -113t-127 -28h-24z
M617 341q-27 0 -51 14t-37 39q-16 31 -11 64l-1 -2v5q-1 6 -5 13q-5 10 -16 21q-13 13 -35 26q-38 24 -68 24q-14 0 -21 -5v0l2 3l-6 -3q-37 -20 -77 -7.5t-60 49.5t-7.5 77t49.5 60q27 14 57.5 11t53.5 -22l-1 2l5 -3q6 -2 15 -2q12 0 27 4q18 5 41 17q33 18 48 39
q10 13 11 26q-1 28 13.5 53.5t40.5 39.5q37 19 77 7t60 -49t7.5 -77.5t-49.5 -59.5q-5 -3 -10 -5h2l-3 -3q-5 -4 -8 -11q-6 -11 -9 -25q-4 -19 -4.5 -44t2.5 -44q3 -15 8 -26q3 -7 8 -12l3 -3h-3q28 -14 43 -43q20 -37 7.5 -77t-49.5 -60q-23 -12 -49 -11zM619 357
q27 0 46 17v0q-10 -9 -31 -7t-41 15q6 6 6 14.5t-5.5 15t-14.5 6.5q-3 0 -6 -1h-1q-9 -3 -15 6q-10 19 -11 37t8 29q-19 -21 -18 -51t22 -53q13 -14 29 -21t32 -7zM550 524q7 0 11 2l10 6q9 4 19 7q21 20 23 81q0 36 -8 59q-6 15 -15 23q-14 6 -25 16q-10 3 -25 1
q-24 -4 -56 -21q-34 -19 -51 -38q-10 -12 -11 -22q1 -13 -2 -26l1 1v-5q1 -5 5 -12q6 -9 17 -19q13 -13 34 -26q42 -26 70 -27h3zM323 548q27 0 46 17h-1q-10 -9 -30.5 -7t-40.5 15q5 6 5.5 14t-5.5 14.5t-15 7.5q-2 0 -5 -1h-2q-8 -3 -15 6q-9 18 -10 36.5t8 28.5
q-20 -20 -19 -50t23 -53q12 -14 28 -21t33 -7zM637 709q27 0 46 17h-1q-10 -9 -30.5 -6.5t-40.5 15.5q5 5 5.5 13.5t-5.5 15t-15 6.5h-5l-2 -1q-8 -2 -15 6q-10 19 -10.5 37t8.5 29q-20 -21 -19 -50.5t23 -53.5q12 -13 28 -20.5t33 -7.5z" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,6 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''}>
<path d="M14 3a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h12zM2 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H2z"/>
<path fill-rule="evenodd" d="M9.146 8.146a.5.5 0 0 1 .708 0L11.5 9.793l1.646-1.647a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 0-.708z"/>
<path fill-rule="evenodd" d="M11.5 5a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 1 .5-.5z"/>
<path d="M3.56 11V7.01h.056l1.428 3.239h.774l1.42-3.24h.056V11h1.073V5.001h-1.2l-1.71 3.894h-.039l-1.71-3.894H2.5V11h1.06z"/>
</svg>

After

Width:  |  Height:  |  Size: 725 B

@ -0,0 +1,64 @@
<!--
// 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.
-->
<!--
@component DeviceIcon
Display an icon for a device class provided by the `device` attribute.
Pass `config` for custom attributes.
-->
<script lang="ts">
import {
Icon,
Bolt,
Megaphone,
QuestionMarkCircle,
ExclamationCircle,
Key,
LockClosed,
GlobeAlt,
UserGroup,
PaperAirplane,
} from "svelte-heros-v2";
import DataClassIcon from "./DataClassIcon.svelte";
export let config = {};
export let img: string;
const mapping = {
stream: Bolt,
channel: Megaphone,
private: Key,
protected: LockClosed,
public: GlobeAlt,
group: UserGroup,
dialog: PaperAirplane,
unknown_doc: ExclamationCircle,
};
const find = (dataClass: string) => {
return mapping[dataClass] || QuestionMarkCircle;
};
</script>
{#if img.startsWith("blob:")}
<img style="aspect-ratio:1;" class="rounded-full" src={img} alt="profile"/>
{:else if img.startsWith("class:")}
<DataClassIcon {config} dataClass={img.slice(6)} />
{:else if img.startsWith("nav:")}
<Icon {...config} variation="outline" color="currentColor" icon={find(img.slice(4))} />
{:else}
<QuestionMarkCircle {...config}/>
{/if}

@ -0,0 +1,3 @@
<svg viewBox="-4 0 40 40" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path d="M25.6686 26.0962C25.1812 26.2401 24.4656 26.2563 23.6984 26.145C22.875 26.0256 22.0351 25.7739 21.2096 25.403C22.6817 25.1888 23.8237 25.2548 24.8005 25.6009C25.0319 25.6829 25.412 25.9021 25.6686 26.0962ZM17.4552 24.7459C17.3953 24.7622 17.3363 24.7776 17.2776 24.7939C16.8815 24.9017 16.4961 25.0069 16.1247 25.1005L15.6239 25.2275C14.6165 25.4824 13.5865 25.7428 12.5692 26.0529C12.9558 25.1206 13.315 24.178 13.6667 23.2564C13.9271 22.5742 14.193 21.8773 14.468 21.1894C14.6075 21.4198 14.7531 21.6503 14.9046 21.8814C15.5948 22.9326 16.4624 23.9045 17.4552 24.7459ZM14.8927 14.2326C14.958 15.383 14.7098 16.4897 14.3457 17.5514C13.8972 16.2386 13.6882 14.7889 14.2489 13.6185C14.3927 13.3185 14.5105 13.1581 14.5869 13.0744C14.7049 13.2566 14.8601 13.6642 14.8927 14.2326ZM9.63347 28.8054C9.38148 29.2562 9.12426 29.6782 8.86063 30.0767C8.22442 31.0355 7.18393 32.0621 6.64941 32.0621C6.59681 32.0621 6.53316 32.0536 6.44015 31.9554C6.38028 31.8926 6.37069 31.8476 6.37359 31.7862C6.39161 31.4337 6.85867 30.8059 7.53527 30.2238C8.14939 29.6957 8.84352 29.2262 9.63347 28.8054ZM27.3706 26.1461C27.2889 24.9719 25.3123 24.2186 25.2928 24.2116C24.5287 23.9407 23.6986 23.8091 22.7552 23.8091C21.7453 23.8091 20.6565 23.9552 19.2582 24.2819C18.014 23.3999 16.9392 22.2957 16.1362 21.0733C15.7816 20.5332 15.4628 19.9941 15.1849 19.4675C15.8633 17.8454 16.4742 16.1013 16.3632 14.1479C16.2737 12.5816 15.5674 11.5295 14.6069 11.5295C13.948 11.5295 13.3807 12.0175 12.9194 12.9813C12.0965 14.6987 12.3128 16.8962 13.562 19.5184C13.1121 20.5751 12.6941 21.6706 12.2895 22.7311C11.7861 24.0498 11.2674 25.4103 10.6828 26.7045C9.04334 27.3532 7.69648 28.1399 6.57402 29.1057C5.8387 29.7373 4.95223 30.7028 4.90163 31.7107C4.87693 32.1854 5.03969 32.6207 5.37044 32.9695C5.72183 33.3398 6.16329 33.5348 6.6487 33.5354C8.25189 33.5354 9.79489 31.3327 10.0876 30.8909C10.6767 30.0029 11.2281 29.0124 11.7684 27.8699C13.1292 27.3781 14.5794 27.011 15.985 26.6562L16.4884 26.5283C16.8668 26.4321 17.2601 26.3257 17.6635 26.2153C18.0904 26.0999 18.5296 25.9802 18.976 25.8665C20.4193 26.7844 21.9714 27.3831 23.4851 27.6028C24.7601 27.7883 25.8924 27.6807 26.6589 27.2811C27.3486 26.9219 27.3866 26.3676 27.3706 26.1461ZM30.4755 36.2428C30.4755 38.3932 28.5802 38.5258 28.1978 38.5301H3.74486C1.60224 38.5301 1.47322 36.6218 1.46913 36.2428L1.46884 3.75642C1.46884 1.6039 3.36763 1.4734 3.74457 1.46908H20.263L20.2718 1.4778V7.92396C20.2718 9.21763 21.0539 11.6669 24.0158 11.6669H30.4203L30.4753 11.7218L30.4755 36.2428ZM28.9572 10.1976H24.0169C21.8749 10.1976 21.7453 8.29969 21.7424 7.92417V2.95307L28.9572 10.1976ZM31.9447 36.2428V11.1157L21.7424 0.871022V0.823357H21.6936L20.8742 0H3.74491C2.44954 0 0 0.785336 0 3.75711V36.2435C0 37.5427 0.782956 40 3.74491 40H28.2001C29.4952 39.9997 31.9447 39.2143 31.9447 36.2428Z" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -0,0 +1,9 @@
<svg fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} viewBox="-10 255 1034 734" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M704 227q-40 0 -74.5 20.5t-53.5 56.5q-23 43 -15 92l-2 -2v6q-1 9 -7 19q-7 15 -23 30q-20 19 -50.5 38t-57.5 28q-22 7 -40 7q-14 0 -24 -4l-8 -3l3 3l-8 -4q-35 -19 -73.5 -17.5t-72 22t-52 55.5t-17 74t22 72t55.5 52q40 21 83.5 16.5t76.5 -31.5l-1 2l7 -4
q9 -3 21 -3q17 -1 39 5q27 8 59 25q48 26 70 56q15 19 16 37q-1 42 19.5 78.5t58.5 56.5q35 18 73.5 16.5t72 -22t52 -55.5t17 -74t-22 -72t-55.5 -51q-7 -4 -15 -7l3 -1l-5 -4q-6 -5 -11 -16q-8 -15 -12 -36q-6 -27 -7 -63t3 -63q4 -22 11 -37q5 -11 12 -18l5 -4h-5
q41 -21 62 -62q19 -35 17.5 -73.5t-22 -71.5t-55.5 -52q-33 -18 -70 -17zM706 250q39 0 66 25v0q-15 -13 -44.5 -10t-58.5 22q8 8 8.5 20t-8 21.5t-21.5 9.5h-8l-2 -1q-12 -4 -21 8q-16 29 -16 56.5t15 42.5q-20 -19 -27 -46.5t0.5 -56t28.5 -51.5q18 -19 41 -29.5t47 -10.5
zM605 491q11 0 18 3.5t14 7.5q13 7 27 11q11 11 20 33q11 33 12.5 84t-10.5 84q-9 22 -22 34q-20 9 -36 23q-14 5 -36 1q-34 -6 -81 -31q-49 -26 -73 -54q-14 -17 -17 -32q2 -18 -1 -37v1l1 -6q1 -8 7 -18q8 -13 24 -28q20 -18 50 -37q62 -38 103 -39zM279 525q39 0 66 25
h-1q-14 -13 -44 -10t-58 22q7 8 7.5 20t-8 21.5t-20.5 9.5q-4 1 -8 0l-2 -1q-13 -4 -22 8q-15 29 -15.5 56.5t14.5 42.5q-20 -19 -26.5 -46.5t1 -56t28.5 -51.5q18 -19 41 -29.5t47 -10.5zM731 758q39 0 66 25v0q-14 -13 -44 -10t-59 22q8 8 8.5 20t-8 21.5t-21.5 9.5
q-4 0 -8 -1h-2q-12 -4 -21 8q-16 29 -16 56.5t15 42.5q-20 -19 -27 -46.5t0.5 -56t28.5 -51.5q18 -19 41 -29.5t47 -10.5z" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,3 @@
<svg fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} viewBox="0 -1.5 27 27" xmlns="http://www.w3.org/2000/svg">
<path d="m15.902 11.974c0 1.331-1.079 2.41-2.41 2.41s-2.41-1.079-2.41-2.41 1.079-2.41 2.41-2.41 2.41 1.079 2.41 2.41z"/><path d="m13.491 17.494c-.201.005-.438.008-.676.008-2.811 0-5.522-.425-8.073-1.214l.193.051c-1.351-.436-2.522-1.079-3.541-1.903l.021.016c-.789-.571-1.32-1.455-1.41-2.467l-.001-.013c0-1.594 1.747-3.154 4.681-4.172 2.512-.827 5.403-1.304 8.405-1.304.139 0 .278.001.417.003h-.021c.116-.002.252-.003.389-.003 2.965 0 5.82.469 8.496 1.337l-.195-.055c1.305.424 2.438 1.036 3.433 1.814l-.024-.018c.775.546 1.298 1.404 1.386 2.387l.001.013c0 1.656-1.954 3.332-5.103 4.374-2.363.729-5.08 1.149-7.895 1.149-.17 0-.339-.002-.508-.005h.025zm0-9.84c-.106-.002-.232-.002-.358-.002-2.881 0-5.656.452-8.259 1.289l.191-.053c-2.698.941-3.908 2.228-3.908 3.087 0 .893 1.301 2.3 4.153 3.274 2.249.697 4.834 1.099 7.513 1.099.235 0 .469-.003.702-.009l-.034.001c.146.003.318.005.49.005 2.684 0 5.274-.4 7.715-1.143l-.187.049c2.96-.984 4.32-2.391 4.32-3.28-.102-.638-.462-1.176-.966-1.515l-.008-.005c-.866-.674-1.876-1.217-2.97-1.574l-.07-.02c-2.368-.766-5.093-1.208-7.92-1.208-.142 0-.283.001-.424.003h.021z"/><path d="m8.023 23.986c-.016 0-.035.001-.053.001-.453 0-.878-.119-1.245-.327l.013.007c-1.378-.8-1.858-3.092-1.28-6.141.697-3.128 1.827-5.894 3.344-8.4l-.069.124c1.499-2.655 3.31-4.926 5.425-6.872l.02-.018c.923-.844 2.003-1.537 3.187-2.028l.073-.027c.407-.196.885-.31 1.39-.31.501 0 .976.113 1.4.314l-.02-.008c1.435.826 1.911 3.36 1.238 6.606-.72 2.967-1.818 5.58-3.262 7.959l.07-.123c-1.452 2.63-3.209 4.882-5.266 6.819l-.014.013c-.958.877-2.083 1.59-3.321 2.082l-.074.026c-.46.181-.992.292-1.549.305h-.006zm1.205-14.447.499.288c-1.381 2.28-2.465 4.926-3.106 7.74l-.034.18c-.533 2.809-.019 4.498.72 4.926.191.102.417.161.657.161.02 0 .041 0 .061-.001h-.003c.955 0 2.458-.605 4.196-2.122 1.976-1.867 3.654-4.023 4.972-6.404l.068-.135c1.318-2.156 2.371-4.653 3.025-7.307l.038-.182c.629-3.058.086-4.93-.686-5.378-.238-.094-.514-.148-.802-.148-.364 0-.708.087-1.012.24l.013-.006c-1.118.465-2.078 1.086-2.907 1.846l.007-.006c-2.041 1.881-3.772 4.057-5.136 6.468l-.069.132z"/><path d="m18.96 24c-1.306 0-2.96-.787-4.69-2.276-2.175-1.983-4.017-4.282-5.464-6.834l-.072-.137c-1.43-2.349-2.551-5.075-3.215-7.975l-.036-.185c-.162-.709-.255-1.522-.255-2.357 0-.527.037-1.044.108-1.551l-.007.058c.085-.998.608-1.858 1.372-2.399l.01-.007c1.43-.83 3.865.024 6.342 2.228 2.022 1.914 3.748 4.113 5.118 6.54l.072.138c1.428 2.297 2.557 4.961 3.242 7.798l.038.186c.163.709.257 1.524.257 2.36 0 .577-.044 1.143-.13 1.696l.008-.062c-.103 1.022-.644 1.901-1.429 2.456l-.011.007c-.348.199-.764.316-1.207.316-.018 0-.036 0-.054-.001h.003zm-9.228-9.823c1.458 2.571 3.217 4.771 5.271 6.656l.02.018c2.166 1.863 3.884 2.266 4.628 1.834.773-.446 1.339-2.276.754-5.233-.702-2.89-1.787-5.434-3.219-7.741l.065.112c-1.379-2.456-3.027-4.562-4.948-6.384l-.012-.011c-2.334-2.074-4.225-2.54-4.998-2.094-.473.385-.784.952-.825 1.593v.007c-.055.39-.086.84-.086 1.298 0 .757.086 1.493.248 2.201l-.013-.066c.673 2.952 1.747 5.559 3.182 7.924l-.066-.117z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''}>
<path d="M8 .1c.422 0 .765.342.765.765v.916c.296.035.587.091.87.166l.283-.683a.764.764 0 111.413.585l-.276.665c.287.157.56.335.817.533l.633-.633a.765.765 0 011.081 1.081l-.62.62c.24.298.453.618.637.956l.631-.261a.765.765 0 11.585 1.413l-.654.27c.072.277.126.56.16.849h.81a.765.765 0 010 1.529h-.81a6.334 6.334 0 01-.21 1.027l.664.275a.765.765 0 11-.585 1.413l-.672-.278c-.13.24-.278.471-.44.693l.504.504a.765.765 0 01-1.081 1.081l-.465-.465c-.3.253-.627.48-.978.677l.269.65a.765.765 0 11-1.413.586l-.27-.65a6.332 6.332 0 01-.883.181v.57a.765.765 0 01-1.53 0v-.555a6.386 6.386 0 01-1.087-.218l-.278.672a.765.765 0 11-1.413-.585l.285-.688a6.38 6.38 0 01-.84-.581l-.407.406a.765.765 0 01-1.081-1.081l.428-.428a6.348 6.348 0 01-.459-.703l-.748.31a.765.765 0 01-.585-1.413l.748-.31a6.339 6.339 0 01-.205-1.09H.865a.765.765 0 010-1.53h.762c.044-.298.108-.593.192-.88l-.81-.336a.765.765 0 01.585-1.413l.815.337c.181-.33.39-.643.625-.934l-.62-.62a.765.765 0 011.081-1.081l.633.633c.205-.158.42-.303.645-.435l-.316-.763a.765.765 0 011.413-.585L6.175 2a6.34 6.34 0 011.06-.22V.865C7.235.442 7.578.1 8 .1zm-.765 3.354a4.854 4.854 0 00-3.487 2.36l.61.199a.765.765 0 11-.472 1.454l-.653-.212a4.823 4.823 0 001.294 4.225l.458-.63a.764.764 0 011.237.898l-.47.648A4.818 4.818 0 008 12.948c.812 0 1.577-.2 2.249-.552l-.352-.484a.765.765 0 011.237-.899l.34.467c.349-.36.642-.773.867-1.227a4.843 4.843 0 00.34-2.97l-.567.184a.764.764 0 11-.472-1.454l.5-.163a4.827 4.827 0 00-3.377-2.374v.447a.765.765 0 11-1.53 0v-.469z" fill="currentColor" fill-opacity="0.80"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1,3 @@
<svg viewBox="0 0 15 15" fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2829 1.94409C14.2083 3.42511 14.046 5.27647 13.0128 6.56576C13.8628 8.37306 13.2462 10.5713 11.5104 11.656L7.27012 14.3056C5.39667 15.4763 2.92892 14.9066 1.75825 13.0331C0.832798 11.5521 0.99519 9.70071 2.02833 8.41145C1.17829 6.60415 1.79488 4.40587 3.53076 3.32117L7.771 0.671576C9.64446 -0.499089 12.1122 0.070635 13.2829 1.94409ZM8.30092 1.51962C9.70601 0.641625 11.5568 1.06892 12.4348 2.47401C13.0645 3.48168 13.023 4.71945 12.4301 5.66263C11.8046 4.92992 10.9523 4.46839 10.0447 4.31762L10.7691 3.86494L10.2392 3.0169L5.15088 6.19641L5.6808 7.04446L7.80091 5.71967C9.206 4.84167 11.0568 5.26899 11.9348 6.67408C12.8128 8.07918 12.3855 9.92999 10.9804 10.808L6.7402 13.4576C5.33511 14.3356 3.4843 13.9083 2.6063 12.5032C1.97665 11.4955 2.01814 10.2578 2.61103 9.31458C3.23653 10.0473 4.08885 10.5088 4.99651 10.6596L4.27222 11.1122L4.80214 11.9602L9.89043 8.78071L9.36051 7.93267L7.24023 9.25756C5.83514 10.1356 3.98429 9.70821 3.10629 8.30312C2.2283 6.89803 2.65559 5.04722 4.06068 4.16922L8.30092 1.51962Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" fill="none" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} xmlns="http://www.w3.org/2000/svg">
<path fill-rule="nonzero" clip-rule="nonzero" d="M0 1.75C0 0.783501 0.783502 0 1.75 0H14.25C15.2165 0 16 0.783502 16 1.75V3.75C16 4.16421 15.6642 4.5 15.25 4.5C14.8358 4.5 14.5 4.16421 14.5 3.75V1.75C14.5 1.61193 14.3881 1.5 14.25 1.5H1.75C1.61193 1.5 1.5 1.61193 1.5 1.75V14.25C1.5 14.3881 1.61193 14.5 1.75 14.5H15.25C15.6642 14.5 16 14.8358 16 15.25C16 15.6642 15.6642 16 15.25 16H1.75C0.783501 16 0 15.2165 0 14.25V1.75ZM4.75 6.5C4.75 6.08579 5.08579 5.75 5.5 5.75H9.25C9.66421 5.75 10 6.08579 10 6.5C10 6.91421 9.66421 7.25 9.25 7.25H8.25V12.5C8.25 12.9142 7.91421 13.25 7.5 13.25C7.08579 13.25 6.75 12.9142 6.75 12.5V7.25H5.5C5.08579 7.25 4.75 6.91421 4.75 6.5ZM11.2757 6.58011C11.6944 6.08164 12.3507 5.75 13.25 5.75C14.0849 5.75 14.7148 6.03567 15.1394 6.48481C15.4239 6.78583 15.4105 7.26052 15.1095 7.54505C14.8085 7.82958 14.3338 7.81621 14.0493 7.51519C13.9394 7.39898 13.7204 7.25 13.25 7.25C12.7493 7.25 12.5306 7.41836 12.4243 7.54489C12.2934 7.70065 12.25 7.896 12.25 8C12.25 8.104 12.2934 8.29935 12.4243 8.45511C12.5306 8.58164 12.7493 8.75 13.25 8.75C13.3257 8.75 13.3988 8.76121 13.4676 8.78207C14.1307 8.87646 14.6319 9.17251 14.9743 9.58011C15.3684 10.0493 15.5 10.604 15.5 11C15.5 11.396 15.3684 11.9507 14.9743 12.4199C14.5556 12.9184 13.8993 13.25 13 13.25C12.1651 13.25 11.5352 12.9643 11.1106 12.5152C10.8261 12.2142 10.8395 11.7395 11.1405 11.4549C11.4415 11.1704 11.9162 11.1838 12.2007 11.4848C12.3106 11.601 12.5296 11.75 13 11.75C13.5007 11.75 13.7194 11.5816 13.8257 11.4551C13.9566 11.2993 14 11.104 14 11C14 10.896 13.9566 10.7007 13.8257 10.5449C13.7194 10.4184 13.5007 10.25 13 10.25C12.9243 10.25 12.8512 10.2388 12.7824 10.2179C12.1193 10.1235 11.6181 9.82749 11.2757 9.41989C10.8816 8.95065 10.75 8.396 10.75 8C10.75 7.604 10.8816 7.04935 11.2757 6.58011Z" fill="currentColor" fill-opacity="0.80"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,35 @@
<svg fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} viewBox="0 0 493.249 493.249" xmlns="http://www.w3.org/2000/svg">
<path d="M463.007,168.49c0-28.703-33.46-51.188-76.174-51.188c-18.346,0-35.576,4.216-49.249,11.953
c-13.401-9.919-28.469-17.705-44.7-22.856c6.058-13.7,9.44-29.27,9.44-44.106C302.323,19.261,274.349,0,246.625,0
s-55.699,19.261-55.699,62.292c0,14.836,3.383,30.406,9.44,44.106c-16.231,5.152-31.299,12.938-44.7,22.857
c-13.674-7.737-30.904-11.954-49.249-11.954c-42.715,0-76.175,22.484-76.175,51.188c0,13.253,10.782,22.164,32.047,26.485
c12.216,2.482,27.183,3.384,40.87,3.714c-6.298,16.744-9.754,34.868-9.754,53.787c0,24.754,5.938,52.82,16.436,80.015
c-8.104,2.426-16.193,5.903-23.818,10.306c-17.027,9.83-30.687,23.381-38.463,38.157c-8.446,16.049-9.126,31.767-1.913,44.26
c2.842,4.922,7.37,8.331,13.094,9.856c2.395,0.638,4.977,0.941,7.73,0.941c19.806,0,48.526-15.642,80.912-34.457
c0.346-0.201,0.679-0.394,1.021-0.593c26.628,33.774,57.808,53.408,89.722,56.382v27.408c0,4.694,3.806,8.5,8.5,8.5
c4.694,0,8.5-3.806,8.5-8.5v-27.408c31.914-2.974,63.094-22.607,89.721-56.382c0.343,0.199,0.677,0.393,1.023,0.594
c32.387,18.815,61.104,34.456,80.91,34.456c2.752,0,5.336-0.302,7.73-0.94c5.724-1.525,10.252-4.934,13.094-9.856
c14.351-24.858-3.385-61.06-40.376-82.416c-7.626-4.403-15.715-7.88-23.818-10.307c10.497-27.194,16.435-55.26,16.435-80.015
c0-18.919-3.456-37.042-9.754-53.787c13.688-0.331,28.653-1.232,40.87-3.714C452.225,190.654,463.007,181.743,463.007,168.49z
M382.844,252.476c0,3.003-0.11,6.077-0.306,9.2h-63.661l-29.605-51.278l53.91-53.91
C367.681,181.13,382.844,215.064,382.844,252.476z M341.784,376.22l-50.758-50.758l27.012-46.786h62.58
C375.449,310.854,361.534,346.538,341.784,376.22z M112.518,277.981h62.293l27.413,47.479l-50.759,50.759
C131.574,346.325,117.597,310.34,112.518,277.981z M110.405,252.476c0-36.355,14.316-69.428,37.606-93.881l54.442,54.442
l-27.681,47.944h-64.103C110.501,258.098,110.405,255.256,110.405,252.476z M246.625,116.256c31.587,0,60.696,10.81,83.827,28.92
l-57.812,57.813h-56.193l-55.958-55.959C183.976,127.807,213.976,116.256,246.625,116.256z M218.07,318.907l-28.556-49.459
l28.556-49.459h57.11l28.555,49.459l-28.555,49.459H218.07z M207.926,62.292c0-27.938,14.829-45.292,38.699-45.292
s38.698,17.355,38.698,45.292c0,13.275-3.384,27.74-9.191,39.826c-9.552-1.871-19.415-2.862-29.507-2.862
c-10.092,0-19.955,0.991-29.507,2.862C211.31,90.031,207.926,75.567,207.926,62.292z M65.674,178.315
c-11.542-2.345-18.432-6.018-18.432-9.825c0-18.532,27.099-34.188,59.175-34.188c12.666,0,24.903,2.444,35.035,6.858
c-12.375,11.699-22.804,25.432-30.752,40.66C96.007,181.647,78.785,180.979,65.674,178.315z M66.876,419.08
c-1.383,0-2.644-0.141-3.758-0.438c-1.699-0.453-2.325-1.195-2.749-1.929c-4.19-7.256-3.397-17.145,2.234-27.842
c6.301-11.972,17.636-23.105,31.92-31.352c7.061-4.077,14.547-7.218,21.969-9.265c5.949,12.995,12.908,25.592,20.732,37.305
c0.361,0.541,0.729,1.065,1.092,1.6C117.117,399.474,83.236,419.082,66.876,419.08z M246.625,440.753
c-32.18,0-61.76-20.875-85.077-50.575l54.271-54.271h61.611l54.272,54.271C308.385,419.878,278.805,440.753,246.625,440.753z
M398.727,357.519c27.779,16.038,43.419,43.145,34.154,59.194c-0.424,0.734-1.051,1.477-2.749,1.929
c-14.29,3.806-52.207-18.127-75.199-31.483c0.363-0.536,0.731-1.059,1.092-1.6c7.824-11.712,14.783-24.31,20.732-37.305
C384.179,350.3,391.666,353.442,398.727,357.519z M382.549,181.82c-7.948-15.228-18.376-28.962-30.752-40.66
c10.132-4.414,22.37-6.858,35.036-6.858c32.076,0,59.174,15.656,59.174,34.188c0,3.808-6.89,7.48-18.432,9.825
C414.464,180.979,397.242,181.647,382.549,181.82z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -0,0 +1,5 @@
<svg fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<polygon points="21 11 24 11 24 23 26 23 26 11 29 11 29 9 21 9 21 11"/>
<polygon points="20 9 18 9 16 15 14 9 12 9 14.75 16 12 23 14 23 16 17 18 23 20 23 17.25 16 20 9"/>
<polygon points="3 11 6 11 6 23 8 23 8 11 11 11 11 9 3 9 3 11"/>
</svg>

After

Width:  |  Height:  |  Size: 429 B

@ -70,28 +70,38 @@
CloudArrowDown, CloudArrowDown,
Beaker, Beaker,
Eye, Eye,
Square3Stack3d,
QueueList,
} from "svelte-heros-v2"; } from "svelte-heros-v2";
import JsonIcon from "./JsonIcon.svelte";
import JsonLdIcon from "./JsonLdIcon.svelte";
import RdfIcon from "./RdfIcon.svelte";
import TurtleIcon from "./TurtleIcon.svelte";
import GraphQLIcon from "./GraphQLIcon.svelte";
export let config = {}; export let config = {};
export let zera: string; export let zera: string;
const exact_mapping = { const exact_mapping = {
json_ld_editor: TableCells, json_ld_editor: JsonLdIcon,
json_editor: TableCells, json_editor: JsonIcon,
triple_editor: Share, triple_editor: RdfIcon,
turtle_viewer: TurtleIcon,
rdf_viewer: CircleStack, rdf_viewer: CircleStack,
graph_viewer: Sun, graph_viewer: Sun,
compose: QueueList,
sparql_query: RocketLaunch, sparql_query: RocketLaunch,
sparnatural: CursorArrowRays, sparnatural: CursorArrowRays,
graphql: Cube, graphql: GraphQLIcon,
invoke: Play, invoke: Play,
ontology_viewer: ArrowsPointingOut, ontology_viewer: ArrowsPointingOut,
download: DocumentArrowDown, download: DocumentArrowDown,
post_edit: PencilSquare, edit: PencilSquare,
file: Document, file: Document,
source: CodeBracket, source: CodeBracket,
post: DocumentText, post: DocumentText,
pad: Square2Stack, pad: Square2Stack,
container: Square3Stack3d,
card: Clipboard, card: Clipboard,
gallery: RectangleStack, gallery: RectangleStack,
load_graph: CloudArrowUp, load_graph: CloudArrowUp,
@ -117,4 +127,4 @@
}; };
</script> </script>
<Icon {...config} variation="outline" color="black" icon={find(zera)} /> <Icon {...config} variation="outline" color="currentColor" icon={find(zera)} />

@ -0,0 +1,236 @@
<!--
// 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 ng from "../../api";
import {
branch_subscribe,
active_session,
online,
get_blob,
} from "../../store";
import { cur_tab, cur_tab_doc_can_edit } from "../../tab";
import {
ExclamationTriangle,
ArrowDownTray,
ArrowUpTray,
} from "svelte-heros-v2";
import { onMount, onDestroy, tick } from "svelte";
import { Button, Progressbar, Spinner } from "flowbite-svelte";
import { t } from "svelte-i18n";
let is_tauri = import.meta.env.TAURI_PLATFORM;
let upload_progress: null | { total: number; current: number; error?: any } = null;
let commits = $active_session && branch_subscribe($cur_tab.branch.nuri+":"+$cur_tab.store.overlay, false);
let fileinput;
let file_urls = {};
const prepare_url = (nuri) => {
if (!file_urls[nuri]) {
file_urls[nuri] = {
click: false
};
}
return true;
}
const download = async (file) => {
if (is_tauri) {
await ng.file_save_to_downloads($active_session.session_id, file.reference, file.name, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay);
} else {
file_urls[file.nuri].url = await get_blob(file, false);
//console.log(file.name);
//console.log(file_urls[file.nuri].click);
await tick();
file_urls[file.nuri].click.click();
}
}
const isImage = async (url) : Promise<boolean> => {
if ( typeof url === 'string' ) {
let blob = await fetch(url).then(r => r.blob());
return blob.type.startsWith("image/");
}
return false;
}
function uploadFile(upload_id, nuri, file, success) {
//console.log(nuri);
let chunkSize = 1_048_564;
let fileSize = file.size;
let offset = 0;
let readBlock = null;
upload_progress = { total: fileSize, current: offset };
let onLoadHandler = async function (event) {
let result = event.target.result;
if (event.target.error == null) {
offset += result.byteLength;
upload_progress = { total: fileSize, current: offset };
// console.log("chunk", result);
let res = await ng.upload_chunk(
$active_session.session_id,
upload_id,
result,
nuri
);
//console.log("chunk upload res", res);
// if (onChunkRead) {
// onChunkRead(result);
// }
} else {
// if (onChunkError) {
// onChunkError(event.target.error);
// }
upload_progress = { total: fileSize, current: fileSize, error: true };
return;
}
// If finished:
if (offset >= fileSize) {
//console.log("file uploaded");
let res = await ng.upload_chunk(
$active_session.session_id,
upload_id,
[],
nuri
);
//console.log("end upload res", res);
if (success) {
upload_progress = { total: fileSize, current: fileSize };
await success(res);
// Make progress bar disappear
setTimeout(() => {
upload_progress = null;
}, 1_000);
}
return;
}
readBlock(offset, chunkSize, file);
};
readBlock = function (offset, length, file) {
let fileReader = new FileReader();
let blob = file.slice(offset, length + offset);
fileReader.onload = onLoadHandler;
fileReader.readAsArrayBuffer(blob);
};
readBlock(offset, chunkSize, file);
return;
}
const onFileSelected = async (e) => {
let image = e.target.files[0];
if (!image) return;
//console.log(image.type);
let start_request_payload = {
RandomAccessFilePut: image.type,
};
let nuri = "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay;
let start_res = await ng.app_request_with_nuri_command(nuri, "FilePut", $active_session.session_id, start_request_payload);
let upload_id = start_res.V0.FileUploading;
uploadFile(upload_id, nuri, image, async (reference) => {
if (reference) {
let file_put_payload = {
AddFile: {
filename: image.name,
object: reference.V0.FileUploaded,
},
};
await ng.app_request_with_nuri_command(nuri, "FilePut", $active_session.session_id, file_put_payload);
}
});
fileinput.value = "";
};
</script>
<div class="w-full">
{#if $cur_tab_doc_can_edit}
<div class="row pt-2 w-full">
<Button
disabled={!$online && !is_tauri}
type="button"
on:click={() => {
fileinput.click();
}}
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mr-2 mb-2"
>
<ArrowUpTray class="w-8 h-8 mr-2 -ml-1"/>
{$t("doc.file.upload")}
</Button>
<input
style="display:none"
type="file"
on:change={(e) => onFileSelected(e)}
bind:this={fileinput}
/>
</div>
{/if}
{#if upload_progress !== null}
<div class="mx-6 mt-2">
<Progressbar
progress={(
(100 * upload_progress.current) /
upload_progress.total
).toFixed(0)}
labelOutside={$t("doc.file.upload_progress")}
/>
</div>
{/if}
{#if commits}
{#await commits.load()}
<p>{$t("connectivity.loading")}...</p>
{:then}
{#each $commits.files as file}
<p class="mb-5">
{#await get_blob(file, true)}
<div class="ml-2">
<Spinner />
</div>
{:then url}
{#await isImage(url) then is}
{#if is}
<img src={url} title={file.nuri} alt={file.name} />
{/if}
{/await}
<span class="ml-2 text-gray-600">{file.name}<br/></span>
{#if url === false}
<span><ExclamationTriangle tabindex="-1" class="ml-2 w-6 h-8 focus:outline-none" style="display:inline"/>{$t("errors.cannot_load_this_file")}</span>
{:else if prepare_url(file.nuri)}
<a bind:this={file_urls[file.nuri].click}
href={file_urls[file.nuri].url || ""}
target="_blank"
download={file.name}
></a>
<button class="ml-2 select-none p-1 pb-0 pt-0 text-gray-600" style="box-shadow:none;" on:click={()=>download(file)}>
<span><ArrowDownTray tabindex="-1" class="w-6 h-8 mr-3 focus:outline-none" style="display:inline"/>{$t("doc.file.download")}</span>
</button>
{/if}
{/await}
</p>
{/each}
{/await}
{/if}
</div>

@ -0,0 +1,166 @@
<!--
// 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 {
branch_subscribe,
active_session,
} from "../../store";
import { get } from "svelte/store";
import { onMount, onDestroy, tick } from "svelte";
import {
Sun,
Cloud,
DocumentPlus,
DocumentMinus,
Camera,
Funnel,
FingerPrint,
Key,
Cog,
Icon,
ShieldCheck,
} from "svelte-heros-v2";
import BranchIcon from "../icons/BranchIcon.svelte";
import { t } from "svelte-i18n";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import { cur_tab } from "../../tab";
import ng from "../../api";
import {
createGitgraph,
templateExtend,
TemplateName,
} from "./history/gitgraph-js/gitgraph";
let gitgraph;
let history = [];
let unsub = () => {};
onMount(async ()=>{
setTimeout(async()=> {
const graphContainer = document.getElementById("graph-container");
gitgraph = createGitgraph(graphContainer, {
template: templateExtend(TemplateName.Metro, {
branch: { label: { display: false } },
commit: { message: { displayAuthor: false, displayHash: false } },
}),
});
let res = await ng.branch_history($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay);
// for (const h of res.history) {
// console.log(h[0], h[1]);
// }
//console.log(res.swimlane_state);
history = [...res.history].reverse();
gitgraph.swimlanes(res.swimlane_state.map((s)=> s || false));
gitgraph.import(res.history.map((h)=>{return {
hash:h[0],
branch:h[1].branch,
author:h[1].author,
parents:h[1].past,
x:h[1].x,
y:h[1].y,
subject:h[1].timestamp,
onClick:()=>openCommit(h[0]),
onMessageClick:()=>openCommit(h[0])
};}));
let branch = branch_subscribe($cur_tab.branch.nuri+":"+$cur_tab.store.overlay,false);
unsub();
unsub = branch.subscribe((b) => {
//console.log("subscription callbak",b.history.commits);
if (Array.isArray(b.history.commits)) {
for (var h; h = b.history.commits.pop(); ) {
//console.log(h);
history.unshift(h);
if (h[1].async_sig) {
for (let hh of history) {
const index = h[1].async_sig[1].indexOf(hh[0]);
if (index > -1) {
h[1].async_sig[1].splice(index, 1);
hh[1].final_consistency = false;
hh[1].signature = h[1].async_sig[0];
}
if (h[1].async_sig[1].length == 0) break;
}
}
history = history;
gitgraph.commit({
hash: h[0],
author:h[1].author,
parents:h[1].past,
subject:h[1].timestamp,
onClick:()=>openCommit(h[0]),
onMessageClick:()=>openCommit(h[0])
});
}
}
});
get(branch).history.start();
},100);
});
onDestroy( ()=>{
let branch = branch_subscribe($cur_tab.branch.nuri+":"+$cur_tab.store.overlay,false);
get(branch).history.stop();
unsub();
});
const openCommit = (id:string) => {
console.log("open commit",id);
}
const commit_type_icons = {
"TransactionGraph": Sun,
"TransactionDiscrete": Cloud,
"TransactionBoth": Sun,
"FileAdd": DocumentPlus,
"FileRemove": DocumentMinus,
"Snapshot": Camera,
"Compact": Funnel,
"AsyncSignature": FingerPrint,
"SyncSignature": FingerPrint,
"Branch": BranchIcon,
"UpdateBranch": BranchIcon,
"BranchCapRefresh": Key,
"CapRefreshed": Key,
"Other": Cog,
};
</script>
<div style="width:120px; min-width:120px;font-family: monospace; font: Courier; font-size:16px;">
{#each history as commit}
<div class="w-full commit relative text-gray-500" style="height:60px;" role="button" title={commit[0]} tabindex=0 on:click={()=>openCommit(commit[0])} on:keypress={()=>openCommit(commit[0])}>
{#if commit[1].final_consistency}<ShieldCheck tabindex="-1" class="w-5 h-5 absolute text-primary-600" style="top:9px;right:20px;" />
{:else if commit[1].signature}<ShieldCheck tabindex="-1" class="w-5 h-5 absolute text-green-600" style="top:9px;right:20px;" />
{/if}
<Icon tabindex="-1" class="w-5 h-5 focus:outline-none absolute " style="top:9px;right:0px;" variation="outline" color="currentColor" icon={commit_type_icons[commit[1].commit_type]} />
{#if commit[1].commit_type==="TransactionBoth"}<Cloud tabindex="-1" class="w-5 h-5 absolute " style="top:28px;right:0px;" />{/if}
<b>{commit[0].substring(0,7)}</b><br/>
<span class="text-xs leading-tight">{commit[1].author.substring(0,9)}</span>
</div>
{/each}
</div>
<div style="cursor:pointer;" id="graph-container"></div>
<style>
.commit {
padding: 8px;
}
</style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save