holosphere 1.1.20 → 2.0.0-alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +36 -0
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +7 -0
- package/LICENSE +162 -38
- package/README.md +483 -367
- package/bin/holosphere-activitypub.js +158 -0
- package/cleanup-test-data.js +204 -0
- package/examples/demo.html +1333 -0
- package/examples/example-bot.js +197 -0
- package/package.json +47 -87
- package/scripts/check-bundle-size.js +54 -0
- package/scripts/check-quest-ids.js +77 -0
- package/scripts/import-holons.js +578 -0
- package/scripts/publish-to-relay.js +101 -0
- package/scripts/read-example.js +186 -0
- package/scripts/relay-diagnostic.js +59 -0
- package/scripts/relay-example.js +179 -0
- package/scripts/resync-to-relay.js +245 -0
- package/scripts/revert-import.js +196 -0
- package/scripts/test-hybrid-mode.js +108 -0
- package/scripts/test-local-storage.js +63 -0
- package/scripts/test-nostr-direct.js +55 -0
- package/scripts/test-read-data.js +45 -0
- package/scripts/test-write-read.js +63 -0
- package/scripts/verify-import.js +95 -0
- package/scripts/verify-relay-data.js +139 -0
- package/src/ai/aggregation.js +319 -0
- package/src/ai/breakdown.js +511 -0
- package/src/ai/classifier.js +217 -0
- package/src/ai/council.js +228 -0
- package/src/ai/embeddings.js +279 -0
- package/src/ai/federation-ai.js +324 -0
- package/src/ai/h3-ai.js +955 -0
- package/src/ai/index.js +112 -0
- package/src/ai/json-ops.js +225 -0
- package/src/ai/llm-service.js +205 -0
- package/src/ai/nl-query.js +223 -0
- package/src/ai/relationships.js +353 -0
- package/src/ai/schema-extractor.js +218 -0
- package/src/ai/spatial.js +293 -0
- package/src/ai/tts.js +194 -0
- package/src/content/social-protocols.js +168 -0
- package/src/core/holosphere.js +273 -0
- package/src/crypto/secp256k1.js +259 -0
- package/src/federation/discovery.js +334 -0
- package/src/federation/hologram.js +1042 -0
- package/src/federation/registry.js +386 -0
- package/src/hierarchical/upcast.js +110 -0
- package/src/index.js +2669 -0
- package/src/schema/validator.js +91 -0
- package/src/spatial/h3-operations.js +110 -0
- package/src/storage/backend-factory.js +125 -0
- package/src/storage/backend-interface.js +142 -0
- package/src/storage/backends/activitypub/server.js +653 -0
- package/src/storage/backends/activitypub-backend.js +272 -0
- package/src/storage/backends/gundb-backend.js +233 -0
- package/src/storage/backends/nostr-backend.js +136 -0
- package/src/storage/filesystem-storage-browser.js +41 -0
- package/src/storage/filesystem-storage.js +138 -0
- package/src/storage/global-tables.js +81 -0
- package/src/storage/gun-async.js +281 -0
- package/src/storage/gun-wrapper.js +221 -0
- package/src/storage/indexeddb-storage.js +122 -0
- package/src/storage/key-storage-simple.js +76 -0
- package/src/storage/key-storage.js +136 -0
- package/src/storage/memory-storage.js +59 -0
- package/src/storage/migration.js +338 -0
- package/src/storage/nostr-async.js +811 -0
- package/src/storage/nostr-client.js +939 -0
- package/src/storage/nostr-wrapper.js +211 -0
- package/src/storage/outbox-queue.js +208 -0
- package/src/storage/persistent-storage.js +109 -0
- package/src/storage/sync-service.js +164 -0
- package/src/subscriptions/manager.js +142 -0
- package/test-ai-real-api.js +202 -0
- package/tests/unit/ai/aggregation.test.js +295 -0
- package/tests/unit/ai/breakdown.test.js +446 -0
- package/tests/unit/ai/classifier.test.js +294 -0
- package/tests/unit/ai/council.test.js +262 -0
- package/tests/unit/ai/embeddings.test.js +384 -0
- package/tests/unit/ai/federation-ai.test.js +344 -0
- package/tests/unit/ai/h3-ai.test.js +458 -0
- package/tests/unit/ai/index.test.js +304 -0
- package/tests/unit/ai/json-ops.test.js +307 -0
- package/tests/unit/ai/llm-service.test.js +390 -0
- package/tests/unit/ai/nl-query.test.js +383 -0
- package/tests/unit/ai/relationships.test.js +311 -0
- package/tests/unit/ai/schema-extractor.test.js +384 -0
- package/tests/unit/ai/spatial.test.js +279 -0
- package/tests/unit/ai/tts.test.js +279 -0
- package/tests/unit/content.test.js +332 -0
- package/tests/unit/contract/core.test.js +88 -0
- package/tests/unit/contract/crypto.test.js +198 -0
- package/tests/unit/contract/data.test.js +223 -0
- package/tests/unit/contract/federation.test.js +181 -0
- package/tests/unit/contract/hierarchical.test.js +113 -0
- package/tests/unit/contract/schema.test.js +114 -0
- package/tests/unit/contract/social.test.js +217 -0
- package/tests/unit/contract/spatial.test.js +110 -0
- package/tests/unit/contract/subscriptions.test.js +128 -0
- package/tests/unit/contract/utils.test.js +159 -0
- package/tests/unit/core.test.js +152 -0
- package/tests/unit/crypto.test.js +328 -0
- package/tests/unit/federation.test.js +234 -0
- package/tests/unit/gun-async.test.js +252 -0
- package/tests/unit/hierarchical.test.js +399 -0
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
- package/tests/unit/integration/scenario-02-federation.test.js +76 -0
- package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
- package/tests/unit/integration/scenario-04-validation.test.js +129 -0
- package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
- package/tests/unit/integration/scenario-06-social.test.js +135 -0
- package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
- package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
- package/tests/unit/performance/benchmark.test.js +85 -0
- package/tests/unit/schema.test.js +213 -0
- package/tests/unit/spatial.test.js +158 -0
- package/tests/unit/storage.test.js +195 -0
- package/tests/unit/subscriptions.test.js +328 -0
- package/tests/unit/test-data-permanence-debug.js +197 -0
- package/tests/unit/test-data-permanence.js +340 -0
- package/tests/unit/test-key-persistence-fixed.js +148 -0
- package/tests/unit/test-key-persistence.js +172 -0
- package/tests/unit/test-relay-permanence.js +376 -0
- package/tests/unit/test-second-node.js +95 -0
- package/tests/unit/test-simple-write.js +89 -0
- package/vite.config.js +49 -0
- package/vitest.config.js +20 -0
- package/FEDERATION.md +0 -213
- package/compute.js +0 -298
- package/content.js +0 -980
- package/federation.js +0 -1234
- package/global.js +0 -736
- package/hexlib.js +0 -335
- package/hologram.js +0 -183
- package/holosphere-bundle.esm.js +0 -33256
- package/holosphere-bundle.js +0 -33287
- package/holosphere-bundle.min.js +0 -39
- package/holosphere.d.ts +0 -601
- package/holosphere.js +0 -719
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
|
@@ -0,0 +1,1333 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>HoloSphere - Interactive Demo</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
16
|
+
line-height: 1.6;
|
|
17
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
18
|
+
min-height: 100vh;
|
|
19
|
+
padding: 20px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.container {
|
|
23
|
+
max-width: 1400px;
|
|
24
|
+
margin: 0 auto;
|
|
25
|
+
background: rgba(255, 255, 255, 0.95);
|
|
26
|
+
border-radius: 20px;
|
|
27
|
+
padding: 30px;
|
|
28
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
h1 {
|
|
32
|
+
color: #333;
|
|
33
|
+
margin-bottom: 10px;
|
|
34
|
+
font-size: 2.5em;
|
|
35
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
36
|
+
-webkit-background-clip: text;
|
|
37
|
+
-webkit-text-fill-color: transparent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.subtitle {
|
|
41
|
+
color: #666;
|
|
42
|
+
margin-bottom: 30px;
|
|
43
|
+
font-size: 1.1em;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.tabs {
|
|
47
|
+
display: flex;
|
|
48
|
+
gap: 10px;
|
|
49
|
+
margin-bottom: 30px;
|
|
50
|
+
border-bottom: 2px solid #e0e0e0;
|
|
51
|
+
flex-wrap: wrap;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.tab {
|
|
55
|
+
padding: 12px 24px;
|
|
56
|
+
background: none;
|
|
57
|
+
border: none;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
color: #666;
|
|
60
|
+
font-size: 1em;
|
|
61
|
+
transition: all 0.3s;
|
|
62
|
+
border-bottom: 3px solid transparent;
|
|
63
|
+
margin-bottom: -2px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.tab:hover {
|
|
67
|
+
color: #667eea;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.tab.active {
|
|
71
|
+
color: #667eea;
|
|
72
|
+
border-bottom-color: #667eea;
|
|
73
|
+
font-weight: 600;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.tab-content {
|
|
77
|
+
display: none;
|
|
78
|
+
animation: fadeIn 0.5s;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.tab-content.active {
|
|
82
|
+
display: block;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@keyframes fadeIn {
|
|
86
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
87
|
+
to { opacity: 1; transform: translateY(0); }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.demo-section {
|
|
91
|
+
background: #f8f9fa;
|
|
92
|
+
padding: 20px;
|
|
93
|
+
border-radius: 10px;
|
|
94
|
+
margin-bottom: 20px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.demo-section h3 {
|
|
98
|
+
color: #333;
|
|
99
|
+
margin-bottom: 15px;
|
|
100
|
+
font-size: 1.3em;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.demo-section h4 {
|
|
104
|
+
color: #555;
|
|
105
|
+
margin-top: 15px;
|
|
106
|
+
margin-bottom: 10px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.input-group {
|
|
110
|
+
display: flex;
|
|
111
|
+
gap: 10px;
|
|
112
|
+
margin-bottom: 15px;
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
input[type="text"],
|
|
117
|
+
input[type="number"],
|
|
118
|
+
textarea,
|
|
119
|
+
select {
|
|
120
|
+
padding: 10px 15px;
|
|
121
|
+
border: 2px solid #e0e0e0;
|
|
122
|
+
border-radius: 8px;
|
|
123
|
+
font-size: 1em;
|
|
124
|
+
transition: border-color 0.3s;
|
|
125
|
+
flex: 1;
|
|
126
|
+
min-width: 200px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
input[type="text"]:focus,
|
|
130
|
+
input[type="number"]:focus,
|
|
131
|
+
textarea:focus,
|
|
132
|
+
select:focus {
|
|
133
|
+
outline: none;
|
|
134
|
+
border-color: #667eea;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
textarea {
|
|
138
|
+
min-height: 100px;
|
|
139
|
+
resize: vertical;
|
|
140
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
141
|
+
font-size: 0.9em;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
button {
|
|
145
|
+
padding: 10px 20px;
|
|
146
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
147
|
+
color: white;
|
|
148
|
+
border: none;
|
|
149
|
+
border-radius: 8px;
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
font-size: 1em;
|
|
152
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
button:hover {
|
|
156
|
+
transform: translateY(-2px);
|
|
157
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
button:active {
|
|
161
|
+
transform: translateY(0);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
button:disabled {
|
|
165
|
+
opacity: 0.5;
|
|
166
|
+
cursor: not-allowed;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.output {
|
|
170
|
+
background: #1e1e1e;
|
|
171
|
+
color: #fff;
|
|
172
|
+
padding: 15px;
|
|
173
|
+
border-radius: 8px;
|
|
174
|
+
margin-top: 15px;
|
|
175
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
176
|
+
font-size: 0.9em;
|
|
177
|
+
min-height: 100px;
|
|
178
|
+
max-height: 400px;
|
|
179
|
+
overflow-y: auto;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.output pre {
|
|
183
|
+
margin: 0;
|
|
184
|
+
white-space: pre-wrap;
|
|
185
|
+
word-wrap: break-word;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.status {
|
|
189
|
+
padding: 8px 15px;
|
|
190
|
+
border-radius: 5px;
|
|
191
|
+
margin-top: 10px;
|
|
192
|
+
font-weight: 500;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.status.success {
|
|
196
|
+
background: #d4edda;
|
|
197
|
+
color: #155724;
|
|
198
|
+
border: 1px solid #c3e6cb;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.status.error {
|
|
202
|
+
background: #f8d7da;
|
|
203
|
+
color: #721c24;
|
|
204
|
+
border: 1px solid #f5c6cb;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.status.info {
|
|
208
|
+
background: #d1ecf1;
|
|
209
|
+
color: #0c5460;
|
|
210
|
+
border: 1px solid #bee5eb;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.feature-grid {
|
|
214
|
+
display: grid;
|
|
215
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
216
|
+
gap: 20px;
|
|
217
|
+
margin-top: 20px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.feature-card {
|
|
221
|
+
background: white;
|
|
222
|
+
padding: 20px;
|
|
223
|
+
border-radius: 10px;
|
|
224
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.feature-card h4 {
|
|
228
|
+
color: #667eea;
|
|
229
|
+
margin-bottom: 10px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.code-example {
|
|
233
|
+
background: #2d2d2d;
|
|
234
|
+
color: #f8f8f2;
|
|
235
|
+
padding: 15px;
|
|
236
|
+
border-radius: 8px;
|
|
237
|
+
margin: 10px 0;
|
|
238
|
+
overflow-x: auto;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.code-example code {
|
|
242
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
243
|
+
font-size: 0.9em;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.metrics-grid {
|
|
247
|
+
display: grid;
|
|
248
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
249
|
+
gap: 15px;
|
|
250
|
+
margin-top: 20px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.metric-card {
|
|
254
|
+
background: white;
|
|
255
|
+
padding: 15px;
|
|
256
|
+
border-radius: 8px;
|
|
257
|
+
text-align: center;
|
|
258
|
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.metric-value {
|
|
262
|
+
font-size: 2em;
|
|
263
|
+
font-weight: bold;
|
|
264
|
+
color: #667eea;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.metric-label {
|
|
268
|
+
color: #666;
|
|
269
|
+
font-size: 0.9em;
|
|
270
|
+
margin-top: 5px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.map-container {
|
|
274
|
+
height: 400px;
|
|
275
|
+
border-radius: 8px;
|
|
276
|
+
overflow: hidden;
|
|
277
|
+
margin-top: 15px;
|
|
278
|
+
background: #e0e0e0;
|
|
279
|
+
position: relative;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
#map {
|
|
283
|
+
width: 100%;
|
|
284
|
+
height: 100%;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.connection-status {
|
|
288
|
+
display: inline-block;
|
|
289
|
+
width: 10px;
|
|
290
|
+
height: 10px;
|
|
291
|
+
border-radius: 50%;
|
|
292
|
+
margin-right: 5px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.connection-status.connected {
|
|
296
|
+
background: #28a745;
|
|
297
|
+
animation: pulse 2s infinite;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.connection-status.disconnected {
|
|
301
|
+
background: #dc3545;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@keyframes pulse {
|
|
305
|
+
0% { box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.7); }
|
|
306
|
+
70% { box-shadow: 0 0 0 10px rgba(40, 167, 69, 0); }
|
|
307
|
+
100% { box-shadow: 0 0 0 0 rgba(40, 167, 69, 0); }
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.loader {
|
|
311
|
+
border: 3px solid #f3f3f3;
|
|
312
|
+
border-top: 3px solid #667eea;
|
|
313
|
+
border-radius: 50%;
|
|
314
|
+
width: 30px;
|
|
315
|
+
height: 30px;
|
|
316
|
+
animation: spin 1s linear infinite;
|
|
317
|
+
display: inline-block;
|
|
318
|
+
margin: 20px auto;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@keyframes spin {
|
|
322
|
+
0% { transform: rotate(0deg); }
|
|
323
|
+
100% { transform: rotate(360deg); }
|
|
324
|
+
}
|
|
325
|
+
</style>
|
|
326
|
+
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
|
|
327
|
+
</head>
|
|
328
|
+
<body>
|
|
329
|
+
<div class="container">
|
|
330
|
+
<h1>HoloSphere Interactive Demo</h1>
|
|
331
|
+
<p class="subtitle">Holonic Geospatial Communication Infrastructure - Explore all features interactively</p>
|
|
332
|
+
|
|
333
|
+
<div class="connection-status" id="connectionStatus"></div>
|
|
334
|
+
<span id="connectionText">Initializing...</span>
|
|
335
|
+
|
|
336
|
+
<div style="margin: 20px 0; padding: 15px; background: #f8f9fa; border-radius: 10px; display: flex; align-items: center; gap: 15px;">
|
|
337
|
+
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; font-weight: 500;">
|
|
338
|
+
<input type="checkbox" id="useRealRelays" style="width: 20px; height: 20px; cursor: pointer;">
|
|
339
|
+
<span>Use Real Nostr Relays (Publish to actual network)</span>
|
|
340
|
+
</label>
|
|
341
|
+
<button onclick="toggleRelayMode()" style="padding: 8px 16px;">Apply & Reconnect</button>
|
|
342
|
+
<span id="relayModeStatus" style="margin-left: auto; font-size: 0.9em; color: #666;"></span>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<div class="tabs">
|
|
346
|
+
<button class="tab active" onclick="showTab('overview')">Overview</button>
|
|
347
|
+
<button class="tab" onclick="showTab('spatial')">Spatial Operations</button>
|
|
348
|
+
<button class="tab" onclick="showTab('data')">Data Operations</button>
|
|
349
|
+
<button class="tab" onclick="showTab('schema')">Schema Validation</button>
|
|
350
|
+
<button class="tab" onclick="showTab('federation')">Federation</button>
|
|
351
|
+
<button class="tab" onclick="showTab('social')">Social Protocols</button>
|
|
352
|
+
<button class="tab" onclick="showTab('crypto')">Cryptography</button>
|
|
353
|
+
<button class="tab" onclick="showTab('subscriptions')">Subscriptions</button>
|
|
354
|
+
<button class="tab" onclick="showTab('metrics')">Metrics</button>
|
|
355
|
+
</div>
|
|
356
|
+
|
|
357
|
+
<!-- Overview Tab -->
|
|
358
|
+
<div id="overview" class="tab-content active">
|
|
359
|
+
<div class="demo-section">
|
|
360
|
+
<h3>Welcome to HoloSphere</h3>
|
|
361
|
+
<p>HoloSphere is a powerful holonic geospatial communication infrastructure that combines H3 hexagonal indexing with distributed P2P storage.</p>
|
|
362
|
+
|
|
363
|
+
<div class="feature-grid">
|
|
364
|
+
<div class="feature-card">
|
|
365
|
+
<h4>🗺️ Geospatial Indexing</h4>
|
|
366
|
+
<p>Uses H3 hexagonal grid system for efficient spatial data organization and querying.</p>
|
|
367
|
+
</div>
|
|
368
|
+
<div class="feature-card">
|
|
369
|
+
<h4>🔗 Federation</h4>
|
|
370
|
+
<p>Connect and synchronize data across different holons with flexible federation modes.</p>
|
|
371
|
+
</div>
|
|
372
|
+
<div class="feature-card">
|
|
373
|
+
<h4>📱 Social Protocols</h4>
|
|
374
|
+
<p>Native support for Nostr and ActivityPub social protocols.</p>
|
|
375
|
+
</div>
|
|
376
|
+
<div class="feature-card">
|
|
377
|
+
<h4>🔐 Cryptography</h4>
|
|
378
|
+
<p>Built-in secp256k1 cryptography for signing and verification.</p>
|
|
379
|
+
</div>
|
|
380
|
+
<div class="feature-card">
|
|
381
|
+
<h4>✅ Schema Validation</h4>
|
|
382
|
+
<p>JSON Schema validation for ensuring data integrity.</p>
|
|
383
|
+
</div>
|
|
384
|
+
<div class="feature-card">
|
|
385
|
+
<h4>📊 Real-time Updates</h4>
|
|
386
|
+
<p>Subscribe to data changes with real-time notifications.</p>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<h4>Quick Start Example</h4>
|
|
391
|
+
<div class="code-example">
|
|
392
|
+
<code>// Initialize HoloSphere
|
|
393
|
+
const hs = new HoloSphere({
|
|
394
|
+
appName: 'my-app',
|
|
395
|
+
relays: ['wss://relay.example.com']
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Convert coordinates to H3 holon
|
|
399
|
+
const holon = await hs.toHolon(37.7749, -122.4194, 9);
|
|
400
|
+
|
|
401
|
+
// Write data to a holon (multiple ways!)
|
|
402
|
+
await hs.write(holon, 'events', { title: 'SF Meetup' });
|
|
403
|
+
await hs.put(holon, 'events', { title: 'SF Meetup' }); // REST-style
|
|
404
|
+
await hs.save(holon, 'events', { title: 'SF Meetup' }); // User-friendly
|
|
405
|
+
|
|
406
|
+
// Read data from holon (multiple ways!)
|
|
407
|
+
const events = await hs.read(holon, 'events');
|
|
408
|
+
const events = await hs.get(holon, 'events'); // REST-style
|
|
409
|
+
const events = await hs.load(holon, 'events'); // User-friendly</code>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<div class="status info" style="margin-top: 15px;">
|
|
413
|
+
<strong>💡 API Aliases Available!</strong><br>
|
|
414
|
+
Use <code>put()/get()</code> for REST-style, <code>save()/load()</code> for user-friendly,
|
|
415
|
+
or <code>write()/read()</code> for database-style. They all work the same way!
|
|
416
|
+
<br>See <a href="API_ALIASES.md" target="_blank">API_ALIASES.md</a> for full details.
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
<!-- Spatial Operations Tab -->
|
|
422
|
+
<div id="spatial" class="tab-content">
|
|
423
|
+
<div class="demo-section">
|
|
424
|
+
<h3>Spatial Operations with H3</h3>
|
|
425
|
+
<p>Convert geographic coordinates to H3 hexagons and explore their hierarchy.</p>
|
|
426
|
+
|
|
427
|
+
<h4>Convert Coordinates to Holon</h4>
|
|
428
|
+
<div class="input-group">
|
|
429
|
+
<input type="number" id="lat" placeholder="Latitude" value="37.7749" step="0.0001">
|
|
430
|
+
<input type="number" id="lng" placeholder="Longitude" value="-122.4194" step="0.0001">
|
|
431
|
+
<input type="number" id="resolution" placeholder="Resolution (0-15)" value="9" min="0" max="15">
|
|
432
|
+
<button onclick="convertToHolon()">Convert to Holon</button>
|
|
433
|
+
</div>
|
|
434
|
+
|
|
435
|
+
<div id="holonResult" class="output"></div>
|
|
436
|
+
|
|
437
|
+
<div id="map" class="map-container"></div>
|
|
438
|
+
|
|
439
|
+
<h4>Holon Hierarchy</h4>
|
|
440
|
+
<div class="input-group">
|
|
441
|
+
<input type="text" id="holonId" placeholder="Holon ID (H3 index)">
|
|
442
|
+
<button onclick="getParents()">Get Parents</button>
|
|
443
|
+
<button onclick="getChildren()">Get Children</button>
|
|
444
|
+
</div>
|
|
445
|
+
|
|
446
|
+
<div id="hierarchyResult" class="output"></div>
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
<!-- Data Operations Tab -->
|
|
451
|
+
<div id="data" class="tab-content">
|
|
452
|
+
<div class="demo-section">
|
|
453
|
+
<h3>Data Operations</h3>
|
|
454
|
+
<p>Store, retrieve, update, and delete data in holons using lens-based organization.</p>
|
|
455
|
+
|
|
456
|
+
<h4>Write Data</h4>
|
|
457
|
+
<div class="input-group">
|
|
458
|
+
<input type="text" id="writeHolon" placeholder="Holon ID">
|
|
459
|
+
<input type="text" id="writeLens" placeholder="Lens Name" value="demo">
|
|
460
|
+
</div>
|
|
461
|
+
<textarea id="writeData" placeholder='Data (JSON format, e.g., {"title": "Test", "value": 123})'>{
|
|
462
|
+
"title": "Demo Event",
|
|
463
|
+
"description": "This is a test event",
|
|
464
|
+
"timestamp": null
|
|
465
|
+
}</textarea>
|
|
466
|
+
<button onclick="writeData()">Write Data</button>
|
|
467
|
+
|
|
468
|
+
<h4>Read Data</h4>
|
|
469
|
+
<div class="input-group">
|
|
470
|
+
<input type="text" id="readHolon" placeholder="Holon ID">
|
|
471
|
+
<input type="text" id="readLens" placeholder="Lens Name" value="demo">
|
|
472
|
+
<input type="text" id="readDataId" placeholder="Data ID (optional)">
|
|
473
|
+
<button onclick="readData()">Read Data</button>
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
<h4>Update Data</h4>
|
|
477
|
+
<div class="input-group">
|
|
478
|
+
<input type="text" id="updateHolon" placeholder="Holon ID">
|
|
479
|
+
<input type="text" id="updateLens" placeholder="Lens Name" value="demo">
|
|
480
|
+
<input type="text" id="updateDataId" placeholder="Data ID">
|
|
481
|
+
</div>
|
|
482
|
+
<textarea id="updateData" placeholder='Updates (JSON format)'>{
|
|
483
|
+
"status": "updated"
|
|
484
|
+
}</textarea>
|
|
485
|
+
<button onclick="updateData()">Update Data</button>
|
|
486
|
+
|
|
487
|
+
<h4>Delete Data</h4>
|
|
488
|
+
<div class="input-group">
|
|
489
|
+
<input type="text" id="deleteHolon" placeholder="Holon ID">
|
|
490
|
+
<input type="text" id="deleteLens" placeholder="Lens Name" value="demo">
|
|
491
|
+
<input type="text" id="deleteDataId" placeholder="Data ID">
|
|
492
|
+
<button onclick="deleteData()">Delete Data</button>
|
|
493
|
+
</div>
|
|
494
|
+
|
|
495
|
+
<div id="dataResult" class="output"></div>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
<!-- Schema Validation Tab -->
|
|
500
|
+
<div id="schema" class="tab-content">
|
|
501
|
+
<div class="demo-section">
|
|
502
|
+
<h3>Schema Validation</h3>
|
|
503
|
+
<p>Define and enforce JSON schemas for data consistency.</p>
|
|
504
|
+
|
|
505
|
+
<h4>Set Schema for Lens</h4>
|
|
506
|
+
<div class="input-group">
|
|
507
|
+
<input type="text" id="schemaLens" placeholder="Lens Name" value="validated">
|
|
508
|
+
<label><input type="checkbox" id="strictMode"> Strict Mode</label>
|
|
509
|
+
</div>
|
|
510
|
+
<textarea id="schemaDefinition" placeholder="JSON Schema">{
|
|
511
|
+
"type": "object",
|
|
512
|
+
"properties": {
|
|
513
|
+
"title": { "type": "string" },
|
|
514
|
+
"value": { "type": "number", "minimum": 0 },
|
|
515
|
+
"tags": { "type": "array", "items": { "type": "string" } }
|
|
516
|
+
},
|
|
517
|
+
"required": ["title", "value"]
|
|
518
|
+
}</textarea>
|
|
519
|
+
<button onclick="setSchema()">Set Schema</button>
|
|
520
|
+
|
|
521
|
+
<h4>Test Validation</h4>
|
|
522
|
+
<textarea id="testData" placeholder="Test data to validate">{
|
|
523
|
+
"title": "Test Item",
|
|
524
|
+
"value": 42,
|
|
525
|
+
"tags": ["demo", "test"]
|
|
526
|
+
}</textarea>
|
|
527
|
+
<button onclick="testValidation()">Validate Data</button>
|
|
528
|
+
|
|
529
|
+
<div id="schemaResult" class="output"></div>
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
|
|
533
|
+
<!-- Federation Tab -->
|
|
534
|
+
<div id="federation" class="tab-content">
|
|
535
|
+
<div class="demo-section">
|
|
536
|
+
<h3>Federation</h3>
|
|
537
|
+
<p>Connect holons and synchronize data across them.</p>
|
|
538
|
+
|
|
539
|
+
<h4>Setup Federation</h4>
|
|
540
|
+
<div class="input-group">
|
|
541
|
+
<input type="text" id="sourceHolon" placeholder="Source Holon ID">
|
|
542
|
+
<input type="text" id="targetHolon" placeholder="Target Holon ID">
|
|
543
|
+
<input type="text" id="federationLens" placeholder="Lens Name" value="federated">
|
|
544
|
+
</div>
|
|
545
|
+
<div class="input-group">
|
|
546
|
+
<select id="federationDirection">
|
|
547
|
+
<option value="bidirectional">Bidirectional</option>
|
|
548
|
+
<option value="outbound">Outbound Only</option>
|
|
549
|
+
<option value="inbound">Inbound Only</option>
|
|
550
|
+
</select>
|
|
551
|
+
<select id="federationMode">
|
|
552
|
+
<option value="reference">Reference (Hologram)</option>
|
|
553
|
+
<option value="copy">Copy</option>
|
|
554
|
+
</select>
|
|
555
|
+
<button onclick="setupFederation()">Setup Federation</button>
|
|
556
|
+
</div>
|
|
557
|
+
|
|
558
|
+
<h4>Get Federated Data</h4>
|
|
559
|
+
<div class="input-group">
|
|
560
|
+
<input type="text" id="fedHolon" placeholder="Holon ID">
|
|
561
|
+
<input type="text" id="fedLens" placeholder="Lens Name" value="federated">
|
|
562
|
+
<label><input type="checkbox" id="resolveHolograms" checked> Resolve Holograms</label>
|
|
563
|
+
<button onclick="getFederatedData()">Get Federated Data</button>
|
|
564
|
+
</div>
|
|
565
|
+
|
|
566
|
+
<div id="federationResult" class="output"></div>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
|
|
570
|
+
<!-- Social Protocols Tab -->
|
|
571
|
+
<div id="social" class="tab-content">
|
|
572
|
+
<div class="demo-section">
|
|
573
|
+
<h3>Social Protocols</h3>
|
|
574
|
+
<p>Publish and query content using Nostr and ActivityPub protocols.</p>
|
|
575
|
+
|
|
576
|
+
<div id="socialProtocolsBanner" class="status info">
|
|
577
|
+
<strong>🌐 Connected to Real Nostr Relays!</strong><br>
|
|
578
|
+
Your Nostr events will be published to the real Nostr network and can be seen by others using Nostr clients.
|
|
579
|
+
</div>
|
|
580
|
+
|
|
581
|
+
<h4>Publish Nostr Event</h4>
|
|
582
|
+
<div class="input-group">
|
|
583
|
+
<input type="text" id="nostrHolon" placeholder="Holon ID">
|
|
584
|
+
</div>
|
|
585
|
+
<textarea id="nostrEvent" placeholder="Nostr event (JSON)">{
|
|
586
|
+
"kind": 1,
|
|
587
|
+
"created_at": null,
|
|
588
|
+
"content": "Hello from HoloSphere!",
|
|
589
|
+
"tags": []
|
|
590
|
+
}</textarea>
|
|
591
|
+
<button onclick="publishNostr()">Publish Nostr Event</button>
|
|
592
|
+
|
|
593
|
+
<h4>Publish ActivityPub Object</h4>
|
|
594
|
+
<div class="input-group">
|
|
595
|
+
<input type="text" id="apHolon" placeholder="Holon ID">
|
|
596
|
+
</div>
|
|
597
|
+
<textarea id="apObject" placeholder="ActivityPub object (JSON)">{
|
|
598
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
599
|
+
"type": "Note",
|
|
600
|
+
"content": "Hello from the Fediverse!",
|
|
601
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
|
602
|
+
}</textarea>
|
|
603
|
+
<button onclick="publishActivityPub()">Publish ActivityPub</button>
|
|
604
|
+
|
|
605
|
+
<h4>Query Social Data</h4>
|
|
606
|
+
<div class="input-group">
|
|
607
|
+
<input type="text" id="socialHolon" placeholder="Holon ID">
|
|
608
|
+
<select id="socialProtocol">
|
|
609
|
+
<option value="">All Protocols</option>
|
|
610
|
+
<option value="nostr">Nostr Only</option>
|
|
611
|
+
<option value="activitypub">ActivityPub Only</option>
|
|
612
|
+
</select>
|
|
613
|
+
<button onclick="querySocial()">Query Social Data</button>
|
|
614
|
+
</div>
|
|
615
|
+
|
|
616
|
+
<div id="socialResult" class="output"></div>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
|
|
620
|
+
<!-- Cryptography Tab -->
|
|
621
|
+
<div id="crypto" class="tab-content">
|
|
622
|
+
<div class="demo-section">
|
|
623
|
+
<h3>Cryptography</h3>
|
|
624
|
+
<p>Sign, verify, and manage capability tokens using secp256k1.</p>
|
|
625
|
+
|
|
626
|
+
<h4>Generate Keys</h4>
|
|
627
|
+
<button onclick="generateKeys()">Generate New Key Pair</button>
|
|
628
|
+
<div id="keysResult" class="output"></div>
|
|
629
|
+
|
|
630
|
+
<h4>Sign & Verify</h4>
|
|
631
|
+
<textarea id="signContent" placeholder="Content to sign">Hello, this is a test message!</textarea>
|
|
632
|
+
<div class="input-group">
|
|
633
|
+
<input type="text" id="privateKey" placeholder="Private Key (hex)">
|
|
634
|
+
<button onclick="signContent()">Sign Content</button>
|
|
635
|
+
</div>
|
|
636
|
+
|
|
637
|
+
<div class="input-group">
|
|
638
|
+
<input type="text" id="signature" placeholder="Signature">
|
|
639
|
+
<input type="text" id="publicKey" placeholder="Public Key">
|
|
640
|
+
<button onclick="verifySignature()">Verify Signature</button>
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
<h4>Capability Tokens</h4>
|
|
644
|
+
<div class="input-group">
|
|
645
|
+
<input type="text" id="capPermissions" placeholder="Permissions (comma-separated)" value="read,write">
|
|
646
|
+
<input type="text" id="capScope" placeholder="Scope (JSON)" value='{"holonId": "*", "lensName": "demo"}'>
|
|
647
|
+
<input type="text" id="capRecipient" placeholder="Recipient Public Key">
|
|
648
|
+
<button onclick="issueCapability()">Issue Capability</button>
|
|
649
|
+
</div>
|
|
650
|
+
|
|
651
|
+
<div id="cryptoResult" class="output"></div>
|
|
652
|
+
</div>
|
|
653
|
+
</div>
|
|
654
|
+
|
|
655
|
+
<!-- Subscriptions Tab -->
|
|
656
|
+
<div id="subscriptions" class="tab-content">
|
|
657
|
+
<div class="demo-section">
|
|
658
|
+
<h3>Real-time Subscriptions</h3>
|
|
659
|
+
<p>Subscribe to data changes and receive real-time updates.</p>
|
|
660
|
+
|
|
661
|
+
<h4>Create Subscription</h4>
|
|
662
|
+
<div class="input-group">
|
|
663
|
+
<input type="text" id="subHolon" placeholder="Holon ID">
|
|
664
|
+
<input type="text" id="subLens" placeholder="Lens Name" value="realtime">
|
|
665
|
+
<button onclick="createSubscription()">Subscribe</button>
|
|
666
|
+
<button onclick="unsubscribe()" disabled id="unsubBtn">Unsubscribe</button>
|
|
667
|
+
</div>
|
|
668
|
+
|
|
669
|
+
<h4>Test Real-time Updates</h4>
|
|
670
|
+
<p>After subscribing, write data to see real-time updates:</p>
|
|
671
|
+
<div class="input-group">
|
|
672
|
+
<input type="text" id="testMessage" placeholder="Test message" value="Real-time test">
|
|
673
|
+
<button onclick="sendTestMessage()">Send Test Message</button>
|
|
674
|
+
</div>
|
|
675
|
+
|
|
676
|
+
<h4>Subscription Events</h4>
|
|
677
|
+
<div id="subscriptionEvents" class="output"></div>
|
|
678
|
+
</div>
|
|
679
|
+
</div>
|
|
680
|
+
|
|
681
|
+
<!-- Metrics Tab -->
|
|
682
|
+
<div id="metrics" class="tab-content">
|
|
683
|
+
<div class="demo-section">
|
|
684
|
+
<h3>Performance Metrics</h3>
|
|
685
|
+
<p>Monitor HoloSphere performance and usage statistics.</p>
|
|
686
|
+
|
|
687
|
+
<button onclick="refreshMetrics()">Refresh Metrics</button>
|
|
688
|
+
|
|
689
|
+
<div class="metrics-grid" id="metricsGrid">
|
|
690
|
+
<div class="metric-card">
|
|
691
|
+
<div class="metric-value" id="metricReads">0</div>
|
|
692
|
+
<div class="metric-label">Total Reads</div>
|
|
693
|
+
</div>
|
|
694
|
+
<div class="metric-card">
|
|
695
|
+
<div class="metric-value" id="metricWrites">0</div>
|
|
696
|
+
<div class="metric-label">Total Writes</div>
|
|
697
|
+
</div>
|
|
698
|
+
<div class="metric-card">
|
|
699
|
+
<div class="metric-value" id="metricSubs">0</div>
|
|
700
|
+
<div class="metric-label">Active Subscriptions</div>
|
|
701
|
+
</div>
|
|
702
|
+
<div class="metric-card">
|
|
703
|
+
<div class="metric-value" id="metricFeds">0</div>
|
|
704
|
+
<div class="metric-label">Federations</div>
|
|
705
|
+
</div>
|
|
706
|
+
<div class="metric-card">
|
|
707
|
+
<div class="metric-value" id="metricAvgRead">0</div>
|
|
708
|
+
<div class="metric-label">Avg Read Time (ms)</div>
|
|
709
|
+
</div>
|
|
710
|
+
<div class="metric-card">
|
|
711
|
+
<div class="metric-value" id="metricAvgWrite">0</div>
|
|
712
|
+
<div class="metric-label">Avg Write Time (ms)</div>
|
|
713
|
+
</div>
|
|
714
|
+
</div>
|
|
715
|
+
|
|
716
|
+
<h4>Raw Metrics Data</h4>
|
|
717
|
+
<div id="metricsResult" class="output"></div>
|
|
718
|
+
</div>
|
|
719
|
+
</div>
|
|
720
|
+
</div>
|
|
721
|
+
|
|
722
|
+
<script type="module">
|
|
723
|
+
// Import HoloSphere from the built ES module
|
|
724
|
+
import { HoloSphere } from './dist/esm/holosphere.js';
|
|
725
|
+
|
|
726
|
+
// Make it globally available for the demo functions
|
|
727
|
+
window.HoloSphere = HoloSphere;
|
|
728
|
+
|
|
729
|
+
// Initialize HoloSphere instance
|
|
730
|
+
let hs;
|
|
731
|
+
let currentSubscription = null;
|
|
732
|
+
let map = null;
|
|
733
|
+
let currentHexLayer = null;
|
|
734
|
+
|
|
735
|
+
// Real relay configuration
|
|
736
|
+
const REAL_RELAYS = [
|
|
737
|
+
'wss://relay.holons.io',
|
|
738
|
+
'wss://nos.lol',
|
|
739
|
+
'wss://relay.nostr.band',
|
|
740
|
+
'wss://nostr.wine',
|
|
741
|
+
'wss://relay.snort.social'
|
|
742
|
+
];
|
|
743
|
+
|
|
744
|
+
async function initHoloSphere() {
|
|
745
|
+
try {
|
|
746
|
+
// Check if user wants real relays
|
|
747
|
+
const useRealRelays = document.getElementById('useRealRelays').checked;
|
|
748
|
+
const relays = useRealRelays ? REAL_RELAYS : [];
|
|
749
|
+
|
|
750
|
+
// Initialize with demo configuration
|
|
751
|
+
hs = new HoloSphere({
|
|
752
|
+
appName: 'holosphere-demo',
|
|
753
|
+
relays: relays
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
window.hs = hs; // Make available globally for debugging
|
|
757
|
+
|
|
758
|
+
// Update connection status
|
|
759
|
+
document.getElementById('connectionStatus').className = 'connection-status connected';
|
|
760
|
+
|
|
761
|
+
if (useRealRelays) {
|
|
762
|
+
document.getElementById('connectionText').textContent = 'HoloSphere Initialized (Connected to 5 Nostr relays)';
|
|
763
|
+
document.getElementById('relayModeStatus').innerHTML = '🌐 <strong>Real Network Mode</strong>';
|
|
764
|
+
console.log('Connected to Nostr relays:', hs.config.relays);
|
|
765
|
+
} else {
|
|
766
|
+
document.getElementById('connectionText').textContent = 'HoloSphere Initialized (Local Storage Only)';
|
|
767
|
+
document.getElementById('relayModeStatus').innerHTML = '💾 <strong>Local Only Mode</strong>';
|
|
768
|
+
console.log('Using local storage only');
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
console.log('HoloSphere initialized successfully');
|
|
772
|
+
|
|
773
|
+
// Initialize the map
|
|
774
|
+
initMap();
|
|
775
|
+
|
|
776
|
+
// Set some demo data
|
|
777
|
+
await setupDemoData();
|
|
778
|
+
|
|
779
|
+
} catch (error) {
|
|
780
|
+
console.error('Failed to initialize HoloSphere:', error);
|
|
781
|
+
document.getElementById('connectionStatus').className = 'connection-status disconnected';
|
|
782
|
+
document.getElementById('connectionText').textContent = 'Initialization Failed';
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
async function setupDemoData() {
|
|
787
|
+
try {
|
|
788
|
+
// Convert San Francisco coordinates to holon
|
|
789
|
+
const sfHolon = await hs.toHolon(37.7749, -122.4194, 9);
|
|
790
|
+
|
|
791
|
+
// Write some demo data
|
|
792
|
+
await hs.write(sfHolon, 'demo', {
|
|
793
|
+
title: 'San Francisco Demo',
|
|
794
|
+
description: 'Welcome to HoloSphere!',
|
|
795
|
+
timestamp: Date.now()
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
console.log('Demo data setup complete');
|
|
799
|
+
} catch (error) {
|
|
800
|
+
console.error('Error setting up demo data:', error);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Toggle relay mode
|
|
805
|
+
window.toggleRelayMode = async function() {
|
|
806
|
+
const checkbox = document.getElementById('useRealRelays');
|
|
807
|
+
|
|
808
|
+
// Save preference to localStorage
|
|
809
|
+
localStorage.setItem('holosphere-use-real-relays', checkbox.checked);
|
|
810
|
+
|
|
811
|
+
// Reinitialize HoloSphere
|
|
812
|
+
await initHoloSphere();
|
|
813
|
+
|
|
814
|
+
// Update social protocols info banner
|
|
815
|
+
updateSocialProtocolsBanner();
|
|
816
|
+
|
|
817
|
+
alert(`Switched to ${checkbox.checked ? 'Real Network' : 'Local Only'} mode. HoloSphere has been reinitialized.`);
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// Update social protocols banner based on relay mode
|
|
821
|
+
function updateSocialProtocolsBanner() {
|
|
822
|
+
const useRealRelays = document.getElementById('useRealRelays').checked;
|
|
823
|
+
const banner = document.getElementById('socialProtocolsBanner');
|
|
824
|
+
|
|
825
|
+
if (useRealRelays) {
|
|
826
|
+
banner.className = 'status info';
|
|
827
|
+
banner.innerHTML = `<strong>🌐 Connected to Real Nostr Relays!</strong><br>
|
|
828
|
+
Your Nostr events will be published to the real Nostr network and can be seen by others using Nostr clients.
|
|
829
|
+
`;
|
|
830
|
+
} else {
|
|
831
|
+
banner.className = 'status success';
|
|
832
|
+
banner.innerHTML = `<strong>💾 Local Storage Mode</strong><br>
|
|
833
|
+
Your Nostr events are stored locally only and will NOT be published to the Nostr network.
|
|
834
|
+
Perfect for testing without broadcasting to the real network.`;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Load saved preference on page load
|
|
839
|
+
function loadRelayPreference() {
|
|
840
|
+
const saved = localStorage.getItem('holosphere-use-real-relays');
|
|
841
|
+
const checkbox = document.getElementById('useRealRelays');
|
|
842
|
+
|
|
843
|
+
// Default to local only (unchecked) for safety
|
|
844
|
+
checkbox.checked = saved === 'true';
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Tab switching
|
|
848
|
+
window.showTab = function(tabName) {
|
|
849
|
+
// Hide all tabs
|
|
850
|
+
document.querySelectorAll('.tab-content').forEach(tab => {
|
|
851
|
+
tab.classList.remove('active');
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// Remove active class from all tab buttons
|
|
855
|
+
document.querySelectorAll('.tab').forEach(button => {
|
|
856
|
+
button.classList.remove('active');
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// Show selected tab
|
|
860
|
+
document.getElementById(tabName).classList.add('active');
|
|
861
|
+
|
|
862
|
+
// Add active class to clicked button
|
|
863
|
+
event.target.classList.add('active');
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
// Spatial Operations
|
|
867
|
+
window.convertToHolon = async function() {
|
|
868
|
+
try {
|
|
869
|
+
const lat = parseFloat(document.getElementById('lat').value);
|
|
870
|
+
const lng = parseFloat(document.getElementById('lng').value);
|
|
871
|
+
const resolution = parseInt(document.getElementById('resolution').value);
|
|
872
|
+
|
|
873
|
+
const holon = await hs.toHolon(lat, lng, resolution);
|
|
874
|
+
|
|
875
|
+
const output = document.getElementById('holonResult');
|
|
876
|
+
output.innerHTML = `<pre>Holon ID: ${holon}
|
|
877
|
+
Resolution: ${resolution}
|
|
878
|
+
Coordinates: ${lat}, ${lng}
|
|
879
|
+
|
|
880
|
+
Valid H3 Index: ${hs.isValidH3(holon)}</pre>`;
|
|
881
|
+
|
|
882
|
+
// Update the holon input field for hierarchy operations
|
|
883
|
+
document.getElementById('holonId').value = holon;
|
|
884
|
+
|
|
885
|
+
// Show on map
|
|
886
|
+
if (map) {
|
|
887
|
+
showHexagonOnMap(holon, lat, lng);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
} catch (error) {
|
|
891
|
+
showError('holonResult', error);
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
window.getParents = async function() {
|
|
896
|
+
try {
|
|
897
|
+
const holonId = document.getElementById('holonId').value;
|
|
898
|
+
const parents = await hs.getParents(holonId, 0);
|
|
899
|
+
|
|
900
|
+
const output = document.getElementById('hierarchyResult');
|
|
901
|
+
output.innerHTML = `<pre>Parents of ${holonId}:\n${JSON.stringify(parents, null, 2)}</pre>`;
|
|
902
|
+
} catch (error) {
|
|
903
|
+
showError('hierarchyResult', error);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
window.getChildren = async function() {
|
|
908
|
+
try {
|
|
909
|
+
const holonId = document.getElementById('holonId').value;
|
|
910
|
+
const children = await hs.getChildren(holonId);
|
|
911
|
+
|
|
912
|
+
const output = document.getElementById('hierarchyResult');
|
|
913
|
+
output.innerHTML = `<pre>Children of ${holonId}:\n${JSON.stringify(children, null, 2)}</pre>`;
|
|
914
|
+
} catch (error) {
|
|
915
|
+
showError('hierarchyResult', error);
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
// Data Operations
|
|
920
|
+
window.writeData = async function() {
|
|
921
|
+
try {
|
|
922
|
+
const holon = document.getElementById('writeHolon').value;
|
|
923
|
+
const lens = document.getElementById('writeLens').value;
|
|
924
|
+
const data = JSON.parse(document.getElementById('writeData').value);
|
|
925
|
+
|
|
926
|
+
// Add timestamp if null
|
|
927
|
+
if (data.timestamp === null) {
|
|
928
|
+
data.timestamp = Date.now();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const result = await hs.write(holon, lens, data);
|
|
932
|
+
|
|
933
|
+
const output = document.getElementById('dataResult');
|
|
934
|
+
output.innerHTML = `<pre>Write successful: ${result}
|
|
935
|
+
Data ID: ${data.id}
|
|
936
|
+
Timestamp: ${new Date(data._meta?.timestamp || data.timestamp).toISOString()}</pre>`;
|
|
937
|
+
} catch (error) {
|
|
938
|
+
showError('dataResult', error);
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
window.readData = async function() {
|
|
943
|
+
try {
|
|
944
|
+
const holon = document.getElementById('readHolon').value;
|
|
945
|
+
const lens = document.getElementById('readLens').value;
|
|
946
|
+
const dataId = document.getElementById('readDataId').value || null;
|
|
947
|
+
|
|
948
|
+
const data = await hs.read(holon, lens, dataId);
|
|
949
|
+
|
|
950
|
+
const output = document.getElementById('dataResult');
|
|
951
|
+
output.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
|
|
952
|
+
} catch (error) {
|
|
953
|
+
showError('dataResult', error);
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
window.updateData = async function() {
|
|
958
|
+
try {
|
|
959
|
+
const holon = document.getElementById('updateHolon').value;
|
|
960
|
+
const lens = document.getElementById('updateLens').value;
|
|
961
|
+
const dataId = document.getElementById('updateDataId').value;
|
|
962
|
+
const updates = JSON.parse(document.getElementById('updateData').value);
|
|
963
|
+
|
|
964
|
+
const result = await hs.update(holon, lens, dataId, updates);
|
|
965
|
+
|
|
966
|
+
const output = document.getElementById('dataResult');
|
|
967
|
+
output.innerHTML = `<pre>Update successful: ${result}</pre>`;
|
|
968
|
+
} catch (error) {
|
|
969
|
+
showError('dataResult', error);
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
|
|
973
|
+
window.deleteData = async function() {
|
|
974
|
+
try {
|
|
975
|
+
const holon = document.getElementById('deleteHolon').value;
|
|
976
|
+
const lens = document.getElementById('deleteLens').value;
|
|
977
|
+
const dataId = document.getElementById('deleteDataId').value;
|
|
978
|
+
|
|
979
|
+
const result = await hs.delete(holon, lens, dataId);
|
|
980
|
+
|
|
981
|
+
const output = document.getElementById('dataResult');
|
|
982
|
+
output.innerHTML = `<pre>Delete successful: ${result}</pre>`;
|
|
983
|
+
} catch (error) {
|
|
984
|
+
showError('dataResult', error);
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
// Schema Operations
|
|
989
|
+
window.setSchema = async function() {
|
|
990
|
+
try {
|
|
991
|
+
const lens = document.getElementById('schemaLens').value;
|
|
992
|
+
const schema = JSON.parse(document.getElementById('schemaDefinition').value);
|
|
993
|
+
const strict = document.getElementById('strictMode').checked;
|
|
994
|
+
|
|
995
|
+
await hs.setSchema(lens, schema, strict);
|
|
996
|
+
|
|
997
|
+
const output = document.getElementById('schemaResult');
|
|
998
|
+
output.innerHTML = `<pre>Schema set successfully for lens: ${lens}
|
|
999
|
+
Strict mode: ${strict}</pre>`;
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
showError('schemaResult', error);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
window.testValidation = async function() {
|
|
1006
|
+
try {
|
|
1007
|
+
const lens = document.getElementById('schemaLens').value;
|
|
1008
|
+
const data = JSON.parse(document.getElementById('testData').value);
|
|
1009
|
+
|
|
1010
|
+
// Try to write with schema validation
|
|
1011
|
+
const holon = await hs.toHolon(37.7749, -122.4194, 9); // Demo holon
|
|
1012
|
+
const result = await hs.write(holon, lens, data);
|
|
1013
|
+
|
|
1014
|
+
const output = document.getElementById('schemaResult');
|
|
1015
|
+
output.innerHTML = `<pre>Validation passed! Data written successfully.
|
|
1016
|
+
Result: ${result}</pre>`;
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
showError('schemaResult', error);
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
// Federation Operations
|
|
1023
|
+
window.setupFederation = async function() {
|
|
1024
|
+
try {
|
|
1025
|
+
const sourceHolon = document.getElementById('sourceHolon').value;
|
|
1026
|
+
const targetHolon = document.getElementById('targetHolon').value;
|
|
1027
|
+
const lens = document.getElementById('federationLens').value;
|
|
1028
|
+
const direction = document.getElementById('federationDirection').value;
|
|
1029
|
+
const mode = document.getElementById('federationMode').value;
|
|
1030
|
+
|
|
1031
|
+
const result = await hs.federate(sourceHolon, targetHolon, lens, {
|
|
1032
|
+
direction,
|
|
1033
|
+
mode
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
const output = document.getElementById('federationResult');
|
|
1037
|
+
output.innerHTML = `<pre>Federation setup successful: ${result}
|
|
1038
|
+
Source: ${sourceHolon}
|
|
1039
|
+
Target: ${targetHolon}
|
|
1040
|
+
Lens: ${lens}
|
|
1041
|
+
Direction: ${direction}
|
|
1042
|
+
Mode: ${mode}</pre>`;
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
showError('federationResult', error);
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
window.getFederatedData = async function() {
|
|
1049
|
+
try {
|
|
1050
|
+
const holon = document.getElementById('fedHolon').value;
|
|
1051
|
+
const lens = document.getElementById('fedLens').value;
|
|
1052
|
+
const resolveHolograms = document.getElementById('resolveHolograms').checked;
|
|
1053
|
+
|
|
1054
|
+
const data = await hs.getFederatedData(holon, lens, { resolveHolograms });
|
|
1055
|
+
|
|
1056
|
+
const output = document.getElementById('federationResult');
|
|
1057
|
+
output.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
showError('federationResult', error);
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
// Social Protocol Operations
|
|
1064
|
+
window.publishNostr = async function() {
|
|
1065
|
+
try {
|
|
1066
|
+
const holon = document.getElementById('nostrHolon').value;
|
|
1067
|
+
const event = JSON.parse(document.getElementById('nostrEvent').value);
|
|
1068
|
+
|
|
1069
|
+
// Add timestamp if null
|
|
1070
|
+
if (event.created_at === null) {
|
|
1071
|
+
event.created_at = Math.floor(Date.now() / 1000);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
const result = await hs.publishNostr(event, holon);
|
|
1075
|
+
|
|
1076
|
+
const output = document.getElementById('socialResult');
|
|
1077
|
+
output.innerHTML = `<pre>Nostr event published: ${result}
|
|
1078
|
+
Event ID: ${event.id}
|
|
1079
|
+
Created at: ${new Date(event.created_at * 1000).toISOString()}</pre>`;
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
showError('socialResult', error);
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
window.publishActivityPub = async function() {
|
|
1086
|
+
try {
|
|
1087
|
+
const holon = document.getElementById('apHolon').value;
|
|
1088
|
+
const object = JSON.parse(document.getElementById('apObject').value);
|
|
1089
|
+
|
|
1090
|
+
const result = await hs.publishActivityPub(object, holon);
|
|
1091
|
+
|
|
1092
|
+
const output = document.getElementById('socialResult');
|
|
1093
|
+
output.innerHTML = `<pre>ActivityPub object published: ${result}</pre>`;
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
showError('socialResult', error);
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
window.querySocial = async function() {
|
|
1100
|
+
try {
|
|
1101
|
+
const holon = document.getElementById('socialHolon').value;
|
|
1102
|
+
const protocol = document.getElementById('socialProtocol').value || undefined;
|
|
1103
|
+
|
|
1104
|
+
const data = await hs.querySocial(holon, { protocol });
|
|
1105
|
+
|
|
1106
|
+
const output = document.getElementById('socialResult');
|
|
1107
|
+
output.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
showError('socialResult', error);
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
// Cryptography Operations
|
|
1114
|
+
window.generateKeys = async function() {
|
|
1115
|
+
try {
|
|
1116
|
+
// Generate a random private key
|
|
1117
|
+
const privateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
1118
|
+
const privateKeyHex = Array.from(privateKey, b => b.toString(16).padStart(2, '0')).join('');
|
|
1119
|
+
|
|
1120
|
+
// Get public key
|
|
1121
|
+
const publicKey = await hs.getPublicKey(privateKeyHex);
|
|
1122
|
+
|
|
1123
|
+
const output = document.getElementById('keysResult');
|
|
1124
|
+
output.innerHTML = `<pre>Private Key: ${privateKeyHex}
|
|
1125
|
+
Public Key: ${publicKey}
|
|
1126
|
+
|
|
1127
|
+
⚠️ Keep your private key secret!</pre>`;
|
|
1128
|
+
|
|
1129
|
+
// Auto-fill the fields
|
|
1130
|
+
document.getElementById('privateKey').value = privateKeyHex;
|
|
1131
|
+
document.getElementById('publicKey').value = publicKey;
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
showError('keysResult', error);
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
window.signContent = async function() {
|
|
1138
|
+
try {
|
|
1139
|
+
const content = document.getElementById('signContent').value;
|
|
1140
|
+
const privateKey = document.getElementById('privateKey').value;
|
|
1141
|
+
|
|
1142
|
+
const signature = await hs.sign(content, privateKey);
|
|
1143
|
+
|
|
1144
|
+
const output = document.getElementById('cryptoResult');
|
|
1145
|
+
output.innerHTML = `<pre>Content: ${content}
|
|
1146
|
+
Signature: ${signature}</pre>`;
|
|
1147
|
+
|
|
1148
|
+
// Auto-fill signature field
|
|
1149
|
+
document.getElementById('signature').value = signature;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
showError('cryptoResult', error);
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
window.verifySignature = async function() {
|
|
1156
|
+
try {
|
|
1157
|
+
const content = document.getElementById('signContent').value;
|
|
1158
|
+
const signature = document.getElementById('signature').value;
|
|
1159
|
+
const publicKey = document.getElementById('publicKey').value;
|
|
1160
|
+
|
|
1161
|
+
const valid = await hs.verify(content, signature, publicKey);
|
|
1162
|
+
|
|
1163
|
+
const output = document.getElementById('cryptoResult');
|
|
1164
|
+
output.innerHTML = `<pre>Signature verification: ${valid ? '✅ Valid' : '❌ Invalid'}
|
|
1165
|
+
Content: ${content}
|
|
1166
|
+
Signature: ${signature}
|
|
1167
|
+
Public Key: ${publicKey}</pre>`;
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
showError('cryptoResult', error);
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
window.issueCapability = async function() {
|
|
1174
|
+
try {
|
|
1175
|
+
const permissions = document.getElementById('capPermissions').value.split(',').map(p => p.trim());
|
|
1176
|
+
const scope = JSON.parse(document.getElementById('capScope').value);
|
|
1177
|
+
const recipient = document.getElementById('capRecipient').value;
|
|
1178
|
+
|
|
1179
|
+
const token = await hs.issueCapability(permissions, scope, recipient, {
|
|
1180
|
+
expiresIn: 3600 // 1 hour
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
const output = document.getElementById('cryptoResult');
|
|
1184
|
+
output.innerHTML = `<pre>Capability Token Issued:
|
|
1185
|
+
${JSON.stringify(token, null, 2)}</pre>`;
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
showError('cryptoResult', error);
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
// Subscription Operations
|
|
1192
|
+
window.createSubscription = function() {
|
|
1193
|
+
try {
|
|
1194
|
+
const holon = document.getElementById('subHolon').value;
|
|
1195
|
+
const lens = document.getElementById('subLens').value;
|
|
1196
|
+
|
|
1197
|
+
// Clear previous events
|
|
1198
|
+
document.getElementById('subscriptionEvents').innerHTML = '<pre>Waiting for events...</pre>';
|
|
1199
|
+
|
|
1200
|
+
// Create subscription
|
|
1201
|
+
currentSubscription = hs.subscribe(holon, lens, (data, key) => {
|
|
1202
|
+
const eventsDiv = document.getElementById('subscriptionEvents');
|
|
1203
|
+
const timestamp = new Date().toISOString();
|
|
1204
|
+
const event = `[${timestamp}] Data change in ${holon}/${lens}:
|
|
1205
|
+
${JSON.stringify(data, null, 2)}
|
|
1206
|
+
|
|
1207
|
+
`;
|
|
1208
|
+
eventsDiv.innerHTML = '<pre>' + event + eventsDiv.innerText + '</pre>';
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
// Enable/disable buttons
|
|
1212
|
+
document.getElementById('unsubBtn').disabled = false;
|
|
1213
|
+
|
|
1214
|
+
showSuccess('subscriptionEvents', 'Subscription created successfully');
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
showError('subscriptionEvents', error);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
window.unsubscribe = function() {
|
|
1221
|
+
if (currentSubscription) {
|
|
1222
|
+
currentSubscription.unsubscribe();
|
|
1223
|
+
currentSubscription = null;
|
|
1224
|
+
|
|
1225
|
+
document.getElementById('unsubBtn').disabled = true;
|
|
1226
|
+
showSuccess('subscriptionEvents', 'Unsubscribed successfully');
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
window.sendTestMessage = async function() {
|
|
1231
|
+
try {
|
|
1232
|
+
const holon = document.getElementById('subHolon').value;
|
|
1233
|
+
const lens = document.getElementById('subLens').value;
|
|
1234
|
+
const message = document.getElementById('testMessage').value;
|
|
1235
|
+
|
|
1236
|
+
await hs.write(holon, lens, {
|
|
1237
|
+
type: 'test',
|
|
1238
|
+
message: message,
|
|
1239
|
+
timestamp: Date.now()
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
showSuccess('subscriptionEvents', 'Test message sent');
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
showError('subscriptionEvents', error);
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
// Metrics Operations
|
|
1249
|
+
window.refreshMetrics = function() {
|
|
1250
|
+
try {
|
|
1251
|
+
const metrics = hs.metrics();
|
|
1252
|
+
|
|
1253
|
+
// Update metric cards
|
|
1254
|
+
document.getElementById('metricReads').textContent = metrics.reads || 0;
|
|
1255
|
+
document.getElementById('metricWrites').textContent = metrics.writes || 0;
|
|
1256
|
+
document.getElementById('metricSubs').textContent = metrics.subscriptions || 0;
|
|
1257
|
+
document.getElementById('metricFeds').textContent = metrics.federations || 0;
|
|
1258
|
+
document.getElementById('metricAvgRead').textContent =
|
|
1259
|
+
metrics.avgReadTime ? metrics.avgReadTime.toFixed(2) : '0';
|
|
1260
|
+
document.getElementById('metricAvgWrite').textContent =
|
|
1261
|
+
metrics.avgWriteTime ? metrics.avgWriteTime.toFixed(2) : '0';
|
|
1262
|
+
|
|
1263
|
+
// Show raw data
|
|
1264
|
+
document.getElementById('metricsResult').innerHTML =
|
|
1265
|
+
`<pre>${JSON.stringify(metrics, null, 2)}</pre>`;
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
showError('metricsResult', error);
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
// Map initialization
|
|
1272
|
+
function initMap() {
|
|
1273
|
+
// Check if Leaflet is loaded
|
|
1274
|
+
if (typeof L === 'undefined') {
|
|
1275
|
+
console.warn('Leaflet not loaded, skipping map initialization');
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// Initialize the map
|
|
1280
|
+
map = L.map('map').setView([37.7749, -122.4194], 10);
|
|
1281
|
+
|
|
1282
|
+
// Add tile layer
|
|
1283
|
+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
1284
|
+
attribution: '© OpenStreetMap contributors'
|
|
1285
|
+
}).addTo(map);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function showHexagonOnMap(holonId, lat, lng) {
|
|
1289
|
+
if (!map) return;
|
|
1290
|
+
|
|
1291
|
+
// Clear previous layer
|
|
1292
|
+
if (currentHexLayer) {
|
|
1293
|
+
map.removeLayer(currentHexLayer);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// For demo purposes, show a marker at the center
|
|
1297
|
+
// In production, you'd use h3-js to get the hex boundary
|
|
1298
|
+
currentHexLayer = L.marker([lat, lng])
|
|
1299
|
+
.addTo(map)
|
|
1300
|
+
.bindPopup(`Holon: ${holonId}`)
|
|
1301
|
+
.openPopup();
|
|
1302
|
+
|
|
1303
|
+
// Center the map
|
|
1304
|
+
map.setView([lat, lng], 12);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// Helper functions
|
|
1308
|
+
function showError(elementId, error) {
|
|
1309
|
+
const output = document.getElementById(elementId);
|
|
1310
|
+
output.innerHTML = `<pre style="color: #ff4444;">Error: ${error.message}
|
|
1311
|
+
${error.stack || ''}</pre>`;
|
|
1312
|
+
console.error(error);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function showSuccess(elementId, message) {
|
|
1316
|
+
const output = document.getElementById(elementId);
|
|
1317
|
+
const current = output.innerHTML;
|
|
1318
|
+
output.innerHTML = `<pre style="color: #44ff44;">${message}</pre>
|
|
1319
|
+
${current}`;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// Initialize on page load
|
|
1323
|
+
window.addEventListener('DOMContentLoaded', async () => {
|
|
1324
|
+
loadRelayPreference();
|
|
1325
|
+
await initHoloSphere();
|
|
1326
|
+
updateSocialProtocolsBanner();
|
|
1327
|
+
});
|
|
1328
|
+
</script>
|
|
1329
|
+
|
|
1330
|
+
<!-- Load Leaflet for map visualization -->
|
|
1331
|
+
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
|
1332
|
+
</body>
|
|
1333
|
+
</html>
|