|
1 | 1 | <script lang="ts"> |
2 | 2 | import { goto } from '$app/navigation'; |
3 | 3 | import { getCatalogItem, CATALOG_CATEGORIES } from '$lib/macos-prefs-catalog'; |
| 4 | + import ShareModal from '$lib/components/ShareModal.svelte'; |
| 5 | + import MacOSPreferencesDisplay from '$lib/components/MacOSPreferencesDisplay.svelte'; |
4 | 6 |
|
5 | 7 | let { |
6 | 8 | configUser, |
|
14 | 16 |
|
15 | 17 | let copied = $state(false); |
16 | 18 | let showShareModal = $state(false); |
17 | | - let shareCopied = $state(false); |
18 | 19 | let forking = $state(false); |
19 | 20 | let forkError = $state(''); |
20 | 21 | let showAllApps = $state(false); |
|
56 | 57 | } |
57 | 58 |
|
58 | 59 | function openShareModal() { |
59 | | - shareCopied = false; |
60 | 60 | showShareModal = true; |
61 | 61 | } |
62 | 62 |
|
63 | | - function closeShareModal() { |
64 | | - showShareModal = false; |
65 | | - } |
66 | | -
|
67 | | - function shareCopyLink() { |
68 | | - navigator.clipboard.writeText(getShareUrl()); |
69 | | - shareCopied = true; |
70 | | - setTimeout(() => shareCopied = false, 2000); |
71 | | - } |
72 | | -
|
73 | | - function shareOnTwitter() { |
74 | | - const text = `My dev stack: ${config.name} — set up in minutes with @openbootdotdev`; |
75 | | - const hashtags = 'OpenBoot,macOS,DevTools'; |
76 | | - const tweetUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(getShareUrl())}&hashtags=${encodeURIComponent(hashtags)}`; |
77 | | - window.open(tweetUrl, '_blank', 'width=550,height=420'); |
78 | | - } |
79 | | -
|
80 | 63 | async function forkConfig() { |
81 | 64 | forking = true; |
82 | 65 | forkError = ''; |
|
469 | 452 | {#if macosPrefs.length > 0} |
470 | 453 | <section class="section"> |
471 | 454 | <h2 class="section-title">🍎 macOS Preferences</h2> |
472 | | - <div class="prefs-accordion"> |
473 | | - {#each prefCategoryNames as cat} |
474 | | - {@const items = prefsByCategory[cat]} |
475 | | - <div class="prefs-acc-group"> |
476 | | - <div class="prefs-acc-header"> |
477 | | - <span class="prefs-acc-name">{cat}</span> |
478 | | - <span class="prefs-acc-count">{items.length}</span> |
479 | | - </div> |
480 | | - <div class="prefs-acc-body"> |
481 | | - {#each items as { pref, catalogItem }} |
482 | | - <div class="prefs-kv-row"> |
483 | | - <span class="prefs-kv-label">{catalogItem?.label ?? pref.key}</span> |
484 | | - <span class="prefs-kv-value"> |
485 | | - {#if catalogItem?.options} |
486 | | - {@const opt = catalogItem.options.find((o: { value: string; label: string }) => o.value === pref.value)} |
487 | | - <span class="pref-option-val">{opt?.label ?? pref.value}</span> |
488 | | - {:else if (catalogItem?.type ?? pref.type) === 'bool'} |
489 | | - {@const boolOn = pref.value === 'true' || pref.value === '1'} |
490 | | - <span class="pref-bool {boolOn ? 'pref-bool-on' : 'pref-bool-off'}"> |
491 | | - {boolOn ? 'ON' : 'OFF'} |
492 | | - </span> |
493 | | - {:else} |
494 | | - <span class="pref-raw-val">{pref.value}</span> |
495 | | - {/if} |
496 | | - </span> |
497 | | - </div> |
498 | | - {/each} |
499 | | - </div> |
500 | | - </div> |
501 | | - {/each} |
502 | | - </div> |
| 455 | + <MacOSPreferencesDisplay {prefsByCategory} {prefCategoryNames} /> |
503 | 456 | </section> |
504 | 457 | {/if} |
505 | 458 |
|
|
536 | 489 | </main> |
537 | 490 | </div> |
538 | 491 |
|
539 | | -{#if showShareModal} |
540 | | - <div class="share-overlay" onclick={closeShareModal} onkeydown={(e) => e.key === 'Escape' && closeShareModal()} role="dialog" tabindex="0"> |
541 | | - <div class="share-modal" onclick={(e) => e.stopPropagation()} onkeydown={(e) => e.stopPropagation()} role="presentation"> |
542 | | - <div class="share-modal-header"> |
543 | | - <h3 class="share-modal-title">Share Configuration</h3> |
544 | | - <button class="share-close-btn" onclick={closeShareModal} aria-label="Close share modal"> |
545 | | - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> |
546 | | - </button> |
547 | | - </div> |
548 | | - <div class="share-modal-body"> |
549 | | - <div class="share-url-display"> |
550 | | - <code>{getShareUrl()}</code> |
551 | | - </div> |
552 | | - |
553 | | - <div class="share-options"> |
554 | | - <button class="share-option" onclick={shareCopyLink}> |
555 | | - <span class="share-option-icon"> |
556 | | - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> |
557 | | - </span> |
558 | | - <span class="share-option-label">{shareCopied ? 'Copied!' : 'Copy Link'}</span> |
559 | | - </button> |
560 | | - |
561 | | - <button class="share-option" onclick={shareOnTwitter}> |
562 | | - <span class="share-option-icon"> |
563 | | - <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg> |
564 | | - </span> |
565 | | - <span class="share-option-label">Share on X</span> |
566 | | - </button> |
567 | | - </div> |
568 | | - </div> |
569 | | - </div> |
570 | | - </div> |
571 | | -{/if} |
572 | | - |
573 | | -<svelte:window onkeydown={(e) => { if (e.key === 'Escape' && showShareModal) closeShareModal(); }} /> |
| 492 | +<ShareModal bind:show={showShareModal} shareUrl={getShareUrl()} configName={config.name} /> |
574 | 493 |
|
575 | 494 | <style> |
576 | 495 | * { |
|
1179 | 1098 | color: var(--text-primary); |
1180 | 1099 | } |
1181 | 1100 |
|
1182 | | - .prefs-accordion { |
1183 | | - border: 1px solid var(--border); |
1184 | | - border-radius: 12px; |
1185 | | - overflow: hidden; |
1186 | | - } |
1187 | | -
|
1188 | | - .prefs-acc-group { |
1189 | | - border-bottom: 1px solid var(--border); |
1190 | | - } |
1191 | | -
|
1192 | | - .prefs-acc-group:last-child { |
1193 | | - border-bottom: none; |
1194 | | - } |
1195 | | -
|
1196 | | - .prefs-acc-header { |
1197 | | - display: flex; |
1198 | | - align-items: center; |
1199 | | - gap: 8px; |
1200 | | - padding: 10px 16px; |
1201 | | - background: var(--bg-tertiary); |
1202 | | - } |
1203 | | -
|
1204 | | - .prefs-acc-name { |
1205 | | - font-size: 0.75rem; |
1206 | | - font-weight: 700; |
1207 | | - text-transform: uppercase; |
1208 | | - letter-spacing: 0.08em; |
1209 | | - color: var(--text-muted); |
1210 | | - flex: 1; |
1211 | | - } |
1212 | | -
|
1213 | | - .prefs-acc-count { |
1214 | | - font-size: 0.7rem; |
1215 | | - color: var(--text-muted); |
1216 | | - } |
1217 | | -
|
1218 | | - .prefs-acc-body { |
1219 | | - display: grid; |
1220 | | - grid-template-columns: 1fr; |
1221 | | - } |
1222 | | -
|
1223 | | - @media (min-width: 640px) { |
1224 | | - .prefs-acc-body { |
1225 | | - grid-template-columns: 1fr 1fr; |
1226 | | - } |
1227 | | - } |
1228 | | -
|
1229 | | - .prefs-kv-row { |
1230 | | - display: flex; |
1231 | | - align-items: center; |
1232 | | - justify-content: space-between; |
1233 | | - gap: 12px; |
1234 | | - padding: 8px 16px; |
1235 | | - border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent); |
1236 | | - } |
1237 | | -
|
1238 | | - .prefs-kv-label { |
1239 | | - font-size: 0.88rem; |
1240 | | - color: var(--text-primary); |
1241 | | - font-weight: 500; |
1242 | | - } |
1243 | | -
|
1244 | | - .prefs-kv-value { |
1245 | | - flex-shrink: 0; |
1246 | | - } |
1247 | | -
|
1248 | | - .pref-bool { |
1249 | | - display: inline-block; |
1250 | | - padding: 3px 8px; |
1251 | | - border-radius: 5px; |
1252 | | - font-size: 0.72rem; |
1253 | | - font-weight: 700; |
1254 | | - letter-spacing: 0.05em; |
1255 | | - } |
1256 | | -
|
1257 | | - .pref-bool-on { |
1258 | | - background: rgba(34, 197, 94, 0.15); |
1259 | | - color: var(--accent); |
1260 | | - border: 1px solid rgba(34, 197, 94, 0.3); |
1261 | | - } |
1262 | | -
|
1263 | | - .pref-bool-off { |
1264 | | - background: var(--bg-secondary); |
1265 | | - color: var(--text-muted); |
1266 | | - border: 1px solid var(--border); |
1267 | | - } |
1268 | | -
|
1269 | | - .pref-option-val, |
1270 | | - .pref-raw-val { |
1271 | | - font-family: 'JetBrains Mono', monospace; |
1272 | | - font-size: 0.8rem; |
1273 | | - color: var(--accent); |
1274 | | - background: var(--bg-secondary); |
1275 | | - padding: 3px 8px; |
1276 | | - border-radius: 5px; |
1277 | | - border: 1px solid var(--border); |
1278 | | - } |
1279 | | -
|
1280 | 1101 | .cta { |
1281 | 1102 | display: flex; |
1282 | 1103 | align-items: center; |
|
1337 | 1158 | transform: translateY(-2px); |
1338 | 1159 | } |
1339 | 1160 |
|
1340 | | - .share-overlay { |
1341 | | - position: fixed; |
1342 | | - inset: 0; |
1343 | | - background: rgba(0, 0, 0, 0.8); |
1344 | | - backdrop-filter: blur(4px); |
1345 | | - display: flex; |
1346 | | - justify-content: center; |
1347 | | - align-items: center; |
1348 | | - z-index: 1000; |
1349 | | - padding: 20px; |
1350 | | - animation: fadeIn 0.2s; |
1351 | | - } |
1352 | | -
|
1353 | | - @keyframes fadeIn { |
1354 | | - from { opacity: 0; } |
1355 | | - to { opacity: 1; } |
1356 | | - } |
1357 | | -
|
1358 | | - .share-modal { |
1359 | | - background: var(--bg-secondary); |
1360 | | - border: 1px solid var(--border); |
1361 | | - border-radius: 16px; |
1362 | | - width: 100%; |
1363 | | - max-width: 480px; |
1364 | | - overflow: hidden; |
1365 | | - animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
1366 | | - } |
1367 | | -
|
1368 | | - @keyframes slideUp { |
1369 | | - from { |
1370 | | - opacity: 0; |
1371 | | - transform: translateY(20px); |
1372 | | - } |
1373 | | - to { |
1374 | | - opacity: 1; |
1375 | | - transform: translateY(0); |
1376 | | - } |
1377 | | - } |
1378 | | -
|
1379 | | - .share-modal-header { |
1380 | | - padding: 24px; |
1381 | | - border-bottom: 1px solid var(--border); |
1382 | | - display: flex; |
1383 | | - justify-content: space-between; |
1384 | | - align-items: center; |
1385 | | - } |
1386 | | -
|
1387 | | - .share-modal-title { |
1388 | | - font-size: 1.3rem; |
1389 | | - font-weight: 700; |
1390 | | - margin: 0; |
1391 | | - color: var(--text-primary); |
1392 | | - } |
1393 | | -
|
1394 | | - .share-close-btn { |
1395 | | - background: none; |
1396 | | - border: none; |
1397 | | - cursor: pointer; |
1398 | | - color: var(--text-muted); |
1399 | | - padding: 4px; |
1400 | | - border-radius: 6px; |
1401 | | - transition: all 0.2s; |
1402 | | - display: flex; |
1403 | | - align-items: center; |
1404 | | - justify-content: center; |
1405 | | - } |
1406 | | -
|
1407 | | - .share-close-btn:hover { |
1408 | | - color: var(--text-primary); |
1409 | | - background: var(--bg-tertiary); |
1410 | | - } |
1411 | | -
|
1412 | | - .share-modal-body { |
1413 | | - padding: 24px; |
1414 | | - display: flex; |
1415 | | - flex-direction: column; |
1416 | | - gap: 20px; |
1417 | | - } |
1418 | | -
|
1419 | | - .share-url-display { |
1420 | | - background: var(--bg-tertiary); |
1421 | | - border: 1px solid var(--border); |
1422 | | - border-radius: 10px; |
1423 | | - padding: 14px 18px; |
1424 | | - } |
1425 | | -
|
1426 | | - .share-url-display code { |
1427 | | - font-family: 'JetBrains Mono', monospace; |
1428 | | - font-size: 0.85rem; |
1429 | | - color: var(--accent); |
1430 | | - word-break: break-all; |
1431 | | - } |
1432 | | -
|
1433 | | - .share-options { |
1434 | | - display: flex; |
1435 | | - flex-direction: column; |
1436 | | - gap: 10px; |
1437 | | - } |
1438 | | -
|
1439 | | - .share-option { |
1440 | | - display: flex; |
1441 | | - align-items: center; |
1442 | | - gap: 14px; |
1443 | | - padding: 16px 18px; |
1444 | | - background: var(--bg-tertiary); |
1445 | | - border: 1px solid var(--border); |
1446 | | - border-radius: 10px; |
1447 | | - color: var(--text-primary); |
1448 | | - font-size: 0.95rem; |
1449 | | - font-weight: 500; |
1450 | | - cursor: pointer; |
1451 | | - transition: all 0.2s; |
1452 | | - } |
1453 | | -
|
1454 | | - .share-option:hover { |
1455 | | - border-color: var(--accent); |
1456 | | - background: var(--bg-secondary); |
1457 | | - transform: translateX(4px); |
1458 | | - } |
1459 | | -
|
1460 | | - .share-option-icon { |
1461 | | - display: flex; |
1462 | | - align-items: center; |
1463 | | - justify-content: center; |
1464 | | - width: 40px; |
1465 | | - height: 40px; |
1466 | | - background: var(--bg-secondary); |
1467 | | - border-radius: 8px; |
1468 | | - color: var(--text-secondary); |
1469 | | - flex-shrink: 0; |
1470 | | - } |
1471 | | -
|
1472 | | - .share-option:hover .share-option-icon { |
1473 | | - color: var(--accent); |
1474 | | - } |
1475 | | -
|
1476 | | - .share-option-label { |
1477 | | - flex: 1; |
1478 | | - } |
1479 | | -
|
1480 | 1161 | @media (max-width: 768px) { |
1481 | 1162 | .hero { |
1482 | 1163 | padding: 100px 20px 60px; |
|
0 commit comments