|
19 | 19 | let saving = $state(false); |
20 | 20 | let error = $state(''); |
21 | 21 | let editingConfig = $state<any>(null); |
| 22 | + let showScriptModal = $state(false); |
| 23 | + let scriptDraft = $state(''); |
22 | 24 |
|
23 | 25 | let formData = $state({ |
24 | 26 | name: '', |
|
607 | 609 | <div class="script-card"> |
608 | 610 | <div class="script-bar"> |
609 | 611 | <span class="script-filename">post-install.sh</span> |
| 612 | + <button |
| 613 | + class="script-edit-btn" |
| 614 | + type="button" |
| 615 | + onclick={() => { scriptDraft = formData.custom_script; showScriptModal = true; }} |
| 616 | + > |
| 617 | + {formData.custom_script ? 'Edit' : 'Add Script'} |
| 618 | + </button> |
610 | 619 | </div> |
611 | | - <textarea |
612 | | - class="script-textarea" |
613 | | - bind:value={formData.custom_script} |
614 | | - placeholder="#!/bin/bash # Commands to run after package installation" |
615 | | - spellcheck="false" |
616 | | - ></textarea> |
| 620 | + <!-- svelte-ignore a11y_click_events_have_key_events --> |
| 621 | + <!-- svelte-ignore a11y_no_static_element_interactions --> |
| 622 | + {#if formData.custom_script} |
| 623 | + <pre class="script-preview" onclick={() => { scriptDraft = formData.custom_script; showScriptModal = true; }}>{formData.custom_script}</pre> |
| 624 | + {:else} |
| 625 | + <div class="script-empty" onclick={() => { scriptDraft = ''; showScriptModal = true; }}>No post-install script — click to add</div> |
| 626 | + {/if} |
617 | 627 | </div> |
618 | 628 | </section> |
619 | 629 | </div> |
620 | 630 | </div> |
621 | 631 | {/if} |
622 | 632 |
|
| 633 | +{#if showScriptModal} |
| 634 | + <!-- svelte-ignore a11y_no_static_element_interactions --> |
| 635 | + <div class="script-modal-overlay" onkeydown={(e) => { if (e.key === 'Escape') showScriptModal = false; }}> |
| 636 | + <!-- svelte-ignore a11y_click_events_have_key_events --> |
| 637 | + <div class="script-modal-backdrop" onclick={() => showScriptModal = false}></div> |
| 638 | + <div class="script-modal"> |
| 639 | + <div class="script-modal-bar"> |
| 640 | + <span class="script-modal-filename">post-install.sh</span> |
| 641 | + <div class="script-modal-actions"> |
| 642 | + <button |
| 643 | + type="button" |
| 644 | + class="script-modal-cancel" |
| 645 | + onclick={() => showScriptModal = false} |
| 646 | + >Cancel</button> |
| 647 | + <button |
| 648 | + type="button" |
| 649 | + class="script-modal-save" |
| 650 | + onclick={() => { formData.custom_script = scriptDraft; showScriptModal = false; }} |
| 651 | + >Save</button> |
| 652 | + </div> |
| 653 | + </div> |
| 654 | + <div class="script-modal-body"> |
| 655 | + <textarea |
| 656 | + class="script-modal-textarea" |
| 657 | + bind:value={scriptDraft} |
| 658 | + placeholder="#!/bin/bash # Commands to run after package installation # Example: # mkdir -p ~/Projects # npm install -g vercel # defaults write com.apple.dock autohide -bool true" |
| 659 | + spellcheck="false" |
| 660 | + ></textarea> |
| 661 | + </div> |
| 662 | + <div class="script-modal-hint"> |
| 663 | + Commands run sequentially in your home directory after packages, shell, dotfiles, and macOS preferences are applied. |
| 664 | + </div> |
| 665 | + </div> |
| 666 | + </div> |
| 667 | +{/if} |
| 668 | + |
623 | 669 | <style> |
624 | 670 | .editor-loading { |
625 | 671 | display: flex; |
|
1289 | 1335 | padding: 12px 18px; |
1290 | 1336 | background: var(--bg-tertiary); |
1291 | 1337 | border-bottom: 1px solid var(--border); |
| 1338 | + display: flex; |
| 1339 | + align-items: center; |
| 1340 | + justify-content: space-between; |
1292 | 1341 | } |
1293 | 1342 |
|
1294 | 1343 | .script-filename { |
|
1298 | 1347 | color: var(--text-secondary); |
1299 | 1348 | } |
1300 | 1349 |
|
1301 | | - .script-textarea { |
| 1350 | + .script-edit-btn { |
| 1351 | + padding: 5px 14px; |
| 1352 | + background: none; |
| 1353 | + border: 1px solid var(--border); |
| 1354 | + border-radius: 6px; |
| 1355 | + color: var(--accent); |
| 1356 | + font-size: 0.8rem; |
| 1357 | + font-family: inherit; |
| 1358 | + cursor: pointer; |
| 1359 | + transition: all 0.15s; |
| 1360 | + } |
| 1361 | +
|
| 1362 | + .script-edit-btn:hover { |
| 1363 | + border-color: var(--accent); |
| 1364 | + background: rgba(34, 197, 94, 0.08); |
| 1365 | + } |
| 1366 | +
|
| 1367 | + .script-preview { |
| 1368 | + padding: 16px 20px; |
| 1369 | + margin: 0; |
| 1370 | + color: var(--text-secondary); |
| 1371 | + font-family: 'JetBrains Mono', monospace; |
| 1372 | + font-size: 0.82rem; |
| 1373 | + line-height: 1.7; |
| 1374 | + white-space: pre-wrap; |
| 1375 | + word-break: break-all; |
| 1376 | + max-height: 160px; |
| 1377 | + overflow: hidden; |
| 1378 | + cursor: pointer; |
| 1379 | + } |
| 1380 | +
|
| 1381 | + .script-empty { |
| 1382 | + padding: 24px 20px; |
| 1383 | + color: var(--text-muted); |
| 1384 | + font-size: 0.85rem; |
| 1385 | + text-align: center; |
| 1386 | + cursor: pointer; |
| 1387 | + } |
| 1388 | +
|
| 1389 | + .script-empty:hover { |
| 1390 | + color: var(--text-secondary); |
| 1391 | + } |
| 1392 | +
|
| 1393 | + /* Script modal */ |
| 1394 | + .script-modal-overlay { |
| 1395 | + position: fixed; |
| 1396 | + inset: 0; |
| 1397 | + z-index: 1000; |
| 1398 | + display: flex; |
| 1399 | + align-items: center; |
| 1400 | + justify-content: center; |
| 1401 | + padding: 32px; |
| 1402 | + } |
| 1403 | +
|
| 1404 | + .script-modal-backdrop { |
| 1405 | + position: absolute; |
| 1406 | + inset: 0; |
| 1407 | + background: rgba(0, 0, 0, 0.7); |
| 1408 | + backdrop-filter: blur(4px); |
| 1409 | + } |
| 1410 | +
|
| 1411 | + .script-modal { |
| 1412 | + position: relative; |
| 1413 | + width: 100%; |
| 1414 | + max-width: 760px; |
| 1415 | + height: 80vh; |
| 1416 | + max-height: 700px; |
| 1417 | + background: var(--bg-primary); |
| 1418 | + border: 1px solid var(--border); |
| 1419 | + border-radius: 16px; |
| 1420 | + display: flex; |
| 1421 | + flex-direction: column; |
| 1422 | + overflow: hidden; |
| 1423 | + box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4); |
| 1424 | + } |
| 1425 | +
|
| 1426 | + .script-modal-bar { |
| 1427 | + padding: 14px 20px; |
| 1428 | + background: var(--bg-tertiary); |
| 1429 | + border-bottom: 1px solid var(--border); |
| 1430 | + display: flex; |
| 1431 | + align-items: center; |
| 1432 | + justify-content: space-between; |
| 1433 | + flex-shrink: 0; |
| 1434 | + } |
| 1435 | +
|
| 1436 | + .script-modal-filename { |
| 1437 | + font-family: 'JetBrains Mono', monospace; |
| 1438 | + font-size: 0.85rem; |
| 1439 | + font-weight: 600; |
| 1440 | + color: var(--text-secondary); |
| 1441 | + } |
| 1442 | +
|
| 1443 | + .script-modal-actions { |
| 1444 | + display: flex; |
| 1445 | + gap: 8px; |
| 1446 | + } |
| 1447 | +
|
| 1448 | + .script-modal-cancel { |
| 1449 | + padding: 6px 16px; |
| 1450 | + background: none; |
| 1451 | + border: 1px solid var(--border); |
| 1452 | + border-radius: 8px; |
| 1453 | + color: var(--text-secondary); |
| 1454 | + font-size: 0.82rem; |
| 1455 | + font-family: inherit; |
| 1456 | + cursor: pointer; |
| 1457 | + transition: all 0.15s; |
| 1458 | + } |
| 1459 | +
|
| 1460 | + .script-modal-cancel:hover { |
| 1461 | + border-color: var(--text-muted); |
| 1462 | + } |
| 1463 | +
|
| 1464 | + .script-modal-save { |
| 1465 | + padding: 6px 16px; |
| 1466 | + background: var(--accent); |
| 1467 | + border: none; |
| 1468 | + border-radius: 8px; |
| 1469 | + color: #000; |
| 1470 | + font-size: 0.82rem; |
| 1471 | + font-weight: 600; |
| 1472 | + font-family: inherit; |
| 1473 | + cursor: pointer; |
| 1474 | + transition: all 0.15s; |
| 1475 | + } |
| 1476 | +
|
| 1477 | + .script-modal-save:hover { |
| 1478 | + opacity: 0.9; |
| 1479 | + } |
| 1480 | +
|
| 1481 | + .script-modal-body { |
| 1482 | + flex: 1; |
| 1483 | + overflow: hidden; |
| 1484 | + } |
| 1485 | +
|
| 1486 | + .script-modal-textarea { |
1302 | 1487 | width: 100%; |
1303 | | - min-height: 200px; |
| 1488 | + height: 100%; |
1304 | 1489 | padding: 20px; |
1305 | 1490 | background: var(--bg-primary); |
1306 | 1491 | border: none; |
1307 | 1492 | color: var(--text-primary); |
1308 | 1493 | font-family: 'JetBrains Mono', monospace; |
1309 | 1494 | font-size: 0.85rem; |
1310 | | - line-height: 1.6; |
1311 | | - resize: vertical; |
| 1495 | + line-height: 1.7; |
| 1496 | + resize: none; |
1312 | 1497 | outline: none; |
| 1498 | + tab-size: 4; |
1313 | 1499 | } |
1314 | 1500 |
|
1315 | | - .script-textarea::placeholder { |
| 1501 | + .script-modal-textarea::placeholder { |
1316 | 1502 | color: var(--text-muted); |
1317 | | - opacity: 0.5; |
| 1503 | + opacity: 0.4; |
| 1504 | + } |
| 1505 | +
|
| 1506 | + .script-modal-hint { |
| 1507 | + padding: 10px 20px; |
| 1508 | + background: var(--bg-tertiary); |
| 1509 | + border-top: 1px solid var(--border); |
| 1510 | + color: var(--text-muted); |
| 1511 | + font-size: 0.75rem; |
| 1512 | + flex-shrink: 0; |
1318 | 1513 | } |
1319 | 1514 |
|
1320 | 1515 | @media (max-width: 768px) { |
|
0 commit comments