|
4 | 4 | import Icon from '$lib/components/global/Icon.svelte'; |
5 | 5 | import { bookmarks } from '$lib/stores/bookmarks'; |
6 | 6 | import { formatShortcut } from '$lib/utils/keyboard'; |
| 7 | + import SegmentedControl from '$lib/components/global/SegmentedControl.svelte'; |
7 | 8 |
|
8 | 9 | interface Shortcut { |
9 | 10 | keys: string; |
|
102 | 103 | }); |
103 | 104 | return groups; |
104 | 105 | }); |
| 106 | +
|
| 107 | + const viewOptions = [ |
| 108 | + { label: 'Keyboard Shortcuts', value: 'keyboard-shortcuts', icon: 'keyboard' }, |
| 109 | + { label: 'About', value: 'about', icon: 'info' }, |
| 110 | + ]; |
| 111 | +
|
| 112 | + let activeView = $state('keyboard-shortcuts'); |
| 113 | +
|
| 114 | + // Compute dialog title based on active view |
| 115 | + const dialogTitle = $derived(activeView === 'keyboard-shortcuts' ? 'Command Palette' : 'Networking Toolbox'); |
| 116 | +
|
| 117 | + // Close dialog when clicking links in About tab |
| 118 | + function handleLinkClick() { |
| 119 | + if (activeView === 'about') { |
| 120 | + close(); |
| 121 | + } |
| 122 | + } |
105 | 123 | </script> |
106 | 124 |
|
107 | 125 | {#if isOpen} |
108 | 126 | <div class="shortcuts-backdrop" onclick={handleBackdropClick} role="presentation"> |
109 | 127 | <div class="shortcuts-dialog" role="dialog" aria-labelledby="shortcuts-title" aria-modal="true"> |
110 | 128 | <div class="dialog-header"> |
111 | | - <h2 id="shortcuts-title">Command Palette</h2> |
| 129 | + <div class="dialog-header-main"> |
| 130 | + <h2 id="shortcuts-title">{dialogTitle}</h2> |
| 131 | + <div> |
| 132 | + <SegmentedControl |
| 133 | + hideLabel={true} |
| 134 | + options={viewOptions} |
| 135 | + bind:value={activeView} |
| 136 | + onchange={(value) => (activeView = value)} |
| 137 | + /> |
| 138 | + </div> |
| 139 | + </div> |
112 | 140 | <button class="close-btn" onclick={close} aria-label="Close shortcuts"> |
113 | 141 | <Icon name="x" size="sm" /> |
114 | 142 | </button> |
115 | 143 | </div> |
116 | 144 |
|
117 | | - <div class="shortcuts-content"> |
118 | | - {#each Object.entries(groupedShortcuts()) as [category, items] (category)} |
119 | | - <div class="shortcuts-category"> |
120 | | - <h3>{category}</h3> |
121 | | - <ul> |
122 | | - {#each items as shortcut (shortcut.keys)} |
123 | | - <li> |
124 | | - <kbd>{formatShortcut(shortcut.keys)}</kbd> |
125 | | - <span>{shortcut.description}</span> |
126 | | - </li> |
127 | | - {/each} |
128 | | - </ul> |
129 | | - |
130 | | - <!-- Show bookmarked tools if in Bookmarks category --> |
131 | | - {#if category === 'Bookmarks' && $bookmarks.length > 0} |
132 | | - <details class="bookmarks-details"> |
133 | | - <summary>Your bookmarked tools ({Math.min($bookmarks.length, 9)})</summary> |
134 | | - <ul class="bookmarks-list"> |
135 | | - {#each $bookmarks.slice(0, 10) as bookmark, index (bookmark.href)} |
136 | | - <li> |
137 | | - <kbd>{formatShortcut(`^${index + 1 === 10 ? 0 : index + 1}`)}</kbd> |
138 | | - <span>{bookmark.label}</span> |
139 | | - </li> |
140 | | - {/each} |
141 | | - {#if $bookmarks.length <= 9} |
142 | | - <li> |
143 | | - <kbd>{formatShortcut('^0')}</kbd> |
144 | | - <span>Homepage</span> |
145 | | - </li> |
146 | | - {/if} |
| 145 | + {#if activeView === 'keyboard-shortcuts'} |
| 146 | + <div class="shortcuts-content"> |
| 147 | + {#each Object.entries(groupedShortcuts()) as [category, items] (category)} |
| 148 | + <div class="shortcuts-category"> |
| 149 | + <h3>{category}</h3> |
| 150 | + <ul> |
| 151 | + {#each items as shortcut (shortcut.keys)} |
147 | 152 | <li> |
148 | | - <kbd>{formatShortcut('^B')}</kbd> |
149 | | - <span>View all Bookmarks</span> |
| 153 | + <kbd>{formatShortcut(shortcut.keys)}</kbd> |
| 154 | + <span>{shortcut.description}</span> |
150 | 155 | </li> |
151 | | - </ul> |
152 | | - </details> |
153 | | - {/if} |
154 | | - |
155 | | - {#if category === 'Bookmarks' && $bookmarks.length === 0} |
156 | | - <p class="no-bookmarks-tip"> |
157 | | - <i>You don't have any bookmarks yet.</i> |
158 | | - <span>Right-click on a tool to bookmark it for quick access and offline use.</span> |
159 | | - </p> |
160 | | - {/if} |
| 156 | + {/each} |
| 157 | + </ul> |
| 158 | + |
| 159 | + <!-- Show bookmarked tools if in Bookmarks category --> |
| 160 | + {#if category === 'Bookmarks' && $bookmarks.length > 0} |
| 161 | + <details class="bookmarks-details"> |
| 162 | + <summary>Your bookmarked tools ({Math.min($bookmarks.length, 9)})</summary> |
| 163 | + <ul class="bookmarks-list"> |
| 164 | + {#each $bookmarks.slice(0, 10) as bookmark, index (bookmark.href)} |
| 165 | + <li> |
| 166 | + <kbd>{formatShortcut(`^${index + 1 === 10 ? 0 : index + 1}`)}</kbd> |
| 167 | + <span>{bookmark.label}</span> |
| 168 | + </li> |
| 169 | + {/each} |
| 170 | + {#if $bookmarks.length <= 9} |
| 171 | + <li> |
| 172 | + <kbd>{formatShortcut('^0')}</kbd> |
| 173 | + <span>Homepage</span> |
| 174 | + </li> |
| 175 | + {/if} |
| 176 | + <li> |
| 177 | + <kbd>{formatShortcut('^B')}</kbd> |
| 178 | + <span>View all Bookmarks</span> |
| 179 | + </li> |
| 180 | + </ul> |
| 181 | + </details> |
| 182 | + {/if} |
| 183 | + |
| 184 | + {#if category === 'Bookmarks' && $bookmarks.length === 0} |
| 185 | + <p class="no-bookmarks-tip"> |
| 186 | + <i>You don't have any bookmarks yet.</i> |
| 187 | + <span>Right-click on a tool to bookmark it for quick access and offline use.</span> |
| 188 | + </p> |
| 189 | + {/if} |
| 190 | + </div> |
| 191 | + {/each} |
| 192 | + </div> |
| 193 | + {:else if activeView === 'about'} |
| 194 | + <div class="about-content"> |
| 195 | + <p> |
| 196 | + Networking Toolbox is an open-source collection of web-based networking tools designed to make |
| 197 | + network-related tasks quicker and easier. |
| 198 | + </p> |
| 199 | + <p> |
| 200 | + With 100+ tools, it's privacy-focused and self-hostable, fully customizable, and includes a free REST API |
| 201 | + for automation. |
| 202 | + </p> |
| 203 | + <ul> |
| 204 | + <li><a href="https://github.com/lissy93/networking-toolbox" onclick={handleLinkClick}>GitHub</a></li> |
| 205 | + <li><a href="/sitemap" onclick={handleLinkClick}>Page Listing</a></li> |
| 206 | + <li><a href="/settings" onclick={handleLinkClick}>App Settings</a></li> |
| 207 | + <li><a href="/about" onclick={handleLinkClick}>Documentation</a></li> |
| 208 | + <li><a href="/about/support" onclick={handleLinkClick}>Support</a></li> |
| 209 | + <li><a href="/about/legal" onclick={handleLinkClick}>Legal</a></li> |
| 210 | + </ul> |
| 211 | + <p class="sponsor"> |
| 212 | + <Icon name="heart" size="md" /> |
| 213 | + <span> |
| 214 | + <b>Finding Networking Toolbox useful?</b> |
| 215 | + Consider <a href="https://github.com/sponsors/Lissy93">sponsoring us on GitHub</a> to support ongoing development! |
| 216 | + </span> |
| 217 | + </p> |
| 218 | + |
| 219 | + <div class="about-license-section"> |
| 220 | + <a |
| 221 | + href="https://github.com/lissy93/networking-toolbox" |
| 222 | + target="_blank" |
| 223 | + rel="noopener" |
| 224 | + onclick={handleLinkClick}>Networking Toolbox</a |
| 225 | + > |
| 226 | + v{import.meta.env.VITE_APP_VERSION}, licensed under |
| 227 | + <a href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener" onclick={handleLinkClick} |
| 228 | + >MIT</a |
| 229 | + > |
| 230 | + © {new Date().getFullYear()} Alicia Sykes |
161 | 231 | </div> |
162 | | - {/each} |
163 | | - </div> |
| 232 | + </div> |
| 233 | + {/if} |
164 | 234 | </div> |
165 | 235 | </div> |
166 | 236 | {/if} |
|
197 | 267 | display: flex; |
198 | 268 | flex-direction: column; |
199 | 269 | animation: slideInScale 0.2s ease-out; |
| 270 | + min-height: 548px; |
200 | 271 |
|
201 | 272 | @media (max-width: 768px) { |
202 | 273 | max-width: 100%; |
|
207 | 278 | border: none; |
208 | 279 | box-shadow: none; |
209 | 280 | animation: slideInFromBottom var(--transition-normal); |
| 281 | + min-height: auto; |
210 | 282 | } |
211 | 283 | } |
212 | 284 |
|
|
216 | 288 | justify-content: space-between; |
217 | 289 | padding: var(--spacing-md) var(--spacing-lg); |
218 | 290 | border-bottom: 1px solid var(--border-primary); |
| 291 | + .dialog-header-main { |
| 292 | + display: flex; |
| 293 | + width: 100%; |
| 294 | + padding-right: var(--spacing-sm); |
| 295 | + align-items: center; |
| 296 | + justify-content: space-between; |
| 297 | + } |
219 | 298 |
|
220 | 299 | @media (max-width: 768px) { |
221 | 300 | padding: var(--spacing-md); |
|
397 | 476 | } |
398 | 477 | } |
399 | 478 |
|
| 479 | + .about-content { |
| 480 | + padding: var(--spacing-md) var(--spacing-lg); |
| 481 | +
|
| 482 | + p { |
| 483 | + color: var(--text-primary); |
| 484 | + font-size: var(--font-size-md); |
| 485 | + } |
| 486 | + .sponsor { |
| 487 | + font-size: var(--font-size-xs); |
| 488 | + color: var(--color-pink); |
| 489 | + background: color-mix(in srgb, var(--color-pink), transparent 90%); |
| 490 | + padding: var(--spacing-sm); |
| 491 | + border-radius: var(--radius-sm); |
| 492 | + border: 2px solid var(--color-pink); |
| 493 | + display: flex; |
| 494 | + align-items: center; |
| 495 | + gap: var(--spacing-sm); |
| 496 | + margin: var(--spacing-md) auto; |
| 497 | + a { |
| 498 | + color: var(--color-pink); |
| 499 | + text-decoration: underline; |
| 500 | + } |
| 501 | + opacity: 0; |
| 502 | + animation: fadeIn 1s ease-out 10s forwards; |
| 503 | + } |
| 504 | + .about-license-section { |
| 505 | + font-size: var(--font-size-xs); |
| 506 | + text-align: center; |
| 507 | + color: var(--text-secondary); |
| 508 | + a { |
| 509 | + color: var(--text-secondary); |
| 510 | + text-decoration: underline; |
| 511 | + } |
| 512 | + } |
| 513 | + ul { |
| 514 | + padding-left: var(--spacing-lg); |
| 515 | + margin: var(--spacing-lg) 0; |
| 516 | + li { |
| 517 | + a { |
| 518 | + color: var(--color-primary); |
| 519 | + text-decoration: underline; |
| 520 | + } |
| 521 | + } |
| 522 | + } |
| 523 | + } |
| 524 | +
|
400 | 525 | @keyframes fadeIn { |
401 | 526 | from { |
402 | 527 | opacity: 0; |
|
0 commit comments