cyclecad 0.2.1 → 0.2.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.
- package/API-BUILD-MANIFEST.txt +339 -0
- package/API-SERVER.md +535 -0
- package/Architecture-Deck.pptx +0 -0
- package/CLAUDE.md +186 -15
- package/CLI-BUILD-SUMMARY.md +504 -0
- package/CLI-INDEX.md +356 -0
- package/CLI-README.md +466 -0
- package/COLLABORATION-INTEGRATION-GUIDE.md +325 -0
- package/CONNECTED_FABS_GUIDE.md +612 -0
- package/CONNECTED_FABS_README.md +310 -0
- package/DELIVERABLES.md +343 -0
- package/DFM-ANALYZER-INTEGRATION.md +368 -0
- package/DFM-QUICK-START.js +253 -0
- package/Dockerfile +69 -0
- package/IMPLEMENTATION.md +327 -0
- package/LICENSE +31 -0
- package/MARKETPLACE_QUICK_REFERENCE.txt +294 -0
- package/MCP-INDEX.md +264 -0
- package/QUICKSTART-API.md +388 -0
- package/QUICKSTART-CLI.md +211 -0
- package/QUICKSTART-MCP.md +196 -0
- package/README-MCP.md +208 -0
- package/TEST-TOKEN-ENGINE.md +319 -0
- package/TOKEN-ENGINE-SUMMARY.md +266 -0
- package/TOKENS-README.md +263 -0
- package/TOOLS-REFERENCE.md +254 -0
- package/app/index.html +168 -3
- package/app/js/TOKEN-INTEGRATION.md +391 -0
- package/app/js/agent-api.js +3 -3
- package/app/js/ai-copilot.js +1435 -0
- package/app/js/cam-pipeline.js +840 -0
- package/app/js/collaboration-ui.js +995 -0
- package/app/js/collaboration.js +1116 -0
- package/app/js/connected-fabs-example.js +404 -0
- package/app/js/connected-fabs.js +1449 -0
- package/app/js/dfm-analyzer.js +1760 -0
- package/app/js/marketplace.js +1994 -0
- package/app/js/material-library.js +2115 -0
- package/app/js/token-dashboard.js +563 -0
- package/app/js/token-engine.js +743 -0
- package/app/test-agent.html +1801 -0
- package/bin/cyclecad-cli.js +662 -0
- package/bin/cyclecad-mcp +2 -0
- package/bin/server.js +242 -0
- package/cycleCAD-Architecture.pptx +0 -0
- package/cycleCAD-Investor-Deck.pptx +0 -0
- package/demo-mcp.sh +60 -0
- package/docs/API-SERVER-SUMMARY.md +375 -0
- package/docs/API-SERVER.md +667 -0
- package/docs/CAM-EXAMPLES.md +344 -0
- package/docs/CAM-INTEGRATION.md +612 -0
- package/docs/CAM-QUICK-REFERENCE.md +199 -0
- package/docs/CLI-INTEGRATION.md +510 -0
- package/docs/CLI.md +872 -0
- package/docs/MARKETPLACE-API-SCHEMA.json +564 -0
- package/docs/MARKETPLACE-INTEGRATION.md +467 -0
- package/docs/MARKETPLACE-SETUP.html +439 -0
- package/docs/MCP-SERVER.md +403 -0
- package/examples/api-client-example.js +488 -0
- package/examples/api-client-example.py +359 -0
- package/examples/batch-manufacturing.txt +28 -0
- package/examples/batch-simple.txt +26 -0
- package/index.html +56 -0
- package/model-marketplace.html +1273 -0
- package/package.json +14 -3
- package/server/api-server.js +1120 -0
- package/server/mcp-server.js +1161 -0
- package/test-api-server.js +432 -0
- package/test-mcp.js +198 -0
- package/~$cycleCAD-Investor-Deck.pptx +0 -0
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* collaboration-ui.js — UI Panel for Collaboration Features
|
|
3
|
+
*
|
|
4
|
+
* Provides a comprehensive right-panel UI for:
|
|
5
|
+
* - Session management and sharing
|
|
6
|
+
* - Participant list with presence indicators
|
|
7
|
+
* - Chat interface with message history
|
|
8
|
+
* - Version timeline and snapshot management
|
|
9
|
+
* - Permissions dialog
|
|
10
|
+
*
|
|
11
|
+
* Integrates with collaboration.js via window.cycleCAD.collab
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
let _collabModule = null;
|
|
15
|
+
let _panelOpen = false;
|
|
16
|
+
|
|
17
|
+
export function initCollaborationUI(collabModule) {
|
|
18
|
+
_collabModule = collabModule;
|
|
19
|
+
|
|
20
|
+
// Listen for collaboration events
|
|
21
|
+
collabModule.on('session-created', onSessionCreated);
|
|
22
|
+
collabModule.on('session-joined', onSessionJoined);
|
|
23
|
+
collabModule.on('user-joined', onUserJoined);
|
|
24
|
+
collabModule.on('message-sent', onMessageReceived);
|
|
25
|
+
collabModule.on('snapshot-saved', onSnapshotSaved);
|
|
26
|
+
|
|
27
|
+
// Create panel HTML
|
|
28
|
+
createCollaborationPanel();
|
|
29
|
+
|
|
30
|
+
// Wire button to panel toggle
|
|
31
|
+
const collabBtn = document.getElementById('collab-btn');
|
|
32
|
+
if (collabBtn) {
|
|
33
|
+
collabBtn.addEventListener('click', toggleCollaborationPanel);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('[Collab UI] Initialized');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create the collaboration panel HTML
|
|
41
|
+
*/
|
|
42
|
+
function createCollaborationPanel() {
|
|
43
|
+
const panelHtml = `
|
|
44
|
+
<div id="collaboration-panel" class="collab-panel" style="display: none;">
|
|
45
|
+
<!-- Header -->
|
|
46
|
+
<div class="collab-header">
|
|
47
|
+
<h3>Collaboration</h3>
|
|
48
|
+
<button id="collab-close" class="collab-close-btn" title="Close">✕</button>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Tabs -->
|
|
52
|
+
<div class="collab-tabs">
|
|
53
|
+
<button class="collab-tab active" data-tab="session">Session</button>
|
|
54
|
+
<button class="collab-tab" data-tab="participants">Participants</button>
|
|
55
|
+
<button class="collab-tab" data-tab="chat">Chat</button>
|
|
56
|
+
<button class="collab-tab" data-tab="versions">Versions</button>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Tab Content -->
|
|
60
|
+
<div class="collab-content">
|
|
61
|
+
<!-- Session Tab -->
|
|
62
|
+
<div id="session-tab" class="collab-tab-content active">
|
|
63
|
+
<div class="session-controls">
|
|
64
|
+
<button id="create-session-btn" class="collab-btn primary">Create Session</button>
|
|
65
|
+
<button id="join-session-btn" class="collab-btn">Join Session</button>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div id="session-info" style="display: none;">
|
|
69
|
+
<div class="session-info-card">
|
|
70
|
+
<label>Session ID</label>
|
|
71
|
+
<div class="session-id-display">
|
|
72
|
+
<code id="session-id-code"></code>
|
|
73
|
+
<button id="copy-session-btn" class="copy-btn" title="Copy">📋</button>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<label style="margin-top: 12px;">Share Link</label>
|
|
77
|
+
<div class="share-controls">
|
|
78
|
+
<button id="generate-link-btn" class="collab-btn small">Generate Link</button>
|
|
79
|
+
<button id="share-readonly-btn" class="collab-btn small">Read-Only Link</button>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div id="share-link-display" style="display: none; margin-top: 12px;">
|
|
83
|
+
<input type="text" id="share-link-input" class="share-link-input" readonly>
|
|
84
|
+
<button id="copy-link-btn" class="copy-btn">📋</button>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<label style="margin-top: 12px;">Embed Code</label>
|
|
88
|
+
<button id="generate-embed-btn" class="collab-btn small">Generate Embed</button>
|
|
89
|
+
|
|
90
|
+
<div id="embed-display" style="display: none; margin-top: 12px;">
|
|
91
|
+
<textarea id="embed-code" class="embed-code" readonly></textarea>
|
|
92
|
+
<button id="copy-embed-btn" class="copy-btn">📋</button>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-color);">
|
|
96
|
+
<label>Participants: <span id="participant-count">1</span></label>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<button id="leave-session-btn" class="collab-btn danger" style="margin-top: 12px; width: 100%;">Leave Session</button>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<!-- Participants Tab -->
|
|
105
|
+
<div id="participants-tab" class="collab-tab-content">
|
|
106
|
+
<div id="participants-list" class="participants-list">
|
|
107
|
+
<!-- Populated dynamically -->
|
|
108
|
+
</div>
|
|
109
|
+
<div style="margin-top: 12px;">
|
|
110
|
+
<button id="start-agent-demo-btn" class="collab-btn small">Start Agent Demo</button>
|
|
111
|
+
<button id="stop-agent-demo-btn" class="collab-btn small danger" style="display: none;">Stop Agents</button>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- Chat Tab -->
|
|
116
|
+
<div id="chat-tab" class="collab-tab-content">
|
|
117
|
+
<div id="chat-messages" class="chat-messages">
|
|
118
|
+
<!-- Messages populated dynamically -->
|
|
119
|
+
</div>
|
|
120
|
+
<div class="chat-input-area">
|
|
121
|
+
<input type="text" id="chat-input" class="chat-input" placeholder="Send message...">
|
|
122
|
+
<button id="send-msg-btn" class="collab-btn small">Send</button>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- Versions Tab -->
|
|
127
|
+
<div id="versions-tab" class="collab-tab-content">
|
|
128
|
+
<div class="version-controls">
|
|
129
|
+
<input type="text" id="snapshot-name-input" class="snapshot-input" placeholder="Snapshot name...">
|
|
130
|
+
<button id="save-snapshot-btn" class="collab-btn small">Save</button>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div id="snapshots-list" class="snapshots-list">
|
|
134
|
+
<!-- Snapshots populated dynamically -->
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
// Create container if it doesn't exist
|
|
142
|
+
let container = document.getElementById('collab-panel-container');
|
|
143
|
+
if (!container) {
|
|
144
|
+
container = document.createElement('div');
|
|
145
|
+
container.id = 'collab-panel-container';
|
|
146
|
+
document.body.appendChild(container);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
container.innerHTML = panelHtml;
|
|
150
|
+
|
|
151
|
+
// Wire up event listeners
|
|
152
|
+
wireUpEventListeners();
|
|
153
|
+
|
|
154
|
+
// Add styles if not already present
|
|
155
|
+
if (!document.getElementById('collab-styles')) {
|
|
156
|
+
addCollaborationStyles();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Wire up all event listeners for the panel
|
|
162
|
+
*/
|
|
163
|
+
function wireUpEventListeners() {
|
|
164
|
+
// Close button
|
|
165
|
+
document.getElementById('collab-close').addEventListener('click', toggleCollaborationPanel);
|
|
166
|
+
|
|
167
|
+
// Tab switching
|
|
168
|
+
document.querySelectorAll('.collab-tab').forEach(tab => {
|
|
169
|
+
tab.addEventListener('click', (e) => {
|
|
170
|
+
const tabName = e.target.dataset.tab;
|
|
171
|
+
switchTab(tabName);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Session controls
|
|
176
|
+
document.getElementById('create-session-btn').addEventListener('click', createNewSession);
|
|
177
|
+
document.getElementById('join-session-btn').addEventListener('click', joinExistingSession);
|
|
178
|
+
document.getElementById('leave-session-btn').addEventListener('click', leaveCurrentSession);
|
|
179
|
+
|
|
180
|
+
// Share controls
|
|
181
|
+
document.getElementById('generate-link-btn').addEventListener('click', generateShareLink);
|
|
182
|
+
document.getElementById('share-readonly-btn').addEventListener('click', () => generateShareLink(true));
|
|
183
|
+
document.getElementById('copy-link-btn').addEventListener('click', copyShareLink);
|
|
184
|
+
document.getElementById('copy-session-btn').addEventListener('click', copySessionId);
|
|
185
|
+
|
|
186
|
+
// Embed
|
|
187
|
+
document.getElementById('generate-embed-btn').addEventListener('click', generateEmbedCode);
|
|
188
|
+
document.getElementById('copy-embed-btn').addEventListener('click', copyEmbedCode);
|
|
189
|
+
|
|
190
|
+
// Chat
|
|
191
|
+
document.getElementById('send-msg-btn').addEventListener('click', sendChatMessage);
|
|
192
|
+
document.getElementById('chat-input').addEventListener('keypress', (e) => {
|
|
193
|
+
if (e.key === 'Enter') sendChatMessage();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Snapshots
|
|
197
|
+
document.getElementById('save-snapshot-btn').addEventListener('click', saveNewSnapshot);
|
|
198
|
+
|
|
199
|
+
// Agent demo
|
|
200
|
+
document.getElementById('start-agent-demo-btn').addEventListener('click', startAgentDemo);
|
|
201
|
+
document.getElementById('stop-agent-demo-btn').addEventListener('click', stopAgentDemo);
|
|
202
|
+
|
|
203
|
+
// Update UI
|
|
204
|
+
updateSessionDisplay();
|
|
205
|
+
updateParticipantsList();
|
|
206
|
+
updateChatDisplay();
|
|
207
|
+
updateSnapshotsList();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Toggle collaboration panel visibility
|
|
212
|
+
*/
|
|
213
|
+
function toggleCollaborationPanel() {
|
|
214
|
+
_panelOpen = !_panelOpen;
|
|
215
|
+
const panel = document.getElementById('collaboration-panel');
|
|
216
|
+
panel.style.display = _panelOpen ? 'flex' : 'none';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Switch between tabs
|
|
221
|
+
*/
|
|
222
|
+
function switchTab(tabName) {
|
|
223
|
+
// Deactivate all tabs
|
|
224
|
+
document.querySelectorAll('.collab-tab').forEach(tab => {
|
|
225
|
+
tab.classList.remove('active');
|
|
226
|
+
});
|
|
227
|
+
document.querySelectorAll('.collab-tab-content').forEach(content => {
|
|
228
|
+
content.classList.remove('active');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Activate selected tab
|
|
232
|
+
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
|
233
|
+
document.getElementById(`${tabName}-tab`).classList.add('active');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create a new collaboration session
|
|
238
|
+
*/
|
|
239
|
+
function createNewSession() {
|
|
240
|
+
if (!_collabModule) return;
|
|
241
|
+
|
|
242
|
+
const session = _collabModule.createSession({
|
|
243
|
+
maxUsers: 10,
|
|
244
|
+
readOnly: false
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
updateSessionDisplay();
|
|
248
|
+
switchTab('session');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Join an existing session
|
|
253
|
+
*/
|
|
254
|
+
function joinExistingSession() {
|
|
255
|
+
const sessionId = prompt('Enter Session ID:');
|
|
256
|
+
if (!sessionId) return;
|
|
257
|
+
|
|
258
|
+
if (!_collabModule) return;
|
|
259
|
+
|
|
260
|
+
const name = prompt('Enter your name:', 'User');
|
|
261
|
+
const session = _collabModule.joinSession(sessionId, { name });
|
|
262
|
+
|
|
263
|
+
updateSessionDisplay();
|
|
264
|
+
switchTab('session');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Leave current session
|
|
269
|
+
*/
|
|
270
|
+
function leaveCurrentSession() {
|
|
271
|
+
if (confirm('Leave session?')) {
|
|
272
|
+
if (_collabModule) {
|
|
273
|
+
_collabModule.leaveSession();
|
|
274
|
+
}
|
|
275
|
+
updateSessionDisplay();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Update session display
|
|
281
|
+
*/
|
|
282
|
+
function updateSessionDisplay() {
|
|
283
|
+
if (!_collabModule) return;
|
|
284
|
+
|
|
285
|
+
const session = _collabModule.getSession();
|
|
286
|
+
const infoDiv = document.getElementById('session-info');
|
|
287
|
+
const controlsDiv = document.querySelector('.session-controls');
|
|
288
|
+
|
|
289
|
+
if (session) {
|
|
290
|
+
infoDiv.style.display = 'block';
|
|
291
|
+
controlsDiv.style.display = 'none';
|
|
292
|
+
|
|
293
|
+
document.getElementById('session-id-code').textContent = session.sessionId;
|
|
294
|
+
document.getElementById('participant-count').textContent = _collabModule.listParticipants().length;
|
|
295
|
+
} else {
|
|
296
|
+
infoDiv.style.display = 'none';
|
|
297
|
+
controlsDiv.style.display = 'block';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Generate share link
|
|
303
|
+
*/
|
|
304
|
+
function generateShareLink(readOnly = false) {
|
|
305
|
+
if (!_collabModule) return;
|
|
306
|
+
|
|
307
|
+
const link = _collabModule.generateShareLink({
|
|
308
|
+
readOnly,
|
|
309
|
+
expiry: '24h'
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (link) {
|
|
313
|
+
const input = document.getElementById('share-link-input');
|
|
314
|
+
input.value = link.url;
|
|
315
|
+
document.getElementById('share-link-display').style.display = 'block';
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Copy share link to clipboard
|
|
321
|
+
*/
|
|
322
|
+
function copyShareLink() {
|
|
323
|
+
const input = document.getElementById('share-link-input');
|
|
324
|
+
input.select();
|
|
325
|
+
document.execCommand('copy');
|
|
326
|
+
alert('Link copied to clipboard!');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Copy session ID to clipboard
|
|
331
|
+
*/
|
|
332
|
+
function copySessionId() {
|
|
333
|
+
const code = document.getElementById('session-id-code');
|
|
334
|
+
const text = code.textContent;
|
|
335
|
+
const textarea = document.createElement('textarea');
|
|
336
|
+
textarea.value = text;
|
|
337
|
+
document.body.appendChild(textarea);
|
|
338
|
+
textarea.select();
|
|
339
|
+
document.execCommand('copy');
|
|
340
|
+
document.body.removeChild(textarea);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Generate embed code
|
|
345
|
+
*/
|
|
346
|
+
function generateEmbedCode() {
|
|
347
|
+
if (!_collabModule) return;
|
|
348
|
+
|
|
349
|
+
const embed = _collabModule.generateEmbedCode({
|
|
350
|
+
width: 800,
|
|
351
|
+
height: 600,
|
|
352
|
+
showToolbar: true,
|
|
353
|
+
showTree: true
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (embed) {
|
|
357
|
+
document.getElementById('embed-code').value = embed.html;
|
|
358
|
+
document.getElementById('embed-display').style.display = 'block';
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Copy embed code to clipboard
|
|
364
|
+
*/
|
|
365
|
+
function copyEmbedCode() {
|
|
366
|
+
const textarea = document.getElementById('embed-code');
|
|
367
|
+
textarea.select();
|
|
368
|
+
document.execCommand('copy');
|
|
369
|
+
alert('Embed code copied to clipboard!');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Update participants list display
|
|
374
|
+
*/
|
|
375
|
+
function updateParticipantsList() {
|
|
376
|
+
if (!_collabModule) return;
|
|
377
|
+
|
|
378
|
+
const list = document.getElementById('participants-list');
|
|
379
|
+
const participants = _collabModule.listParticipants();
|
|
380
|
+
|
|
381
|
+
list.innerHTML = participants.map(p => `
|
|
382
|
+
<div class="participant-item">
|
|
383
|
+
<div class="participant-avatar" style="background-color: ${p.avatar};" title="${p.name}"></div>
|
|
384
|
+
<div class="participant-info">
|
|
385
|
+
<div class="participant-name">${p.name} ${p.isLocalUser ? '(You)' : ''}</div>
|
|
386
|
+
<div class="participant-status">${p.role} • ${p.status}</div>
|
|
387
|
+
</div>
|
|
388
|
+
${!p.isLocalUser ? `<button class="participant-action-btn" data-user-id="${p.userId}">⋮</button>` : ''}
|
|
389
|
+
</div>
|
|
390
|
+
`).join('');
|
|
391
|
+
|
|
392
|
+
// Wire participant action buttons
|
|
393
|
+
document.querySelectorAll('.participant-action-btn').forEach(btn => {
|
|
394
|
+
btn.addEventListener('click', (e) => {
|
|
395
|
+
const userId = e.target.dataset.userId;
|
|
396
|
+
showParticipantMenu(userId);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Show participant context menu
|
|
403
|
+
*/
|
|
404
|
+
function showParticipantMenu(userId) {
|
|
405
|
+
const participant = _collabModule.listParticipants().find(p => p.userId === userId);
|
|
406
|
+
if (!participant) return;
|
|
407
|
+
|
|
408
|
+
const menu = prompt(`Menu for ${participant.name}:\n\n1. View Profile\n2. Change Role\n3. Kick User\n\nEnter option number:`, '1');
|
|
409
|
+
|
|
410
|
+
if (menu === '2') {
|
|
411
|
+
const newRole = prompt('New role (host/editor/viewer):', participant.role);
|
|
412
|
+
if (newRole && _collabModule.setRole) {
|
|
413
|
+
_collabModule.setRole(userId, newRole);
|
|
414
|
+
updateParticipantsList();
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Send chat message
|
|
421
|
+
*/
|
|
422
|
+
function sendChatMessage() {
|
|
423
|
+
const input = document.getElementById('chat-input');
|
|
424
|
+
const text = input.value.trim();
|
|
425
|
+
|
|
426
|
+
if (!text || !_collabModule) return;
|
|
427
|
+
|
|
428
|
+
_collabModule.sendMessage(text);
|
|
429
|
+
input.value = '';
|
|
430
|
+
|
|
431
|
+
updateChatDisplay();
|
|
432
|
+
|
|
433
|
+
// Scroll to bottom
|
|
434
|
+
setTimeout(() => {
|
|
435
|
+
const chatDiv = document.getElementById('chat-messages');
|
|
436
|
+
chatDiv.scrollTop = chatDiv.scrollHeight;
|
|
437
|
+
}, 100);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Update chat display
|
|
442
|
+
*/
|
|
443
|
+
function updateChatDisplay() {
|
|
444
|
+
if (!_collabModule) return;
|
|
445
|
+
|
|
446
|
+
const messages = _collabModule.getMessageHistory();
|
|
447
|
+
const chatDiv = document.getElementById('chat-messages');
|
|
448
|
+
|
|
449
|
+
chatDiv.innerHTML = messages.map(msg => `
|
|
450
|
+
<div class="chat-message">
|
|
451
|
+
<div class="chat-message-header">
|
|
452
|
+
<span class="chat-user-name" style="color: ${msg.userColor};">${msg.userName}</span>
|
|
453
|
+
<span class="chat-timestamp">${new Date(msg.timestamp).toLocaleTimeString()}</span>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="chat-message-text">${escapeHtml(msg.text)}</div>
|
|
456
|
+
</div>
|
|
457
|
+
`).join('');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Save new snapshot
|
|
462
|
+
*/
|
|
463
|
+
function saveNewSnapshot() {
|
|
464
|
+
const input = document.getElementById('snapshot-name-input');
|
|
465
|
+
const name = input.value.trim();
|
|
466
|
+
|
|
467
|
+
if (!name || !_collabModule) return;
|
|
468
|
+
|
|
469
|
+
_collabModule.saveSnapshot(name);
|
|
470
|
+
input.value = '';
|
|
471
|
+
|
|
472
|
+
updateSnapshotsList();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Update snapshots list display
|
|
477
|
+
*/
|
|
478
|
+
function updateSnapshotsList() {
|
|
479
|
+
if (!_collabModule) return;
|
|
480
|
+
|
|
481
|
+
const snapshots = _collabModule.listSnapshots();
|
|
482
|
+
const list = document.getElementById('snapshots-list');
|
|
483
|
+
|
|
484
|
+
if (snapshots.length === 0) {
|
|
485
|
+
list.innerHTML = '<p style="text-align: center; color: var(--text-secondary);">No snapshots yet</p>';
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
list.innerHTML = snapshots.map(snap => `
|
|
490
|
+
<div class="snapshot-item">
|
|
491
|
+
<div class="snapshot-info">
|
|
492
|
+
<div class="snapshot-name">${snap.name}</div>
|
|
493
|
+
<div class="snapshot-meta">${snap.userName} • ${snap.featureCount} features • ${snap.formattedTime}</div>
|
|
494
|
+
</div>
|
|
495
|
+
<button class="snapshot-action-btn" data-snap-id="${snap.snapshotId}">⋮</button>
|
|
496
|
+
</div>
|
|
497
|
+
`).join('');
|
|
498
|
+
|
|
499
|
+
// Wire snapshot action buttons
|
|
500
|
+
document.querySelectorAll('.snapshot-action-btn').forEach(btn => {
|
|
501
|
+
btn.addEventListener('click', (e) => {
|
|
502
|
+
const snapId = e.target.dataset.snapId;
|
|
503
|
+
showSnapshotMenu(snapId);
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Show snapshot context menu
|
|
510
|
+
*/
|
|
511
|
+
function showSnapshotMenu(snapId) {
|
|
512
|
+
const snapshots = _collabModule.listSnapshots();
|
|
513
|
+
const snapshot = snapshots.find(s => s.snapshotId === snapId);
|
|
514
|
+
if (!snapshot) return;
|
|
515
|
+
|
|
516
|
+
const menu = prompt(`Menu for "${snapshot.name}":\n\n1. Load\n2. Duplicate\n3. Delete\n\nEnter option number:`, '1');
|
|
517
|
+
|
|
518
|
+
if (menu === '1') {
|
|
519
|
+
_collabModule.loadSnapshot(snapId);
|
|
520
|
+
alert(`Loaded snapshot: ${snapshot.name}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Start agent demo
|
|
526
|
+
*/
|
|
527
|
+
function startAgentDemo() {
|
|
528
|
+
if (_collabModule && _collabModule.startAgentDemo) {
|
|
529
|
+
_collabModule.startAgentDemo();
|
|
530
|
+
|
|
531
|
+
document.getElementById('start-agent-demo-btn').style.display = 'none';
|
|
532
|
+
document.getElementById('stop-agent-demo-btn').style.display = 'inline-block';
|
|
533
|
+
|
|
534
|
+
updateParticipantsList();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Stop agent demo
|
|
540
|
+
*/
|
|
541
|
+
function stopAgentDemo() {
|
|
542
|
+
if (_collabModule && _collabModule.stopAgentDemo) {
|
|
543
|
+
_collabModule.stopAgentDemo();
|
|
544
|
+
|
|
545
|
+
document.getElementById('start-agent-demo-btn').style.display = 'inline-block';
|
|
546
|
+
document.getElementById('stop-agent-demo-btn').style.display = 'none';
|
|
547
|
+
|
|
548
|
+
updateParticipantsList();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Event callbacks
|
|
554
|
+
*/
|
|
555
|
+
function onSessionCreated(session) {
|
|
556
|
+
console.log('[Collab UI] Session created');
|
|
557
|
+
updateSessionDisplay();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function onSessionJoined(session) {
|
|
561
|
+
console.log('[Collab UI] Session joined');
|
|
562
|
+
updateSessionDisplay();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function onUserJoined(participant) {
|
|
566
|
+
console.log('[Collab UI] User joined:', participant.name);
|
|
567
|
+
updateParticipantsList();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function onMessageReceived(message) {
|
|
571
|
+
updateChatDisplay();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function onSnapshotSaved(snapshot) {
|
|
575
|
+
console.log('[Collab UI] Snapshot saved:', snapshot.name);
|
|
576
|
+
updateSnapshotsList();
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Utility: escape HTML
|
|
581
|
+
*/
|
|
582
|
+
function escapeHtml(text) {
|
|
583
|
+
const div = document.createElement('div');
|
|
584
|
+
div.textContent = text;
|
|
585
|
+
return div.innerHTML;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Add CSS styles for collaboration panel
|
|
590
|
+
*/
|
|
591
|
+
function addCollaborationStyles() {
|
|
592
|
+
const style = document.createElement('style');
|
|
593
|
+
style.id = 'collab-styles';
|
|
594
|
+
style.textContent = `
|
|
595
|
+
.collab-panel {
|
|
596
|
+
display: flex;
|
|
597
|
+
flex-direction: column;
|
|
598
|
+
position: fixed;
|
|
599
|
+
right: 0;
|
|
600
|
+
top: var(--toolbar-height);
|
|
601
|
+
width: 380px;
|
|
602
|
+
height: calc(100vh - var(--toolbar-height) - var(--statusbar-height));
|
|
603
|
+
background: var(--bg-secondary);
|
|
604
|
+
border-left: 1px solid var(--border-color);
|
|
605
|
+
z-index: 100;
|
|
606
|
+
box-shadow: var(--shadow-lg);
|
|
607
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.collab-header {
|
|
611
|
+
display: flex;
|
|
612
|
+
justify-content: space-between;
|
|
613
|
+
align-items: center;
|
|
614
|
+
padding: 12px 16px;
|
|
615
|
+
border-bottom: 1px solid var(--border-color);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.collab-header h3 {
|
|
619
|
+
margin: 0;
|
|
620
|
+
font-size: 14px;
|
|
621
|
+
font-weight: 600;
|
|
622
|
+
color: var(--text-primary);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.collab-close-btn {
|
|
626
|
+
background: none;
|
|
627
|
+
border: none;
|
|
628
|
+
color: var(--text-secondary);
|
|
629
|
+
cursor: pointer;
|
|
630
|
+
font-size: 16px;
|
|
631
|
+
padding: 0;
|
|
632
|
+
width: 24px;
|
|
633
|
+
height: 24px;
|
|
634
|
+
display: flex;
|
|
635
|
+
align-items: center;
|
|
636
|
+
justify-content: center;
|
|
637
|
+
border-radius: 4px;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.collab-close-btn:hover {
|
|
641
|
+
background: var(--bg-tertiary);
|
|
642
|
+
color: var(--text-primary);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.collab-tabs {
|
|
646
|
+
display: flex;
|
|
647
|
+
border-bottom: 1px solid var(--border-color);
|
|
648
|
+
gap: 0;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.collab-tab {
|
|
652
|
+
flex: 1;
|
|
653
|
+
padding: 10px 8px;
|
|
654
|
+
background: none;
|
|
655
|
+
border: none;
|
|
656
|
+
color: var(--text-secondary);
|
|
657
|
+
cursor: pointer;
|
|
658
|
+
font-size: 12px;
|
|
659
|
+
font-weight: 500;
|
|
660
|
+
border-bottom: 2px solid transparent;
|
|
661
|
+
transition: all var(--transition-fast);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.collab-tab:hover {
|
|
665
|
+
color: var(--text-primary);
|
|
666
|
+
background: var(--bg-tertiary);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.collab-tab.active {
|
|
670
|
+
color: var(--accent-blue);
|
|
671
|
+
border-bottom-color: var(--accent-blue);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.collab-content {
|
|
675
|
+
flex: 1;
|
|
676
|
+
overflow-y: auto;
|
|
677
|
+
padding: 12px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.collab-tab-content {
|
|
681
|
+
display: none;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.collab-tab-content.active {
|
|
685
|
+
display: block;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.collab-btn {
|
|
689
|
+
background: var(--accent-blue);
|
|
690
|
+
color: white;
|
|
691
|
+
border: none;
|
|
692
|
+
padding: 8px 12px;
|
|
693
|
+
border-radius: 4px;
|
|
694
|
+
cursor: pointer;
|
|
695
|
+
font-size: 12px;
|
|
696
|
+
font-weight: 500;
|
|
697
|
+
transition: all var(--transition-fast);
|
|
698
|
+
width: 100%;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.collab-btn:hover {
|
|
702
|
+
background: var(--accent-blue-dark);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.collab-btn.small {
|
|
706
|
+
padding: 6px 10px;
|
|
707
|
+
font-size: 11px;
|
|
708
|
+
width: auto;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.collab-btn.danger {
|
|
712
|
+
background: var(--accent-red);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.collab-btn.danger:hover {
|
|
716
|
+
background: #e63946;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.collab-btn.primary {
|
|
720
|
+
background: var(--accent-green);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.collab-btn.primary:hover {
|
|
724
|
+
background: #2d8659;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.session-controls {
|
|
728
|
+
display: flex;
|
|
729
|
+
flex-direction: column;
|
|
730
|
+
gap: 8px;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.session-info-card {
|
|
734
|
+
background: var(--bg-tertiary);
|
|
735
|
+
padding: 12px;
|
|
736
|
+
border-radius: 6px;
|
|
737
|
+
border: 1px solid var(--border-color);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.session-info-card label {
|
|
741
|
+
display: block;
|
|
742
|
+
font-size: 11px;
|
|
743
|
+
font-weight: 600;
|
|
744
|
+
color: var(--text-secondary);
|
|
745
|
+
text-transform: uppercase;
|
|
746
|
+
margin-bottom: 6px;
|
|
747
|
+
letter-spacing: 0.5px;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.session-id-display {
|
|
751
|
+
display: flex;
|
|
752
|
+
gap: 6px;
|
|
753
|
+
align-items: center;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.session-id-display code {
|
|
757
|
+
flex: 1;
|
|
758
|
+
background: var(--bg-primary);
|
|
759
|
+
padding: 6px 8px;
|
|
760
|
+
border-radius: 4px;
|
|
761
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
762
|
+
font-size: 11px;
|
|
763
|
+
color: var(--accent-blue);
|
|
764
|
+
word-break: break-all;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.copy-btn {
|
|
768
|
+
background: var(--bg-primary);
|
|
769
|
+
border: 1px solid var(--border-color);
|
|
770
|
+
color: var(--text-secondary);
|
|
771
|
+
padding: 4px 8px;
|
|
772
|
+
border-radius: 3px;
|
|
773
|
+
cursor: pointer;
|
|
774
|
+
font-size: 14px;
|
|
775
|
+
transition: all var(--transition-fast);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.copy-btn:hover {
|
|
779
|
+
color: var(--text-primary);
|
|
780
|
+
border-color: var(--accent-blue);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.share-controls {
|
|
784
|
+
display: flex;
|
|
785
|
+
gap: 6px;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.share-link-input,
|
|
789
|
+
.embed-code {
|
|
790
|
+
width: 100%;
|
|
791
|
+
padding: 8px;
|
|
792
|
+
background: var(--bg-primary);
|
|
793
|
+
border: 1px solid var(--border-color);
|
|
794
|
+
border-radius: 4px;
|
|
795
|
+
color: var(--text-primary);
|
|
796
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
797
|
+
font-size: 11px;
|
|
798
|
+
resize: none;
|
|
799
|
+
margin-bottom: 6px;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
.embed-code {
|
|
803
|
+
height: 100px;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
.participants-list {
|
|
807
|
+
display: flex;
|
|
808
|
+
flex-direction: column;
|
|
809
|
+
gap: 8px;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.participant-item {
|
|
813
|
+
display: flex;
|
|
814
|
+
align-items: center;
|
|
815
|
+
gap: 8px;
|
|
816
|
+
padding: 8px;
|
|
817
|
+
background: var(--bg-tertiary);
|
|
818
|
+
border-radius: 4px;
|
|
819
|
+
border: 1px solid var(--border-color);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.participant-avatar {
|
|
823
|
+
width: 32px;
|
|
824
|
+
height: 32px;
|
|
825
|
+
border-radius: 50%;
|
|
826
|
+
flex-shrink: 0;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.participant-info {
|
|
830
|
+
flex: 1;
|
|
831
|
+
min-width: 0;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.participant-name {
|
|
835
|
+
font-size: 12px;
|
|
836
|
+
font-weight: 500;
|
|
837
|
+
color: var(--text-primary);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.participant-status {
|
|
841
|
+
font-size: 11px;
|
|
842
|
+
color: var(--text-secondary);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.participant-action-btn {
|
|
846
|
+
background: none;
|
|
847
|
+
border: none;
|
|
848
|
+
color: var(--text-secondary);
|
|
849
|
+
cursor: pointer;
|
|
850
|
+
font-size: 14px;
|
|
851
|
+
padding: 4px;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.participant-action-btn:hover {
|
|
855
|
+
color: var(--text-primary);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.chat-messages {
|
|
859
|
+
display: flex;
|
|
860
|
+
flex-direction: column;
|
|
861
|
+
gap: 8px;
|
|
862
|
+
height: 300px;
|
|
863
|
+
overflow-y: auto;
|
|
864
|
+
margin-bottom: 12px;
|
|
865
|
+
padding: 8px;
|
|
866
|
+
background: var(--bg-tertiary);
|
|
867
|
+
border-radius: 4px;
|
|
868
|
+
border: 1px solid var(--border-color);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.chat-message {
|
|
872
|
+
background: var(--bg-primary);
|
|
873
|
+
padding: 8px;
|
|
874
|
+
border-radius: 4px;
|
|
875
|
+
border-left: 3px solid var(--accent-blue);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
.chat-message-header {
|
|
879
|
+
display: flex;
|
|
880
|
+
justify-content: space-between;
|
|
881
|
+
align-items: baseline;
|
|
882
|
+
margin-bottom: 4px;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
.chat-user-name {
|
|
886
|
+
font-size: 11px;
|
|
887
|
+
font-weight: 600;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.chat-timestamp {
|
|
891
|
+
font-size: 10px;
|
|
892
|
+
color: var(--text-secondary);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.chat-message-text {
|
|
896
|
+
font-size: 12px;
|
|
897
|
+
color: var(--text-primary);
|
|
898
|
+
line-height: 1.4;
|
|
899
|
+
word-break: break-word;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.chat-input-area {
|
|
903
|
+
display: flex;
|
|
904
|
+
gap: 6px;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.chat-input {
|
|
908
|
+
flex: 1;
|
|
909
|
+
padding: 8px;
|
|
910
|
+
background: var(--bg-tertiary);
|
|
911
|
+
border: 1px solid var(--border-color);
|
|
912
|
+
border-radius: 4px;
|
|
913
|
+
color: var(--text-primary);
|
|
914
|
+
font-size: 12px;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.chat-input::placeholder {
|
|
918
|
+
color: var(--text-secondary);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.chat-input:focus {
|
|
922
|
+
outline: none;
|
|
923
|
+
border-color: var(--accent-blue);
|
|
924
|
+
box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.1);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.version-controls {
|
|
928
|
+
display: flex;
|
|
929
|
+
gap: 6px;
|
|
930
|
+
margin-bottom: 12px;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
.snapshot-input {
|
|
934
|
+
flex: 1;
|
|
935
|
+
padding: 8px;
|
|
936
|
+
background: var(--bg-tertiary);
|
|
937
|
+
border: 1px solid var(--border-color);
|
|
938
|
+
border-radius: 4px;
|
|
939
|
+
color: var(--text-primary);
|
|
940
|
+
font-size: 12px;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.snapshot-input::placeholder {
|
|
944
|
+
color: var(--text-secondary);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.snapshots-list {
|
|
948
|
+
display: flex;
|
|
949
|
+
flex-direction: column;
|
|
950
|
+
gap: 6px;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
.snapshot-item {
|
|
954
|
+
display: flex;
|
|
955
|
+
justify-content: space-between;
|
|
956
|
+
align-items: center;
|
|
957
|
+
padding: 8px;
|
|
958
|
+
background: var(--bg-tertiary);
|
|
959
|
+
border-radius: 4px;
|
|
960
|
+
border: 1px solid var(--border-color);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.snapshot-info {
|
|
964
|
+
flex: 1;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.snapshot-name {
|
|
968
|
+
font-size: 12px;
|
|
969
|
+
font-weight: 500;
|
|
970
|
+
color: var(--text-primary);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.snapshot-meta {
|
|
974
|
+
font-size: 11px;
|
|
975
|
+
color: var(--text-secondary);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.snapshot-action-btn {
|
|
979
|
+
background: none;
|
|
980
|
+
border: none;
|
|
981
|
+
color: var(--text-secondary);
|
|
982
|
+
cursor: pointer;
|
|
983
|
+
font-size: 14px;
|
|
984
|
+
padding: 4px;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
.snapshot-action-btn:hover {
|
|
988
|
+
color: var(--text-primary);
|
|
989
|
+
}
|
|
990
|
+
`;
|
|
991
|
+
|
|
992
|
+
document.head.appendChild(style);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
export { initCollaborationUI, toggleCollaborationPanel };
|