wireweave 0.1.1 → 0.1.3

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.
@@ -1,74 +0,0 @@
1
- var serverRoles = {
2
- _store: new Map(),
3
- _sub: null,
4
-
5
- _creatorOf(serverId) {
6
- return serverId ? serverId.split(':')[0] : null;
7
- },
8
-
9
- isOwner(serverId) {
10
- return !!state.nostrPubkey && serverRoles._creatorOf(serverId) === state.nostrPubkey;
11
- },
12
-
13
- isAdmin(serverId) {
14
- if (serverRoles.isOwner(serverId)) return true;
15
- var roles = serverRoles._store.get(serverId) || {};
16
- return !!(roles.admins || []).includes(state.nostrPubkey);
17
- },
18
-
19
- isMod(serverId) {
20
- if (serverRoles.isAdmin(serverId)) return true;
21
- var roles = serverRoles._store.get(serverId) || {};
22
- return !!(roles.mods || []).includes(state.nostrPubkey);
23
- },
24
-
25
- getRole(serverId, pubkey) {
26
- var creator = serverRoles._creatorOf(serverId);
27
- if (pubkey === creator) return 'owner';
28
- var roles = serverRoles._store.get(serverId) || {};
29
- if ((roles.admins || []).includes(pubkey)) return 'admin';
30
- if ((roles.mods || []).includes(pubkey)) return 'moderator';
31
- return 'member';
32
- },
33
-
34
- async setRole(serverId, targetPubkey, role) {
35
- if (!serverRoles.isOwner(serverId) && role === 'admin') throw new Error('Only owner can assign admin');
36
- if (!serverRoles.isAdmin(serverId)) throw new Error('Insufficient permissions');
37
- var existing = serverRoles._store.get(serverId) || { admins: [], mods: [] };
38
- var admins = (existing.admins || []).filter(function(p) { return p !== targetPubkey; });
39
- var mods = (existing.mods || []).filter(function(p) { return p !== targetPubkey; });
40
- if (role === 'admin') admins = admins.concat([targetPubkey]);
41
- else if (role === 'moderator') mods = mods.concat([targetPubkey]);
42
- var next = { admins: admins, mods: mods };
43
- serverRoles._store.set(serverId, next);
44
- var dTag = 'zellous-roles:' + serverId;
45
- var signed = await auth.sign({ kind: 30078, created_at: Math.floor(Date.now() / 1000), tags: [['d', dTag]], content: JSON.stringify(next) });
46
- await nostrNet.publish(signed);
47
- },
48
-
49
- subscribe(serverId) {
50
- if (serverRoles._sub) { nostrNet.unsubscribe(serverRoles._sub); serverRoles._sub = null; }
51
- if (!serverId) return;
52
- var creator = serverRoles._creatorOf(serverId);
53
- if (!creator) return;
54
- var dTag = 'zellous-roles:' + serverId;
55
- serverRoles._sub = 'roles-' + serverId;
56
- nostrNet.subscribe(serverRoles._sub,
57
- [{ kinds: [30078], authors: [creator], '#d': [dTag] }],
58
- function(event) {
59
- if (event.pubkey !== creator) return;
60
- try {
61
- var data = JSON.parse(event.content);
62
- serverRoles._store.set(serverId, { admins: data.admins || [], mods: data.mods || [] });
63
- if (window.uiMembers) uiMembers.render();
64
- } catch(e) {}
65
- },
66
- function() {}
67
- );
68
- }
69
- };
70
-
71
- window.__zellous.roles = serverRoles;
72
- window.serverRoles = serverRoles;
73
- if (!window.__debug) window.__debug = {};
74
- Object.defineProperty(window.__debug, 'roles', { get: function() { return { store: Object.fromEntries(serverRoles._store), sub: serverRoles._sub }; }, configurable: true });
@@ -1,223 +0,0 @@
1
- var serverManager = {
2
- loadServers: function() {
3
- try {
4
- var raw = localStorage.getItem('zn_servers');
5
- state.servers = raw ? JSON.parse(raw) : [];
6
- } catch (e) { state.servers = []; }
7
-
8
- if (state.nostrPubkey) {
9
- nostrNet.subscribe(
10
- 'my-servers',
11
- [{ kinds: [34550], authors: [state.nostrPubkey] }],
12
- function(event) { serverManager._handleServerEvent(event); },
13
- function() {}
14
- );
15
- }
16
-
17
- try {
18
- var joined = localStorage.getItem('zn_joined_servers');
19
- var joinedIds = joined ? JSON.parse(joined) : [];
20
- joinedIds.forEach(function(sid) {
21
- if (!state.servers.find(function(s) { return s.id === sid; })) {
22
- state.servers = state.servers.concat([{ id: sid, name: sid.slice(0, 8), iconColor: '#5865F2' }]);
23
- }
24
- });
25
- // Subscribe to relay to fetch real names for joined (non-owned) servers
26
- var unresolved = joinedIds.filter(function(sid) {
27
- // Format: pubkey:dTag — only subscribe if not already owned by us
28
- var parts = sid.split(':');
29
- return parts.length === 2 && parts[0] !== state.nostrPubkey;
30
- });
31
- if (unresolved.length) {
32
- // Group by author to minimise subscriptions
33
- var byAuthor = {};
34
- unresolved.forEach(function(sid) {
35
- var parts = sid.split(':');
36
- var author = parts[0], dTag = parts[1];
37
- if (!byAuthor[author]) byAuthor[author] = [];
38
- byAuthor[author].push(dTag);
39
- });
40
- Object.keys(byAuthor).forEach(function(author) {
41
- var dTags = byAuthor[author];
42
- nostrNet.subscribe(
43
- 'joined-server-' + author.slice(0, 8),
44
- [{ kinds: [34550], authors: [author], '#d': dTags }],
45
- function(event) { serverManager._handleServerEvent(event); },
46
- function() {}
47
- );
48
- });
49
- }
50
- } catch (e) {}
51
- },
52
-
53
- _handleServerEvent: function(event) {
54
- var nameTag = event.tags.find(function(t) { return t[0] === 'name'; });
55
- var colorTag = event.tags.find(function(t) { return t[0] === 'color'; });
56
- var dTag = event.tags.find(function(t) { return t[0] === 'd'; });
57
- if (!dTag) return;
58
- var serverId = event.pubkey + ':' + dTag[1];
59
- var name = nameTag ? nameTag[1] : serverId.slice(0, 8);
60
- var iconColor = colorTag ? colorTag[1] : '#5865F2';
61
- var existing = state.servers.find(function(s) { return s.id === serverId; });
62
- if (existing) {
63
- existing.name = name; existing.iconColor = iconColor; existing.ownerId = event.pubkey;
64
- state.servers = state.servers.slice();
65
- } else {
66
- state.servers = state.servers.concat([{ id: serverId, name: name, iconColor: iconColor, ownerId: event.pubkey }]);
67
- }
68
- serverManager._persistServers();
69
- ui.render.all();
70
- },
71
-
72
- create: async function(name, iconColor) {
73
- var dTag = Math.random().toString(36).slice(2, 10);
74
- var serverId = state.nostrPubkey + ':' + dTag;
75
- var template = { kind: 34550, created_at: Math.floor(Date.now() / 1000), tags: [['d', dTag], ['name', name], ['color', iconColor || '#5865F2']], content: '' };
76
- var signed = await auth.sign(template);
77
- await nostrNet.publish(signed);
78
- var server = { id: serverId, name: name, iconColor: iconColor || '#5865F2', ownerId: state.nostrPubkey };
79
- state.servers = state.servers.concat([server]);
80
- serverManager._persistServers();
81
- serverManager.switchTo(serverId);
82
- },
83
-
84
- join: async function(serverId) {
85
- try {
86
- var joined = JSON.parse(localStorage.getItem('zn_joined_servers') || '[]');
87
- if (!joined.includes(serverId)) { joined.push(serverId); localStorage.setItem('zn_joined_servers', JSON.stringify(joined)); }
88
- } catch (e) {}
89
- if (!state.servers.find(function(s) { return s.id === serverId; })) {
90
- state.servers = state.servers.concat([{ id: serverId, name: serverId.slice(0, 8), iconColor: '#5865F2' }]);
91
- serverManager._persistServers();
92
- }
93
- serverManager.switchTo(serverId);
94
- },
95
-
96
- delete: async function(serverId) {
97
- state.servers = (state.servers || []).filter(function(s) { return s.id !== serverId; });
98
- serverManager._persistServers();
99
- try {
100
- var joined = JSON.parse(localStorage.getItem('zn_joined_servers') || '[]');
101
- localStorage.setItem('zn_joined_servers', JSON.stringify(joined.filter(function(id) { return id !== serverId; })));
102
- } catch (e) {}
103
- if (state.currentServerId === serverId) serverManager.switchTo(null);
104
- else ui.render.all();
105
- },
106
-
107
- leave: function(serverId) { serverManager.delete(serverId); },
108
- switchTo: async function(serverId) {
109
- state.currentServerId = serverId;
110
- state.chatMessages = []; state.channels = []; state.categories = [];
111
- if (window.serverRoles) serverRoles.subscribe(serverId);
112
- if (window.serverSettings) serverSettings.subscribe(serverId);
113
- if (window.serverPages) serverPages.subscribe(serverId);
114
- if (serverId) localStorage.setItem('zn_lastServer', serverId);
115
- else localStorage.removeItem('zn_lastServer');
116
- if (window.channelManager && serverId) {
117
- await new Promise(function(resolve) {
118
- var done = false;
119
- var finish = function() { if (done) return; done = true; if (!(state.channels || []).length) channelManager._setDefaults(); resolve(); };
120
- channelManager.loadChannels(serverId, finish);
121
- setTimeout(finish, 3000);
122
- });
123
- }
124
- var firstText = (state.channels || []).find(function(c) { return c.type === 'text'; });
125
- if (firstText && state.currentChannelId !== firstText.id) {
126
- state.currentChannelId = firstText.id; state.currentChannel = firstText;
127
- if (window.chat) chat.loadHistory(firstText.id);
128
- }
129
- ui.render.all();
130
- },
131
- _persistServers: function() { localStorage.setItem('zn_servers', JSON.stringify(state.servers)); },
132
- init: function() {
133
- serverManager.loadServers();
134
- var lastServer = localStorage.getItem('zn_lastServer');
135
- if (lastServer && state.servers.find(function(s) { return s.id === lastServer; })) serverManager.switchTo(lastServer);
136
- else if (state.servers.length) serverManager.switchTo(state.servers[0].id);
137
- },
138
-
139
- _getOrder: function() { try { return JSON.parse(localStorage.getItem('zn_serverOrder') || '[]'); } catch(e) { return []; } },
140
- _saveOrder: function(ids) { localStorage.setItem('zn_serverOrder', JSON.stringify(ids)); },
141
-
142
- _sortedServers: function() {
143
- var srvs = state.servers || [];
144
- var order = serverManager._getOrder();
145
- if (!order.length) return srvs;
146
- var indexed = {};
147
- order.forEach(function(id, i) { indexed[id] = i; });
148
- return srvs.slice().sort(function(a, b) {
149
- var ai = indexed[a.id] !== undefined ? indexed[a.id] : Infinity;
150
- var bi = indexed[b.id] !== undefined ? indexed[b.id] : Infinity;
151
- return ai - bi;
152
- });
153
- },
154
-
155
- renderList: function() {
156
- var container = document.getElementById('serverIcons');
157
- if (!container) return;
158
- var srvs = serverManager._sortedServers();
159
- var current = state.currentServerId;
160
- var currentServer = current && srvs.find(function(s) { return s.id === current; });
161
- var headerEl = document.getElementById('serverHeaderName');
162
- if (headerEl) headerEl.textContent = currentServer ? currentServer.name : 'Zellous';
163
- var colors = ['#5865f2', '#57f287', '#feb347', '#fe7168', '#9b59b6', '#1abc9c', '#e67e22', '#e74c3c'];
164
- var html = '';
165
- srvs.forEach(function(s) {
166
- var active = s.id === current ? ' active' : '';
167
- var color = s.iconColor || colors[s.name.length % colors.length];
168
- var initial = (s.name || '?')[0].toUpperCase();
169
- html += '<div class="server-icon' + active + '" draggable="true" data-server="' + s.id + '" title="' + s.name + '" style="background:' + (active ? '' : color) + '">' +
170
- '<div class="server-pill"></div>' + initial + '</div>';
171
- });
172
- html += '<div class="server-separator" id="serverSeparator"></div>' +
173
- '<div class="server-icon add-server" id="addServerBtn" title="Add a Server"><div class="server-pill"></div>+</div>';
174
- container.innerHTML = html;
175
- var homeIcon = document.getElementById('homeServer');
176
- if (homeIcon) {
177
- homeIcon.classList.toggle('active', !current);
178
- homeIcon.addEventListener('click', function() { serverManager.switchTo(null); });
179
- }
180
- container.querySelectorAll('[data-server]').forEach(function(el) {
181
- el.addEventListener('click', function() { serverManager.switchTo(el.dataset.server); });
182
- el.addEventListener('contextmenu', function(e) { e.preventDefault(); serverManager.showContextMenu(el.dataset.server, e.clientX, e.clientY); });
183
- });
184
- var addBtn = document.getElementById('addServerBtn');
185
- if (addBtn) addBtn.addEventListener('click', function() { serverManager.showCreateModal(); });
186
- serverManager._initDragDrop(container);
187
- },
188
-
189
- _initDragDrop: function(container) {
190
- var dragId = null;
191
- container.addEventListener('dragstart', function(e) {
192
- var el = e.target.closest('[data-server]');
193
- if (!el) return;
194
- dragId = el.dataset.server; e.dataTransfer.effectAllowed = 'move';
195
- setTimeout(function() { el.classList.add('dragging'); }, 0);
196
- });
197
- container.addEventListener('dragend', function() {
198
- container.querySelectorAll('.dragging').forEach(function(el) { el.classList.remove('dragging'); });
199
- container.querySelectorAll('.drag-over').forEach(function(el) { el.classList.remove('drag-over'); });
200
- dragId = null;
201
- });
202
- container.addEventListener('dragover', function(e) {
203
- e.preventDefault(); e.dataTransfer.dropEffect = 'move';
204
- var target = e.target.closest('[data-server]');
205
- container.querySelectorAll('.drag-over').forEach(function(el) { el.classList.remove('drag-over'); });
206
- if (target && target.dataset.server !== dragId) target.classList.add('drag-over');
207
- });
208
- container.addEventListener('drop', function(e) {
209
- e.preventDefault();
210
- var target = e.target.closest('[data-server]');
211
- if (!target || !dragId || target.dataset.server === dragId) return;
212
- var srvs = serverManager._sortedServers();
213
- var ids = srvs.map(function(s) { return s.id; });
214
- var fromIdx = ids.indexOf(dragId), toIdx = ids.indexOf(target.dataset.server);
215
- if (fromIdx === -1 || toIdx === -1) return;
216
- ids.splice(fromIdx, 1); ids.splice(toIdx, 0, dragId);
217
- serverManager._saveOrder(ids); serverManager.renderList();
218
- });
219
- }
220
- };
221
-
222
- window.__zellous.servers = serverManager;
223
- window.serverManager = serverManager;
@@ -1,97 +0,0 @@
1
- var serverSettings = {
2
- _store: new Map(),
3
- _sub: null,
4
- _VALID_BITRATES: [8000, 16000, 24000, 48000, 96000],
5
-
6
- _clampBitrate(v) {
7
- return serverSettings._VALID_BITRATES.reduce(function(prev, cur) {
8
- return Math.abs(cur - v) < Math.abs(prev - v) ? cur : prev;
9
- });
10
- },
11
-
12
- getBitrate(serverId) {
13
- var s = serverSettings._store.get(serverId);
14
- return (s && s.opusBitrate) || 24000;
15
- },
16
-
17
- async setBitrate(serverId, bitrate) {
18
- if (!serverRoles.isOwner(serverId) && !serverRoles.isAdmin(serverId)) throw new Error('Insufficient permissions');
19
- var clamped = serverSettings._clampBitrate(Number(bitrate));
20
- var existing = serverSettings._store.get(serverId) || {};
21
- var next = Object.assign({}, existing, { opusBitrate: clamped });
22
- serverSettings._store.set(serverId, next);
23
- var dTag = 'zellous-settings:' + serverId;
24
- var signed = await auth.sign({ kind: 30078, created_at: Math.floor(Date.now() / 1000), tags: [['d', dTag]], content: JSON.stringify(next) });
25
- await nostrNet.publish(signed);
26
- if (state.audioEncoder) {
27
- try { state.audioEncoder.configure({ codec: 'opus', sampleRate: config.sampleRate, numberOfChannels: 1, bitrate: clamped }); } catch(e) {}
28
- }
29
- return clamped;
30
- },
31
-
32
- getEmbedAllowlist(serverId) {
33
- var s = serverSettings._store.get(serverId);
34
- return (s && s.embedAllowlist) ? (typeof s.embedAllowlist === 'string' ? s.embedAllowlist.split(',').map(function(d) { return d.trim(); }).filter(Boolean) : s.embedAllowlist) : [];
35
- },
36
-
37
- async setEmbedAllowlist(serverId, domainsStr) {
38
- if (!serverRoles.isOwner(serverId) && !serverRoles.isAdmin(serverId)) throw new Error('Insufficient permissions');
39
- var domains = domainsStr.split(',').map(function(d) { return d.trim(); }).filter(Boolean);
40
- var existing = serverSettings._store.get(serverId) || {};
41
- var next = Object.assign({}, existing, { embedAllowlist: domains });
42
- serverSettings._store.set(serverId, next);
43
- var dTag = 'zellous-settings:' + serverId;
44
- var signed = await auth.sign({ kind: 30078, created_at: Math.floor(Date.now() / 1000), tags: [['d', dTag]], content: JSON.stringify(next) });
45
- await nostrNet.publish(signed);
46
- return domains;
47
- },
48
-
49
- isOriginAllowed(serverId, origin) {
50
- if (!origin) return false;
51
- var allowlist = serverSettings.getEmbedAllowlist(serverId);
52
- if (allowlist.length === 0) return true;
53
- var url = {};
54
- try { url = new URL(origin); } catch(e) { return false; }
55
- var hostname = url.hostname;
56
- return allowlist.some(function(pattern) {
57
- if (pattern === '*') return true;
58
- if (pattern.startsWith('*.')) {
59
- var suffix = pattern.slice(2);
60
- return hostname === suffix || hostname.endsWith('.' + suffix);
61
- }
62
- return hostname === pattern || hostname === pattern.replace('https://', '').replace('http://', '');
63
- });
64
- },
65
-
66
- subscribe(serverId) {
67
- if (serverSettings._sub) { nostrNet.unsubscribe(serverSettings._sub); serverSettings._sub = null; }
68
- if (!serverId) return;
69
- var creator = serverId.split(':')[0];
70
- if (!creator) return;
71
- var dTag = 'zellous-settings:' + serverId;
72
- serverSettings._sub = 'settings-' + serverId;
73
- nostrNet.subscribe(serverSettings._sub,
74
- [{ kinds: [30078], authors: [creator], '#d': [dTag] }],
75
- function(event) {
76
- if (event.pubkey !== creator) return;
77
- try {
78
- var data = JSON.parse(event.content);
79
- serverSettings._store.set(serverId, data);
80
- } catch(e) {}
81
- },
82
- function() {}
83
- );
84
- },
85
-
86
- applyToEncoder() {
87
- var bitrate = serverSettings.getBitrate(state.currentServerId);
88
- if (state.audioEncoder) {
89
- try { state.audioEncoder.configure({ codec: 'opus', sampleRate: config.sampleRate, numberOfChannels: 1, bitrate: bitrate }); } catch(e) {}
90
- }
91
- }
92
- };
93
-
94
- window.__zellous.settings = serverSettings;
95
- window.serverSettings = serverSettings;
96
- if (!window.__debug) window.__debug = {};
97
- Object.defineProperty(window.__debug, 'settings', { get: function() { return { store: Object.fromEntries(serverSettings._store), sub: serverSettings._sub }; }, configurable: true });
@@ -1,19 +0,0 @@
1
- (() => {
2
- const sig = window.__signal;
3
- const raw = window.stateSignals;
4
-
5
- raw.nostrPubkey = sig('');
6
- raw.nostrPrivkey = sig(null);
7
- raw.nostrRelays = sig([
8
- 'wss://relay.damus.io',
9
- 'wss://relay.primal.net',
10
- 'wss://relay.snort.social',
11
- 'wss://nos.lol',
12
- 'wss://relay.olas.app',
13
- 'wss://nostr.mom'
14
- ]);
15
- raw.nostrRelayStatus = sig(new Map());
16
- raw.nostrProfile = sig(null);
17
- raw.nostrSubscriptions = sig(new Map());
18
- raw.currentChannelId = sig(null);
19
- })();
@@ -1,63 +0,0 @@
1
- var nostrVoiceCamera = {
2
- _fsm: null,
3
-
4
- _initFSM: function() {
5
- var actor = XState.createActor(nostrFsm.cameraMachine);
6
- actor.subscribe(function(snap) {
7
- var state_val = snap.value;
8
- document.getElementById('voiceCamBtn')?.classList.toggle('active', state_val === 'active');
9
- });
10
- actor.start();
11
- nostrVoiceCamera._fsm = actor;
12
- },
13
-
14
- toggle() {
15
- if (!nostrVoiceCamera._fsm) nostrVoiceCamera._initFSM();
16
- var snap = nostrVoiceCamera._fsm.getSnapshot();
17
- if (snap.matches('active')) {
18
- nostrVoiceCamera.stop();
19
- } else if (snap.matches('idle')) {
20
- nostrVoiceCamera.start();
21
- }
22
- },
23
-
24
- async start() {
25
- if (!nostrVoiceCamera._fsm) nostrVoiceCamera._initFSM();
26
- var snap = nostrVoiceCamera._fsm.getSnapshot();
27
- if (!snap.can({type:'enable'})) return;
28
- nostrVoiceCamera._fsm.send({type:'enable'});
29
- state.cameraEnabled = true;
30
- if (nostrVoice._cameraStream) {
31
- var track = nostrVoice._cameraStream.getVideoTracks()[0];
32
- nostrVoice._peers.forEach(function(peer) {
33
- var sender = peer.pc.getSenders().find(function(s) { return s.track && s.track.kind === 'video'; });
34
- if (sender) { sender.replaceTrack(track).catch(function(){}); }
35
- else { peer.pc.addTrack(track, nostrVoice._cameraStream); }
36
- });
37
- var local = nostrVoice._participants.get('local');
38
- if (local) local.hasVideo = true;
39
- nostrVoiceCamera._fsm.send({type:'enabled'});
40
- nostrVoice.updateParticipants();
41
- } else {
42
- nostrVoiceCamera._fsm.send({type:'denied'});
43
- state.cameraEnabled = false;
44
- }
45
- },
46
-
47
- stop() {
48
- if (!nostrVoiceCamera._fsm) nostrVoiceCamera._initFSM();
49
- if (!nostrVoiceCamera._fsm.getSnapshot().can({type:'disable'})) return;
50
- nostrVoiceCamera._fsm.send({type:'disable'});
51
- state.cameraEnabled = false;
52
- nostrVoice._peers.forEach(function(peer) {
53
- var sender = peer.pc.getSenders().find(function(s) { return s.track && s.track.kind === 'video'; });
54
- if (sender) { sender.replaceTrack(null).catch(function(){}); }
55
- });
56
- var local = nostrVoice._participants.get('local');
57
- if (local) local.hasVideo = false;
58
- nostrVoice.updateParticipants();
59
- }
60
- };
61
-
62
- window.__zellous.voiceCamera = nostrVoiceCamera;
63
- window.nostrVoiceCamera = nostrVoiceCamera;