@shaykec/bridge 0.1.0

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.
@@ -0,0 +1,382 @@
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>ClaudeTeach — Flowchart</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background: #0d1117;
12
+ color: #c9d1d9;
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: center;
16
+ justify-content: center;
17
+ min-height: 100vh;
18
+ padding: 2rem;
19
+ }
20
+ h2 {
21
+ color: #58a6ff;
22
+ margin-bottom: 0.5rem;
23
+ font-size: 1.2rem;
24
+ }
25
+ .caption {
26
+ color: #8b949e;
27
+ font-size: 0.9rem;
28
+ text-align: center;
29
+ max-width: 600px;
30
+ margin-bottom: 1.5rem;
31
+ line-height: 1.5;
32
+ }
33
+ .diagram-container {
34
+ background: #161b22;
35
+ border: 1px solid #30363d;
36
+ border-radius: 12px;
37
+ padding: 2rem;
38
+ max-width: 90vw;
39
+ max-height: 70vh;
40
+ overflow: auto;
41
+ margin-bottom: 1rem;
42
+ position: relative;
43
+ }
44
+ .diagram-container svg {
45
+ max-width: 100%;
46
+ height: auto;
47
+ }
48
+ /* Mermaid dark theme overrides */
49
+ .mermaid .node rect,
50
+ .mermaid .node circle,
51
+ .mermaid .node polygon {
52
+ fill: #161b22;
53
+ stroke: #58a6ff;
54
+ }
55
+ .mermaid .edgePath .path {
56
+ stroke: #8b949e;
57
+ }
58
+ .mermaid .label {
59
+ color: #c9d1d9;
60
+ }
61
+ .mermaid .edgeLabel {
62
+ background-color: #161b22;
63
+ color: #8b949e;
64
+ }
65
+ .controls {
66
+ display: flex;
67
+ gap: 0.5rem;
68
+ margin-top: 0.5rem;
69
+ flex-wrap: wrap;
70
+ justify-content: center;
71
+ }
72
+ .controls button {
73
+ background: #21262d;
74
+ color: #c9d1d9;
75
+ border: 1px solid #30363d;
76
+ padding: 0.4rem 0.8rem;
77
+ border-radius: 6px;
78
+ cursor: pointer;
79
+ font-size: 0.85rem;
80
+ transition: background 0.15s;
81
+ }
82
+ .controls button:hover { background: #30363d; }
83
+ .controls button:focus { outline: 2px solid #58a6ff; outline-offset: 2px; }
84
+ .step-controls {
85
+ display: flex;
86
+ gap: 0.5rem;
87
+ align-items: center;
88
+ margin-top: 1rem;
89
+ display: none;
90
+ }
91
+ .step-controls.visible { display: flex; }
92
+ .step-counter {
93
+ color: #8b949e;
94
+ font-size: 0.85rem;
95
+ min-width: 80px;
96
+ text-align: center;
97
+ }
98
+ .step-description {
99
+ margin-top: 0.75rem;
100
+ padding: 0.75rem 1rem;
101
+ background: #1c2333;
102
+ border: 1px solid #30363d;
103
+ border-radius: 8px;
104
+ font-size: 0.9rem;
105
+ line-height: 1.5;
106
+ max-width: 600px;
107
+ text-align: center;
108
+ display: none;
109
+ }
110
+ .step-description.visible { display: block; }
111
+ .error {
112
+ color: #f85149;
113
+ background: rgba(248, 81, 73, 0.1);
114
+ border: 1px solid #f85149;
115
+ border-radius: 8px;
116
+ padding: 1rem;
117
+ margin-top: 1rem;
118
+ display: none;
119
+ max-width: 600px;
120
+ word-break: break-word;
121
+ }
122
+ .loading {
123
+ color: #8b949e;
124
+ font-size: 0.9rem;
125
+ padding: 2rem;
126
+ }
127
+ </style>
128
+ </head>
129
+ <body>
130
+ <h2 id="title">Flowchart</h2>
131
+ <div class="caption" id="caption"></div>
132
+ <div class="diagram-container" id="diagramContainer">
133
+ <div class="mermaid" id="diagram">
134
+ <div class="loading">Loading diagram...</div>
135
+ </div>
136
+ </div>
137
+ <div class="controls">
138
+ <button onclick="zoomIn()" aria-label="Zoom in">Zoom In</button>
139
+ <button onclick="zoomOut()" aria-label="Zoom out">Zoom Out</button>
140
+ <button onclick="resetZoom()" aria-label="Reset zoom">Reset</button>
141
+ <button onclick="fitToScreen()" aria-label="Fit to screen">Fit</button>
142
+ </div>
143
+ <div class="step-controls" id="stepControls">
144
+ <button onclick="prevStep()" aria-label="Previous step">Previous</button>
145
+ <span class="step-counter" id="stepCounter">Step 1 / 1</span>
146
+ <button onclick="nextStep()" aria-label="Next step">Next</button>
147
+ </div>
148
+ <div class="step-description" id="stepDescription"></div>
149
+ <div class="error" id="error"></div>
150
+
151
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
152
+ <script>
153
+ /* --- Bridge connection --- */
154
+ var ws = null;
155
+ function connectBridge() {
156
+ try {
157
+ ws = new WebSocket('ws://' + location.host + '/ws');
158
+ ws.onopen = function() {
159
+ ws.send(JSON.stringify({
160
+ v: 1, type: 'sys:connect',
161
+ payload: { clientType: 'template' },
162
+ source: 'template', timestamp: Date.now()
163
+ }));
164
+ };
165
+ ws.onerror = function() { ws = null; };
166
+ ws.onclose = function() { ws = null; };
167
+ } catch(e) { ws = null; }
168
+ }
169
+ connectBridge();
170
+
171
+ /* --- Mermaid config --- */
172
+ mermaid.initialize({
173
+ startOnLoad: false,
174
+ theme: 'dark',
175
+ themeVariables: {
176
+ darkMode: true,
177
+ background: '#0d1117',
178
+ primaryColor: '#1c2333',
179
+ primaryTextColor: '#c9d1d9',
180
+ primaryBorderColor: '#58a6ff',
181
+ lineColor: '#8b949e',
182
+ secondaryColor: '#161b22',
183
+ tertiaryColor: '#21262d',
184
+ fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif'
185
+ },
186
+ flowchart: {
187
+ htmlLabels: true,
188
+ curve: 'basis',
189
+ padding: 15
190
+ },
191
+ securityLevel: 'strict'
192
+ });
193
+
194
+ var currentZoom = 1;
195
+ var currentStep = 0;
196
+ var diagramData = {
197
+ content: null,
198
+ title: 'Flowchart',
199
+ caption: '',
200
+ steps: [],
201
+ direction: 'TD'
202
+ };
203
+
204
+ async function init(data) {
205
+ if (data) diagramData = Object.assign({}, diagramData, data);
206
+
207
+ if (diagramData.title) {
208
+ document.getElementById('title').textContent = diagramData.title;
209
+ }
210
+ if (diagramData.caption) {
211
+ document.getElementById('caption').textContent = diagramData.caption;
212
+ }
213
+
214
+ var content = diagramData.content;
215
+
216
+ // If nodes/edges are provided instead of raw mermaid, build the content
217
+ if (!content && diagramData.nodes) {
218
+ content = buildMermaidFromNodes(diagramData.nodes, diagramData.edges, diagramData.direction);
219
+ }
220
+
221
+ if (!content) {
222
+ // Use a default demo flowchart
223
+ content = 'graph TD\n' +
224
+ ' A[Start] --> B{Decision}\n' +
225
+ ' B -->|Yes| C[Process A]\n' +
226
+ ' B -->|No| D[Process B]\n' +
227
+ ' C --> E[End]\n' +
228
+ ' D --> E';
229
+ }
230
+
231
+ var diagramEl = document.getElementById('diagram');
232
+ var errorEl = document.getElementById('error');
233
+
234
+ try {
235
+ diagramEl.textContent = content;
236
+ var result = await mermaid.render('mermaid-flow-svg', content);
237
+ diagramEl.innerHTML = result.svg;
238
+ errorEl.style.display = 'none';
239
+ } catch (err) {
240
+ errorEl.textContent = 'Failed to render flowchart: ' + err.message;
241
+ errorEl.style.display = 'block';
242
+ diagramEl.innerHTML = '<div class="loading">Render error</div>';
243
+ }
244
+
245
+ // Setup steps if provided
246
+ if (diagramData.steps && diagramData.steps.length > 0) {
247
+ currentStep = 0;
248
+ document.getElementById('stepControls').classList.add('visible');
249
+ updateStep();
250
+ }
251
+ }
252
+
253
+ function buildMermaidFromNodes(nodes, edges, direction) {
254
+ var lines = ['graph ' + (direction || 'TD')];
255
+ nodes.forEach(function(node) {
256
+ var id = node.id || node.label.replace(/\s+/g, '_');
257
+ var shape = node.shape || 'rect';
258
+ if (shape === 'diamond') {
259
+ lines.push(' ' + id + '{' + node.label + '}');
260
+ } else if (shape === 'rounded') {
261
+ lines.push(' ' + id + '(' + node.label + ')');
262
+ } else if (shape === 'circle') {
263
+ lines.push(' ' + id + '((' + node.label + '))');
264
+ } else {
265
+ lines.push(' ' + id + '[' + node.label + ']');
266
+ }
267
+ });
268
+ (edges || []).forEach(function(edge) {
269
+ var arrow = edge.label
270
+ ? '-->|' + edge.label + '|'
271
+ : '-->';
272
+ lines.push(' ' + edge.from + ' ' + arrow + ' ' + edge.to);
273
+ });
274
+ return lines.join('\n');
275
+ }
276
+
277
+ function zoomIn() {
278
+ currentZoom = Math.min(currentZoom + 0.2, 3);
279
+ applyZoom();
280
+ }
281
+ function zoomOut() {
282
+ currentZoom = Math.max(currentZoom - 0.2, 0.3);
283
+ applyZoom();
284
+ }
285
+ function resetZoom() {
286
+ currentZoom = 1;
287
+ applyZoom();
288
+ }
289
+ function fitToScreen() {
290
+ var svg = document.querySelector('#diagram svg');
291
+ var container = document.getElementById('diagramContainer');
292
+ if (svg && container) {
293
+ var svgRect = svg.getBBox ? svg.getBBox() : { width: svg.clientWidth, height: svg.clientHeight };
294
+ var containerW = container.clientWidth - 64;
295
+ var containerH = container.clientHeight - 64;
296
+ var scaleW = containerW / (svgRect.width || 1);
297
+ var scaleH = containerH / (svgRect.height || 1);
298
+ currentZoom = Math.min(scaleW, scaleH, 2);
299
+ applyZoom();
300
+ }
301
+ }
302
+ function applyZoom() {
303
+ var svg = document.querySelector('#diagram svg');
304
+ if (svg) {
305
+ svg.style.transform = 'scale(' + currentZoom + ')';
306
+ svg.style.transformOrigin = 'center top';
307
+ }
308
+ }
309
+
310
+ /* --- Step navigation --- */
311
+ function updateStep() {
312
+ var steps = diagramData.steps;
313
+ if (!steps || steps.length === 0) return;
314
+
315
+ document.getElementById('stepCounter').textContent =
316
+ 'Step ' + (currentStep + 1) + ' / ' + steps.length;
317
+
318
+ var step = steps[currentStep];
319
+ var desc = document.getElementById('stepDescription');
320
+ desc.textContent = step.description || step.text || step;
321
+ desc.classList.add('visible');
322
+
323
+ // Highlight the relevant node if specified
324
+ highlightNodes(step.highlight || []);
325
+ }
326
+
327
+ function nextStep() {
328
+ if (currentStep < diagramData.steps.length - 1) {
329
+ currentStep++;
330
+ updateStep();
331
+ }
332
+ }
333
+ function prevStep() {
334
+ if (currentStep > 0) {
335
+ currentStep--;
336
+ updateStep();
337
+ }
338
+ }
339
+
340
+ function highlightNodes(nodeIds) {
341
+ // Reset all node highlights
342
+ document.querySelectorAll('#diagram .node rect, #diagram .node polygon, #diagram .node circle').forEach(function(el) {
343
+ el.style.stroke = '';
344
+ el.style.strokeWidth = '';
345
+ el.style.filter = '';
346
+ });
347
+
348
+ // Highlight specified nodes
349
+ nodeIds.forEach(function(id) {
350
+ var node = document.querySelector('#diagram .node#' + id + ' rect, #diagram .node#' + id + ' polygon');
351
+ if (node) {
352
+ node.style.stroke = '#58a6ff';
353
+ node.style.strokeWidth = '3px';
354
+ node.style.filter = 'drop-shadow(0 0 6px rgba(88, 166, 255, 0.4))';
355
+ }
356
+ });
357
+ }
358
+
359
+ /* --- Keyboard nav --- */
360
+ document.addEventListener('keydown', function(e) {
361
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') nextStep();
362
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') prevStep();
363
+ if (e.key === '+' || e.key === '=') zoomIn();
364
+ if (e.key === '-') zoomOut();
365
+ if (e.key === '0') resetZoom();
366
+ });
367
+
368
+ /* --- Initialization --- */
369
+ window.addEventListener('message', function(e) {
370
+ if (e.data && e.data.type === 'diagram-data') {
371
+ init(e.data.payload);
372
+ }
373
+ });
374
+
375
+ if (window.__DIAGRAM_DATA__) {
376
+ init(window.__DIAGRAM_DATA__);
377
+ } else {
378
+ init();
379
+ }
380
+ </script>
381
+ </body>
382
+ </html>
@@ -0,0 +1,220 @@
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>ClaudeTeach — Diagram</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background: #0d1117;
12
+ color: #c9d1d9;
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: center;
16
+ justify-content: center;
17
+ min-height: 100vh;
18
+ padding: 2rem;
19
+ }
20
+ h2 {
21
+ color: #58a6ff;
22
+ margin-bottom: 1rem;
23
+ font-size: 1.2rem;
24
+ }
25
+ .diagram-container {
26
+ background: #161b22;
27
+ border: 1px solid #30363d;
28
+ border-radius: 12px;
29
+ padding: 2rem;
30
+ max-width: 90vw;
31
+ overflow-x: auto;
32
+ margin-bottom: 1rem;
33
+ }
34
+ .diagram-container svg {
35
+ max-width: 100%;
36
+ height: auto;
37
+ }
38
+ /* Mermaid theme overrides for dark mode */
39
+ .mermaid .node rect,
40
+ .mermaid .node circle,
41
+ .mermaid .node polygon {
42
+ fill: #161b22;
43
+ stroke: #58a6ff;
44
+ }
45
+ .mermaid .edgePath .path {
46
+ stroke: #8b949e;
47
+ }
48
+ .mermaid .label {
49
+ color: #c9d1d9;
50
+ }
51
+ .caption {
52
+ color: #8b949e;
53
+ font-size: 0.85rem;
54
+ text-align: center;
55
+ max-width: 600px;
56
+ }
57
+ .error {
58
+ color: #f85149;
59
+ background: rgba(248, 81, 73, 0.1);
60
+ border: 1px solid #f85149;
61
+ border-radius: 8px;
62
+ padding: 1rem;
63
+ margin-top: 1rem;
64
+ display: none;
65
+ }
66
+ .controls {
67
+ display: flex;
68
+ gap: 0.5rem;
69
+ margin-top: 1rem;
70
+ }
71
+ .controls button {
72
+ background: #21262d;
73
+ color: #c9d1d9;
74
+ border: 1px solid #30363d;
75
+ padding: 0.4rem 0.8rem;
76
+ border-radius: 6px;
77
+ cursor: pointer;
78
+ font-size: 0.85rem;
79
+ transition: background 0.15s;
80
+ }
81
+ .controls button:hover {
82
+ background: #30363d;
83
+ }
84
+ </style>
85
+ </head>
86
+ <body>
87
+ <h2 id="title">Diagram</h2>
88
+ <div class="diagram-container">
89
+ <div class="mermaid" id="diagram">
90
+ graph TD
91
+ A[Start] --> B{Decision}
92
+ B -->|Yes| C[Do Something]
93
+ B -->|No| D[Do Something Else]
94
+ C --> E[End]
95
+ D --> E
96
+ </div>
97
+ </div>
98
+ <div class="caption" id="caption"></div>
99
+ <div class="error" id="error"></div>
100
+ <div class="controls">
101
+ <button onclick="zoomIn()">Zoom In</button>
102
+ <button onclick="zoomOut()">Zoom Out</button>
103
+ <button onclick="resetZoom()">Reset</button>
104
+ </div>
105
+
106
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
107
+ <script>
108
+ /* --- Bridge connection --- */
109
+ var wsBridge = null;
110
+ (function connectBridge() {
111
+ try {
112
+ wsBridge = new WebSocket('ws://' + location.host + '/ws');
113
+ wsBridge.onopen = function() {
114
+ wsBridge.send(JSON.stringify({
115
+ v: 1, type: 'sys:connect',
116
+ payload: { clientType: 'template' },
117
+ source: 'template', timestamp: Date.now()
118
+ }));
119
+ };
120
+ wsBridge.onerror = function() { wsBridge = null; };
121
+ wsBridge.onclose = function() { wsBridge = null; };
122
+ } catch(e) { wsBridge = null; }
123
+ })();
124
+
125
+ let currentZoom = 1;
126
+
127
+ mermaid.initialize({
128
+ startOnLoad: false,
129
+ theme: 'dark',
130
+ themeVariables: {
131
+ darkMode: true,
132
+ background: '#0d1117',
133
+ primaryColor: '#1c2333',
134
+ primaryTextColor: '#c9d1d9',
135
+ primaryBorderColor: '#58a6ff',
136
+ lineColor: '#8b949e',
137
+ secondaryColor: '#161b22',
138
+ tertiaryColor: '#21262d',
139
+ },
140
+ securityLevel: 'strict',
141
+ });
142
+
143
+ let diagramData = {
144
+ content: null,
145
+ title: 'Diagram',
146
+ caption: '',
147
+ };
148
+
149
+ async function init(data) {
150
+ if (data) diagramData = { ...diagramData, ...data };
151
+
152
+ if (diagramData.title) {
153
+ document.getElementById('title').textContent = diagramData.title;
154
+ }
155
+ if (diagramData.caption) {
156
+ document.getElementById('caption').textContent = diagramData.caption;
157
+ }
158
+
159
+ const content = diagramData.content;
160
+ if (!content) return;
161
+
162
+ const diagramEl = document.getElementById('diagram');
163
+ const errorEl = document.getElementById('error');
164
+
165
+ try {
166
+ diagramEl.textContent = content;
167
+ const { svg } = await mermaid.render('mermaid-svg', content);
168
+ diagramEl.innerHTML = svg;
169
+ errorEl.style.display = 'none';
170
+ } catch (err) {
171
+ errorEl.textContent = 'Failed to render diagram: ' + err.message;
172
+ errorEl.style.display = 'block';
173
+ }
174
+ }
175
+
176
+ function zoomIn() {
177
+ currentZoom = Math.min(currentZoom + 0.2, 3);
178
+ applyZoom();
179
+ }
180
+
181
+ function zoomOut() {
182
+ currentZoom = Math.max(currentZoom - 0.2, 0.4);
183
+ applyZoom();
184
+ }
185
+
186
+ function resetZoom() {
187
+ currentZoom = 1;
188
+ applyZoom();
189
+ }
190
+
191
+ function applyZoom() {
192
+ const svg = document.querySelector('#diagram svg');
193
+ if (svg) {
194
+ svg.style.transform = `scale(${currentZoom})`;
195
+ svg.style.transformOrigin = 'center top';
196
+ }
197
+ }
198
+
199
+ // Keyboard shortcuts for zoom
200
+ document.addEventListener('keydown', function(e) {
201
+ if (e.key === '+' || e.key === '=') zoomIn();
202
+ if (e.key === '-') zoomOut();
203
+ if (e.key === '0') resetZoom();
204
+ });
205
+
206
+ window.addEventListener('message', (e) => {
207
+ if (e.data && e.data.type === 'diagram-data') {
208
+ init(e.data.payload);
209
+ }
210
+ });
211
+
212
+ if (window.__DIAGRAM_DATA__) {
213
+ init(window.__DIAGRAM_DATA__);
214
+ } else {
215
+ // Render the default diagram on load
216
+ mermaid.run();
217
+ }
218
+ </script>
219
+ </body>
220
+ </html>