prototypey.org - atproto lexicon typescript toolkit - mirror https://github.com/tylersayshi/prototypey

start ui improvements for mobile

Todos
☐ Remove vertical scrollbars from mobile code snippets
☐ Use actual playground code in mobile view
☐ Test mobile snippet display

Tyler ea95cbd7 360a92ac

+210 -27
+2 -10
packages/site/src/components/Header.tsx
··· 7 7 }} 8 8 > 9 9 <div style={{ maxWidth: "1400px", margin: "0 auto" }}> 10 - <div 11 - style={{ 12 - display: "flex", 13 - justifyContent: "space-between", 14 - alignItems: "flex-start", 15 - }} 16 - > 10 + <div className="header-content"> 17 11 <div> 18 12 <h1 19 13 style={{ ··· 34 28 Type-safe lexicon inference for ATProto schemas 35 29 </p> 36 30 </div> 37 - <div 38 - style={{ display: "flex", gap: "1.25rem", paddingTop: "0.5rem" }} 39 - > 31 + <div className="header-links"> 40 32 <a 41 33 href="https://github.com/tylersayshi/prototypey" 42 34 target="_blank"
+164 -17
packages/site/src/components/Playground.tsx
··· 6 6 import type * as Monaco from "monaco-editor"; 7 7 import { parseAsString, useQueryState } from "nuqs"; 8 8 import LZString from "lz-string"; 9 + import MonacoEditor from "@monaco-editor/react"; 9 10 10 11 let tsWorkerInstance: Monaco.languages.typescript.TypeScriptWorker | null = 11 12 null; ··· 139 140 }, [code, monaco]); 140 141 141 142 return ( 142 - <div 143 - style={{ 144 - flex: 1, 145 - display: "flex", 146 - overflow: "hidden", 147 - }} 148 - > 143 + <> 144 + {/* Desktop playground */} 149 145 <div 146 + className="desktop-only" 150 147 style={{ 151 148 flex: 1, 152 - display: "flex", 153 - borderRight: "1px solid #e5e7eb", 149 + overflow: "hidden", 154 150 }} 155 151 > 156 - <Editor 157 - value={code} 158 - onChange={handleCodeChange} 159 - onReady={handleEditorReady} 160 - /> 152 + <div 153 + style={{ 154 + flex: 1, 155 + display: "flex", 156 + borderRight: "1px solid #e5e7eb", 157 + }} 158 + > 159 + <Editor 160 + value={code} 161 + onChange={handleCodeChange} 162 + onReady={handleEditorReady} 163 + /> 164 + </div> 165 + <div style={{ flex: 1, display: "flex" }}> 166 + <OutputPanel output={output} /> 167 + </div> 161 168 </div> 162 - <div style={{ flex: 1, display: "flex" }}> 163 - <OutputPanel output={output} /> 169 + 170 + {/* Mobile static demo */} 171 + <div 172 + className="mobile-only" 173 + style={{ 174 + flex: 1, 175 + flexDirection: "column", 176 + overflow: "auto", 177 + padding: "1rem", 178 + }} 179 + > 180 + <div 181 + style={{ 182 + backgroundColor: "#f9fafb", 183 + padding: "1rem", 184 + borderRadius: "0.5rem", 185 + marginBottom: "1rem", 186 + textAlign: "center", 187 + color: "#6b7280", 188 + fontSize: "0.875rem", 189 + }} 190 + > 191 + Playground available on desktop 192 + </div> 193 + 194 + <MobileStaticDemo /> 164 195 </div> 165 - </div> 196 + </> 166 197 ); 167 198 } 168 199 ··· 194 225 } 195 226 196 227 return indentedLines.join("\n"); 228 + } 229 + 230 + function MobileStaticDemo({ 231 + code, 232 + json, 233 + }: { 234 + code: string; 235 + json: string; 236 + }) { 237 + // Calculate line counts to size editors appropriately 238 + const codeLines = code.split("\n").length; 239 + const jsonLines = json.split("\n").length; 240 + 241 + return ( 242 + <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}> 243 + {/* You write section */} 244 + <div style={{ display: "flex", flexDirection: "column" }}> 245 + <div 246 + style={{ 247 + padding: "0.75rem 1rem", 248 + backgroundColor: "#f9fafb", 249 + borderBottom: "1px solid #e5e7eb", 250 + fontSize: "0.875rem", 251 + fontWeight: "600", 252 + color: "#374151", 253 + borderTopLeftRadius: "0.5rem", 254 + borderTopRightRadius: "0.5rem", 255 + }} 256 + > 257 + You write 258 + </div> 259 + <div 260 + style={{ 261 + border: "1px solid #e5e7eb", 262 + borderTop: "none", 263 + borderBottomLeftRadius: "0.5rem", 264 + borderBottomRightRadius: "0.5rem", 265 + overflow: "hidden", 266 + }} 267 + > 268 + <MonacoEditor 269 + height={`${Math.min(codeLines * 19 + 32, 500)}px`} 270 + defaultLanguage="typescript" 271 + value={code} 272 + theme="vs-light" 273 + options={{ 274 + readOnly: true, 275 + minimap: { enabled: false }, 276 + fontSize: 12, 277 + lineNumbers: "on", 278 + renderLineHighlight: "none", 279 + scrollBeyondLastLine: false, 280 + padding: { top: 16, bottom: 16 }, 281 + scrollbar: { 282 + vertical: "hidden", 283 + horizontal: "auto", 284 + horizontalScrollbarSize: 8, 285 + }, 286 + wordWrap: "on", 287 + overviewRulerLanes: 0, 288 + }} 289 + /> 290 + </div> 291 + </div> 292 + 293 + {/* JSON generated section */} 294 + <div style={{ display: "flex", flexDirection: "column" }}> 295 + <div 296 + style={{ 297 + padding: "0.75rem 1rem", 298 + backgroundColor: "#f9fafb", 299 + borderBottom: "1px solid #e5e7eb", 300 + fontSize: "0.875rem", 301 + fontWeight: "600", 302 + color: "#374151", 303 + borderTopLeftRadius: "0.5rem", 304 + borderTopRightRadius: "0.5rem", 305 + }} 306 + > 307 + JSON generated 308 + </div> 309 + <div 310 + style={{ 311 + border: "1px solid #e5e7eb", 312 + borderTop: "none", 313 + borderBottomLeftRadius: "0.5rem", 314 + borderBottomRightRadius: "0.5rem", 315 + overflow: "hidden", 316 + }} 317 + > 318 + <MonacoEditor 319 + height={`${Math.min(jsonLines * 19 + 32, 600)}px`} 320 + defaultLanguage="json" 321 + value={json} 322 + theme="vs-light" 323 + options={{ 324 + readOnly: true, 325 + minimap: { enabled: false }, 326 + fontSize: 12, 327 + lineNumbers: "on", 328 + renderLineHighlight: "none", 329 + scrollBeyondLastLine: false, 330 + padding: { top: 16, bottom: 16 }, 331 + scrollbar: { 332 + vertical: "hidden", 333 + horizontal: "auto", 334 + horizontalScrollbarSize: 8, 335 + }, 336 + wordWrap: "on", 337 + overviewRulerLanes: 0, 338 + }} 339 + /> 340 + </div> 341 + </div> 342 + </div> 343 + ); 197 344 } 198 345 199 346 const DEFAULT_CODE = `import { lx, type Infer } from "prototypey";
+44
packages/site/src/index.css
··· 30 30 display: flex; 31 31 flex-direction: column; 32 32 } 33 + 34 + /* Desktop layout - default */ 35 + .header-content { 36 + display: flex; 37 + justify-content: space-between; 38 + align-items: flex-start; 39 + } 40 + 41 + .header-links { 42 + display: flex; 43 + gap: 1.25rem; 44 + padding-top: 0.5rem; 45 + } 46 + 47 + .mobile-only { 48 + display: none; 49 + } 50 + 51 + .desktop-only { 52 + display: flex; 53 + } 54 + 55 + /* Mobile layout */ 56 + @media (max-width: 768px) { 57 + .header-content { 58 + flex-direction: column; 59 + gap: 1rem; 60 + } 61 + 62 + .header-links { 63 + padding-top: 0; 64 + width: 100%; 65 + justify-content: flex-start; 66 + } 67 + 68 + .mobile-only { 69 + display: flex !important; 70 + flex-direction: column; 71 + } 72 + 73 + .desktop-only { 74 + display: none !important; 75 + } 76 + }