shell-mirror 1.0.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.
- package/README.md +272 -0
- package/auth.js +22 -0
- package/bin/shell-mirror +127 -0
- package/lib/config-manager.js +203 -0
- package/lib/health-checker.js +192 -0
- package/lib/server-manager.js +225 -0
- package/lib/setup-wizard.js +222 -0
- package/package.json +70 -0
- package/public/app/terminal.html +867 -0
- package/public/index.html +809 -0
- package/server.js +171 -0
|
@@ -0,0 +1,809 @@
|
|
|
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>Shell Mirror - Use Claude Code CLI and Gemini CLI from your phone</title>
|
|
7
|
+
<meta name="description" content="Access your Mac terminal from your phone to use Claude Code CLI, Gemini CLI, and other command-line tools. Your computer, your accounts, secure access.">
|
|
8
|
+
<meta name="keywords" content="mobile coding, remote shell, Claude Code mobile, Gemini CLI phone, Mac shell browser, secure shell sharing">
|
|
9
|
+
|
|
10
|
+
<!-- Open Graph / Facebook -->
|
|
11
|
+
<meta property="og:type" content="website">
|
|
12
|
+
<meta property="og:title" content="Shell Mirror - Use Claude Code CLI and Gemini CLI from your phone">
|
|
13
|
+
<meta property="og:description" content="Access your Mac terminal from your phone to use Claude Code CLI, Gemini CLI, and other tools. Your computer, your accounts.">
|
|
14
|
+
<meta property="og:url" content="https://www.igori.eu">
|
|
15
|
+
|
|
16
|
+
<!-- Twitter -->
|
|
17
|
+
<meta property="twitter:card" content="summary_large_image">
|
|
18
|
+
<meta property="twitter:title" content="Shell Mirror - Use Claude Code CLI from your phone">
|
|
19
|
+
<meta property="twitter:description" content="Access your Mac terminal from your phone to use Claude Code CLI, Gemini CLI, and other command-line tools.">
|
|
20
|
+
|
|
21
|
+
<style>
|
|
22
|
+
* {
|
|
23
|
+
margin: 0;
|
|
24
|
+
padding: 0;
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
body {
|
|
29
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
30
|
+
line-height: 1.6;
|
|
31
|
+
color: #333;
|
|
32
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.container {
|
|
37
|
+
max-width: 1200px;
|
|
38
|
+
margin: 0 auto;
|
|
39
|
+
padding: 0 20px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Header */
|
|
43
|
+
header {
|
|
44
|
+
padding: 20px 0;
|
|
45
|
+
position: relative;
|
|
46
|
+
z-index: 100;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
nav {
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
align-items: center;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.logo {
|
|
56
|
+
font-size: 24px;
|
|
57
|
+
font-weight: bold;
|
|
58
|
+
color: white;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.cta-button {
|
|
62
|
+
background: rgba(255, 255, 255, 0.2);
|
|
63
|
+
color: white;
|
|
64
|
+
padding: 12px 24px;
|
|
65
|
+
text-decoration: none;
|
|
66
|
+
border-radius: 25px;
|
|
67
|
+
font-weight: 500;
|
|
68
|
+
backdrop-filter: blur(10px);
|
|
69
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
70
|
+
transition: all 0.3s ease;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.cta-button:hover {
|
|
74
|
+
background: rgba(255, 255, 255, 0.3);
|
|
75
|
+
transform: translateY(-2px);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Hero Section */
|
|
79
|
+
.hero {
|
|
80
|
+
text-align: center;
|
|
81
|
+
padding: 60px 0 80px;
|
|
82
|
+
color: white;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.hero h1 {
|
|
86
|
+
font-size: clamp(2.5rem, 5vw, 4rem);
|
|
87
|
+
font-weight: 700;
|
|
88
|
+
margin-bottom: 20px;
|
|
89
|
+
line-height: 1.2;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.hero p {
|
|
93
|
+
font-size: clamp(1.1rem, 2vw, 1.3rem);
|
|
94
|
+
margin-bottom: 40px;
|
|
95
|
+
opacity: 0.9;
|
|
96
|
+
max-width: 600px;
|
|
97
|
+
margin-left: auto;
|
|
98
|
+
margin-right: auto;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.hero-buttons {
|
|
102
|
+
display: flex;
|
|
103
|
+
gap: 20px;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
flex-wrap: wrap;
|
|
106
|
+
margin-bottom: 60px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.btn-primary {
|
|
110
|
+
background: #4285F4;
|
|
111
|
+
color: white;
|
|
112
|
+
padding: 16px 32px;
|
|
113
|
+
text-decoration: none;
|
|
114
|
+
border-radius: 30px;
|
|
115
|
+
font-weight: 600;
|
|
116
|
+
font-size: 1.1rem;
|
|
117
|
+
transition: all 0.3s ease;
|
|
118
|
+
box-shadow: 0 4px 15px rgba(66, 133, 244, 0.3);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.btn-primary:hover {
|
|
122
|
+
background: #3367d6;
|
|
123
|
+
transform: translateY(-2px);
|
|
124
|
+
box-shadow: 0 6px 20px rgba(66, 133, 244, 0.4);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.btn-secondary {
|
|
128
|
+
background: transparent;
|
|
129
|
+
color: white;
|
|
130
|
+
padding: 16px 32px;
|
|
131
|
+
text-decoration: none;
|
|
132
|
+
border-radius: 30px;
|
|
133
|
+
font-weight: 600;
|
|
134
|
+
font-size: 1.1rem;
|
|
135
|
+
border: 2px solid white;
|
|
136
|
+
transition: all 0.3s ease;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.btn-secondary:hover {
|
|
140
|
+
background: white;
|
|
141
|
+
color: #667eea;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Setup Steps */
|
|
145
|
+
.setup-steps {
|
|
146
|
+
margin: 40px auto;
|
|
147
|
+
max-width: 600px;
|
|
148
|
+
display: flex;
|
|
149
|
+
flex-direction: column;
|
|
150
|
+
gap: 20px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.setup-step {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
gap: 20px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.step-number {
|
|
160
|
+
font-size: 2rem;
|
|
161
|
+
font-weight: bold;
|
|
162
|
+
color: white;
|
|
163
|
+
opacity: 0.8;
|
|
164
|
+
min-width: 40px;
|
|
165
|
+
text-align: center;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Installation Code */
|
|
169
|
+
.install-code {
|
|
170
|
+
background: rgba(0, 0, 0, 0.3);
|
|
171
|
+
padding: 20px;
|
|
172
|
+
border-radius: 15px;
|
|
173
|
+
backdrop-filter: blur(10px);
|
|
174
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
175
|
+
position: relative;
|
|
176
|
+
flex: 1;
|
|
177
|
+
height: 60px;
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.install-code pre {
|
|
183
|
+
color: #00ff88;
|
|
184
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
185
|
+
font-size: 1.1rem;
|
|
186
|
+
margin: 0;
|
|
187
|
+
padding-right: 40px;
|
|
188
|
+
flex: 1;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.copy-btn {
|
|
192
|
+
position: absolute;
|
|
193
|
+
right: 15px;
|
|
194
|
+
top: 50%;
|
|
195
|
+
transform: translateY(-50%);
|
|
196
|
+
background: rgba(255, 255, 255, 0.1);
|
|
197
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
198
|
+
border-radius: 8px;
|
|
199
|
+
padding: 8px;
|
|
200
|
+
color: white;
|
|
201
|
+
cursor: pointer;
|
|
202
|
+
transition: all 0.2s ease;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.copy-btn:hover {
|
|
206
|
+
background: rgba(255, 255, 255, 0.2);
|
|
207
|
+
transform: translateY(-50%) scale(1.05);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.copy-btn:active {
|
|
211
|
+
transform: translateY(-50%) scale(0.95);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.copy-btn svg {
|
|
215
|
+
display: block;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Google Login Button */
|
|
219
|
+
.btn-google {
|
|
220
|
+
background: white;
|
|
221
|
+
color: #3c4043;
|
|
222
|
+
padding: 20px 24px;
|
|
223
|
+
border: 1px solid #dadce0;
|
|
224
|
+
border-radius: 15px;
|
|
225
|
+
font-weight: 500;
|
|
226
|
+
font-size: 1rem;
|
|
227
|
+
display: flex;
|
|
228
|
+
align-items: center;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
gap: 12px;
|
|
231
|
+
transition: all 0.2s ease;
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
234
|
+
flex: 1;
|
|
235
|
+
height: 60px;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.btn-google:hover {
|
|
239
|
+
background: #f8f9fa;
|
|
240
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
241
|
+
/* transform: translateY(-1px); */
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.btn-google:active {
|
|
245
|
+
transform: translateY(0);
|
|
246
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* Installation Modal */
|
|
250
|
+
.install-modal {
|
|
251
|
+
display: none;
|
|
252
|
+
position: fixed;
|
|
253
|
+
top: 0;
|
|
254
|
+
left: 0;
|
|
255
|
+
width: 100%;
|
|
256
|
+
height: 100%;
|
|
257
|
+
background: rgba(0, 0, 0, 0.8);
|
|
258
|
+
z-index: 1000;
|
|
259
|
+
align-items: center;
|
|
260
|
+
justify-content: center;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.install-modal.show {
|
|
264
|
+
display: flex;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.install-modal-content {
|
|
268
|
+
background: white;
|
|
269
|
+
padding: 30px;
|
|
270
|
+
border-radius: 15px;
|
|
271
|
+
max-width: 500px;
|
|
272
|
+
margin: 20px;
|
|
273
|
+
text-align: center;
|
|
274
|
+
position: relative;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.install-modal-content h3 {
|
|
278
|
+
color: #333;
|
|
279
|
+
margin-bottom: 20px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.install-modal-content p {
|
|
283
|
+
color: #666;
|
|
284
|
+
margin-bottom: 20px;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.install-modal-content .install-code {
|
|
288
|
+
background: #f5f5f5;
|
|
289
|
+
color: #333;
|
|
290
|
+
margin: 20px 0;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.install-modal-content .install-code pre {
|
|
294
|
+
color: #333;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.close-modal {
|
|
298
|
+
position: absolute;
|
|
299
|
+
top: 10px;
|
|
300
|
+
right: 15px;
|
|
301
|
+
background: none;
|
|
302
|
+
border: none;
|
|
303
|
+
font-size: 24px;
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
color: #666;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.close-modal:hover {
|
|
309
|
+
color: #000;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* How It Works */
|
|
313
|
+
.how-it-works {
|
|
314
|
+
background: white;
|
|
315
|
+
padding: 80px 0;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.section-title {
|
|
319
|
+
text-align: center;
|
|
320
|
+
font-size: 2.5rem;
|
|
321
|
+
margin-bottom: 60px;
|
|
322
|
+
color: #333;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.steps {
|
|
326
|
+
display: grid;
|
|
327
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
328
|
+
gap: 40px;
|
|
329
|
+
margin-bottom: 60px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.step {
|
|
333
|
+
text-align: center;
|
|
334
|
+
padding: 30px 20px;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.step-icon {
|
|
338
|
+
width: 80px;
|
|
339
|
+
height: 80px;
|
|
340
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
341
|
+
border-radius: 50%;
|
|
342
|
+
display: flex;
|
|
343
|
+
align-items: center;
|
|
344
|
+
justify-content: center;
|
|
345
|
+
margin: 0 auto 20px;
|
|
346
|
+
font-size: 2rem;
|
|
347
|
+
color: white;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.step h3 {
|
|
351
|
+
font-size: 1.3rem;
|
|
352
|
+
margin-bottom: 15px;
|
|
353
|
+
color: #333;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.step p {
|
|
357
|
+
color: #666;
|
|
358
|
+
line-height: 1.6;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* Security Section */
|
|
362
|
+
.security {
|
|
363
|
+
background: #f8f9fa;
|
|
364
|
+
padding: 80px 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.security-flow {
|
|
368
|
+
display: flex;
|
|
369
|
+
align-items: center;
|
|
370
|
+
justify-content: center;
|
|
371
|
+
gap: 30px;
|
|
372
|
+
flex-wrap: wrap;
|
|
373
|
+
margin: 40px 0;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.security-step {
|
|
377
|
+
background: white;
|
|
378
|
+
padding: 20px;
|
|
379
|
+
border-radius: 15px;
|
|
380
|
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
|
381
|
+
text-align: center;
|
|
382
|
+
min-width: 200px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.security-step h4 {
|
|
386
|
+
color: #4285F4;
|
|
387
|
+
margin-bottom: 10px;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.arrow {
|
|
391
|
+
font-size: 2rem;
|
|
392
|
+
color: #4285F4;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Use Cases */
|
|
396
|
+
.use-cases {
|
|
397
|
+
padding: 80px 0;
|
|
398
|
+
background: white;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.use-case-grid {
|
|
402
|
+
display: grid;
|
|
403
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
404
|
+
gap: 30px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.use-case {
|
|
408
|
+
background: #f8f9fa;
|
|
409
|
+
padding: 30px;
|
|
410
|
+
border-radius: 15px;
|
|
411
|
+
border-left: 4px solid #4285F4;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.use-case h3 {
|
|
415
|
+
color: #333;
|
|
416
|
+
margin-bottom: 15px;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.use-case p {
|
|
420
|
+
color: #666;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* Footer CTA */
|
|
424
|
+
.footer-cta {
|
|
425
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
426
|
+
padding: 60px 0;
|
|
427
|
+
text-align: center;
|
|
428
|
+
color: white;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.footer-cta h2 {
|
|
432
|
+
font-size: 2.5rem;
|
|
433
|
+
margin-bottom: 20px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.footer-cta p {
|
|
437
|
+
font-size: 1.2rem;
|
|
438
|
+
margin-bottom: 40px;
|
|
439
|
+
opacity: 0.9;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* Responsive Design */
|
|
443
|
+
@media (max-width: 768px) {
|
|
444
|
+
.setup-steps {
|
|
445
|
+
max-width: 90%;
|
|
446
|
+
gap: 15px;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.setup-step {
|
|
450
|
+
gap: 15px;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.step-number {
|
|
454
|
+
font-size: 1.5rem;
|
|
455
|
+
min-width: 30px;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.install-code {
|
|
459
|
+
height: 50px;
|
|
460
|
+
padding: 15px;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.btn-google {
|
|
464
|
+
height: 50px;
|
|
465
|
+
padding: 15px 20px;
|
|
466
|
+
font-size: 0.9rem;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.hero-buttons {
|
|
470
|
+
flex-direction: column;
|
|
471
|
+
align-items: center;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.btn-primary {
|
|
475
|
+
width: 250px;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.security-flow {
|
|
479
|
+
flex-direction: column;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.arrow {
|
|
483
|
+
transform: rotate(90deg);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.steps {
|
|
487
|
+
grid-template-columns: 1fr;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* Animations */
|
|
492
|
+
@keyframes fadeInUp {
|
|
493
|
+
from {
|
|
494
|
+
opacity: 0;
|
|
495
|
+
transform: translateY(30px);
|
|
496
|
+
}
|
|
497
|
+
to {
|
|
498
|
+
opacity: 1;
|
|
499
|
+
transform: translateY(0);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.hero, .step, .use-case {
|
|
504
|
+
animation: fadeInUp 0.6s ease-out;
|
|
505
|
+
}
|
|
506
|
+
</style>
|
|
507
|
+
</head>
|
|
508
|
+
<body>
|
|
509
|
+
<header>
|
|
510
|
+
<nav class="container">
|
|
511
|
+
<div class="logo">Shell Mirror</div>
|
|
512
|
+
<a href="/app" class="cta-button">Launch App</a>
|
|
513
|
+
</nav>
|
|
514
|
+
</header>
|
|
515
|
+
|
|
516
|
+
<main>
|
|
517
|
+
<!-- Hero Section -->
|
|
518
|
+
<section class="hero">
|
|
519
|
+
<div class="container">
|
|
520
|
+
<h1>Use Claude Code CLI<br>from your phone</h1>
|
|
521
|
+
<p>Access your Mac terminal from your phone to use Claude Code CLI, Gemini CLI, and other command-line tools. Your computer, your accounts, secure access.</p>
|
|
522
|
+
|
|
523
|
+
<div class="setup-steps">
|
|
524
|
+
<div class="setup-step">
|
|
525
|
+
<div class="step-number">1.</div>
|
|
526
|
+
<div class="install-code">
|
|
527
|
+
<pre id="install-command">npm install -g shell-mirror</pre>
|
|
528
|
+
<button class="copy-btn" onclick="copyInstallCommand()" title="Copy to clipboard">
|
|
529
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
530
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
531
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
532
|
+
</svg>
|
|
533
|
+
</button>
|
|
534
|
+
</div>
|
|
535
|
+
</div>
|
|
536
|
+
|
|
537
|
+
<div class="setup-step">
|
|
538
|
+
<div class="step-number">2.</div>
|
|
539
|
+
<button class="btn-google" onclick="handleGoogleLogin()">
|
|
540
|
+
<svg width="20" height="20" viewBox="0 0 24 24">
|
|
541
|
+
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
|
|
542
|
+
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
|
|
543
|
+
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
|
544
|
+
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
|
545
|
+
</svg>
|
|
546
|
+
Login with Google
|
|
547
|
+
</button>
|
|
548
|
+
</div>
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
</section>
|
|
552
|
+
|
|
553
|
+
<!-- How It Works -->
|
|
554
|
+
<section class="how-it-works" id="how-it-works">
|
|
555
|
+
<div class="container">
|
|
556
|
+
<h2 class="section-title">Simple 3-Step Setup</h2>
|
|
557
|
+
|
|
558
|
+
<div class="steps">
|
|
559
|
+
<div class="step">
|
|
560
|
+
<div class="step-icon">📦</div>
|
|
561
|
+
<h3>Install & Configure</h3>
|
|
562
|
+
<p>Install via npm and connect your Google account. Simple 2-minute setup on your Mac.</p>
|
|
563
|
+
</div>
|
|
564
|
+
|
|
565
|
+
<div class="step">
|
|
566
|
+
<div class="step-icon">🔐</div>
|
|
567
|
+
<h3>Private & Secure</h3>
|
|
568
|
+
<p>Runs locally on your Mac. Uses Google OAuth for secure access - only you can connect to your own computer.</p>
|
|
569
|
+
</div>
|
|
570
|
+
|
|
571
|
+
<div class="step">
|
|
572
|
+
<div class="step-icon">📱</div>
|
|
573
|
+
<h3>Use from Phone</h3>
|
|
574
|
+
<p>Open this website on your phone, log in with your Google account, and access your Mac terminal instantly.</p>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
</section>
|
|
579
|
+
|
|
580
|
+
<!-- Security -->
|
|
581
|
+
<section class="security" id="security">
|
|
582
|
+
<div class="container">
|
|
583
|
+
<h2 class="section-title">How it works</h2>
|
|
584
|
+
<p style="text-align: center; margin-bottom: 40px; color: #666; font-size: 1.1rem;">
|
|
585
|
+
Everything stays on your Mac. Uses Google OAuth for secure authentication.
|
|
586
|
+
</p>
|
|
587
|
+
|
|
588
|
+
<div class="security-flow">
|
|
589
|
+
<div class="security-step">
|
|
590
|
+
<h4>Your Phone</h4>
|
|
591
|
+
<p>Open shellmirror.app</p>
|
|
592
|
+
</div>
|
|
593
|
+
<div class="arrow">→</div>
|
|
594
|
+
<div class="security-step">
|
|
595
|
+
<h4>Secure Login</h4>
|
|
596
|
+
<p>Google account verification</p>
|
|
597
|
+
</div>
|
|
598
|
+
<div class="arrow">→</div>
|
|
599
|
+
<div class="security-step">
|
|
600
|
+
<h4>Your Mac Terminal</h4>
|
|
601
|
+
<p>Direct terminal access</p>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<div style="text-align: center; margin-top: 40px;">
|
|
606
|
+
<p style="color: #666;">✅ No credential storage in browser<br>
|
|
607
|
+
✅ Encrypted connections (WSS)<br>
|
|
608
|
+
✅ No data leaves your Mac<br>
|
|
609
|
+
✅ Session-based authentication</p>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
</section>
|
|
613
|
+
|
|
614
|
+
<!-- Use Cases -->
|
|
615
|
+
<section class="use-cases" id="use-cases">
|
|
616
|
+
<div class="container">
|
|
617
|
+
<h2 class="section-title">Use Cases</h2>
|
|
618
|
+
|
|
619
|
+
<div class="use-case-grid">
|
|
620
|
+
<div class="use-case">
|
|
621
|
+
<h3>🤖 Claude Code CLI</h3>
|
|
622
|
+
<p>Use Claude Code CLI from your phone. Run AI-powered coding commands using your own Claude API account.</p>
|
|
623
|
+
</div>
|
|
624
|
+
|
|
625
|
+
<div class="use-case">
|
|
626
|
+
<h3>✨ Gemini CLI</h3>
|
|
627
|
+
<p>Use Gemini CLI from your phone. Access Google's AI tools with your own Google account and API access.</p>
|
|
628
|
+
</div>
|
|
629
|
+
|
|
630
|
+
<div class="use-case">
|
|
631
|
+
<h3>🔧 Standard Tools</h3>
|
|
632
|
+
<p>Git, npm, pip, Docker, SSH - all your usual command-line tools work normally from your phone.</p>
|
|
633
|
+
</div>
|
|
634
|
+
|
|
635
|
+
<div class="use-case">
|
|
636
|
+
<h3>🔧 Quick Fixes</h3>
|
|
637
|
+
<p>Fix bugs, check logs, or deploy code from your phone. Full access to your development environment.</p>
|
|
638
|
+
</div>
|
|
639
|
+
|
|
640
|
+
<div class="use-case">
|
|
641
|
+
<h3>📱 Mobile Development</h3>
|
|
642
|
+
<p>Code during commutes or away from your desk. Follow tutorials and practice programming from your phone.</p>
|
|
643
|
+
</div>
|
|
644
|
+
|
|
645
|
+
<div class="use-case">
|
|
646
|
+
<h3>🏠 Personal Projects</h3>
|
|
647
|
+
<p>Work on personal projects from anywhere. Your own computer, your own accounts, your own code.</p>
|
|
648
|
+
</div>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
</section>
|
|
652
|
+
|
|
653
|
+
<!-- Footer CTA -->
|
|
654
|
+
<section class="footer-cta" id="install">
|
|
655
|
+
<div class="container">
|
|
656
|
+
<h2>Get Started</h2>
|
|
657
|
+
<p>Install on your Mac, then access from your phone</p>
|
|
658
|
+
|
|
659
|
+
<div class="install-code">
|
|
660
|
+
<pre>npm install -g shell-mirror && shell-mirror setup</pre>
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
<div style="margin-top: 40px;">
|
|
664
|
+
<a href="/app" class="btn-primary" style="margin-right: 20px;">Launch App</a>
|
|
665
|
+
<a href="https://github.com/yourusername/shell-mirror" class="btn-secondary">View on GitHub</a>
|
|
666
|
+
</div>
|
|
667
|
+
|
|
668
|
+
<p style="margin-top: 30px; opacity: 0.8; font-size: 0.9rem;">
|
|
669
|
+
Free and open source • Your computer • Your accounts • Your data
|
|
670
|
+
</p>
|
|
671
|
+
</div>
|
|
672
|
+
</section>
|
|
673
|
+
</main>
|
|
674
|
+
|
|
675
|
+
<!-- Installation Modal -->
|
|
676
|
+
<div id="install-modal" class="install-modal">
|
|
677
|
+
<div class="install-modal-content">
|
|
678
|
+
<button class="close-modal" onclick="closeInstallModal()">×</button>
|
|
679
|
+
<h3>🔧 Setup Required</h3>
|
|
680
|
+
<p>You need to install and run Shell Mirror on your local machine first.</p>
|
|
681
|
+
|
|
682
|
+
<div class="install-code">
|
|
683
|
+
<pre>npm install -g shell-mirror</pre>
|
|
684
|
+
<button class="copy-btn" onclick="copyInstallCommand()" title="Copy to clipboard">
|
|
685
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
686
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
687
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
688
|
+
</svg>
|
|
689
|
+
</button>
|
|
690
|
+
</div>
|
|
691
|
+
|
|
692
|
+
<p><strong>Next steps:</strong></p>
|
|
693
|
+
<ol style="text-align: left; margin: 20px 0;">
|
|
694
|
+
<li>Install Shell Mirror using the command above</li>
|
|
695
|
+
<li>Run <code>shell-mirror setup</code> to configure</li>
|
|
696
|
+
<li>Run <code>shell-mirror start</code> to start the server</li>
|
|
697
|
+
<li>Come back and click "Login with Google" again</li>
|
|
698
|
+
</ol>
|
|
699
|
+
|
|
700
|
+
<button class="btn-primary" onclick="closeInstallModal()" style="margin-top: 20px;">Got it!</button>
|
|
701
|
+
</div>
|
|
702
|
+
</div>
|
|
703
|
+
|
|
704
|
+
<script>
|
|
705
|
+
// Copy install command functionality
|
|
706
|
+
function copyInstallCommand() {
|
|
707
|
+
const command = document.getElementById('install-command').textContent;
|
|
708
|
+
navigator.clipboard.writeText(command).then(() => {
|
|
709
|
+
const btn = document.querySelector('.copy-btn');
|
|
710
|
+
const originalContent = btn.innerHTML;
|
|
711
|
+
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20,6 9,17 4,12"></polyline></svg>';
|
|
712
|
+
btn.style.color = '#00ff88';
|
|
713
|
+
setTimeout(() => {
|
|
714
|
+
btn.innerHTML = originalContent;
|
|
715
|
+
btn.style.color = 'white';
|
|
716
|
+
}, 2000);
|
|
717
|
+
}).catch(() => {
|
|
718
|
+
// Fallback for older browsers
|
|
719
|
+
const textArea = document.createElement('textarea');
|
|
720
|
+
textArea.value = command;
|
|
721
|
+
document.body.appendChild(textArea);
|
|
722
|
+
textArea.select();
|
|
723
|
+
document.execCommand('copy');
|
|
724
|
+
document.body.removeChild(textArea);
|
|
725
|
+
alert('Command copied to clipboard!');
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Check if local server is running
|
|
730
|
+
async function checkLocalServer() {
|
|
731
|
+
try {
|
|
732
|
+
const response = await fetch('http://localhost:3000/api/auth/status', {
|
|
733
|
+
method: 'GET',
|
|
734
|
+
timeout: 2000
|
|
735
|
+
});
|
|
736
|
+
return response.ok;
|
|
737
|
+
} catch (error) {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Handle Google login with server detection
|
|
743
|
+
async function handleGoogleLogin() {
|
|
744
|
+
const serverRunning = await checkLocalServer();
|
|
745
|
+
|
|
746
|
+
if (!serverRunning) {
|
|
747
|
+
showInstallModal();
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Redirect to Google OAuth
|
|
752
|
+
window.location.href = 'http://localhost:3000/auth/google';
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Show installation reminder modal
|
|
756
|
+
function showInstallModal() {
|
|
757
|
+
const modal = document.getElementById('install-modal');
|
|
758
|
+
modal.classList.add('show');
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Close installation modal
|
|
762
|
+
function closeInstallModal() {
|
|
763
|
+
const modal = document.getElementById('install-modal');
|
|
764
|
+
modal.classList.remove('show');
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Close modal when clicking outside
|
|
768
|
+
document.addEventListener('click', (e) => {
|
|
769
|
+
const modal = document.getElementById('install-modal');
|
|
770
|
+
if (e.target === modal) {
|
|
771
|
+
closeInstallModal();
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// Smooth scrolling for anchor links
|
|
776
|
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
777
|
+
anchor.addEventListener('click', function (e) {
|
|
778
|
+
e.preventDefault();
|
|
779
|
+
document.querySelector(this.getAttribute('href')).scrollIntoView({
|
|
780
|
+
behavior: 'smooth'
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
// Add intersection observer for animations
|
|
786
|
+
const observerOptions = {
|
|
787
|
+
threshold: 0.1,
|
|
788
|
+
rootMargin: '0px 0px -50px 0px'
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
const observer = new IntersectionObserver((entries) => {
|
|
792
|
+
entries.forEach(entry => {
|
|
793
|
+
if (entry.isIntersecting) {
|
|
794
|
+
entry.target.style.opacity = '1';
|
|
795
|
+
entry.target.style.transform = 'translateY(0)';
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
}, observerOptions);
|
|
799
|
+
|
|
800
|
+
// Observe elements for animation
|
|
801
|
+
document.querySelectorAll('.step, .use-case').forEach(el => {
|
|
802
|
+
el.style.opacity = '0';
|
|
803
|
+
el.style.transform = 'translateY(30px)';
|
|
804
|
+
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
|
805
|
+
observer.observe(el);
|
|
806
|
+
});
|
|
807
|
+
</script>
|
|
808
|
+
</body>
|
|
809
|
+
</html>
|