[WIP] A simple wake-on-lan service
at main 170 lines 3.5 kB view raw
1<script lang="ts"> 2 import { ExternalLink, Power } from "@lucide/svelte"; 3 import Api from "./api"; 4 5 const { 6 name, 7 mac, 8 ip, 9 url, 10 }: { 11 name: string; 12 mac: string; 13 ip: string | null; 14 url: string | null; 15 } = $props(); 16 17 let online: boolean | undefined = $state(undefined); 18 const checkStatus: () => Promise<void> = async () => { 19 if (!ip) return; 20 const active = await Api.ping({ ip }).catch(() => false); 21 online = active; 22 // check every 30s if online, or every 5 seconds if offline 23 setTimeout(checkStatus, (active ? 30 : 5) * 1000); 24 }; 25 checkStatus(); 26 27 // try clean up the url but dont lose sleep 28 function cleanUrl(url: string): string { 29 const REGEX = 30 /(?<=^(?:[a-z]*:\/\/)?)(?:[a-z]+(?:\.[a-z]+)+|localhost)(?::\d{1,5})?(?:\/+[^\/]+)*/gm; 31 const res = url.match(REGEX); 32 if (res && res[0]) return res[0]; 33 return url; 34 } 35</script> 36 37<section> 38 <button 39 class="power" 40 onclick={() => 41 Api.wake({ mac }).then((res) => alert(`Wake: ${mac} (${ip}) ${res}`))} 42 aria-label="power" 43 > 44 <Power /> 45 </button> 46 47 <div class="name"> 48 {name} 49 {#if ip}<span class="status" data-status={online}></span>{/if} 50 {#if url}<a href={url} class="url" title={url}> 51 <span class="url-text">{cleanUrl(url)}</span> 52 <ExternalLink /> 53 </a>{/if} 54 </div> 55 <div class="mac">{mac}</div> 56 {#if ip} 57 <div class="at">@</div> 58 <div class="ip">{ip}</div> 59 {/if} 60</section> 61 62<style> 63 .status { 64 display: inline-block; 65 width: 0.5em; 66 height: 0.5em; 67 border-radius: 100%; 68 transition: background 500ms ease-in-out; 69 70 &[data-status="true"] { 71 background: var(--theme-accent-success); 72 } 73 &[data-status="false"] { 74 background: var(--theme-accent-fail); 75 } 76 } 77 78 section { 79 border-radius: 10px; 80 background-color: var(--theme-background-server); 81 82 box-sizing: border-box; 83 padding: 0.5rem; 84 display: grid; 85 grid-template: 86 "power name name name" 1lh 87 "power mac at ip" 1lh 88 / auto auto min-content auto; 89 justify-content: start; 90 align-items: center; 91 gap: 0 5px; 92 93 .name { 94 grid-area: name; 95 .url { 96 text-decoration: none; 97 .url-text { 98 text-decoration: underline; 99 &:hover { 100 text-decoration-style: dotted; 101 } 102 103 &:active { 104 text-decoration: none; 105 } 106 } 107 } 108 } 109 110 .mac { 111 grid-area: mac; 112 color: var(--theme-text-secondary); 113 } 114 115 .at { 116 grid-area: at; 117 color: var(--theme-text-secondary); 118 } 119 120 .ip { 121 grid-area: ip; 122 color: var(--theme-text-secondary); 123 } 124 } 125 126 @property --power-overlay { 127 syntax: "<color>"; 128 inherits: false; 129 initial-value: transparent; 130 } 131 132 .power { 133 color: inherit; 134 grid-area: power; 135 aspect-ratio: 1; 136 height: 100%; 137 display: flex; 138 justify-content: center; 139 align-items: center; 140 141 border-radius: 100%; 142 border: 0px; 143 background: linear-gradient(var(--power-overlay), var(--power-overlay)), 144 linear-gradient( 145 var(--theme-background-button), 146 var(--theme-background-button) 147 ); 148 149 transition: 150 scale 10ms, 151 --power-overlay 100ms; 152 153 &:hover { 154 --power-overlay: rgb( 155 from var(--theme-highlight) r g b / var(--theme-highlight-opacity) 156 ); 157 } 158 159 &:active { 160 scale: 0.9; 161 } 162 } 163 164 :global { 165 .lucide-external-link { 166 width: 1em; 167 height: 1em; 168 } 169 } 170</style>