Full document, spreadsheet, slideshow, and diagram tooling
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, maximum-scale=1, viewport-fit=cover">
6 <link rel="manifest" href="/manifest.json">
7 <meta name="description" content="Documents, spreadsheets, and more — stored locally in your browser. Sign in with your Bluesky account.">
8 <meta property="og:title" content="Atmosphere Office">
9 <meta property="og:description" content="Documents, spreadsheets, and more — stored locally in your browser. Sign in with your Bluesky account.">
10 <meta property="og:type" content="website">
11 <meta property="og:image" content="/favicon.svg">
12 <title>Atmosphere Office</title>
13 <meta name="theme-color" content="#3a8a7a">
14 <link rel="icon" type="image/svg+xml" href="/favicon.svg">
15 <link rel="apple-touch-icon" href="/favicon.svg">
16 <link rel="stylesheet" href="./css/app.css">
17 <!-- Theme init runs before paint to avoid FOUC; externalized to satisfy CSP (see #694). -->
18 <script src="/theme-init.js"></script>
19</head>
20<body>
21 <a class="skip-link" href="#main-content">Skip to content</a>
22 <main class="landing" id="app">
23 <header class="landing-header">
24 <div class="brand">
25 <span class="brand-name">Atmosphere</span>
26 <span style="flex:1"></span>
27 <span class="user-badge" id="user-badge" title="Account" role="button" tabindex="0" aria-label="Account menu" style="display:none"></span>
28 <button class="theme-toggle" id="theme-toggle" title="Toggle dark mode" aria-label="Toggle dark mode"></button>
29 </div>
30 <div class="landing-actions">
31 <div class="new-dropdown" id="new-dropdown">
32 <button class="new-btn" id="new-btn" aria-haspopup="true" aria-expanded="false">
33 <span class="new-btn-plus">+</span>
34 <span>New</span>
35 <span class="new-btn-caret">▾</span>
36 </button>
37 <div class="new-menu" id="new-menu" role="menu">
38 <button class="new-menu-item" data-new="doc" role="menuitem">
39 <span class="new-menu-icon">✎</span>
40 <span class="new-menu-label">Document</span>
41 </button>
42 <button class="new-menu-item" data-new="diagram" role="menuitem">
43 <span class="new-menu-icon">◓</span>
44 <span class="new-menu-label">Diagram</span>
45 </button>
46 <button class="new-menu-item" data-new="sheet" role="menuitem">
47 <span class="new-menu-icon">▦</span>
48 <span class="new-menu-label">Spreadsheet</span>
49 </button>
50 <button class="new-menu-item new-menu-item-disabled" data-new="form" role="menuitem" disabled title="Coming soon">
51 <span class="new-menu-icon">☷</span>
52 <span class="new-menu-label">Form</span>
53 <span class="new-menu-badge">Soon</span>
54 </button>
55 <button class="new-menu-item new-menu-item-disabled" data-new="slide" role="menuitem" disabled title="Coming soon">
56 <span class="new-menu-icon">◫</span>
57 <span class="new-menu-label">Presentation</span>
58 <span class="new-menu-badge">Soon</span>
59 </button>
60 <button class="new-menu-item new-menu-item-disabled" data-new="calendar" role="menuitem" disabled title="Coming soon">
61 <span class="new-menu-icon">☰</span>
62 <span class="new-menu-label">Calendar</span>
63 <span class="new-menu-badge">Soon</span>
64 </button>
65 </div>
66 </div>
67 <button class="daily-note-btn" id="daily-note">
68 <span class="daily-note-icon">♦</span>
69 <span>Today's Note</span>
70 </button>
71 </div>
72 </header>
73
74 <section class="doc-section" id="main-content">
75 <div id="recent-section" class="recent-section"></div>
76 <div id="pinned-section" class="pinned-section"></div>
77 <div class="doc-toolbar">
78 <div class="doc-breadcrumbs" id="breadcrumbs"></div>
79 <div class="doc-toolbar-actions">
80 <div class="search-box" id="search-box">
81 <input type="text" class="search-input" id="search-input" placeholder="Search documents..." aria-label="Search documents" />
82 <button class="search-clear" id="search-clear" title="Clear search" aria-label="Clear search">×</button>
83 </div>
84 <div class="sort-dropdown" id="sort-dropdown">
85 <button class="sort-btn" id="sort-btn">Sort: <span id="sort-label">Last updated</span></button>
86 <div class="sort-menu" id="sort-menu">
87 <button class="sort-option" data-sort="updated">Last updated</button>
88 <button class="sort-option" data-sort="created">Created</button>
89 <button class="sort-option" data-sort="name">Name</button>
90 <button class="sort-option" data-sort="type">Type</button>
91 </div>
92 </div>
93 <button class="view-toggle" id="view-toggle" title="Toggle grid/list view" aria-label="Toggle view">
94 <svg class="view-icon view-icon-grid" viewBox="0 0 16 16" width="16" height="16"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>
95 <svg class="view-icon view-icon-list" viewBox="0 0 16 16" width="16" height="16"><rect x="1" y="2" width="14" height="2.5" rx="0.5"/><rect x="1" y="6.75" width="14" height="2.5" rx="0.5"/><rect x="1" y="11.5" width="14" height="2.5" rx="0.5"/></svg>
96 </button>
97 <div class="more-menu" id="more-menu">
98 <button class="more-btn" id="more-btn" title="More actions" aria-label="More actions" aria-haspopup="true" aria-expanded="false">
99 <svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.5"/><circle cx="8" cy="8" r="1.5"/><circle cx="12.5" cy="8" r="1.5"/></svg>
100 </button>
101 <div class="more-menu-panel" id="more-menu-panel" role="menu">
102 <button class="more-menu-item" id="new-folder-btn" role="menuitem">
103 <span class="more-menu-icon">📁</span> New Folder
104 </button>
105 <button class="more-menu-item" id="file-import-btn" role="menuitem">
106 <span class="more-menu-icon">⇧</span> Import file…
107 </button>
108 <div class="more-menu-sep"></div>
109 <button class="more-menu-item" id="backup-export-btn" role="menuitem">
110 <span class="more-menu-icon">⇩</span> Export backup
111 </button>
112 <button class="more-menu-item" id="backup-import-btn" role="menuitem">
113 <span class="more-menu-icon">⇧</span> Restore backup…
114 </button>
115 </div>
116 </div>
117 <input type="file" id="file-import-input" accept=".docx,.xlsx,.xls,.csv,.tsv,.md,.txt,.pdf,.pptx" style="display:none">
118 <input type="file" id="backup-import-input" accept=".json" style="display:none">
119 </div>
120 </div>
121 <div id="forge-workspaces"></div>
122 <div id="folder-list"></div>
123 <div id="doc-list"></div>
124 <div id="no-results" class="no-results" style="display:none;">No documents match your search.</div>
125 <div id="content-search-results" style="display:none"></div>
126 </section>
127
128 <section class="trash-section" id="trash-section" style="display:none;">
129 <button class="trash-toggle" id="trash-toggle">
130 <span class="trash-toggle-icon">▶</span>
131 Trash <span class="trash-count" id="trash-count"></span>
132 </button>
133 <div class="trash-list" id="trash-list"></div>
134 </section>
135
136 <footer class="site-footer">
137 <div class="encryption-bar">
138 <span class="encryption-dot"></span>
139 <span id="storage-status-text">All documents are stored locally in your browser.</span>
140 <button class="instance-info-btn" id="instance-info-btn" title="About this instance" aria-label="About this instance">?</button>
141 </div>
142 <div class="site-footer-links">
143 <span>Atmosphere Mail LLC</span>
144 <span class="footer-sep">·</span>
145 <a href="https://atmospheremail.com/terms">Terms</a>
146 <span class="footer-sep">·</span>
147 <a href="https://atmospheremail.com/privacy">Privacy</a>
148 <span class="footer-sep">·</span>
149 <a href="https://atmospheremail.com/aup">Acceptable use</a>
150 <span class="footer-sep">·</span>
151 <a href="https://atmospheremail.com/about">About</a>
152 <span class="footer-sep">·</span>
153 <a href="https://tangled.org/scottlanoue.com/atmosphere-office" target="_blank" rel="noopener">Source</a>
154 </div>
155 </footer>
156 </main>
157
158 <!-- Setup screen (shown before sign-in for new visitors) -->
159 <div class="modal-backdrop" id="setup-screen" style="display:none;">
160 <div class="modal setup-modal" role="dialog" aria-modal="true" aria-labelledby="setup-title">
161 <div class="setup-preview-banner" style="display:none;"></div>
162 <h2 class="setup-title" id="setup-title"></h2>
163 <p class="setup-summary"></p>
164 <div class="setup-capabilities-section">
165 <h3>What you can do</h3>
166 <ul class="setup-capabilities"></ul>
167 </div>
168 <div class="setup-limitations-section">
169 <h3>Current limitations</h3>
170 <ul class="setup-limitations"></ul>
171 </div>
172 <div class="setup-disclaimer">
173 <p>All data is stored locally in your browser. Atmosphere Mail LLC does not store, access, or back up your documents. You are solely responsible for your data.</p>
174 </div>
175 <button id="setup-continue" class="btn-primary">Continue to Sign In</button>
176 </div>
177 </div>
178
179 <!-- Sign-in modal (visible by default to prevent flash of main UI) -->
180 <div class="modal-backdrop" id="username-modal">
181 <div class="modal username-modal" role="dialog" aria-modal="true" aria-labelledby="username-modal-title">
182 <h2 id="username-modal-title">Sign in</h2>
183 <p class="welcome-signin-label">Sign in with your Atmosphere account</p>
184 <input type="text" class="username-input" id="username-input" placeholder="you.bsky.social" maxlength="100" autofocus />
185 <div class="username-modal-actions">
186 <button class="btn-primary" id="username-confirm">Sign In</button>
187 </div>
188 </div>
189 </div>
190
191 <!-- Waitlist modal (shown when user is not on allowlist) -->
192 <div class="modal-backdrop" id="waitlist-modal" style="display:none;">
193 <div class="modal waitlist-modal" role="dialog" aria-modal="true" aria-labelledby="waitlist-title">
194 <h2 id="waitlist-title">You're on the list</h2>
195 <p class="waitlist-handle" id="waitlist-handle"></p>
196 <p class="waitlist-message">Atmosphere Office is in private preview. Your account has been noted and you'll be added soon.</p>
197 <p class="waitlist-cta">Want faster access? Follow <a href="https://bsky.app/profile/scottlanoue.com" target="_blank" rel="noopener">@scottlanoue.com</a> on Bluesky and send a DM — especially if you're a PDS operator.</p>
198 <p class="waitlist-source">In the meantime, Atmosphere Office is open source. You can <a href="https://tangled.org/scottlanoue.com/atmosphere-office" target="_blank" rel="noopener">run your own instance</a> today.</p>
199 <div class="waitlist-actions">
200 <a href="https://bsky.app/profile/scottlanoue.com" target="_blank" rel="noopener" class="btn-primary">Follow on Bluesky</a>
201 </div>
202 </div>
203 </div>
204
205 <!-- Folder name modal -->
206 <div class="modal-backdrop" id="folder-modal" style="display:none;">
207 <div class="modal folder-modal" role="dialog" aria-modal="true" aria-labelledby="folder-modal-title">
208 <h2 id="folder-modal-title">New Folder</h2>
209 <input type="text" class="folder-name-input" id="folder-name-input" placeholder="Folder name" maxlength="100" />
210 <div class="folder-modal-actions">
211 <button class="btn-secondary" id="folder-cancel">Cancel</button>
212 <button class="btn-primary" id="folder-confirm">Create</button>
213 </div>
214 </div>
215 </div>
216
217 <!-- Move to folder modal -->
218 <div class="modal-backdrop" id="move-modal" style="display:none;">
219 <div class="modal move-modal" role="dialog" aria-modal="true" aria-labelledby="move-modal-title">
220 <h2 id="move-modal-title">Move to Folder</h2>
221 <div class="move-folder-list" id="move-folder-list"></div>
222 <div class="move-modal-actions">
223 <button class="btn-secondary" id="move-cancel">Cancel</button>
224 </div>
225 </div>
226 </div>
227
228 <div class="version-badge tailnet-footer">v%APP_VERSION%</div>
229
230 <!-- Drag-and-drop import overlay -->
231 <div class="drop-overlay" id="drop-overlay" style="display:none;">
232 <div class="drop-overlay-content">
233 <span class="drop-overlay-icon">⇩</span>
234 <span class="drop-overlay-text">Drop to import</span>
235 <span class="drop-overlay-hint">.docx, .xlsx, .csv, .md, .pdf, .pptx</span>
236 </div>
237 </div>
238
239 <script type="module" src="./landing.ts"></script>
240 <script src="/theme-toggle.js"></script>
241 <script src="/sw-reload.js"></script>
242</body>
243</html>