[READ-ONLY] Mirror of https://github.com/mrgnw/morganwill.com. morganwill.com
0

Configure Feed

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

format

+324 -314
+324 -314
src/components/Packages.svelte
··· 1 1 <script> 2 - import * as Card from "$lib/components/ui/card"; 3 - import { Button } from "$lib/components/ui/button"; 2 + import * as Card from "$lib/components/ui/card"; 3 + import { Button } from "$lib/components/ui/button"; 4 4 5 - // Define the packages and their installers 6 - const uv_tools = [ 7 - 'fpw', 8 - 'harlequin', 9 - 'llm', 10 - 'marimo', 11 - 'mopup', 12 - 'posting', 13 - 'ruff', 14 - 'yt-dlp' 15 - ]; 5 + // Define the packages and their installers 6 + const uv_tools = [ 7 + "fpw", 8 + "harlequin", 9 + "llm", 10 + "marimo", 11 + "mopup", 12 + "posting", 13 + "ruff", 14 + "yt-dlp", 15 + ]; 16 16 17 - const brews = [ 18 - // Base utilities 19 - 'aria2', 20 - 'cursor', 21 - 'duckdb', 22 - 'jq', 23 - 'zoxide', 24 - 'choose-rust', 25 - 26 - // System utilities 27 - 'bartender', 28 - 'istat-menus', 29 - 'macmediakeyforwarder', 30 - 'lingon-x', 31 - 'lunar', 32 - 'onyx', 33 - 'pastebot', 34 - 35 - // Development tools 36 - 'beekeeper-studio', 37 - 'cloudflared', 38 - 'docker', 39 - 'docker-completion', 40 - 'docker-compose', 41 - 'github', 42 - 'orbstack', 43 - 'postico', 44 - 'postman', 45 - 'sublime-text', 46 - 47 - // File management 48 - 'cyberduck', 49 - 'mountain-duck', 50 - 'diskonaut', 51 - 'dust', 52 - 'duf', 53 - 'the-unarchiver', 54 - 55 - // Media tools 56 - 'iina', 57 - 'handbrake', 58 - 'homebrew/cask/handbrake', 59 - 'losslesscut', 60 - 'plex-media-server', 61 - 'qlvideo', 62 - 'subler', 63 - 64 - // Internet & Communication 65 - 'firefox', 66 - 'notion', 67 - 'qbittorrent', 68 - 'homebrew/cask/syncthing', 69 - 'homebrew/cask/transmission', 70 - 'telegram', 71 - 72 - // Text & Documentation 73 - 'typora', 74 - 'espanso' 75 - ]; 17 + const brews = [ 18 + // Base utilities 19 + "aria2", 20 + "cursor", 21 + "duckdb", 22 + "jq", 23 + "zoxide", 24 + "choose-rust", 76 25 77 - const appstore_packages = [ 78 - { name: 'PairVPN', id: '1347012179' }, 79 - { name: 'Tailscale', id: '1475387142' }, 80 - { name: 'HotKey', id: '975890633' }, 81 - { name: 'Dark Reader', id: '1438243180' }, 82 - { name: 'Daisy Disk', id: '411643860' }, 83 - { name: 'Overlap', id: '1516950324' }, 84 - { name: 'Velja', id: '1607635845' }, 85 - { name: 'System Color Picker', id: '1545870783' }, 86 - { name: 'VPN Unlimited', id: '694633015' }, 87 - { name: 'Consent-O-Matic', id: '1606897889' } 88 - ]; 26 + // System utilities 27 + "bartender", 28 + "istat-menus", 29 + "macmediakeyforwarder", 30 + "lingon-x", 31 + "lunar", 32 + "onyx", 33 + "pastebot", 34 + 35 + // Development tools 36 + "beekeeper-studio", 37 + "cloudflared", 38 + "docker", 39 + "docker-completion", 40 + "docker-compose", 41 + "github", 42 + "orbstack", 43 + "postico", 44 + "postman", 45 + "sublime-text", 46 + 47 + // File management 48 + "cyberduck", 49 + "mountain-duck", 50 + "diskonaut", 51 + "dust", 52 + "duf", 53 + "the-unarchiver", 54 + 55 + // Media tools 56 + "iina", 57 + "handbrake", 58 + "homebrew/cask/handbrake", 59 + "losslesscut", 60 + "plex-media-server", 61 + "qlvideo", 62 + "subler", 63 + 64 + // Internet & Communication 65 + "firefox", 66 + "notion", 67 + "qbittorrent", 68 + "homebrew/cask/syncthing", 69 + "homebrew/cask/transmission", 70 + "telegram", 71 + 72 + // Text & Documentation 73 + "typora", 74 + "espanso", 75 + ]; 76 + 77 + const appstore_packages = [ 78 + { name: "PairVPN", id: "1347012179" }, 79 + { name: "Tailscale", id: "1475387142" }, 80 + { name: "HotKey", id: "975890633" }, 81 + { name: "Dark Reader", id: "1438243180" }, 82 + { name: "Daisy Disk", id: "411643860" }, 83 + { name: "Overlap", id: "1516950324" }, 84 + { name: "Velja", id: "1607635845" }, 85 + { name: "System Color Picker", id: "1545870783" }, 86 + { name: "VPN Unlimited", id: "694633015" }, 87 + { name: "Consent-O-Matic", id: "1606897889" }, 88 + ]; 89 + 90 + // Combine all packages into a single list 91 + const packages = [ 92 + ...uv_tools.map((name) => ({ name, installer: "uv" })), 93 + ...brews.map((name) => ({ name, installer: "homebrew" })), 94 + ...appstore_packages.map((pkg) => ({ 95 + name: pkg.name, 96 + id: pkg.id, 97 + installer: "mas", 98 + })), 99 + ].sort((a, b) => a.name.localeCompare(b.name)); 89 100 90 - // Combine all packages into a single list 91 - const packages = [ 92 - ...uv_tools.map(name => ({ name, installer: 'uv' })), 93 - ...brews.map(name => ({ name, installer: 'homebrew' })), 94 - ...appstore_packages.map(pkg => ({ name: pkg.name, id: pkg.id, installer: 'mas' })) 95 - ].sort((a, b) => a.name.localeCompare(b.name)); 101 + // Reactive variables for selected packages and copied state 102 + let selectedPackages = $state(new Set(packages)); 103 + let copied = false; 96 104 97 - // Reactive variables for selected packages and copied state 98 - let selectedPackages = $state(new Set(packages)); 99 - let copied = false; 105 + // Toggle package selection 106 + function togglePackage(pkg) { 107 + selectedPackages = new Set(selectedPackages); // Create new Set for reactivity 108 + const found = Array.from(selectedPackages).find( 109 + (p) => p.name === pkg.name && p.installer === pkg.installer 110 + ); 100 111 101 - // Toggle package selection 102 - function togglePackage(pkg) { 103 - selectedPackages = new Set(selectedPackages); // Create new Set for reactivity 104 - const found = Array.from(selectedPackages).find( 105 - p => p.name === pkg.name && p.installer === pkg.installer 106 - ); 107 - 108 - if (found) { 109 - selectedPackages.delete(found); 110 - } else { 111 - selectedPackages.add(pkg); 112 - } 113 - } 112 + if (found) { 113 + selectedPackages.delete(found); 114 + } else { 115 + selectedPackages.add(pkg); 116 + } 117 + } 114 118 115 119 // Group packages by installer 116 120 const packagesByInstaller = $derived({ 117 - homebrew: Array.from(selectedPackages).filter(pkg => pkg.installer === 'homebrew'), 118 - uv: Array.from(selectedPackages).filter(pkg => pkg.installer === 'uv'), 119 - mas: Array.from(selectedPackages).filter(pkg => pkg.installer === 'mas') 121 + homebrew: Array.from(selectedPackages).filter( 122 + (pkg) => pkg.installer === "homebrew" 123 + ), 124 + uv: Array.from(selectedPackages).filter((pkg) => pkg.installer === "uv"), 125 + mas: Array.from(selectedPackages).filter((pkg) => pkg.installer === "mas"), 120 126 }); 121 127 122 128 // Generate install commands for each installer 123 129 const installerCommands = $derived({ 124 - homebrew: (packagesByInstaller.homebrew.length > 0 || packagesByInstaller.mas.length > 0) ? 125 - `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` : 126 - null, 127 - uv: packagesByInstaller.uv.length > 0 ? [ 128 - 'curl -LsSf https://astral.sh/uv/install.sh | sh', 129 - 'uv python install' 130 - ] : null, 131 - mas: packagesByInstaller.mas.length > 0 ? 132 - 'brew install mas' : 133 - null 130 + homebrew: 131 + packagesByInstaller.homebrew.length > 0 || 132 + packagesByInstaller.mas.length > 0 133 + ? `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` 134 + : null, 135 + uv: 136 + packagesByInstaller.uv.length > 0 137 + ? [ 138 + "curl -LsSf https://astral.sh/uv/install.sh | sh", 139 + "uv python install", 140 + ] 141 + : null, 142 + mas: packagesByInstaller.mas.length > 0 ? "brew install mas" : null, 134 143 }); 135 144 136 145 // Generate package install commands 137 146 const packageCommands = $derived({ 138 - homebrew: packagesByInstaller.homebrew.length > 0 ? 139 - `brew install ${packagesByInstaller.homebrew.map(pkg => pkg.name).join(' ')}` : 140 - null, 141 - uv: packagesByInstaller.uv.length > 0 ? 142 - `uv tool install ${packagesByInstaller.uv.map(pkg => pkg.name).join(' ')}` : 143 - null, 144 - mas: packagesByInstaller.mas.length > 0 ? 145 - `mas install ${packagesByInstaller.mas.map(pkg => pkg.id).join(' ')}` : 146 - null 147 + homebrew: 148 + packagesByInstaller.homebrew.length > 0 149 + ? `brew install ${packagesByInstaller.homebrew.map((pkg) => pkg.name).join(" ")}` 150 + : null, 151 + uv: 152 + packagesByInstaller.uv.length > 0 153 + ? `uv tool install ${packagesByInstaller.uv.map((pkg) => pkg.name).join(" ")}` 154 + : null, 155 + mas: 156 + packagesByInstaller.mas.length > 0 157 + ? `mas install ${packagesByInstaller.mas.map((pkg) => pkg.id).join(" ")}` 158 + : null, 147 159 }); 148 160 149 161 // Combine all commands in the correct order 150 - const cmds = $derived([ 162 + const cmds = $derived( 163 + [ 151 164 installerCommands.homebrew, 152 165 installerCommands.uv, 153 166 installerCommands.mas, 154 167 packageCommands.homebrew, 155 168 packageCommands.uv, 156 - packageCommands.mas 157 - ].filter(Boolean) 158 - .flat() 159 - .join('\n')); 169 + packageCommands.mas, 170 + ] 171 + .filter(Boolean) 172 + .flat() 173 + .join("\n") 174 + ); 160 175 161 - // Copy the generated commands to clipboard 162 - function copyToClipboard() { 163 - navigator.clipboard.writeText(cmds); 164 - showCopiedMessage(); 165 - } 176 + // Copy the generated commands to clipboard 177 + function copyToClipboard() { 178 + navigator.clipboard.writeText(cmds); 179 + showCopiedMessage(); 180 + } 166 181 167 - // Show the "Copied!" message 168 - function showCopiedMessage() { 169 - copied = true; 170 - setTimeout(() => copied = false, 2000); 171 - } 182 + // Show the "Copied!" message 183 + function showCopiedMessage() { 184 + copied = true; 185 + setTimeout(() => (copied = false), 2000); 186 + } 172 187 173 - // Add new derived store to group commands by type 174 - const groupedCommands = $derived({ 175 - installers: [ 176 - installerCommands.homebrew, 177 - installerCommands.uv, 178 - installerCommands.mas 179 - ].filter(Boolean).flat(), 180 - packages: [ 181 - packageCommands.homebrew, 182 - packageCommands.uv, 183 - packageCommands.mas 184 - ].filter(Boolean) 185 - }); 186 - 187 - // Add new copy functions for each section 188 - function copyInstallers() { 189 - navigator.clipboard.writeText(groupedCommands.installers.join('\n')); 190 - showCopiedMessage(); 191 - } 188 + // Add new derived store to group commands by type 189 + const groupedCommands = $derived({ 190 + installers: [ 191 + installerCommands.homebrew, 192 + installerCommands.uv, 193 + installerCommands.mas, 194 + ] 195 + .filter(Boolean) 196 + .flat(), 197 + packages: [ 198 + packageCommands.homebrew, 199 + packageCommands.uv, 200 + packageCommands.mas, 201 + ].filter(Boolean), 202 + }); 192 203 193 - function copyPackages() { 194 - navigator.clipboard.writeText(groupedCommands.packages.join('\n')); 195 - showCopiedMessage(); 196 - } 204 + // Add new copy functions for each section 205 + function copyInstallers() { 206 + navigator.clipboard.writeText(groupedCommands.installers.join("\n")); 207 + showCopiedMessage(); 208 + } 197 209 198 - // Add select/deselect all function 199 - function toggleSelectAll() { 200 - if (selectedPackages.size === packages.length) { 201 - selectedPackages = new Set(); 202 - } else { 203 - selectedPackages = new Set(packages); 204 - } 205 - } 210 + function copyPackages() { 211 + navigator.clipboard.writeText(groupedCommands.packages.join("\n")); 212 + showCopiedMessage(); 213 + } 206 214 215 + // Add select/deselect all function 216 + function toggleSelectAll() { 217 + if (selectedPackages.size === packages.length) { 218 + selectedPackages = new Set(); 219 + } else { 220 + selectedPackages = new Set(packages); 221 + } 222 + } 207 223 </script> 208 224 209 225 <div class="container mx-auto p-4"> 210 226 <Card.Root> 211 227 <Card.Header> 212 228 <Card.Title>Package Selection</Card.Title> 213 - <Card.Description>Select packages to install on your system</Card.Description> 229 + <Card.Description 230 + >Select packages to install on your system</Card.Description 231 + > 214 232 </Card.Header> 215 233 <Card.Content> 216 234 <div class="flex justify-between items-center mb-4"> 217 - <Button 218 - variant="outline" 219 - size="sm" 220 - on:click={toggleSelectAll} 221 - > 222 - {selectedPackages.size === packages.length ? 'Deselect All' : 'Select All'} 235 + <Button variant="outline" size="sm" on:click={toggleSelectAll}> 236 + {selectedPackages.size === packages.length 237 + ? "Deselect All" 238 + : "Select All"} 223 239 </Button> 224 240 <span class="text-sm text-muted-foreground"> 225 241 {selectedPackages.size} of {packages.length} selected ··· 243 259 <Card.Root class="mt-4"> 244 260 <Card.Header> 245 261 <Card.Title>Installation Commands</Card.Title> 246 - <Card.Description>Copy and run these commands to install selected packages</Card.Description> 262 + <Card.Description 263 + >Copy and run these commands to install selected packages</Card.Description 264 + > 247 265 </Card.Header> 248 266 <Card.Content> 249 267 <div class="terminal-output"> 250 268 <Button class="copy-all-button" on:click={copyToClipboard}> 251 269 Copy All Commands 252 270 </Button> 253 - 271 + 254 272 {#if groupedCommands.installers.length > 0} 255 273 <div class="command-section"> 256 274 <div class="section-header"> 257 - <h4>Install Package Managers</h4> 258 - <Button 259 - variant="outline" 260 - size="sm" 261 - on:click={copyInstallers} 262 - > 275 + <Button variant="outline" size="sm" on:click={copyInstallers}> 263 276 Copy 264 277 </Button> 265 278 </div> 266 - <pre><code>{groupedCommands.installers.join('\n')}</code></pre> 279 + <pre><code>{groupedCommands.installers.join("\n")}</code></pre> 267 280 </div> 268 281 {/if} 269 - 282 + 270 283 {#if groupedCommands.packages.length > 0} 271 284 <div class="command-section"> 272 285 <div class="section-header"> 273 - <h4>Install Packages</h4> 274 - <Button 275 - variant="outline" 276 - size="sm" 277 - on:click={copyPackages} 278 - > 286 + <Button variant="outline" size="sm" on:click={copyPackages}> 279 287 Copy 280 288 </Button> 281 289 </div> 282 - <pre><code>{groupedCommands.packages.join('\n')}</code></pre> 290 + <pre><code>{groupedCommands.packages.join("\n")}</code></pre> 283 291 </div> 284 292 {/if} 285 293 ··· 291 299 </Card.Root> 292 300 {/if} 293 301 </div> 302 + 294 303 <style> 295 - .package-list { 296 - display: grid; 297 - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 298 - gap: 0.5rem; 299 - max-height: 60vh; 300 - overflow-y: auto; 301 - } 304 + .package-list { 305 + display: grid; 306 + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 307 + gap: 0.5rem; 308 + max-height: 60vh; 309 + overflow-y: auto; 310 + } 302 311 303 - .package-name { 304 - color: #666; 305 - cursor: pointer; 306 - padding: 0.5rem; 307 - border: none; 308 - background: none; 309 - font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace; 310 - text-align: left; 311 - white-space: nowrap; 312 - overflow: hidden; 313 - text-overflow: ellipsis; 314 - transition: color 0.2s ease; 315 - width: 100%; 316 - border-radius: 0.25rem; 317 - } 312 + .package-name { 313 + color: #666; 314 + cursor: pointer; 315 + padding: 0.5rem; 316 + border: none; 317 + background: none; 318 + font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, 319 + Consolas, "DejaVu Sans Mono", monospace; 320 + text-align: left; 321 + white-space: nowrap; 322 + overflow: hidden; 323 + text-overflow: ellipsis; 324 + transition: color 0.2s ease; 325 + width: 100%; 326 + border-radius: 0.25rem; 327 + } 318 328 319 - .package-name:hover { 320 - background-color: rgba(0, 0, 0, 0.05); 321 - } 329 + .package-name:hover { 330 + background-color: rgba(0, 0, 0, 0.05); 331 + } 322 332 323 - .package-name.selected { 324 - color: #0d6efd; 325 - background-color: rgba(13, 110, 253, 0.1); 326 - } 333 + .package-name.selected { 334 + color: #0d6efd; 335 + background-color: rgba(13, 110, 253, 0.1); 336 + } 327 337 328 - .terminal-output { 329 - background-color: #161b22; 330 - color: #c9d1d9; 331 - padding: 1rem; 332 - border-radius: 0.5rem; 333 - position: fixed; 334 - bottom: 1rem; 335 - left: 1rem; 336 - right: 1rem; 337 - max-height: 40vh; 338 - overflow-y: auto; 339 - font-family: monospace; 340 - white-space: pre-wrap; 341 - z-index: 10; /* Ensure terminal stays on top */ 342 - } 338 + .terminal-output { 339 + background-color: #161b22; 340 + color: #c9d1d9; 341 + padding: 1rem; 342 + border-radius: 0.5rem; 343 + position: fixed; 344 + bottom: 1rem; 345 + left: 1rem; 346 + right: 1rem; 347 + max-height: 40vh; 348 + overflow-y: auto; 349 + font-family: monospace; 350 + white-space: pre-wrap; 351 + z-index: 10; /* Ensure terminal stays on top */ 352 + } 343 353 344 - .copy-button { 345 - position: absolute; 346 - top: 0.5rem; 347 - right: 0.5rem; 348 - background-color: #0d6efd; 349 - color: #fff; 350 - border: none; 351 - padding: 0.5rem; 352 - border-radius: 0.25rem; 353 - cursor: pointer; 354 - } 354 + .copy-button { 355 + position: absolute; 356 + top: 0.5rem; 357 + right: 0.5rem; 358 + background-color: #0d6efd; 359 + color: #fff; 360 + border: none; 361 + padding: 0.5rem; 362 + border-radius: 0.25rem; 363 + cursor: pointer; 364 + } 355 365 356 - .copied-message { 357 - position: absolute; 358 - top: 0.5rem; 359 - right: 5rem; 360 - background-color: #198754; 361 - color: #fff; 362 - padding: 0.5rem; 363 - border-radius: 0.25rem; 364 - } 366 + .copied-message { 367 + position: absolute; 368 + top: 0.5rem; 369 + right: 5rem; 370 + background-color: #198754; 371 + color: #fff; 372 + padding: 0.5rem; 373 + border-radius: 0.25rem; 374 + } 365 375 366 - .terminal-output h4 { 367 - color: #8b949e; 368 - margin: 0.5rem 0; 369 - font-size: 0.9rem; 370 - font-weight: normal; 371 - } 376 + .terminal-output h4 { 377 + color: #8b949e; 378 + margin: 0.5rem 0; 379 + font-size: 0.9rem; 380 + font-weight: normal; 381 + } 372 382 373 - .terminal-output pre { 374 - margin: 0.5rem 0 1rem 0; 375 - } 383 + .terminal-output pre { 384 + margin: 0.5rem 0 1rem 0; 385 + } 376 386 377 - .command-section { 378 - margin-bottom: 1rem; 379 - border: 1px solid #30363d; 380 - border-radius: 0.5rem; 381 - padding: 1rem; 382 - } 387 + .command-section { 388 + margin-bottom: 1rem; 389 + border: 1px solid #30363d; 390 + border-radius: 0.5rem; 391 + padding: 1rem; 392 + } 383 393 384 - .section-header { 385 - display: flex; 386 - justify-content: space-between; 387 - align-items: center; 388 - margin-bottom: 0.5rem; 389 - } 394 + .section-header { 395 + display: flex; 396 + justify-content: space-between; 397 + align-items: center; 398 + margin-bottom: 0.5rem; 399 + } 390 400 391 - .section-header h4 { 392 - margin: 0; 393 - } 401 + .section-header h4 { 402 + margin: 0; 403 + } 394 404 395 - .copy-all-button { 396 - display: block; 397 - width: 100%; 398 - margin-bottom: 1rem; 399 - background-color: #0d6efd; 400 - color: #fff; 401 - border: none; 402 - padding: 0.5rem; 403 - border-radius: 0.25rem; 404 - cursor: pointer; 405 - } 405 + .copy-all-button { 406 + display: block; 407 + width: 100%; 408 + margin-bottom: 1rem; 409 + background-color: #0d6efd; 410 + color: #fff; 411 + border: none; 412 + padding: 0.5rem; 413 + border-radius: 0.25rem; 414 + cursor: pointer; 415 + } 406 416 407 - .copy-section-button { 408 - background-color: #21262d; 409 - color: #c9d1d9; 410 - border: 1px solid #30363d; 411 - padding: 0.25rem 0.5rem; 412 - border-radius: 0.25rem; 413 - cursor: pointer; 414 - font-size: 0.8rem; 415 - } 417 + .copy-section-button { 418 + background-color: #21262d; 419 + color: #c9d1d9; 420 + border: 1px solid #30363d; 421 + padding: 0.25rem 0.5rem; 422 + border-radius: 0.25rem; 423 + cursor: pointer; 424 + font-size: 0.8rem; 425 + } 416 426 417 - .copy-section-button:hover { 418 - background-color: #30363d; 419 - } 427 + .copy-section-button:hover { 428 + background-color: #30363d; 429 + } 420 430 421 - /* Remove the old copy-button style if it exists */ 422 - .copy-button { 423 - display: none; 424 - } 425 - </style> 431 + /* Remove the old copy-button style if it exists */ 432 + .copy-button { 433 + display: none; 434 + } 435 + </style>