A system for building static webapps
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore(hono): cleanup styles

+287 -98
+4 -27
packages/hono/auth/ui_router.ts
··· 16 16 <meta charset="UTF-8"> 17 17 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 18 18 <title>${title} — Civility</title> 19 - <style> 20 - * { box-sizing: border-box; margin: 0; padding: 0; } 21 - body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #333; line-height: 1.6; } 22 - .container { max-width: 800px; margin: 0 auto; padding: 2rem; } 23 - header { background: #fff; border-bottom: 1px solid #e0e0e0; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; } 24 - header h1 { font-size: 1.25rem; font-weight: 600; } 25 - nav a { margin-left: 1.5rem; color: #666; text-decoration: none; } 26 - nav a:hover { color: #333; } 27 - .card { background: #fff; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } 28 - h2 { font-size: 1.25rem; margin-bottom: 1rem; } 29 - .form-group { margin-bottom: 1rem; } 30 - label { display: block; margin-bottom: 0.5rem; font-weight: 500; } 31 - input, select { width: 100%; padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem; } 32 - button { background: #2563eb; color: #fff; border: none; padding: 0.75rem 1.5rem; border-radius: 4px; font-size: 1rem; cursor: pointer; } 33 - button:hover { background: #1d4ed8; } 34 - button.danger { background: #dc2626; } 35 - button.danger:hover { background: #b91c1c; } 36 - .error { color: #dc2626; margin-bottom: 1rem; } 37 - .success { color: #16a34a; margin-bottom: 1rem; } 38 - table { width: 100%; border-collapse: collapse; } 39 - th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #e0e0e0; } 40 - th { font-weight: 600; color: #666; } 41 - .actions { display: flex; gap: 0.5rem; } 42 - .actions button { padding: 0.5rem 1rem; font-size: 0.875rem; } 43 - .empty { color: #999; font-style: italic; } 44 - </style> 45 - <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> 19 + <link rel="stylesheet" href="/styles.css"> 20 + <script 21 + src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" 22 + ></script> 46 23 </head> 47 24 <body> 48 25 <header>
-1
packages/hono/deno.json
··· 26 26 "hono": "npm:hono@^4.12.16" 27 27 } 28 28 } 29 -
+10 -2
packages/hono/mod.ts
··· 45 45 46 46 import { OpenAPIHono } from '@hono/zod-openapi' 47 47 import { swaggerUI } from '@hono/swagger-ui' 48 + import { serveStatic } from 'hono/deno' 48 49 import { 49 50 createDenoKVAuth, 50 51 DenoKVAuth as _DenoKVAuth, ··· 57 58 import { DenoKVDatabase, DenoKVSyncStore } from './sync/adapters/deno-kv.ts' 58 59 import { createSyncRouter, createSyncUiRouter } from './sync/mod.ts' 59 60 import { 60 - resolveLimits, 61 61 type RateLimitConfig, 62 + resolveLimits, 62 63 type ServerLimits, 63 64 } from './sync/limits.ts' 64 65 import { createAiRouter } from './ai/mod.ts' ··· 103 104 104 105 export { createSyncRouter, createSyncUiRouter } from './sync/mod.ts' 105 106 export type { RateLimitConfig, ServerLimits } from './sync/mod.ts' 106 - export { DEFAULT_LIMITS, DEFAULT_RATE_LIMITS, resolveLimits } from './sync/mod.ts' 107 + export { 108 + DEFAULT_LIMITS, 109 + DEFAULT_RATE_LIMITS, 110 + resolveLimits, 111 + } from './sync/mod.ts' 107 112 export { createAiRouter, createAiServer } from './ai/mod.ts' 108 113 export { DenoKVDatabase, DenoKVSyncStore } from './sync/adapters/deno-kv.ts' 109 114 export { createBunnyStorage } from './storage/bunny.ts' ··· 241 246 limits: resolvedLimits, 242 247 }) 243 248 app.route('', syncUiRouter) 249 + 250 + // Serve static files from /static directory (after API routes) 251 + app.use('/*', serveStatic({ root: './static' })) 244 252 245 253 // Error handler 246 254 app.onError((error: Error, c: Context) => {
packages/hono/static/apple-touch-icon-precompose.png

This is a binary file and will not be displayed.

packages/hono/static/apple-touch-icon.png

This is a binary file and will not be displayed.

packages/hono/static/favicon.ico

This is a binary file and will not be displayed.

+205
packages/hono/static/styles.css
··· 1 + * { 2 + box-sizing: border-box; 3 + margin: 0; 4 + padding: 0; 5 + } 6 + body { 7 + font-family: 8 + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 9 + background: #f5f5f5; 10 + color: #333; 11 + line-height: 1.6; 12 + } 13 + .container { 14 + max-width: 800px; 15 + margin: 0 auto; 16 + padding: 2rem; 17 + } 18 + header { 19 + background: #fff; 20 + border-bottom: 1px solid #e0e0e0; 21 + padding: 1rem 2rem; 22 + display: flex; 23 + justify-content: space-between; 24 + align-items: center; 25 + } 26 + header h1 { 27 + font-size: 1.25rem; 28 + font-weight: 600; 29 + } 30 + nav a { 31 + margin-left: 1.5rem; 32 + color: #666; 33 + text-decoration: none; 34 + } 35 + nav a:hover { 36 + color: #333; 37 + } 38 + .card { 39 + background: #fff; 40 + border-radius: 8px; 41 + padding: 1.5rem; 42 + margin-bottom: 1rem; 43 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 44 + } 45 + h2 { 46 + font-size: 1.25rem; 47 + margin-bottom: 1rem; 48 + } 49 + .form-group { 50 + margin-bottom: 1rem; 51 + } 52 + label { 53 + display: block; 54 + margin-bottom: 0.5rem; 55 + font-weight: 500; 56 + } 57 + input, select, textarea { 58 + width: 100%; 59 + padding: 0.75rem; 60 + border: 1px solid #ddd; 61 + border-radius: 4px; 62 + font-size: 1rem; 63 + font-family: inherit; 64 + } 65 + button { 66 + background: #2563eb; 67 + color: #fff; 68 + border: none; 69 + padding: 0.75rem 1.5rem; 70 + border-radius: 4px; 71 + font-size: 1rem; 72 + cursor: pointer; 73 + } 74 + button:hover { 75 + background: #1d4ed8; 76 + } 77 + button.danger { 78 + background: #dc2626; 79 + } 80 + button.danger:hover { 81 + background: #b91c1c; 82 + } 83 + button.secondary { 84 + background: #6b7280; 85 + } 86 + button.secondary:hover { 87 + background: #4b5563; 88 + } 89 + .error { 90 + color: #dc2626; 91 + margin-bottom: 1rem; 92 + } 93 + .success { 94 + color: #16a34a; 95 + margin-bottom: 1rem; 96 + } 97 + table { 98 + width: 100%; 99 + border-collapse: collapse; 100 + } 101 + th, td { 102 + padding: 0.75rem; 103 + text-align: left; 104 + border-bottom: 1px solid #e0e0e0; 105 + } 106 + th { 107 + font-weight: 600; 108 + color: #666; 109 + } 110 + .actions { 111 + display: flex; 112 + gap: 0.5rem; 113 + } 114 + .actions button { 115 + padding: 0.5rem 1rem; 116 + font-size: 0.875rem; 117 + } 118 + .empty { 119 + color: #999; 120 + font-style: italic; 121 + } 122 + .app-name { 123 + font-weight: 600; 124 + } 125 + .app-meta { 126 + color: #666; 127 + font-size: 0.875rem; 128 + } 129 + code { 130 + background: #f3f4f6; 131 + padding: 0.25rem 0.5rem; 132 + border-radius: 4px; 133 + font-size: 0.875rem; 134 + } 135 + .token-item { 136 + display: flex; 137 + align-items: center; 138 + padding: 0.75rem 0; 139 + border-bottom: 1px solid #e0e0e0; 140 + } 141 + .token-item:last-of-type { 142 + border-bottom: none; 143 + } 144 + .token-info { 145 + flex: 1; 146 + } 147 + .token-name { 148 + font-weight: 500; 149 + } 150 + .token-meta { 151 + display: block; 152 + font-size: 0.8rem; 153 + color: #666; 154 + margin-top: 0.125rem; 155 + } 156 + button.small { 157 + padding: 0.375rem 0.75rem; 158 + font-size: 0.875rem; 159 + } 160 + dialog { 161 + border: none; 162 + border-radius: 8px; 163 + padding: 1.5rem; 164 + max-width: 460px; 165 + width: 90vw; 166 + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18); 167 + } 168 + dialog::backdrop { 169 + background: rgba(0, 0, 0, 0.45); 170 + } 171 + dialog h3 { 172 + font-size: 1.1rem; 173 + margin-bottom: 1.25rem; 174 + } 175 + dialog hr { 176 + border: none; 177 + border-top: 1px solid #e5e7eb; 178 + margin: 1.25rem 0; 179 + } 180 + dl { 181 + display: grid; 182 + grid-template-columns: auto 1fr; 183 + gap: 0.375rem 1rem; 184 + } 185 + dt { 186 + font-weight: 600; 187 + color: #666; 188 + font-size: 0.85rem; 189 + } 190 + dd { 191 + font-size: 0.85rem; 192 + word-break: break-all; 193 + } 194 + .dialog-header { 195 + display: flex; 196 + justify-content: space-between; 197 + align-items: center; 198 + margin-bottom: 1.25rem; 199 + } 200 + .dialog-footer { 201 + display: flex; 202 + justify-content: space-between; 203 + align-items: center; 204 + margin-top: 1.25rem; 205 + }
+14 -4
packages/hono/sync/router.ts
··· 10 10 import { err, ok } from '../shared/response.ts' 11 11 import { 12 12 checkRateLimit, 13 + type RateLimitConfig, 13 14 resolveLimits, 14 15 resolveRateLimits, 15 - type RateLimitConfig, 16 16 type ServerLimits, 17 17 } from './limits.ts' 18 18 ··· 182 182 rateLimits?: Partial<RateLimitConfig> 183 183 } 184 184 185 - export type { ServerLimits, RateLimitConfig } 185 + export type { RateLimitConfig, ServerLimits } 186 186 187 187 /** Parse legacy permission strings or new JSON PermissionScope[] format. */ 188 188 export function parsePermissions(permissions: string): PermissionScope[] { ··· 467 467 if (!id) return err(c, 'validation.missing_field', 'App ID required') 468 468 469 469 if (kv) { 470 - const rl = await checkRateLimit(kv, 'sync', ctx.user.id, rateLimitCfg.sync) 470 + const rl = await checkRateLimit( 471 + kv, 472 + 'sync', 473 + ctx.user.id, 474 + rateLimitCfg.sync, 475 + ) 471 476 if (!rl.allowed) { 472 477 c.header( 473 478 'Retry-After', ··· 544 549 if (!id) return err(c, 'validation.missing_field', 'App ID required') 545 550 546 551 if (kv) { 547 - const rl = await checkRateLimit(kv, 'sync', ctx.user.id, rateLimitCfg.sync) 552 + const rl = await checkRateLimit( 553 + kv, 554 + 'sync', 555 + ctx.user.id, 556 + rateLimitCfg.sync, 557 + ) 548 558 if (!rl.allowed) { 549 559 c.header( 550 560 'Retry-After',
+54 -64
packages/hono/sync/ui_router.ts
··· 67 67 <meta charset="UTF-8"> 68 68 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 69 69 <title>${title} — Civility</title> 70 - <style> 71 - * { box-sizing: border-box; margin: 0; padding: 0; } 72 - body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #333; line-height: 1.6; } 73 - .container { max-width: 800px; margin: 0 auto; padding: 2rem; } 74 - header { background: #fff; border-bottom: 1px solid #e0e0e0; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; } 75 - header h1 { font-size: 1.25rem; font-weight: 600; } 76 - nav a { margin-left: 1.5rem; color: #666; text-decoration: none; } 77 - nav a:hover { color: #333; } 78 - .card { background: #fff; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } 79 - h2 { font-size: 1.25rem; margin-bottom: 1rem; } 80 - .form-group { margin-bottom: 1rem; } 81 - label { display: block; margin-bottom: 0.5rem; font-weight: 500; } 82 - input, select, textarea { width: 100%; padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem; font-family: inherit; } 83 - button { background: #2563eb; color: #fff; border: none; padding: 0.75rem 1.5rem; border-radius: 4px; font-size: 1rem; cursor: pointer; } 84 - button:hover { background: #1d4ed8; } 85 - button.danger { background: #dc2626; } 86 - button.danger:hover { background: #b91c1c; } 87 - button.secondary { background: #6b7280; } 88 - button.secondary:hover { background: #4b5563; } 89 - .error { color: #dc2626; margin-bottom: 1rem; } 90 - .success { color: #16a34a; margin-bottom: 1rem; } 91 - table { width: 100%; border-collapse: collapse; } 92 - th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #e0e0e0; } 93 - th { font-weight: 600; color: #666; } 94 - .actions { display: flex; gap: 0.5rem; } 95 - .actions button { padding: 0.5rem 1rem; font-size: 0.875rem; } 96 - .empty { color: #999; font-style: italic; } 97 - .app-name { font-weight: 600; } 98 - .app-meta { color: #666; font-size: 0.875rem; } 99 - code { background: #f3f4f6; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.875rem; } 100 - .token-item { display: flex; align-items: center; padding: 0.75rem 0; border-bottom: 1px solid #e0e0e0; } 101 - .token-item:last-of-type { border-bottom: none; } 102 - .token-info { flex: 1; } 103 - .token-name { font-weight: 500; } 104 - .token-meta { display: block; font-size: 0.8rem; color: #666; margin-top: 0.125rem; } 105 - button.small { padding: 0.375rem 0.75rem; font-size: 0.875rem; } 106 - dialog { border: none; border-radius: 8px; padding: 1.5rem; max-width: 460px; width: 90vw; box-shadow: 0 4px 24px rgba(0,0,0,0.18); } 107 - dialog::backdrop { background: rgba(0,0,0,0.45); } 108 - dialog h3 { font-size: 1.1rem; margin-bottom: 1.25rem; } 109 - dialog hr { border: none; border-top: 1px solid #e5e7eb; margin: 1.25rem 0; } 110 - dl { display: grid; grid-template-columns: auto 1fr; gap: 0.375rem 1rem; } 111 - dt { font-weight: 600; color: #666; font-size: 0.85rem; } 112 - dd { font-size: 0.85rem; word-break: break-all; } 113 - .dialog-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.25rem; } 114 - .dialog-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 1.25rem; } 115 - </style> 116 - <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> 70 + <link rel="stylesheet" href="/styles.css"> 71 + <script 72 + src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" 73 + ></script> 117 74 </head> 118 75 <body> 119 76 <header> ··· 212 169 : '' 213 170 214 171 const PALETTE = [ 215 - '#2563eb', '#d97706', '#059669', '#7c3aed', '#dc2626', 216 - '#0891b2', '#65a30d', '#c2410c', '#9333ea', '#0d9488', 172 + '#2563eb', 173 + '#d97706', 174 + '#059669', 175 + '#7c3aed', 176 + '#dc2626', 177 + '#0891b2', 178 + '#65a30d', 179 + '#c2410c', 180 + '#9333ea', 181 + '#0d9488', 217 182 ] 218 183 219 184 return html` ··· 229 194 datasets: [{ 230 195 label: 'Storage (KB)', 231 196 data, 232 - backgroundColor: labels.map((_, i) => PALETTE[i % PALETTE.length]), 197 + backgroundColor: labels.map((_, i) => 198 + PALETTE[i % PALETTE.length] 199 + ), 233 200 borderRadius: 4, 234 201 }], 235 202 }, ··· 319 286 <button 320 287 type="button" 321 288 class="secondary small" 322 - onclick="document.getElementById('dialog-${token.id}').showModal()" 323 - >Edit</button> 289 + onclick="document.getElementById('dialog-${token 290 + .id}').showModal()" 291 + > 292 + Edit 293 + </button> 324 294 </div> 325 295 326 296 <dialog id="dialog-${token.id}"> ··· 330 300 type="button" 331 301 class="secondary small" 332 302 onclick="this.closest('dialog').close()" 333 - >✕</button> 303 + > 304 + 305 + </button> 334 306 </div> 335 307 <form 336 308 method="POST" ··· 374 346 type="button" 375 347 class="secondary small" 376 348 onclick="this.closest('dialog').close()" 377 - >Close</button> 349 + > 350 + Close 351 + </button> 378 352 </div> 379 353 </dialog> 380 354 ` ··· 405 379 <p><strong>Total:</strong> ${formatBytes( 406 380 storageStats.dataSize + storageStats.blobSize, 407 381 )}${limits 408 - ? html` / ${formatBytes(limits.maxStoragePerAppBytes)}` 382 + ? html` 383 + / ${formatBytes(limits.maxStoragePerAppBytes)} 384 + ` 409 385 : ''}</p> 410 - <p style="margin-top:0.5rem;"><strong>Changes:</strong> ${storageStats.changeCount} (${formatBytes( 411 - storageStats.dataSize, 412 - )})</p> 413 - <p><strong>Blobs:</strong> ${storageStats.blobCount} (${formatBytes( 414 - storageStats.blobSize, 415 - )})</p> 386 + <p style="margin-top:0.5rem;"> 387 + <strong>Changes:</strong> ${storageStats 388 + .changeCount} (${formatBytes( 389 + storageStats.dataSize, 390 + )}) 391 + </p> 392 + <p><strong>Blobs:</strong> ${storageStats 393 + .blobCount} (${formatBytes( 394 + storageStats.blobSize, 395 + )})</p> 416 396 </div> 417 397 ${(storageStats.dataSize + storageStats.blobSize) > 0 418 398 ? renderChart('app-storage-chart', { ··· 514 494 try { 515 495 const parsed = JSON.parse(raw) 516 496 if (Array.isArray(parsed)) { 517 - const levels = [...new Set((parsed as { level: string }[]).map((s) => s.level))] 497 + const levels = [ 498 + ...new Set((parsed as { level: string }[]).map((s) => s.level)), 499 + ] 518 500 return humanLevels(levels) 519 501 } 520 502 } catch { /* fall through */ } ··· 798 780 799 781 return c.html(appsListPage( 800 782 apps, 801 - html`<div></div>`, 783 + html` 784 + <div></div> 785 + `, 802 786 config.path, 803 787 appStats, 804 788 config.limits, ··· 834 818 // Storage stats not available 835 819 } 836 820 return c.html( 837 - appDetailPage(appRecord, tokens, config.path, storageStats, config.limits), 821 + appDetailPage( 822 + appRecord, 823 + tokens, 824 + config.path, 825 + storageStats, 826 + config.limits, 827 + ), 838 828 ) 839 829 }) 840 830