Various AT Protocol integrations with obsidian
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>Atmosphere OAuth - Redirecting...</title>
7 <style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13 body {
14 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
15 display: flex;
16 align-items: center;
17 justify-content: center;
18 min-height: 100vh;
19 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
20 padding: 1rem;
21 }
22 .container {
23 text-align: center;
24 padding: 3rem 2rem;
25 background: white;
26 border-radius: 16px;
27 box-shadow: 0 20px 60px rgba(0,0,0,0.3);
28 max-width: 500px;
29 width: 100%;
30 }
31 h1 {
32 color: #667eea;
33 margin: 0 0 1rem 0;
34 font-size: 1.75rem;
35 font-weight: 600;
36 }
37 .spinner {
38 margin: 2rem auto;
39 width: 50px;
40 height: 50px;
41 border: 4px solid #f3f3f3;
42 border-top: 4px solid #667eea;
43 border-radius: 50%;
44 animation: spin 1s linear infinite;
45 }
46 @keyframes spin {
47 0% { transform: rotate(0deg); }
48 100% { transform: rotate(360deg); }
49 }
50 p {
51 color: #6b7280;
52 margin: 1rem 0;
53 line-height: 1.6;
54 }
55 .manual-link {
56 margin-top: 2rem;
57 padding: 1rem;
58 background: #f9fafb;
59 border-radius: 8px;
60 border: 1px solid #e5e7eb;
61 display: none;
62 }
63 .manual-link.show {
64 display: block;
65 }
66 .link-text {
67 word-break: break-all;
68 font-family: monospace;
69 font-size: 0.85rem;
70 color: #374151;
71 padding: 0.5rem;
72 background: white;
73 border-radius: 4px;
74 margin-top: 0.5rem;
75 }
76 button {
77 margin-top: 1rem;
78 padding: 0.75rem 1.5rem;
79 background: #667eea;
80 color: white;
81 border: none;
82 border-radius: 8px;
83 font-size: 1rem;
84 font-weight: 500;
85 cursor: pointer;
86 transition: background 0.2s;
87 }
88 button:hover {
89 background: #5568d3;
90 }
91 </style>
92</head>
93<body>
94 <div class="container">
95 <h1>✅ Authentication Successful!</h1>
96 <div class="spinner"></div>
97 <p id="status">Redirecting to Obsidian...</p>
98 <div class="manual-link" id="manual-link">
99 <p>If Obsidian doesn't open automatically:</p>
100 <p style="font-size: 0.9rem; margin-bottom: 0.5rem;">1. Copy the link below</p>
101 <div class="link-text" id="link-text"></div>
102 <button onclick="copyLink()">Copy Link</button>
103 <p style="font-size: 0.9rem; margin-top: 1rem;">2. Open Obsidian and paste it in your browser</p>
104 </div>
105 </div>
106
107 <script>
108 (function() {
109 try {
110 // extract OAuth parameters from URL hash (not search string)
111 const params = new URLSearchParams(window.location.hash.slice(1));
112
113 const obsidianUri = `obsidian://atmosphere-oauth?${params.toString()}`;
114
115 // store the URI for manual copy
116 document.getElementById('link-text').textContent = obsidianUri;
117
118 window.location.href = obsidianUri;
119
120 setTimeout(function() {
121 const spinner = document.querySelector('.spinner');
122 if (spinner) spinner.style.display = 'none';
123
124 // Show success message
125 document.querySelector('h1').textContent = '✅ Redirected!';
126 document.getElementById('status').textContent = 'Return to Obsidian to complete login.';
127
128 setTimeout(function() {
129 try {
130 window.close();
131 } catch (e) {
132 document.getElementById('status').textContent = 'You can close this window now.';
133 }
134 }, 500);
135 }, 500);
136
137 // show manual instructions after a longer delay if still open
138 setTimeout(function() {
139 document.getElementById('manual-link').classList.add('show');
140 }, 3000);
141
142 } catch (error) {
143 console.error('Redirect error:', error);
144 document.querySelector('.spinner').style.display = 'none';
145 document.getElementById('status').textContent = 'An error occurred during redirect';
146 document.getElementById('manual-link').classList.add('show');
147 }
148 })();
149
150 function copyLink() {
151 const linkText = document.getElementById('link-text').textContent;
152 navigator.clipboard.writeText(linkText).then(function() {
153 const btn = event.target;
154 btn.textContent = '✓ Copied!';
155 setTimeout(function() {
156 btn.textContent = 'Copy Link';
157 }, 2000);
158 }).catch(function(err) {
159 console.error('Failed to copy:', err);
160 alert('Failed to copy. Please select and copy the link manually.');
161 });
162 }
163 </script>
164</body>
165</html>