|
116 | 116 | return Array.from(selectedPackages.keys()).filter((pkg) => !presetPkgs.has(pkg)); |
117 | 117 | } |
118 | 118 |
|
119 | | - function getGroupedExtras(): { cli: string[]; apps: string[]; taps: string[] } { |
120 | | - const extras = getExtraPackages(); |
| 119 | + function getGroupedPackages(): { cli: string[]; apps: string[] } { |
121 | 120 | const cli: string[] = []; |
122 | 121 | const apps: string[] = []; |
123 | | - const taps: string[] = []; |
124 | | - for (const pkg of extras) { |
125 | | - const t = selectedPackages.get(pkg) || 'formula'; |
| 122 | + for (const [pkg, t] of selectedPackages) { |
126 | 123 | if (t === 'cask') apps.push(pkg); |
127 | | - else if (t === 'tap') taps.push(pkg); |
128 | 124 | else cli.push(pkg); |
129 | 125 | } |
130 | | - return { cli, apps, taps }; |
| 126 | + return { cli, apps }; |
131 | 127 | } |
132 | 128 |
|
133 | 129 |
|
|
517 | 513 | <p class="form-hint">After installing packages, OpenBoot will clone this repo and deploy configs via stow.</p> |
518 | 514 | </div> |
519 | 515 |
|
520 | | - <div class="packages-section"> |
521 | | - <div class="packages-header"> |
522 | | - <span class="packages-title">Packages from "{formData.base_preset}" preset</span> |
523 | | - <button class="expand-btn" onclick={() => presetExpanded = !presetExpanded}> |
524 | | - {presetExpanded ? 'Collapse' : 'Expand'} ({selectedPackages.size} selected) |
525 | | - </button> |
| 516 | + {#if true} |
| 517 | + {@const grouped = getGroupedPackages()} |
| 518 | + <div class="packages-section"> |
| 519 | + <div class="packages-header"> |
| 520 | + <span class="packages-title">Packages</span> |
| 521 | + <span class="extra-count">{selectedPackages.size} selected</span> |
| 522 | + </div> |
| 523 | + |
| 524 | + <div class="packages-group"> |
| 525 | + <div class="group-header"> |
| 526 | + <span class="group-label">CLI</span> |
| 527 | + <span class="group-count">{grouped.cli.length}</span> |
526 | 528 | </div> |
527 | | - <p class="packages-hint">Click to toggle. Gray = won't install.</p> |
528 | | - <div class="preset-packages" class:expanded={presetExpanded}> |
529 | | - {#each presetExpanded ? getPresetPackages(formData.base_preset) : getPresetPackages(formData.base_preset).slice(0, 12) as pkg} |
530 | | - <button |
531 | | - class="preset-tag" |
532 | | - class:excluded={!selectedPackages.has(pkg)} |
533 | | - onclick={() => togglePresetPackage(pkg)} |
534 | | - >{pkg}</button> |
535 | | - {/each} |
536 | | - {#if !presetExpanded && getPresetPackages(formData.base_preset).length > 12} |
537 | | - <button class="preset-tag more" onclick={() => presetExpanded = true}> |
538 | | - +{getPresetPackages(formData.base_preset).length - 12} more |
| 529 | + <div class="group-tags"> |
| 530 | + {#each grouped.cli as pkg} |
| 531 | + <button type="button" class="pkg-tag" onclick={() => togglePackage(pkg, 'formula')}> |
| 532 | + {pkg}<span class="remove-icon">×</span> |
539 | 533 | </button> |
| 534 | + {/each} |
| 535 | + {#if grouped.cli.length === 0} |
| 536 | + <span class="group-empty">No CLI packages</span> |
540 | 537 | {/if} |
541 | 538 | </div> |
542 | 539 | </div> |
543 | 540 |
|
544 | | - <div class="packages-section"> |
545 | | - <div class="packages-header"> |
546 | | - <span class="packages-title">Additional Packages</span> |
547 | | - {#if getExtraPackages().length > 0} |
548 | | - <span class="extra-count">{getExtraPackages().length} added</span> |
549 | | - {/if} |
550 | | - </div> |
551 | | - {#if getExtraPackages().length > 0} |
552 | | - {@const grouped = getGroupedExtras()} |
553 | | - <div class="selected-extras"> |
554 | | - {#if grouped.cli.length > 0} |
555 | | - <div class="extras-group"> |
556 | | - <span class="group-label">CLI</span> |
557 | | - {#each grouped.cli as pkg} |
558 | | - <button type="button" class="extra-tag" onclick={() => togglePackage(pkg, 'formula')}> |
559 | | - {pkg}<span class="remove-icon">×</span> |
560 | | - </button> |
561 | | - {/each} |
562 | | - </div> |
563 | | - {/if} |
564 | | - {#if grouped.apps.length > 0} |
565 | | - <div class="extras-group"> |
566 | | - <span class="group-label">Apps</span> |
567 | | - {#each grouped.apps as pkg} |
568 | | - <button type="button" class="extra-tag" onclick={() => togglePackage(pkg, 'cask')}> |
569 | | - {pkg}<span class="remove-icon">×</span> |
570 | | - </button> |
571 | | - {/each} |
572 | | - </div> |
573 | | - {/if} |
574 | | - {#if grouped.taps.length > 0} |
575 | | - <div class="extras-group"> |
576 | | - <span class="group-label">Taps</span> |
577 | | - {#each grouped.taps as pkg} |
578 | | - <button type="button" class="extra-tag" onclick={() => togglePackage(pkg, 'tap')}> |
579 | | - {pkg}<span class="remove-icon">×</span> |
580 | | - </button> |
581 | | - {/each} |
582 | | - </div> |
| 541 | + <div class="packages-group"> |
| 542 | + <div class="group-header"> |
| 543 | + <span class="group-label">Apps</span> |
| 544 | + <span class="group-count">{grouped.apps.length}</span> |
| 545 | + </div> |
| 546 | + <div class="group-tags"> |
| 547 | + {#each grouped.apps as pkg} |
| 548 | + <button type="button" class="pkg-tag" onclick={() => togglePackage(pkg, 'cask')}> |
| 549 | + {pkg}<span class="remove-icon">×</span> |
| 550 | + </button> |
| 551 | + {/each} |
| 552 | + {#if grouped.apps.length === 0} |
| 553 | + <span class="group-empty">No GUI apps</span> |
583 | 554 | {/if} |
584 | 555 | </div> |
585 | | - {/if} |
| 556 | + </div> |
586 | 557 | <div class="packages-search"> |
587 | 558 | <input |
588 | 559 | type="text" |
|
617 | 588 | <div class="search-hint">Type at least 2 characters to search Homebrew packages</div> |
618 | 589 | {/if} |
619 | 590 | </div> |
| 591 | + {/if} |
620 | 592 |
|
621 | 593 | <div class="form-group"> |
622 | 594 | <label class="form-label">Custom Post-Install Script (Optional)</label> |
|
1045 | 1017 | margin-bottom: 12px; |
1046 | 1018 | } |
1047 | 1019 |
|
1048 | | - .packages-hint { |
1049 | | - font-size: 0.75rem; |
1050 | | - color: var(--text-muted); |
1051 | | - margin-bottom: 10px; |
| 1020 | + .packages-title { |
| 1021 | + font-size: 1rem; |
| 1022 | + font-weight: 500; |
1052 | 1023 | } |
1053 | 1024 |
|
1054 | | - .expand-btn { |
1055 | | - background: none; |
1056 | | - border: none; |
1057 | | - color: var(--accent); |
| 1025 | + .extra-count { |
1058 | 1026 | font-size: 0.8rem; |
1059 | | - cursor: pointer; |
1060 | | - padding: 4px 8px; |
1061 | | - } |
1062 | | -
|
1063 | | - .expand-btn:hover { |
1064 | | - text-decoration: underline; |
1065 | | - } |
1066 | | -
|
1067 | | - .preset-packages { |
1068 | | - display: flex; |
1069 | | - flex-wrap: wrap; |
1070 | | - gap: 6px; |
1071 | | - max-height: 80px; |
1072 | | - overflow: hidden; |
1073 | | - transition: max-height 0.3s ease; |
1074 | | - } |
1075 | | -
|
1076 | | - .preset-packages.expanded { |
1077 | | - max-height: none; |
| 1027 | + color: var(--text-muted); |
1078 | 1028 | } |
1079 | 1029 |
|
1080 | | - .preset-tag { |
1081 | | - padding: 4px 10px; |
1082 | | - background: var(--bg-tertiary); |
1083 | | - border: 1px solid var(--border); |
1084 | | - border-radius: 4px; |
1085 | | - font-size: 0.75rem; |
1086 | | - color: var(--text-secondary); |
1087 | | - font-family: 'JetBrains Mono', monospace; |
1088 | | - cursor: pointer; |
1089 | | - transition: all 0.15s; |
| 1030 | + .packages-group { |
| 1031 | + margin-bottom: 16px; |
1090 | 1032 | } |
1091 | 1033 |
|
1092 | | - .preset-tag:hover { |
1093 | | - border-color: var(--accent); |
| 1034 | + .packages-group:last-child { |
| 1035 | + margin-bottom: 0; |
1094 | 1036 | } |
1095 | 1037 |
|
1096 | | - .preset-tag.excluded { |
1097 | | - opacity: 0.4; |
1098 | | - text-decoration: line-through; |
1099 | | - background: transparent; |
| 1038 | + .group-header { |
| 1039 | + display: flex; |
| 1040 | + align-items: center; |
| 1041 | + gap: 8px; |
| 1042 | + margin-bottom: 8px; |
1100 | 1043 | } |
1101 | 1044 |
|
1102 | | - .preset-tag.more { |
1103 | | - background: transparent; |
1104 | | - border-style: dashed; |
| 1045 | + .group-label { |
| 1046 | + font-size: 0.7rem; |
| 1047 | + text-transform: uppercase; |
| 1048 | + letter-spacing: 0.08em; |
1105 | 1049 | color: var(--text-muted); |
| 1050 | + font-weight: 600; |
1106 | 1051 | } |
1107 | 1052 |
|
1108 | | - .packages-title { |
1109 | | - font-size: 1rem; |
1110 | | - font-weight: 500; |
1111 | | - } |
1112 | | -
|
1113 | | - .extra-count { |
1114 | | - font-size: 0.8rem; |
1115 | | - color: var(--accent); |
1116 | | - } |
1117 | | -
|
1118 | | - .selected-extras { |
1119 | | - margin-bottom: 12px; |
1120 | | - padding: 10px; |
1121 | | - background: rgba(255, 255, 255, 0.02); |
1122 | | - border: 1px solid var(--border); |
1123 | | - border-radius: 8px; |
| 1053 | + .group-count { |
| 1054 | + font-size: 0.65rem; |
| 1055 | + color: var(--text-muted); |
| 1056 | + opacity: 0.6; |
1124 | 1057 | } |
1125 | 1058 |
|
1126 | | - .extras-group { |
| 1059 | + .group-tags { |
1127 | 1060 | display: flex; |
1128 | 1061 | flex-wrap: wrap; |
1129 | | - align-items: center; |
1130 | 1062 | gap: 6px; |
1131 | | - margin-bottom: 8px; |
1132 | | - } |
1133 | | -
|
1134 | | - .extras-group:last-child { |
1135 | | - margin-bottom: 0; |
1136 | 1063 | } |
1137 | 1064 |
|
1138 | | - .group-label { |
1139 | | - font-size: 0.6rem; |
1140 | | - text-transform: uppercase; |
1141 | | - letter-spacing: 0.05em; |
| 1065 | + .group-empty { |
| 1066 | + font-size: 0.8rem; |
1142 | 1067 | color: var(--text-muted); |
1143 | | - font-weight: 600; |
1144 | | - width: 32px; |
1145 | | - flex-shrink: 0; |
| 1068 | + opacity: 0.4; |
1146 | 1069 | } |
1147 | 1070 |
|
1148 | | - .extra-tag { |
| 1071 | + .pkg-tag { |
1149 | 1072 | display: inline-flex; |
1150 | 1073 | align-items: center; |
1151 | 1074 | gap: 4px; |
1152 | 1075 | padding: 4px 8px; |
1153 | | - background: rgba(255, 255, 255, 0.08); |
1154 | | - border: 1px solid rgba(255, 255, 255, 0.12); |
| 1076 | + background: var(--bg-tertiary); |
| 1077 | + border: 1px solid var(--border); |
1155 | 1078 | color: var(--text-secondary); |
1156 | 1079 | border-radius: 4px; |
1157 | 1080 | font-size: 0.75rem; |
|
1160 | 1083 | transition: all 0.15s; |
1161 | 1084 | } |
1162 | 1085 |
|
1163 | | - .extra-tag:hover { |
1164 | | - background: rgba(255, 255, 255, 0.14); |
1165 | | - color: var(--text-primary); |
| 1086 | + .pkg-tag:hover { |
| 1087 | + border-color: var(--danger); |
| 1088 | + color: var(--danger); |
1166 | 1089 | } |
1167 | 1090 |
|
1168 | 1091 | .remove-icon { |
1169 | 1092 | font-size: 0.9rem; |
1170 | 1093 | font-weight: bold; |
1171 | | - opacity: 0.7; |
| 1094 | + opacity: 0.5; |
1172 | 1095 | } |
1173 | 1096 |
|
1174 | | - .extra-tag:hover .remove-icon { |
| 1097 | + .pkg-tag:hover .remove-icon { |
1175 | 1098 | opacity: 1; |
1176 | 1099 | } |
1177 | 1100 |
|
|
0 commit comments