[READ-ONLY] Mirror of https://github.com/probablykasper/ferrum. Music library app for Mac, Linux and Windows ferrum.kasper.space
electron linux macos music music-library music-player napi windows
0

Configure Feed

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

Use underscores for variable names

Kasper (Sep 15, 2024, 7:27 PM +0200) fc386b48 369429b5

+1252 -1240
+18 -18
eslint.config.mjs
··· 32 32 }, 33 33 { 34 34 rules: { 35 - // '@typescript-eslint/naming-convention': [ 36 - // 'error', 37 - // { 38 - // selector: 'variableLike', 39 - // format: ['snake_case', 'UPPER_CASE'], 40 - // leadingUnderscore: 'allow', 41 - // }, 42 - // { 43 - // selector: 'parameter', 44 - // modifiers: ['destructured'], 45 - // format: null, 46 - // }, 47 - // { 48 - // selector: 'variable', 49 - // modifiers: ['destructured'], 50 - // format: null, 51 - // }, 52 - // ], 35 + '@typescript-eslint/naming-convention': [ 36 + 'error', 37 + { 38 + selector: 'variableLike', 39 + format: ['snake_case', 'UPPER_CASE'], 40 + leadingUnderscore: 'allow', 41 + }, 42 + { 43 + selector: 'parameter', 44 + modifiers: ['destructured'], 45 + format: null, 46 + }, 47 + { 48 + selector: 'variable', 49 + modifiers: ['destructured'], 50 + format: null, 51 + }, 52 + ], 53 53 '@typescript-eslint/no-unused-expressions': 'off', 54 54 '@typescript-eslint/no-unused-vars': [ 55 55 'error',
+71 -71
src/App.svelte
··· 7 7 import Queue from './components/Queue.svelte' 8 8 import TrackInfo, { type TrackInfoList } from './components/TrackInfo.svelte' 9 9 import PlaylistInfoModal from './components/PlaylistInfo.svelte' 10 - import { queueVisible } from './lib/queue' 11 - import { ipcListen, ipcRenderer } from '@/lib/window' 10 + import { queue_visible } from './lib/queue' 11 + import { ipc_listen, ipc_renderer } from '@/lib/window' 12 12 import { 13 - importTracks, 13 + import_tracks, 14 14 type PlaylistInfo, 15 15 methods, 16 16 page, 17 - isMac, 17 + is_mac, 18 18 view_as_songs, 19 19 view_as_artists, 20 20 } from './lib/data' 21 - import { playPause } from './lib/player' 21 + import { play_pause } from './lib/player' 22 22 import DragGhost from './components/DragGhost.svelte' 23 23 import ItunesImport from './components/ItunesImport.svelte' 24 24 import type { TrackID } from 'ferrum-addon/addon' 25 - import { modalCount } from './components/Modal.svelte' 25 + import { modal_count } from './components/Modal.svelte' 26 26 import QuickNav from './components/QuickNav.svelte' 27 - import { checkShortcut } from './lib/helpers' 27 + import { check_shortcut } from './lib/helpers' 28 28 import ArtistList from './components/ArtistList.svelte' 29 29 import { tracklist_actions } from './lib/page' 30 30 31 - ipcRenderer.emit('appLoaded') 31 + ipc_renderer.emit('appLoaded') 32 32 33 - async function openImportDialog() { 34 - if ($modalCount !== 0) { 33 + async function open_import_dialog() { 34 + if ($modal_count !== 0) { 35 35 return 36 36 } 37 - let result = await ipcRenderer.invoke('showOpenDialog', false, { 37 + let result = await ipc_renderer.invoke('showOpenDialog', false, { 38 38 properties: ['openFile', 'multiSelections'], 39 39 filters: [{ name: 'Audio', extensions: ['mp3', 'm4a', 'opus'] }], 40 40 }) 41 41 if (!result.canceled && result.filePaths.length >= 1) { 42 - importTracks(result.filePaths) 42 + import_tracks(result.filePaths) 43 43 } 44 44 } 45 - ipcRenderer.on('import', openImportDialog) 45 + ipc_renderer.on('import', open_import_dialog) 46 46 onDestroy(() => { 47 - ipcRenderer.removeListener('import', openImportDialog) 47 + ipc_renderer.removeListener('import', open_import_dialog) 48 48 }) 49 49 50 - function toggleQueue() { 51 - $queueVisible = !$queueVisible 50 + function toggle_queue() { 51 + $queue_visible = !$queue_visible 52 52 } 53 - $: ipcRenderer.invoke('update:Show Queue', $queueVisible) 54 - ipcRenderer.on('Show Queue', toggleQueue) 53 + $: ipc_renderer.invoke('update:Show Queue', $queue_visible) 54 + ipc_renderer.on('Show Queue', toggle_queue) 55 55 onDestroy(() => { 56 - ipcRenderer.removeListener('Show Queue', toggleQueue) 56 + ipc_renderer.removeListener('Show Queue', toggle_queue) 57 57 }) 58 58 59 59 let droppable = false 60 - const allowedMimes = ['audio/mpeg', 'audio/x-m4a', 'audio/ogg'] // mp3, m4a 61 - function getFilePaths(e: DragEvent): string[] { 60 + const allowed_mimes = ['audio/mpeg', 'audio/x-m4a', 'audio/ogg'] // mp3, m4a 61 + function get_file_paths(e: DragEvent): string[] { 62 62 if (!e.dataTransfer) return [] 63 - let validPaths: string[] = [] 63 + let valid_paths: string[] = [] 64 64 for (let i = 0; i < e.dataTransfer.files.length; i++) { 65 65 const file = e.dataTransfer.files[i] 66 - if (allowedMimes.includes(file.type)) { 67 - validPaths.push(file.path) 66 + if (allowed_mimes.includes(file.type)) { 67 + valid_paths.push(file.path) 68 68 } 69 69 } 70 - return validPaths 70 + return valid_paths 71 71 } 72 - function hasFiles(e: DragEvent): boolean { 72 + function has_files(e: DragEvent): boolean { 73 73 if (!e.dataTransfer) return false 74 74 for (let i = 0; i < e.dataTransfer.items.length; i++) { 75 75 const item = e.dataTransfer.items[i] 76 - if (item.kind === 'file' && allowedMimes.includes(item.type)) { 76 + if (item.kind === 'file' && allowed_mimes.includes(item.type)) { 77 77 return true 78 78 } 79 79 } 80 80 return false 81 81 } 82 - function dragEnterOrOver(e: DragEvent) { 83 - droppable = hasFiles(e) 82 + function drag_enter_or_over(e: DragEvent) { 83 + droppable = has_files(e) 84 84 if (droppable) { 85 85 e.preventDefault() 86 86 } 87 87 } 88 - function dragLeave() { 88 + function drag_leave() { 89 89 droppable = false 90 90 } 91 91 function drop(e: DragEvent) { 92 92 e.preventDefault() 93 93 droppable = false 94 - const validPaths = getFilePaths(e) 94 + const valid_paths = get_file_paths(e) 95 95 const paths = [] 96 - for (const path of validPaths) { 96 + for (const path of valid_paths) { 97 97 paths.push(path) 98 98 } 99 - importTracks(paths) 99 + import_tracks(paths) 100 100 } 101 101 function keydown(e: KeyboardEvent) { 102 102 let el = e.target as HTMLAudioElement 103 103 if (el && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA') { 104 104 if (e.key === ' ') { 105 105 e.preventDefault() 106 - playPause() 106 + play_pause() 107 107 } 108 108 } 109 109 } 110 110 111 - let showItunesImport = false 111 + let show_itunes_import = false 112 112 onDestroy( 113 - ipcListen('itunesImport', () => { 114 - if ($modalCount === 0) { 115 - showItunesImport = true 113 + ipc_listen('itunesImport', () => { 114 + if ($modal_count === 0) { 115 + show_itunes_import = true 116 116 } 117 117 }), 118 118 ) 119 119 120 - let trackInfoList: TrackInfoList | null = null 121 - function onTrackInfo(ids: TrackID[], trackIndex: number) { 122 - if ($modalCount === 0) { 123 - trackInfoList = { ids, index: trackIndex } 120 + let track_info_list: TrackInfoList | null = null 121 + function on_track_info(ids: TrackID[], track_index: number) { 122 + if ($modal_count === 0) { 123 + track_info_list = { ids, index: track_index } 124 124 } 125 125 } 126 126 onDestroy( 127 - ipcListen('context.Get Info', (_, ids: TrackID[], trackIndex: number) => { 128 - onTrackInfo(ids, trackIndex) 127 + ipc_listen('context.Get Info', (_, ids: TrackID[], track_index: number) => { 128 + on_track_info(ids, track_index) 129 129 }), 130 130 ) 131 131 132 - let playlistInfo: PlaylistInfo | null = null 132 + let playlist_info: PlaylistInfo | null = null 133 133 onDestroy( 134 - ipcListen('context.playlist.edit', (_, id) => { 134 + ipc_listen('context.playlist.edit', (_, id) => { 135 135 const list = methods.getTrackList(id) 136 - if (list.type !== 'special' && $modalCount === 0) { 137 - playlistInfo = { 136 + if (list.type !== 'special' && $modal_count === 0) { 137 + playlist_info = { 138 138 name: list.name, 139 139 description: list.description || '', 140 140 isFolder: list.type === 'folder', ··· 145 145 }), 146 146 ) 147 147 onDestroy( 148 - ipcListen('context.playlist.delete', async (_, id) => { 148 + ipc_listen('context.playlist.delete', async (_, id) => { 149 149 const list = methods.getTrackList(id) 150 - const result = await ipcRenderer.invoke('showMessageBox', false, { 150 + const result = await ipc_renderer.invoke('showMessageBox', false, { 151 151 type: 'info', 152 152 message: `Delete the ${list.type} "${list.name}"?`, 153 153 detail: list.type === 'folder' ? 'This will also delete all playlists inside.' : '', ··· 160 160 }), 161 161 ) 162 162 onDestroy( 163 - ipcListen('newPlaylist', (_, id, isFolder) => { 164 - playlistInfo = { 163 + ipc_listen('newPlaylist', (_, id, is_folder) => { 164 + playlist_info = { 165 165 name: '', 166 166 description: '', 167 - isFolder: isFolder, 167 + isFolder: is_folder, 168 168 id: id, 169 169 editMode: false, 170 170 } ··· 183 183 184 184 <!-- svelte-ignore a11y-no-noninteractive-element-interactions --> 185 185 <main 186 - on:dragenter|capture={dragEnterOrOver} 186 + on:dragenter|capture={drag_enter_or_over} 187 187 on:keydown={(e) => { 188 188 if (e.target) { 189 - if (checkShortcut(e, 'ArrowUp', { cmdOrCtrl: true })) { 189 + if (check_shortcut(e, 'ArrowUp', { cmd_or_ctrl: true })) { 190 190 e.preventDefault() 191 - ipcRenderer.invoke('volume_change', true) 192 - } else if (checkShortcut(e, 'ArrowDown', { cmdOrCtrl: true })) { 191 + ipc_renderer.invoke('volume_change', true) 192 + } else if (check_shortcut(e, 'ArrowDown', { cmd_or_ctrl: true })) { 193 193 e.preventDefault() 194 - ipcRenderer.invoke('volume_change', false) 194 + ipc_renderer.invoke('volume_change', false) 195 195 } 196 196 } 197 197 }} ··· 202 202 <div class="relative pt-4 px-5 pb-5"> 203 203 <div 204 204 class="absolute top-0 left-0 h-10 w-full" 205 - class:dragbar={$modalCount === 0 && isMac} 206 - class:queue-visible={$queueVisible} 205 + class:dragbar={$modal_count === 0 && is_mac} 206 + class:queue-visible={$queue_visible} 207 207 /> 208 208 <h3 class="m-0 pb-0.5 text-[19px] font-medium leading-none"> 209 209 {#if $page.tracklist.id === 'root'} ··· 226 226 {/if} 227 227 </div> 228 228 {#if $page.viewAs === 0} 229 - <TrackList {onTrackInfo} /> 229 + <TrackList {on_track_info} /> 230 230 {:else} 231 231 <ArtistList /> 232 232 {/if} 233 233 </div> 234 - {#if $queueVisible} 235 - <Queue {onTrackInfo} /> 234 + {#if $queue_visible} 235 + <Queue {on_track_info} /> 236 236 {/if} 237 237 </div> 238 238 <Player /> ··· 243 243 </div> 244 244 <div 245 245 class="dropzone" 246 - on:dragleave={dragLeave} 246 + on:dragleave={drag_leave} 247 247 on:drop={drop} 248 - on:dragover={dragEnterOrOver} 248 + on:dragover={drag_enter_or_over} 249 249 role="dialog" 250 250 aria-label="Drop files to import" 251 251 aria-dropeffect="copy" ··· 253 253 {/if} 254 254 </main> 255 255 256 - {#if trackInfoList} 257 - <TrackInfo currentList={trackInfoList} cancel={() => (trackInfoList = null)} /> 256 + {#if track_info_list} 257 + <TrackInfo current_list={track_info_list} cancel={() => (track_info_list = null)} /> 258 258 {/if} 259 - {#if playlistInfo} 260 - <PlaylistInfoModal info={playlistInfo} cancel={() => (playlistInfo = null)} /> 259 + {#if playlist_info} 260 + <PlaylistInfoModal info={playlist_info} cancel={() => (playlist_info = null)} /> 261 261 {/if} 262 - {#if showItunesImport} 263 - <ItunesImport cancel={() => (showItunesImport = false)} /> 262 + {#if show_itunes_import} 263 + <ItunesImport cancel={() => (show_itunes_import = false)} /> 264 264 {/if} 265 265 <QuickNav /> 266 266
+6 -6
src/components/DragGhost.svelte
··· 1 1 <script lang="ts" context="module"> 2 - export let dragEl: HTMLElement 3 - let dragElDiv: HTMLElement 4 - export function setInnerText(text: string) { 5 - dragElDiv.innerText = text 2 + export let drag_el: HTMLElement 3 + let drag_el_div: HTMLElement 4 + export function set_inner_text(text: string) { 5 + drag_el_div.innerText = text 6 6 } 7 7 </script> 8 8 9 - <div class="drag-ghost" bind:this={dragEl}> 10 - <div bind:this={dragElDiv} /> 9 + <div class="drag-ghost" bind:this={drag_el}> 10 + <div bind:this={drag_el_div} /> 11 11 </div> 12 12 13 13 <style lang="sass">
+5 -5
src/components/Filter.svelte
··· 1 1 <script lang="ts"> 2 2 import { onDestroy } from 'svelte' 3 3 import { filter } from '../lib/data' 4 - import { ipcListen } from '../lib/window' 4 + import { ipc_listen } from '../lib/window' 5 5 6 - let filterInput: HTMLInputElement 6 + let filter_input: HTMLInputElement 7 7 onDestroy( 8 - ipcListen('filter', () => { 9 - filterInput.select() 8 + ipc_listen('filter', () => { 9 + filter_input.select() 10 10 }), 11 11 ) 12 12 </script> ··· 14 14 <input 15 15 on:focus 16 16 on:keydown 17 - bind:this={filterInput} 17 + bind:this={filter_input} 18 18 type="text" 19 19 class="search rounded-[5px] text-[13px] leading-none" 20 20 class:on={$filter}
+15 -15
src/components/ItunesImport.svelte
··· 1 1 <script lang="ts"> 2 - import { ItunesImport, paths, call, methods, page, trackListsDetailsMap } from '@/lib/data' 3 - import { ipcRenderer } from '@/lib/window' 2 + import { ItunesImport, paths, call, methods, page, track_lists_details_map } from '@/lib/data' 3 + import { ipc_renderer } from '@/lib/window' 4 4 import type { ImportStatus } from 'ferrum-addon/addon' 5 5 import Button from './Button.svelte' 6 6 import Modal from './Modal.svelte' 7 7 import { selection as pageSelection } from '@/lib/page' 8 8 9 9 export let cancel: () => void 10 - let itunesImport = ItunesImport.new() 10 + let itunes_import = ItunesImport.new() 11 11 12 12 type Stage = 'select' | 'fileSelect' | 'scanning' | ImportStatus 13 13 let stage: Stage = 'select' 14 14 15 - function cancelHandler() { 15 + function cancel_handler() { 16 16 if (stage === 'fileSelect' || stage === 'scanning') { 17 17 return 18 18 } 19 19 cancel() 20 20 } 21 21 22 - async function selectFile() { 22 + async function select_file() { 23 23 stage = 'fileSelect' 24 - const open = await ipcRenderer.invoke('showOpenDialog', true, { 24 + const open = await ipc_renderer.invoke('showOpenDialog', true, { 25 25 properties: ['openFile'], 26 26 filters: [{ name: 'iTunes Library File', extensions: ['xml'] }], 27 27 }) 28 28 if (!open.canceled && open.filePaths[0]) { 29 29 stage = 'scanning' 30 - const filePath = open.filePaths[0] 31 - stage = await call(() => itunesImport.start(filePath, paths.tracksDir)) 30 + const file_path = open.filePaths[0] 31 + stage = await call(() => itunes_import.start(file_path, paths.tracksDir)) 32 32 } else { 33 33 stage = 'select' 34 34 } 35 35 } 36 36 async function finish() { 37 - itunesImport.finish() 37 + itunes_import.finish() 38 38 methods.save() 39 - page.refreshIdsAndKeepSelection() 39 + page.refresh_ids_and_keep_selection() 40 40 pageSelection.clear() 41 - trackListsDetailsMap.refresh() 41 + track_lists_details_map.refresh() 42 42 cancel() 43 43 } 44 44 async function submit() { 45 45 if (stage === 'select') { 46 - selectFile() 46 + select_file() 47 47 } else if (typeof stage === 'object' && 'tracksCount' in stage) { 48 48 finish() 49 49 } 50 50 } 51 51 </script> 52 52 53 - <Modal onCancel={cancelHandler} cancelOnEscape form={submit} title="Import iTunes Library"> 53 + <Modal on_cancel={cancel_handler} cancel_on_escape form={submit} title="Import iTunes Library"> 54 54 <main> 55 55 {#if stage === 'select' || stage === 'fileSelect'} 56 56 <p> ··· 74 74 <li>View options</li> 75 75 </ul> 76 76 <div class="buttons"> 77 - <Button secondary on:click={cancelHandler}>Cancel</Button> 77 + <Button secondary on:click={cancel_handler}>Cancel</Button> 78 78 <Button type="submit">Select File</Button> 79 79 </div> 80 80 {:else if stage === 'scanning'} ··· 96 96 <li>Tracks: {stage.tracksCount}</li> 97 97 </ul> 98 98 <div class="buttons"> 99 - <Button secondary on:click={cancelHandler}>Cancel</Button> 99 + <Button secondary on:click={cancel_handler}>Cancel</Button> 100 100 <Button type="submit">Continue</Button> 101 101 </div> 102 102 {/if}
+14 -14
src/components/Modal.svelte
··· 1 1 <script lang="ts" context="module"> 2 2 import { writable } from 'svelte/store' 3 3 4 - export const modalCount = writable(0) 4 + export const modal_count = writable(0) 5 5 </script> 6 6 7 7 <script lang="ts"> 8 8 import { onDestroy, onMount } from 'svelte' 9 - import { checkShortcut } from '../lib/helpers' 9 + import { check_shortcut } from '../lib/helpers' 10 10 11 - export let onCancel: () => void 12 - export let cancelOnEscape = false 11 + export let on_cancel: () => void 12 + export let cancel_on_escape = false 13 13 export let form: (() => void) | undefined = undefined 14 14 export let plain = false 15 15 $: tag = form === undefined ? 'div' : 'form' 16 16 export let title: string | null = null 17 - let dialogEl: HTMLDialogElement 17 + let dialog_el: HTMLDialogElement 18 18 19 - $modalCount += 1 19 + $modal_count += 1 20 20 onDestroy(() => { 21 - $modalCount -= 1 21 + $modal_count -= 1 22 22 }) 23 23 24 24 onMount(() => { 25 - dialogEl.showModal() 25 + dialog_el.showModal() 26 26 return () => { 27 - dialogEl.close() 27 + dialog_el.close() 28 28 } 29 29 }) 30 30 ··· 42 42 <!-- svelte-ignore a11y-no-noninteractive-element-interactions --> 43 43 <dialog 44 44 class="modal m-auto" 45 - bind:this={dialogEl} 45 + bind:this={dialog_el} 46 46 on:click|self={() => { 47 47 if (clickable) { 48 - onCancel() 48 + on_cancel() 49 49 } 50 50 }} 51 51 on:keydown 52 52 on:keydown={(e) => { 53 - if (checkShortcut(e, 'Escape') && cancelOnEscape) { 54 - onCancel() 53 + if (check_shortcut(e, 'Escape') && cancel_on_escape) { 54 + on_cancel() 55 55 } 56 56 }} 57 57 on:keydown|self={(e) => { 58 - if (form && checkShortcut(e, 'Enter')) { 58 + if (form && check_shortcut(e, 'Enter')) { 59 59 form() 60 60 e.preventDefault() 61 61 }
+38 -38
src/components/Player.svelte
··· 1 1 <script lang="ts"> 2 2 import { 3 3 stopped, 4 - playPause, 4 + play_pause, 5 5 seek, 6 - playingTrack, 7 - playingId, 6 + playing_track, 7 + playing_id, 8 8 previous, 9 - skipToNext, 10 - coverSrc, 9 + skip_to_next, 10 + cover_src, 11 11 volume, 12 - timeRecord, 12 + time_record, 13 13 } from '../lib/player' 14 - import { getDuration } from '../lib/helpers' 15 - import { queueVisible, toggleQueueVisibility, queue, shuffle, repeat } from '../lib/queue' 16 - import { isDev, methods } from '../lib/data' 17 - import { showTrackMenu } from '@/lib/menus' 14 + import { get_duration } from '../lib/helpers' 15 + import { queue_visible, toggle_queue_visibility, queue, shuffle, repeat } from '../lib/queue' 16 + import { is_dev, methods } from '../lib/data' 17 + import { show_track_menu } from '@/lib/menus' 18 18 import { dragged } from '@/lib/drag-drop' 19 19 import * as dragGhost from './DragGhost.svelte' 20 20 import Slider from './Slider.svelte' 21 21 22 - async function playingContextMenu() { 22 + async function playing_context_menu() { 23 23 const playing = queue.getCurrent() 24 24 if (playing) { 25 - await showTrackMenu([playing.id], [0]) 25 + await show_track_menu([playing.id], [0]) 26 26 } 27 27 } 28 28 29 - function dragStart(e: DragEvent) { 30 - if (e.dataTransfer && $playingId) { 29 + function drag_start(e: DragEvent) { 30 + if (e.dataTransfer && $playing_id) { 31 31 e.dataTransfer.effectAllowed = 'move' 32 - const track = methods.getTrack($playingId) 33 - dragGhost.setInnerText(track.artist + ' - ' + track.name) 32 + const track = methods.getTrack($playing_id) 33 + dragGhost.set_inner_text(track.artist + ' - ' + track.name) 34 34 dragged.tracks = { 35 - ids: [$playingId], 35 + ids: [$playing_id], 36 36 } 37 - e.dataTransfer.setDragImage(dragGhost.dragEl, 0, 0) 37 + e.dataTransfer.setDragImage(dragGhost.drag_el, 0, 0) 38 38 e.dataTransfer.setData('ferrum.tracks', '') 39 39 } 40 40 } 41 41 </script> 42 42 43 - <div class="player" class:stopped={$stopped} class:dev={isDev}> 43 + <div class="player" class:stopped={$stopped} class:dev={is_dev}> 44 44 <div class="left"> 45 - {#if !$stopped && $playingTrack} 45 + {#if !$stopped && $playing_track} 46 46 <div 47 47 class="cover" 48 48 role="none" 49 - on:contextmenu={playingContextMenu} 50 - on:dragstart={dragStart} 49 + on:contextmenu={playing_context_menu} 50 + on:dragstart={drag_start} 51 51 draggable="true" 52 52 > 53 - {#if $coverSrc} 54 - <img src={$coverSrc} alt="" draggable="false" /> 53 + {#if $cover_src} 54 + <img src={$cover_src} alt="" draggable="false" /> 55 55 {:else} 56 56 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> 57 57 <path ··· 61 61 {/if} 62 62 </div> 63 63 <div 64 - on:contextmenu={playingContextMenu} 65 - on:dragstart={dragStart} 64 + on:contextmenu={playing_context_menu} 65 + on:dragstart={drag_start} 66 66 draggable="true" 67 67 role="none" 68 68 > 69 69 <div class="name"> 70 - {$playingTrack.name} 70 + {$playing_track.name} 71 71 </div> 72 72 <div class="artist"> 73 - {$playingTrack.artist} 73 + {$playing_track.artist} 74 74 </div> 75 75 </div> 76 76 {/if} ··· 111 111 </svg> 112 112 </button> 113 113 114 - <button class="play-pause" on:click={playPause} tabindex="-1" on:mousedown|preventDefault> 115 - {#if $timeRecord.paused} 114 + <button class="play-pause" on:click={play_pause} tabindex="-1" on:mousedown|preventDefault> 115 + {#if $time_record.paused} 116 116 <svg 117 117 xmlns="http://www.w3.org/2000/svg" 118 118 class="parent-active-zoom" ··· 139 139 {/if} 140 140 </button> 141 141 142 - <button on:click={skipToNext} tabindex="-1" on:mousedown|preventDefault> 142 + <button on:click={skip_to_next} tabindex="-1" on:mousedown|preventDefault> 143 143 <svg 144 144 xmlns="http://www.w3.org/2000/svg" 145 145 class="parent-active-zoom" ··· 175 175 </button> 176 176 </div> 177 177 <div class="time-bar"> 178 - <small class="current-time">{getDuration($timeRecord.elapsed)}</small> 178 + <small class="current-time">{get_duration($time_record.elapsed)}</small> 179 179 <Slider 180 180 class="mx-1.5 w-full" 181 - value={$timeRecord.elapsed / $timeRecord.duration || 0} 182 - growth_rate={$timeRecord.paused ? 0 : 1 / $timeRecord.duration} 181 + value={$time_record.elapsed / $time_record.duration || 0} 182 + growth_rate={$time_record.paused ? 0 : 1 / $time_record.duration} 183 183 max={1} 184 184 update_on_drag={false} 185 185 on_user_change={(value) => { 186 - seek(value * $timeRecord.duration) 186 + seek(value * $time_record.duration) 187 187 }} 188 188 /> 189 - <small class="duration">{getDuration($timeRecord.duration)}</small> 189 + <small class="duration">{get_duration($time_record.duration)}</small> 190 190 </div> 191 191 </div> 192 192 <div class="right"> ··· 236 236 <button 237 237 tabindex="-1" 238 238 on:mousedown|preventDefault 239 - on:click={toggleQueueVisibility} 240 - class:on={$queueVisible} 239 + on:click={toggle_queue_visibility} 240 + class:on={$queue_visible} 241 241 > 242 242 <svg 243 243 xmlns="http://www.w3.org/2000/svg"
+9 -9
src/components/PlaylistInfo.svelte
··· 1 1 <script lang="ts"> 2 - import { checkShortcut } from '../lib/helpers' 3 - import { newPlaylist, type PlaylistInfo, updatePlaylist } from '../lib/data' 2 + import { check_shortcut } from '../lib/helpers' 3 + import { new_playlist, type PlaylistInfo, update_playlist } from '../lib/data' 4 4 import Modal from './Modal.svelte' 5 5 import Button from './Button.svelte' 6 6 ··· 14 14 15 15 function save() { 16 16 if (info.editMode) { 17 - updatePlaylist(info.id, info.name, info.description) 17 + update_playlist(info.id, info.name, info.description) 18 18 } else { 19 - newPlaylist(info) 19 + new_playlist(info) 20 20 } 21 21 cancel() 22 22 } 23 23 24 - function formKeydown(e: KeyboardEvent) { 25 - if (checkShortcut(e, 'enter', { cmdOrCtrl: true })) { 24 + function form_keydown(e: KeyboardEvent) { 25 + if (check_shortcut(e, 'enter', { cmd_or_ctrl: true })) { 26 26 save() 27 27 } 28 28 } 29 29 </script> 30 30 31 31 <Modal 32 - onCancel={cancel} 33 - cancelOnEscape 32 + on_cancel={cancel} 33 + cancel_on_escape 34 34 form={save} 35 - on:keydown={formKeydown} 35 + on:keydown={form_keydown} 36 36 title={(info.editMode ? 'Edit' : 'New') + ' Playlist' + (info.isFolder ? ' Folder' : '')} 37 37 > 38 38 <main>
+56 -51
src/components/Queue.svelte
··· 1 1 <script lang="ts"> 2 2 import { 3 - appendToUserQueue, 4 - getByQueueIndex, 5 - getQueueLength, 6 - insertIds, 7 - moveIndexes, 8 - prependToUserQueue, 3 + append_to_user_queue, 4 + get_by_queue_index, 5 + get_queue_length, 6 + insert_ids, 7 + move_indexes, 8 + prepend_to_user_queue, 9 9 queue, 10 10 type QueueItem, 11 11 } from '../lib/queue' 12 12 import { paths } from '../lib/data' 13 13 import { onDestroy } from 'svelte' 14 14 import QueueItemComponent from './QueueItem.svelte' 15 - import { newSelection } from '@/lib/selection' 16 - import { showTrackMenu } from '@/lib/menus' 15 + import { new_selection } from '@/lib/selection' 16 + import { show_track_menu } from '@/lib/menus' 17 17 import { dragged } from '@/lib/drag-drop' 18 18 import { methods } from '@/lib/data' 19 19 import * as dragGhost from './DragGhost.svelte' 20 - import { ipcListen, ipcRenderer } from '@/lib/window' 21 - import { assertUnreachable, checkShortcut } from '@/lib/helpers' 20 + import { ipc_listen, ipc_renderer } from '@/lib/window' 21 + import { assert_unreachable, check_shortcut } from '@/lib/helpers' 22 22 import type { TrackID } from 'ferrum-addon/addon' 23 23 import { fly } from 'svelte/transition' 24 24 import VirtualListBlock, { scroll_container_keydown } from './VirtualListBlock.svelte' 25 25 26 - let objectUrls: string[] = [] 26 + let object_urls: string[] = [] 27 27 28 28 onDestroy(() => { 29 - for (let url of objectUrls) { 29 + for (let url of object_urls) { 30 30 URL.revokeObjectURL(url) 31 31 } 32 32 }) ··· 34 34 let show_history = false 35 35 $: current_index = $queue.past.length 36 36 $: up_next_index = current_index + Number(!!$queue.current) 37 - $: autoplay_index = up_next_index + $queue.userQueue.length 37 + $: autoplay_index = up_next_index + $queue.user_queue.length 38 38 39 39 let history_list: VirtualListBlock<QueueItem> 40 40 let up_next_list: VirtualListBlock<QueueItem> 41 41 let autoplay_list: VirtualListBlock<QueueItem> 42 42 43 - const selection = newSelection({ 44 - getItemCount: () => getQueueLength(), 45 - scrollToItem: (i) => { 43 + const selection = new_selection({ 44 + get_item_count: () => get_queue_length(), 45 + scroll_to_item: (i) => { 46 46 if (i < $queue.past.length) { 47 47 return history_list.scroll_to_index(i, 40) 48 48 } ··· 51 51 return history_list.scroll_to_index(i, 40) 52 52 } 53 53 i -= Number(!!$queue.current) 54 - if (i < $queue.userQueue.length) { 54 + if (i < $queue.user_queue.length) { 55 55 return up_next_list.scroll_to_index(i, 40) 56 56 } 57 - i -= $queue.userQueue.length 57 + i -= $queue.user_queue.length 58 58 autoplay_list.scroll_to_index(i, 40) 59 59 }, 60 - async onContextMenu() { 60 + async on_context_menu() { 61 61 const indexes = selection.getSelectedIndexes() 62 62 const current_array = $queue.current ? [$queue.current.item] : [] 63 - const allItems = [...$queue.past, ...current_array, ...$queue.userQueue, ...$queue.autoQueue] 64 - const allIds = allItems.map((item) => item.id) 65 - await showTrackMenu(allIds, indexes, undefined, true) 63 + const all_items = [ 64 + ...$queue.past, 65 + ...current_array, 66 + ...$queue.user_queue, 67 + ...$queue.auto_queue, 68 + ] 69 + const all_ids = all_items.map((item) => item.id) 70 + await show_track_menu(all_ids, indexes, undefined, true) 66 71 }, 67 72 }) 68 73 $: selection.setMinimumIndex(show_history ? 0 : up_next_index) ··· 74 79 queue.removeIndexes(selection.getSelectedIndexes()) 75 80 } 76 81 } 77 - onDestroy(ipcListen('context.Remove from Queue', remove_from_queue)) 82 + onDestroy(ipc_listen('context.Remove from Queue', remove_from_queue)) 78 83 79 84 let queue_element: HTMLElement 80 - export let onTrackInfo: (allIds: TrackID[], index: number) => void 85 + export let on_track_info: (allIds: TrackID[], index: number) => void 81 86 82 - const track_action_unlisten = ipcListen('selectedTracksAction', (_, action) => { 87 + const track_action_unlisten = ipc_listen('selectedTracksAction', (_, action) => { 83 88 let first_index = selection.findFirst() 84 89 85 90 if (first_index === null || !queue_element.contains(document.activeElement)) { ··· 88 93 if (action === 'Play Next') { 89 94 const indexes = selection.getSelectedIndexes() 90 95 const ids = indexes.map((i) => queue.getByQueueIndex(i).id) 91 - prependToUserQueue(ids) 96 + prepend_to_user_queue(ids) 92 97 } else if (action === 'Add to Queue') { 93 98 const indexes = selection.getSelectedIndexes() 94 99 const ids = indexes.map((i) => queue.getByQueueIndex(i).id) 95 - appendToUserQueue(ids) 100 + append_to_user_queue(ids) 96 101 } else if (action === 'Get Info') { 97 - const all_items = [...$queue.userQueue, ...$queue.autoQueue] 102 + const all_items = [...$queue.user_queue, ...$queue.auto_queue] 98 103 const all_ids = all_items.map((item) => item.id) 99 - onTrackInfo(all_ids, first_index) 104 + on_track_info(all_ids, first_index) 100 105 } else if (action === 'revealTrackFile') { 101 106 const track = methods.getTrack(queue.getByQueueIndex(first_index).id) 102 - ipcRenderer.invoke('revealTrackFile', paths.tracksDir, track.file) 107 + ipc_renderer.invoke('revealTrackFile', paths.tracksDir, track.file) 103 108 } else if (action === 'Remove from Playlist') { 104 109 return 105 110 } else if (action === 'Delete from Library') { 106 111 return 107 112 } else { 108 - assertUnreachable(action) 113 + assert_unreachable(action) 109 114 } 110 115 }) 111 116 onDestroy(track_action_unlisten) 112 117 113 118 let drag_line: HTMLElement 114 119 let dagged_indexes: number[] = [] 115 - function onDragStart(e: DragEvent) { 120 + function on_drag_start(e: DragEvent) { 116 121 if (e.dataTransfer) { 117 122 dagged_indexes = [] 118 123 for (let i = 0; i < $selection.list.length; i++) { ··· 122 127 } 123 128 e.dataTransfer.effectAllowed = 'move' 124 129 if (dagged_indexes.length === 1) { 125 - const track = methods.getTrack(getByQueueIndex(dagged_indexes[0]).id) 126 - dragGhost.setInnerText(track.artist + ' - ' + track.name) 130 + const track = methods.getTrack(get_by_queue_index(dagged_indexes[0]).id) 131 + dragGhost.set_inner_text(track.artist + ' - ' + track.name) 127 132 } else { 128 - dragGhost.setInnerText(dagged_indexes.length + ' items') 133 + dragGhost.set_inner_text(dagged_indexes.length + ' items') 129 134 } 130 135 dragged.tracks = { 131 - ids: dagged_indexes.map((i) => getByQueueIndex(i).id), 132 - queueIndexes: dagged_indexes, 136 + ids: dagged_indexes.map((i) => get_by_queue_index(i).id), 137 + queue_indexes: dagged_indexes, 133 138 } 134 - e.dataTransfer.setDragImage(dragGhost.dragEl, 0, 0) 139 + e.dataTransfer.setDragImage(dragGhost.drag_el, 0, 0) 135 140 e.dataTransfer.setData('ferrum.tracks', '') 136 141 } 137 142 } ··· 163 168 const to_boundary = drag_to_index === autoplay_index 164 169 const to_user_queue_bottom = to_boundary && !drag_top_of_item 165 170 const to_auto_queue_top = to_boundary && drag_top_of_item 166 - const create_user_queue = to_auto_queue_top && $queue.userQueue.length === 0 171 + const create_user_queue = to_auto_queue_top && $queue.user_queue.length === 0 167 172 168 173 const to_user_queue = 169 174 drag_to_index < autoplay_index || to_user_queue_bottom || create_user_queue 170 175 171 - const new_selection = dragged.tracks.queueIndexes 172 - ? moveIndexes(dragged.tracks.queueIndexes, drag_to_index, to_user_queue) 173 - : insertIds(dragged.tracks.ids, drag_to_index, to_user_queue) 176 + const new_selection = dragged.tracks.queue_indexes 177 + ? move_indexes(dragged.tracks.queue_indexes, drag_to_index, to_user_queue) 178 + : insert_ids(dragged.tracks.ids, drag_to_index, to_user_queue) 174 179 for (let i = new_selection.from; i <= new_selection.to; i++) { 175 180 selection.add(i) 176 181 } ··· 188 193 tabindex="-1" 189 194 on:keydown={scroll_container_keydown} 190 195 on:keydown={(e) => { 191 - if (checkShortcut(e, 'Backspace') && $selection.count >= 1) { 196 + if (check_shortcut(e, 'Backspace') && $selection.count >= 1) { 192 197 e.preventDefault() 193 198 remove_from_queue() 194 199 } else { ··· 244 249 on:contextmenu={(e) => selection.handleContextMenu(e, qi)} 245 250 on:click={(e) => selection.handleClick(e, qi)} 246 251 draggable="true" 247 - on:dragstart={onDragStart} 252 + on:dragstart={on_drag_start} 248 253 on:dragover={(e) => on_drag_over(e, qi)} 249 254 on:drop={drop_handler} 250 255 on:dragleave={drag_end_handler} ··· 265 270 on:contextmenu={(e) => selection.handleContextMenu(e, qi)} 266 271 on:click={(e) => selection.handleClick(e, qi)} 267 272 draggable="true" 268 - on:dragstart={onDragStart} 273 + on:dragstart={on_drag_start} 269 274 on:dragover={(e) => on_drag_over(e, qi)} 270 275 on:drop={drop_handler} 271 276 on:dragleave={drag_end_handler} ··· 278 283 </div> 279 284 {/if} 280 285 281 - {#if $queue.userQueue.length || queue.getQueueLength() === 0} 286 + {#if $queue.user_queue.length || queue.getQueueLength() === 0} 282 287 <div class="relative"> 283 288 <h4 284 289 class="sticky top-0 z-1 flex h-[40px] items-center bg-black/50 pl-7 font-semibold backdrop-blur-md" ··· 287 292 </h4> 288 293 <VirtualListBlock 289 294 bind:this={up_next_list} 290 - items={$queue.userQueue} 295 + items={$queue.user_queue} 291 296 get_key={(item) => item.qId} 292 297 item_height={54} 293 298 scroll_container={queue_element} ··· 305 310 on:contextmenu={(e) => selection.handleContextMenu(e, qi)} 306 311 on:click={(e) => selection.handleClick(e, qi)} 307 312 draggable="true" 308 - on:dragstart={onDragStart} 313 + on:dragstart={on_drag_start} 309 314 on:dragover={(e) => on_drag_over(e, qi)} 310 315 on:drop={drop_handler} 311 316 on:dragleave={drag_end_handler} ··· 317 322 </div> 318 323 {/if} 319 324 320 - {#if $queue.autoQueue.length} 325 + {#if $queue.auto_queue.length} 321 326 <div class="relative"> 322 327 <h4 323 328 class="sticky top-0 z-1 flex h-[40px] items-center bg-black/50 pl-7 font-semibold backdrop-blur-md" ··· 326 331 </h4> 327 332 <VirtualListBlock 328 333 bind:this={autoplay_list} 329 - items={$queue.autoQueue} 334 + items={$queue.auto_queue} 330 335 get_key={(item) => item.qId} 331 336 item_height={54} 332 337 scroll_container={queue_element} ··· 344 349 on:contextmenu={(e) => selection.handleContextMenu(e, qi)} 345 350 on:click={(e) => selection.handleClick(e, qi)} 346 351 draggable="true" 347 - on:dragstart={onDragStart} 352 + on:dragstart={on_drag_start} 348 353 on:dragover={(e) => on_drag_over(e, qi)} 349 354 on:drop={drop_handler} 350 355 on:dragleave={drag_end_handler}
+4 -4
src/components/QueueItem.svelte
··· 1 1 <script lang="ts"> 2 - import { methods, paths, trackMetadataUpdated } from '@/lib/data' 2 + import { methods, paths, track_metadata_updated } from '@/lib/data' 3 3 import { fade } from 'svelte/transition' 4 4 import type { Track } from '../../ferrum-addon' 5 - import { joinPaths } from '@/lib/window' 5 + import { join_paths } from '@/lib/window' 6 6 7 7 export let id: string 8 8 9 9 let track: Track 10 - $: $trackMetadataUpdated, (track = methods.getTrack(id)) 10 + $: $track_metadata_updated, (track = methods.getTrack(id)) 11 11 let success: boolean | null = null 12 12 13 - $: filePath = joinPaths(paths.tracksDir, track.file) 13 + $: filePath = join_paths(paths.tracksDir, track.file) 14 14 </script> 15 15 16 16 <div class="box">
+35 -35
src/components/QuickNav.svelte
··· 1 1 <script lang="ts"> 2 2 import { onDestroy } from 'svelte' 3 - import { checkShortcut } from '../lib/helpers' 4 - import { ipcListen } from '@/lib/window' 3 + import { check_shortcut } from '../lib/helpers' 4 + import { ipc_listen } from '@/lib/window' 5 5 import fuzzysort from 'fuzzysort' 6 - import { page, trackListsDetailsMap, view_as_artists, view_as_songs } from '@/lib/data' 6 + import { page, track_lists_details_map, view_as_artists, view_as_songs } from '@/lib/data' 7 7 import type { TrackListDetails, ViewAs } from '../../ferrum-addon/addon' 8 8 import Modal from './Modal.svelte' 9 9 import { special_playlists_nav } from './Sidebar.svelte' ··· 18 18 } 19 19 function get_playlists() { 20 20 const playlists: Result[] = special_playlists_nav 21 - for (const playlist of Object.values($trackListsDetailsMap)) { 21 + for (const playlist of Object.values($track_lists_details_map)) { 22 22 if (playlist.kind === 'playlist' || playlist.kind === 'folder') { 23 23 playlists.push(playlist) 24 24 } ··· 28 28 return playlists 29 29 } 30 30 31 - let filteredItems = fuzzysort.go(value, playlists, { key: 'name', all: true }) 31 + let filtered_items = fuzzysort.go(value, playlists, { key: 'name', all: true }) 32 32 $: { 33 - filteredItems = fuzzysort.go(value, playlists, { key: 'name', all: true }) 34 - clampIndex() 33 + filtered_items = fuzzysort.go(value, playlists, { key: 'name', all: true }) 34 + clamp_index() 35 35 } 36 36 37 - function selectInput(el: HTMLInputElement) { 37 + function select_input(el: HTMLInputElement) { 38 38 el.select() 39 39 } 40 40 41 - let selectedIndex = 0 41 + let selected_index = 0 42 42 43 - function clampIndex() { 44 - selectedIndex = Math.max(0, Math.min(filteredItems.length - 1, selectedIndex)) 43 + function clamp_index() { 44 + selected_index = Math.max(0, Math.min(filtered_items.length - 1, selected_index)) 45 45 } 46 - $: listItems, clampIndex() 47 - let listItems: HTMLElement[] = [] 46 + $: list_items, clamp_index() 47 + let list_items: HTMLElement[] = [] 48 48 49 - function handleKeydown(e: KeyboardEvent) { 49 + function handle_keydown(e: KeyboardEvent) { 50 50 if (e.key === 'Tab') { 51 51 e.preventDefault() 52 - } else if (checkShortcut(e, 'Escape')) { 52 + } else if (check_shortcut(e, 'Escape')) { 53 53 show = false 54 54 value = '' 55 - } else if (checkShortcut(e, 'Enter')) { 56 - page.openPlaylist( 57 - filteredItems[selectedIndex].obj.id, 58 - filteredItems[selectedIndex].obj.view_as ?? view_as_songs, 55 + } else if (check_shortcut(e, 'Enter')) { 56 + page.open_playlist( 57 + filtered_items[selected_index].obj.id, 58 + filtered_items[selected_index].obj.view_as ?? view_as_songs, 59 59 ) 60 60 show = false 61 - } else if (checkShortcut(e, 'ArrowUp')) { 62 - selectedIndex-- 63 - clampIndex() 64 - listItems[selectedIndex].scrollIntoView({ 61 + } else if (check_shortcut(e, 'ArrowUp')) { 62 + selected_index-- 63 + clamp_index() 64 + list_items[selected_index].scrollIntoView({ 65 65 block: 'nearest', 66 66 }) 67 67 e.preventDefault() 68 - } else if (checkShortcut(e, 'ArrowDown')) { 69 - selectedIndex++ 70 - clampIndex() 71 - listItems[selectedIndex].scrollIntoView({ 68 + } else if (check_shortcut(e, 'ArrowDown')) { 69 + selected_index++ 70 + clamp_index() 71 + list_items[selected_index].scrollIntoView({ 72 72 block: 'nearest', 73 73 }) 74 74 e.preventDefault() ··· 76 76 } 77 77 78 78 onDestroy( 79 - ipcListen('ToggleQuickNav', () => { 79 + ipc_listen('ToggleQuickNav', () => { 80 80 show = !show 81 81 }), 82 82 ) ··· 85 85 {#if show} 86 86 <Modal 87 87 plain 88 - onCancel={() => { 88 + on_cancel={() => { 89 89 show = false 90 90 }} 91 91 > 92 92 <input 93 93 type="text" 94 94 bind:value 95 - on:keydown={handleKeydown} 95 + on:keydown={handle_keydown} 96 96 placeholder="Search for a playlist..." 97 - use:selectInput 97 + use:select_input 98 98 /> 99 99 <div class="items-container"> 100 - {#each filteredItems as item, i} 100 + {#each filtered_items as item, i} 101 101 <button 102 - bind:this={listItems[i]} 102 + bind:this={list_items[i]} 103 103 type="button" 104 104 on:click={() => { 105 - page.openPlaylist(item.obj.id, item.obj.view_as ?? view_as_songs) 105 + page.open_playlist(item.obj.id, item.obj.view_as ?? view_as_songs) 106 106 show = false 107 107 }} 108 - class:selected={selectedIndex === i} 108 + class:selected={selected_index === i} 109 109 > 110 110 {#if item.obj.kind === 'folder'} 111 111 <svg
+42 -42
src/components/Sidebar.svelte
··· 8 8 <script lang="ts"> 9 9 import SidebarItems, { type SidebarItemHandle } from './SidebarItems.svelte' 10 10 import Filter from './Filter.svelte' 11 - import { isMac, trackListsDetailsMap, page, movePlaylist, view_as_songs } from '../lib/data' 12 - import { ipcListen, ipcRenderer } from '../lib/window' 11 + import { is_mac, track_lists_details_map, page, move_playlist, view_as_songs } from '../lib/data' 12 + import { ipc_listen, ipc_renderer } from '../lib/window' 13 13 import { writable } from 'svelte/store' 14 14 import { onDestroy, setContext, tick } from 'svelte' 15 15 import { dragged } from '../lib/drag-drop' 16 16 import { tracklist_actions } from '@/lib/page' 17 17 18 18 let viewport: HTMLElement 19 - const itemHandle = setContext('itemHandle', writable(null as SidebarItemHandle | null)) 19 + const item_handle = setContext('itemHandle', writable(null as SidebarItemHandle | null)) 20 20 21 21 onDestroy( 22 - ipcListen('Select Previous List', () => { 23 - $itemHandle?.handleKey(new KeyboardEvent('keydown', { key: 'ArrowUp' })) 22 + ipc_listen('Select Previous List', () => { 23 + $item_handle?.handleKey(new KeyboardEvent('keydown', { key: 'ArrowUp' })) 24 24 }), 25 25 ) 26 26 onDestroy( 27 - ipcListen('Select Next List', () => { 28 - $itemHandle?.handleKey(new KeyboardEvent('keydown', { key: 'ArrowDown' })) 27 + ipc_listen('Select Next List', () => { 28 + $item_handle?.handleKey(new KeyboardEvent('keydown', { key: 'ArrowDown' })) 29 29 }), 30 30 ) 31 31 32 - async function onContextMenu() { 33 - await ipcRenderer.invoke('showTracklistMenu', { 32 + async function on_context_menu() { 33 + await ipc_renderer.invoke('showTracklistMenu', { 34 34 id: 'root', 35 35 isFolder: false, 36 36 isRoot: true, 37 37 }) 38 38 } 39 39 40 - let rootDroppable = false 40 + let root_droppable = false 41 41 function dragover(e: DragEvent) { 42 42 if (e.currentTarget && e.dataTransfer?.types[0] === 'ferrum.playlist') { 43 - rootDroppable = true 43 + root_droppable = true 44 44 e.preventDefault() 45 45 } 46 46 } 47 47 function dragleave() { 48 - rootDroppable = false 48 + root_droppable = false 49 49 } 50 50 function drop(e: DragEvent) { 51 51 if (e.currentTarget && e.dataTransfer?.types[0] === 'ferrum.playlist' && dragged.playlist) { 52 - const root = $trackListsDetailsMap['root'] 52 + const root = $track_lists_details_map['root'] 53 53 if (!root.children) { 54 54 return 55 55 } 56 - movePlaylist(dragged.playlist.id, dragged.playlist.fromFolder, 'root', root.children.length) 57 - rootDroppable = false 56 + move_playlist(dragged.playlist.id, dragged.playlist.from_folder, 'root', root.children.length) 57 + root_droppable = false 58 58 } 59 59 } 60 60 61 - let contentElement: HTMLDivElement 61 + let content_element: HTMLDivElement 62 62 63 - $: pageId = $page.id 64 - $: pageId, scrollToActive() 65 - async function scrollToActive() { 63 + $: page_id = $page.id 64 + $: page_id, scroll_to_active() 65 + async function scroll_to_active() { 66 66 await tick() 67 - const active = contentElement?.querySelector('.active') 67 + const active = content_element?.querySelector('.active') 68 68 if (active instanceof HTMLElement) { 69 69 const top = active.offsetTop 70 - if (contentElement.scrollTop > top) { 71 - contentElement.scrollTop = top 70 + if (content_element.scrollTop > top) { 71 + content_element.scrollTop = top 72 72 } else if ( 73 - contentElement.scrollTop + contentElement.clientHeight < 73 + content_element.scrollTop + content_element.clientHeight < 74 74 top + active.clientHeight 75 75 ) { 76 - contentElement.scrollTop = top + active.clientHeight - contentElement.clientHeight 76 + content_element.scrollTop = top + active.clientHeight - content_element.clientHeight 77 77 } 78 78 } 79 79 } 80 80 81 81 /** Prevent focus weirdness */ 82 82 function focuser() { 83 - const scrollTop = contentElement.scrollTop 83 + const scroll_top = content_element.scrollTop 84 84 viewport.focus() 85 - contentElement.scrollTop = scrollTop 86 - scrollToActive() 85 + content_element.scrollTop = scroll_top 86 + scroll_to_active() 87 87 } 88 88 </script> 89 89 90 90 <!-- NOTE: aside is used as css selector in SidebarItems --> 91 91 <aside on:mousedown|self|preventDefault role="none"> 92 - {#if isMac} 92 + {#if is_mac} 93 93 <div class="titlebar" on:mousedown|self|preventDefault role="none" /> 94 94 {/if} 95 - <div class="content" bind:this={contentElement}> 95 + <div class="content" bind:this={content_element}> 96 96 <Filter 97 97 on:focus={() => { 98 - contentElement.scrollTop = 0 98 + content_element.scrollTop = 0 99 99 }} 100 100 on:keydown={(e) => { 101 101 if (e.key === 'Escape') { ··· 114 114 } else if (e.key == 'Home' || e.key == 'End' || e.key == 'PageUp' || e.key == 'PageDown') { 115 115 e.preventDefault() 116 116 } else { 117 - $itemHandle?.handleKey(e) 117 + $item_handle?.handleKey(e) 118 118 } 119 119 }} 120 120 bind:this={viewport} 121 - class:droppable={rootDroppable} 122 - on:contextmenu|self={onContextMenu} 121 + class:droppable={root_droppable} 122 + on:contextmenu|self={on_context_menu} 123 123 on:dragover|self={dragover} 124 124 on:dragleave|self={dragleave} 125 125 on:drop|self={drop} ··· 128 128 <div class="focuser" tabindex="0" on:focus={focuser} /> 129 129 <div class="spacer" /> 130 130 <SidebarItems 131 - parentId={null} 131 + parent_id={null} 132 132 children={special_playlists_nav} 133 133 on_open={(item) => { 134 - page.openPlaylist('root', item.view_as ?? view_as_songs) 134 + page.open_playlist('root', item.view_as ?? view_as_songs) 135 135 }} 136 136 on_select_down={() => { 137 - if ($trackListsDetailsMap.root.children && $trackListsDetailsMap.root.children[0]) { 138 - page.openPlaylist($trackListsDetailsMap.root.children[0], view_as_songs) 137 + if ($track_lists_details_map.root.children && $track_lists_details_map.root.children[0]) { 138 + page.open_playlist($track_lists_details_map.root.children[0], view_as_songs) 139 139 } 140 140 }} 141 141 /> 142 142 <div class="spacer" /> 143 143 <SidebarItems 144 - parentId={$trackListsDetailsMap['root'].id} 145 - children={($trackListsDetailsMap['root'].children || []).map( 146 - (childId) => $trackListsDetailsMap[childId], 144 + parent_id={$track_lists_details_map['root'].id} 145 + children={($track_lists_details_map['root'].children || []).map( 146 + (child_id) => $track_lists_details_map[child_id], 147 147 )} 148 148 on_open={(item) => { 149 149 if ($page.id !== item.id) { 150 150 if (item.id === 'root') { 151 - page.openPlaylist( 151 + page.open_playlist( 152 152 'root', 153 153 item.view_as ?? special_playlists_nav[special_playlists_nav.length - 1].view_as, 154 154 ) 155 155 } else { 156 - page.openPlaylist(item.id, item.view_as ?? view_as_songs) 156 + page.open_playlist(item.id, item.view_as ?? view_as_songs) 157 157 } 158 158 } 159 159 }}
+99 -99
src/components/SidebarItems.svelte
··· 1 1 <script lang="ts" context="module"> 2 2 import { 3 - trackListsDetailsMap, 3 + track_lists_details_map, 4 4 page, 5 5 methods, 6 - addTracksToPlaylist, 7 - movePlaylist, 6 + add_track_to_playlist, 7 + move_playlist, 8 8 } from '../lib/data' 9 9 10 10 export type SidebarItemHandle = { 11 11 handleKey(e: KeyboardEvent): void 12 12 } 13 13 14 - let shownFolders = writable(new Set(methods.shownPlaylistFolders())) 15 - function showFolder(id: string) { 16 - shownFolders.update((folders) => { 14 + let shown_folders = writable(new Set(methods.shownPlaylistFolders())) 15 + function show_folder(id: string) { 16 + shown_folders.update((folders) => { 17 17 folders.add(id) 18 18 return folders 19 19 }) 20 20 methods.viewFolderSetShow(id, true) 21 21 } 22 - function hideFolder(id: string) { 23 - shownFolders.update((folders) => { 22 + function hide_folder(id: string) { 23 + shown_folders.update((folders) => { 24 24 folders.delete(id) 25 25 return folders 26 26 }) ··· 34 34 import { getContext } from 'svelte' 35 35 import { dragged } from '../lib/drag-drop' 36 36 import * as dragGhost from './DragGhost.svelte' 37 - import { ipcRenderer } from '@/lib/window' 38 - import { checkShortcut } from '@/lib/helpers' 37 + import { ipc_renderer } from '@/lib/window' 38 + import { check_shortcut } from '@/lib/helpers' 39 39 40 40 export let show = true 41 - export let parentId: string | null 42 - export let preventDrop = false 41 + export let parent_id: string | null 42 + export let prevent_drop = false 43 43 export let children: (TrackListDetails & { view_as?: ViewAs })[] 44 44 45 45 export let level = 0 46 46 export let on_open: (item: { id: string; view_as?: ViewAs }) => void 47 - async function tracklistContextMenu(id: string, isFolder: boolean) { 48 - await ipcRenderer.invoke('showTracklistMenu', { id, isFolder, isRoot: false }) 47 + async function tracklist_context_menu(id: string, is_folder: boolean) { 48 + await ipc_renderer.invoke('showTracklistMenu', { id, isFolder: is_folder, isRoot: false }) 49 49 } 50 50 51 - function hasShowingChildren(id: string) { 52 - const list = $trackListsDetailsMap[id] 53 - return list.children && list.children.length > 0 && $shownFolders.has(id) 51 + function has_showing_children(id: string) { 52 + const list = $track_lists_details_map[id] 53 + return list.children && list.children.length > 0 && $shown_folders.has(id) 54 54 } 55 55 56 56 export let on_select_down = () => {} 57 - function selectFirst(item: TrackListDetails) { 57 + function select_first(item: TrackListDetails) { 58 58 const child_id = item.children?.[0] 59 59 if (child_id) { 60 - on_open($trackListsDetailsMap[child_id]) 60 + on_open($track_lists_details_map[child_id]) 61 61 } 62 62 } 63 - function selectLast(in_id: string) { 64 - const children = $trackListsDetailsMap[in_id].children 65 - if (children && (hasShowingChildren(in_id) || in_id === 'root')) { 66 - selectLast(children[children.length - 1]) 63 + function select_last(in_id: string) { 64 + const children = $track_lists_details_map[in_id].children 65 + if (children && (has_showing_children(in_id) || in_id === 'root')) { 66 + select_last(children[children.length - 1]) 67 67 } else { 68 - on_open($trackListsDetailsMap[in_id]) 68 + on_open($track_lists_details_map[in_id]) 69 69 } 70 70 } 71 - function selectUp(i: number) { 72 - const prevId = children[i - 1]?.id || null 73 - if (i === 0 && parentId) { 74 - on_open({ id: parentId }) 75 - } else if (prevId && hasShowingChildren(prevId)) { 76 - selectLast(prevId) 77 - } else if (prevId) { 78 - on_open({ id: prevId }) 71 + function select_up(i: number) { 72 + const prev_id = children[i - 1]?.id || null 73 + if (i === 0 && parent_id) { 74 + on_open({ id: parent_id }) 75 + } else if (prev_id && has_showing_children(prev_id)) { 76 + select_last(prev_id) 77 + } else if (prev_id) { 78 + on_open({ id: prev_id }) 79 79 } 80 80 } 81 - function selectDown(i: number) { 82 - if (hasShowingChildren(children[i].id)) { 83 - selectFirst(children[i]) 81 + function select_down(i: number) { 82 + if (has_showing_children(children[i].id)) { 83 + select_first(children[i]) 84 84 } else if (children[i + 1]) { 85 85 console.trace() 86 86 on_open(children[i + 1]) ··· 89 89 } 90 90 } 91 91 92 - export function handleKey(e: KeyboardEvent) { 92 + export function handle_key(e: KeyboardEvent) { 93 93 const index = children.findIndex((child) => { 94 94 if (child.id === 'root') { 95 95 return child.id === $page.tracklist.id && child.view_as === $page.viewAs ··· 100 100 if (index < 0) { 101 101 return 102 102 } 103 - const selectedList = $trackListsDetailsMap[$page.tracklist.id] 104 - if (checkShortcut(e, 'ArrowUp')) { 105 - selectUp(index) 106 - } else if (checkShortcut(e, 'ArrowUp', { alt: true })) { 103 + const selected_list = $track_lists_details_map[$page.tracklist.id] 104 + if (check_shortcut(e, 'ArrowUp')) { 105 + select_up(index) 106 + } else if (check_shortcut(e, 'ArrowUp', { alt: true })) { 107 107 on_open({ id: 'root' }) 108 - } else if (checkShortcut(e, 'ArrowDown', { alt: true })) { 109 - selectLast('root') 110 - } else if (checkShortcut(e, 'ArrowDown')) { 111 - selectDown(index) 112 - } else if (checkShortcut(e, 'ArrowLeft')) { 113 - if (selectedList.kind === 'folder' && $shownFolders.has(selectedList.id)) { 114 - hideFolder(selectedList.id) 115 - } else if (parentId) { 116 - on_open({ id: parentId }) 108 + } else if (check_shortcut(e, 'ArrowDown', { alt: true })) { 109 + select_last('root') 110 + } else if (check_shortcut(e, 'ArrowDown')) { 111 + select_down(index) 112 + } else if (check_shortcut(e, 'ArrowLeft')) { 113 + if (selected_list.kind === 'folder' && $shown_folders.has(selected_list.id)) { 114 + hide_folder(selected_list.id) 115 + } else if (parent_id) { 116 + on_open({ id: parent_id }) 117 117 } 118 - } else if (checkShortcut(e, 'ArrowRight') && selectedList.kind === 'folder') { 119 - showFolder(selectedList.id) 118 + } else if (check_shortcut(e, 'ArrowRight') && selected_list.kind === 'folder') { 119 + show_folder(selected_list.id) 120 120 } else { 121 121 return 122 122 } 123 123 e.preventDefault() 124 124 } 125 125 $: if (children.find((child) => child.id === $page.id)) { 126 - const itemHandle = getContext<Writable<SidebarItemHandle | null>>('itemHandle') 127 - itemHandle.set({ handleKey }) 126 + const item_handle = getContext<Writable<SidebarItemHandle | null>>('itemHandle') 127 + item_handle.set({ handleKey: handle_key }) 128 128 } 129 129 130 - let dragTrackOntoIndex = null as number | null 131 - let dropAbove = false 132 - let dragPlaylistOntoIndex = null as number | null 130 + let drag_track_onto_index = null as number | null 131 + let drop_above = false 132 + let drag_playlist_onto_index = null as number | null 133 133 134 - function onDragStart(e: DragEvent, tracklist: TrackListDetails) { 135 - if (e.dataTransfer && tracklist.kind !== 'special' && parentId) { 134 + function on_drag_start(e: DragEvent, tracklist: TrackListDetails) { 135 + if (e.dataTransfer && tracklist.kind !== 'special' && parent_id) { 136 136 e.dataTransfer.effectAllowed = 'move' 137 - dragGhost.setInnerText(tracklist.name) 137 + dragGhost.set_inner_text(tracklist.name) 138 138 dragged.playlist = { 139 139 id: tracklist.id, 140 - fromFolder: parentId, 140 + from_folder: parent_id, 141 141 level, 142 142 } 143 - e.dataTransfer.setDragImage(dragGhost.dragEl, 0, 0) 143 + e.dataTransfer.setDragImage(dragGhost.drag_el, 0, 0) 144 144 e.dataTransfer.setData('ferrum.playlist', '') 145 145 } 146 146 } ··· 154 154 style:padding-left={14 * level + 'px'} 155 155 class:active={$page.id === child_list.id} 156 156 draggable="true" 157 - on:dragstart={(e) => onDragStart(e, child_list)} 158 - class:show={$shownFolders.has(child_list.id)} 159 - class:droppable={dragPlaylistOntoIndex === i} 157 + on:dragstart={(e) => on_drag_start(e, child_list)} 158 + class:show={$shown_folders.has(child_list.id)} 159 + class:droppable={drag_playlist_onto_index === i} 160 160 role="none" 161 161 on:drop={(e) => { 162 162 if ( 163 163 e.currentTarget && 164 164 e.dataTransfer?.types[0] === 'ferrum.playlist' && 165 165 dragged.playlist && 166 - !preventDrop && 166 + !prevent_drop && 167 167 dragged.playlist.id !== child_list.id && 168 168 child_list.children !== undefined 169 169 ) { 170 - movePlaylist( 170 + move_playlist( 171 171 dragged.playlist.id, 172 - dragged.playlist.fromFolder, 172 + dragged.playlist.from_folder, 173 173 child_list.id, 174 174 Math.max(0, child_list.children.length - 1), 175 175 ) 176 - dragPlaylistOntoIndex = null 176 + drag_playlist_onto_index = null 177 177 } 178 178 }} 179 179 on:mousedown={() => on_open(child_list)} 180 - on:contextmenu={() => tracklistContextMenu(child_list.id, true)} 180 + on:contextmenu={() => tracklist_context_menu(child_list.id, true)} 181 181 > 182 182 <!-- svelte-ignore a11y-click-events-have-key-events --> 183 183 <!-- svelte-ignore a11y-interactive-supports-focus --> ··· 187 187 aria-label="Arrow button" 188 188 on:mousedown|stopPropagation 189 189 on:click={() => { 190 - if ($shownFolders.has(child_list.id)) { 191 - hideFolder(child_list.id) 190 + if ($shown_folders.has(child_list.id)) { 191 + hide_folder(child_list.id) 192 192 } else { 193 - showFolder(child_list.id) 193 + show_folder(child_list.id) 194 194 } 195 195 }} 196 196 xmlns="http://www.w3.org/2000/svg" ··· 209 209 e.currentTarget && 210 210 e.dataTransfer?.types[0] === 'ferrum.playlist' && 211 211 dragged.playlist && 212 - !preventDrop && 212 + !prevent_drop && 213 213 dragged.playlist.id !== child_list.id 214 214 ) { 215 - dragPlaylistOntoIndex = i 215 + drag_playlist_onto_index = i 216 216 e.preventDefault() 217 217 } 218 218 }} 219 219 on:dragleave|self={() => { 220 - dragPlaylistOntoIndex = null 220 + drag_playlist_onto_index = null 221 221 }} 222 222 > 223 223 {child_list.name} 224 224 </div> 225 225 </div> 226 226 <svelte:self 227 - show={$shownFolders.has(child_list.id)} 227 + show={$shown_folders.has(child_list.id)} 228 228 parentId={child_list.id} 229 - children={child_list.children?.map((childId) => $trackListsDetailsMap[childId]) || []} 229 + children={child_list.children?.map((child_id) => $track_lists_details_map[child_id]) || []} 230 230 level={level + 1} 231 - preventDrop={preventDrop || dragged.playlist?.id === child_list.id} 231 + preventDrop={prevent_drop || dragged.playlist?.id === child_list.id} 232 232 {on_open} 233 233 on_select_down={() => { 234 234 if (i < children.length - 1) { ··· 246 246 aria-label="playlist" 247 247 style:padding-left={14 * level + 'px'} 248 248 draggable="true" 249 - on:dragstart={(e) => onDragStart(e, child_list)} 249 + on:dragstart={(e) => on_drag_start(e, child_list)} 250 250 class:active={$page.id === child_list.id} 251 251 on:mousedown={() => on_open(child_list)} 252 - class:droppable={dragTrackOntoIndex === i} 253 - class:droppable-above={dragPlaylistOntoIndex === i && dropAbove} 254 - class:droppable-below={dragPlaylistOntoIndex === i && !dropAbove} 252 + class:droppable={drag_track_onto_index === i} 253 + class:droppable-above={drag_playlist_onto_index === i && drop_above} 254 + class:droppable-below={drag_playlist_onto_index === i && !drop_above} 255 255 on:drop={(e) => { 256 256 if (e.currentTarget && e.dataTransfer?.types[0] === 'ferrum.tracks' && dragged.tracks) { 257 - addTracksToPlaylist(child_list.id, dragged.tracks.ids) 258 - dragTrackOntoIndex = null 257 + add_track_to_playlist(child_list.id, dragged.tracks.ids) 258 + drag_track_onto_index = null 259 259 } else if ( 260 260 e.currentTarget && 261 261 e.dataTransfer?.types[0] === 'ferrum.playlist' && 262 262 dragged.playlist && 263 - !preventDrop && 264 - parentId !== null 263 + !prevent_drop && 264 + parent_id !== null 265 265 ) { 266 266 const rect = e.currentTarget.getBoundingClientRect() 267 - dropAbove = e.pageY < rect.bottom - rect.height / 2 268 - movePlaylist( 267 + drop_above = e.pageY < rect.bottom - rect.height / 2 268 + move_playlist( 269 269 dragged.playlist.id, 270 - dragged.playlist.fromFolder, 271 - parentId, 272 - dropAbove ? i : i + 1, 270 + dragged.playlist.from_folder, 271 + parent_id, 272 + drop_above ? i : i + 1, 273 273 ) 274 - dragPlaylistOntoIndex = null 274 + drag_playlist_onto_index = null 275 275 } 276 276 }} 277 - on:contextmenu={() => tracklistContextMenu(child_list.id, false)} 277 + on:contextmenu={() => tracklist_context_menu(child_list.id, false)} 278 278 > 279 279 <div class="arrow" /> 280 280 <div ··· 282 282 role="link" 283 283 on:dragover={(e) => { 284 284 if (e.currentTarget && e.dataTransfer?.types[0] === 'ferrum.tracks' && dragged.tracks) { 285 - dragTrackOntoIndex = i 285 + drag_track_onto_index = i 286 286 e.preventDefault() 287 287 } else if ( 288 288 e.currentTarget && 289 289 e.dataTransfer?.types[0] === 'ferrum.playlist' && 290 290 dragged.playlist && 291 - !preventDrop 291 + !prevent_drop 292 292 ) { 293 - dragPlaylistOntoIndex = i 293 + drag_playlist_onto_index = i 294 294 e.preventDefault() 295 295 const rect = e.currentTarget.getBoundingClientRect() 296 - dropAbove = e.pageY < rect.bottom - rect.height / 2 296 + drop_above = e.pageY < rect.bottom - rect.height / 2 297 297 } 298 298 }} 299 299 on:dragleave|self={() => { 300 - dragTrackOntoIndex = null 301 - dragPlaylistOntoIndex = null 300 + drag_track_onto_index = null 301 + drag_playlist_onto_index = null 302 302 }} 303 303 > 304 304 {child_list.name}
+137 -137
src/components/TrackInfo.svelte
··· 7 7 8 8 <script lang="ts"> 9 9 import Modal from './Modal.svelte' 10 - import { checkShortcut } from '@/lib/helpers' 10 + import { check_shortcut } from '@/lib/helpers' 11 11 import Button from './Button.svelte' 12 12 import { methods } from '@/lib/data' 13 13 import type { Track, TrackID } from '../../ferrum-addon' 14 - import { ipcRenderer } from '@/lib/window' 15 - import { playingId, reload } from '@/lib/player' 14 + import { ipc_renderer } from '@/lib/window' 15 + import { playing_id, reload } from '@/lib/player' 16 16 import { onDestroy, tick } from 'svelte' 17 17 18 - export let currentList: TrackInfoList 18 + export let current_list: TrackInfoList 19 19 export let cancel: () => void 20 20 let id: TrackID 21 21 let track: Track ··· 28 28 /** Undefined when loading, null when no image exists */ 29 29 let image: ImageStuff | null | undefined 30 30 31 - $: openIndex(currentList) 32 - function openIndex(list: TrackInfoList) { 31 + $: open_index(current_list) 32 + function open_index(list: TrackInfoList) { 33 33 id = list.ids[list.index] 34 34 track = methods.getTrack(list.ids[list.index]) 35 35 methods.loadTags(list.ids[list.index]) 36 - loadImage(0) 36 + load_image(0) 37 37 } 38 - async function loadImage(index: number) { 38 + async function load_image(index: number) { 39 39 if (image) { 40 40 URL.revokeObjectURL(image.objectUrl) 41 41 } 42 42 image = undefined 43 43 await tick() 44 - const imageInfo = methods.getImage(index) 44 + const image_info = methods.getImage(index) 45 45 46 - if (imageInfo === null) { 46 + if (image_info === null) { 47 47 image = null 48 48 } else { 49 49 image = { 50 - index: imageInfo.index, 51 - totalImages: imageInfo.totalImages, 52 - mimeType: imageInfo.mimeType, 53 - objectUrl: URL.createObjectURL(new Blob([imageInfo.data], {})), 50 + index: image_info.index, 51 + totalImages: image_info.totalImages, 52 + mimeType: image_info.mimeType, 53 + objectUrl: URL.createObjectURL(new Blob([image_info.data], {})), 54 54 } 55 55 } 56 56 } ··· 60 60 } 61 61 }) 62 62 63 - function openPrev() { 64 - if (currentList.index > 0) { 65 - currentList.index -= 1 63 + function open_prev() { 64 + if (current_list.index > 0) { 65 + current_list.index -= 1 66 66 } 67 67 } 68 - function openNext() { 69 - if (currentList.index + 1 < currentList.ids.length) { 70 - currentList.index += 1 68 + function open_next() { 69 + if (current_list.index + 1 < current_list.ids.length) { 70 + current_list.index += 1 71 71 } 72 72 } 73 73 74 - function uintFilter(value: string) { 74 + function uint_filter(value: string) { 75 75 return value.replace(/[^0-9]*/g, '') 76 76 } 77 - function toString(value: unknown) { 77 + function to_string(value: unknown) { 78 78 return String(value).replace(/\0/g, '') // remove NULL bytes 79 79 } 80 80 81 - let imageEdited = false 81 + let image_edited = false 82 82 let name = '' 83 83 let artist = '' 84 - let albumName = '' 85 - let albumArtist = '' 84 + let album_name = '' 85 + let album_artist = '' 86 86 let composer = '' 87 87 let grouping = '' 88 88 let genre = '' 89 89 let year = '' 90 - $: year = uintFilter(year) 91 - let trackNum = '' 92 - let trackCount = '' 93 - let discNum = '' 94 - let discCount = '' 90 + $: year = uint_filter(year) 91 + let track_num = '' 92 + let track_count = '' 93 + let disc_num = '' 94 + let disc_count = '' 95 95 let bpm = '' 96 96 let compilation = false 97 97 let rating = 0 98 98 let liked = false 99 - let playCount = 0 99 + let play_count = 0 100 100 let comments = '' 101 - function setInfo(track: Track) { 102 - imageEdited = false 101 + function set_info(track: Track) { 102 + image_edited = false 103 103 name = track.name 104 104 artist = track.artist 105 - albumName = track.albumName || '' 106 - albumArtist = track.albumArtist || '' 105 + album_name = track.albumName || '' 106 + album_artist = track.albumArtist || '' 107 107 composer = track.composer || '' 108 108 grouping = track.grouping || '' 109 109 genre = track.genre || '' 110 - year = toString(track.year || '') 111 - trackNum = toString(track.trackNum || '') 112 - trackCount = toString(track.trackCount || '') 113 - discNum = toString(track.discNum || '') 114 - discCount = toString(track.discCount || '') 115 - bpm = toString(track.bpm || '') 110 + year = to_string(track.year || '') 111 + track_num = to_string(track.trackNum || '') 112 + track_count = to_string(track.trackCount || '') 113 + disc_num = to_string(track.discNum || '') 114 + disc_count = to_string(track.discCount || '') 115 + bpm = to_string(track.bpm || '') 116 116 compilation = track.compilation || false 117 117 rating = track.rating || 0 118 118 liked = track.liked || false 119 - playCount = track.playCount || 0 120 - comments = toString(track.comments || '') 119 + play_count = track.playCount || 0 120 + comments = to_string(track.comments || '') 121 121 } 122 - $: if (track) setInfo(track) 122 + $: if (track) set_info(track) 123 123 124 - function isEdited() { 125 - const isUnedited = 126 - !imageEdited && 124 + function is_edited() { 125 + const is_unedited = 126 + !image_edited && 127 127 name === track.name && 128 128 artist === track.artist && 129 - albumName === (track.albumName || '') && 130 - albumArtist === (track.albumArtist || '') && 129 + album_name === (track.albumName || '') && 130 + album_artist === (track.albumArtist || '') && 131 131 composer === (track.composer || '') && 132 132 grouping === (track.grouping || '') && 133 133 genre === (track.genre || '') && 134 - year === toString(track.year || '') && 135 - trackNum === toString(track.trackNum || '') && 136 - trackCount === toString(track.trackCount || '') && 137 - discNum === toString(track.discNum || '') && 138 - discCount === toString(track.discCount || '') && 139 - bpm === toString(track.bpm || '') && 134 + year === to_string(track.year || '') && 135 + track_num === to_string(track.trackNum || '') && 136 + track_count === to_string(track.trackCount || '') && 137 + disc_num === to_string(track.discNum || '') && 138 + disc_count === to_string(track.discCount || '') && 139 + bpm === to_string(track.bpm || '') && 140 140 compilation === (track.compilation || false) && 141 141 rating === (track.rating || 0) && 142 142 liked === (track.liked || false) && 143 - playCount === (track.playCount || 0) && 144 - comments === toString(track.comments || '') 145 - return !isUnedited 143 + play_count === (track.playCount || 0) && 144 + comments === to_string(track.comments || '') 145 + return !is_unedited 146 146 } 147 - function save(hideAfter = true) { 148 - if (isEdited()) { 147 + function save(hide_after = true) { 148 + if (is_edited()) { 149 149 methods.updateTrackInfo(id, { 150 150 name, 151 151 artist, 152 - albumName, 153 - albumArtist, 152 + albumName: album_name, 153 + albumArtist: album_artist, 154 154 composer, 155 155 grouping, 156 156 genre, 157 157 year, 158 - trackNum, 159 - trackCount, 160 - discNum, 161 - discCount, 158 + trackNum: track_num, 159 + trackCount: track_count, 160 + discNum: disc_num, 161 + discCount: disc_count, 162 162 bpm, 163 163 // compilation, 164 164 // rating, ··· 166 166 // playCount, 167 167 comments, 168 168 }) 169 - if (id === $playingId) { 169 + if (id === $playing_id) { 170 170 reload() 171 171 } 172 172 } 173 - if (hideAfter) { 173 + if (hide_after) { 174 174 cancel() 175 175 } 176 176 } ··· 178 178 return v.length >= 3 179 179 } 180 180 function keydown(e: KeyboardEvent) { 181 - if (checkShortcut(e, '[', { cmdOrCtrl: true })) { 181 + if (check_shortcut(e, '[', { cmd_or_ctrl: true })) { 182 182 save(false) 183 - openPrev() 184 - } else if (checkShortcut(e, ']', { cmdOrCtrl: true })) { 183 + open_prev() 184 + } else if (check_shortcut(e, ']', { cmd_or_ctrl: true })) { 185 185 save(false) 186 - openNext() 186 + open_next() 187 187 } 188 188 } 189 - function keydownNoneSelected(e: KeyboardEvent) { 190 - if (checkShortcut(e, 'Enter')) { 189 + function keydown_none_selected(e: KeyboardEvent) { 190 + if (check_shortcut(e, 'Enter')) { 191 191 save() 192 192 } 193 193 } 194 194 195 - function prevImage() { 195 + function prev_image() { 196 196 if (image && image.index >= 1) { 197 - loadImage(image.index - 1) 197 + load_image(image.index - 1) 198 198 } 199 199 } 200 - function nextImage() { 200 + function next_image() { 201 201 if (image && image.index < image.totalImages - 1) { 202 - loadImage(image.index + 1) 202 + load_image(image.index + 1) 203 203 } 204 204 } 205 205 206 206 let droppable = false 207 - const allowedMimes = ['image/jpeg', 'image/png'] 208 - function getFilePath(e: DragEvent): string | null { 209 - if (e.dataTransfer && hasFile(e)) { 207 + const allowed_mimes = ['image/jpeg', 'image/png'] 208 + function get_file_path(e: DragEvent): string | null { 209 + if (e.dataTransfer && has_file(e)) { 210 210 for (let i = 0; i < e.dataTransfer.files.length; i++) { 211 211 const file = e.dataTransfer.files[i] 212 - if (allowedMimes.includes(file.type)) { 212 + if (allowed_mimes.includes(file.type)) { 213 213 return file.path 214 214 } 215 215 } 216 216 } 217 217 return null 218 218 } 219 - function hasFile(e: DragEvent): boolean { 219 + function has_file(e: DragEvent): boolean { 220 220 if (!e.dataTransfer) return false 221 221 let count = 0 222 222 for (let i = 0; i < e.dataTransfer.items.length; i++) { 223 223 const item = e.dataTransfer.items[i] 224 - if (item.kind === 'file' && allowedMimes.includes(item.type)) { 224 + if (item.kind === 'file' && allowed_mimes.includes(item.type)) { 225 225 count++ 226 226 } 227 227 } 228 228 return count === 1 229 229 } 230 - function dragEnterOrOver(e: DragEvent) { 230 + function drag_enter_or_over(e: DragEvent) { 231 231 e.preventDefault() 232 - droppable = hasFile(e) 232 + droppable = has_file(e) 233 233 } 234 - function dragLeave(e: DragEvent) { 234 + function drag_leave(e: DragEvent) { 235 235 e.preventDefault() 236 236 droppable = false 237 237 } 238 238 function drop(e: DragEvent) { 239 239 e.preventDefault() 240 240 droppable = false 241 - const path = getFilePath(e) 241 + const path = get_file_path(e) 242 242 if (path !== null) { 243 243 methods.setImage(image?.index || 0, path) 244 - imageEdited = true 245 - loadImage(image?.index || 0) 244 + image_edited = true 245 + load_image(image?.index || 0) 246 246 } 247 247 } 248 - function coverKeydown(e: KeyboardEvent) { 249 - if (checkShortcut(e, 'Backspace') && image) { 248 + function cover_keydown(e: KeyboardEvent) { 249 + if (check_shortcut(e, 'Backspace') && image) { 250 250 methods.removeImage(image.index) 251 - imageEdited = true 251 + image_edited = true 252 252 if (image.index < image.totalImages - 1) { 253 - loadImage(image.index) 253 + load_image(image.index) 254 254 } else { 255 - loadImage(Math.max(0, image.index - 1)) 255 + load_image(Math.max(0, image.index - 1)) 256 256 } 257 - } else if (checkShortcut(e, ' ')) { 258 - pickCover() 259 - } else if (checkShortcut(e, 'ArrowLeft')) { 260 - prevImage() 261 - } else if (checkShortcut(e, 'ArrowRight')) { 262 - nextImage() 257 + } else if (check_shortcut(e, ' ')) { 258 + pick_cover() 259 + } else if (check_shortcut(e, 'ArrowLeft')) { 260 + prev_image() 261 + } else if (check_shortcut(e, 'ArrowRight')) { 262 + next_image() 263 263 } else { 264 - keydownNoneSelected(e) 264 + keydown_none_selected(e) 265 265 } 266 266 } 267 - async function pickCover() { 268 - let result = await ipcRenderer.invoke('showOpenDialog', false, { 267 + async function pick_cover() { 268 + let result = await ipc_renderer.invoke('showOpenDialog', false, { 269 269 properties: ['openFile'], 270 270 filters: [{ name: 'Images', extensions: ['jpg', 'jpeg', 'png'] }], 271 271 }) 272 272 if (!result.canceled && result.filePaths.length === 1) { 273 - replaceCover(result.filePaths[0]) 273 + replace_cover(result.filePaths[0]) 274 274 } 275 275 } 276 - function replaceCover(filePath: string) { 277 - methods.setImage(image?.index || 0, filePath) 278 - imageEdited = true 279 - loadImage(image?.index || 0) 276 + function replace_cover(file_path: string) { 277 + methods.setImage(image?.index || 0, file_path) 278 + image_edited = true 279 + load_image(image?.index || 0) 280 280 } 281 - function replaceCoverData(data: ArrayBuffer, mimeType: string) { 282 - methods.setImageData(image?.index || 0, data, mimeType) 283 - imageEdited = true 284 - loadImage(image?.index || 0) 281 + function replace_cover_data(data: ArrayBuffer, mime_type: string) { 282 + methods.setImageData(image?.index || 0, data, mime_type) 283 + image_edited = true 284 + load_image(image?.index || 0) 285 285 } 286 - function coverPaste(e: ClipboardEvent) { 286 + function cover_paste(e: ClipboardEvent) { 287 287 if (e.clipboardData && e.clipboardData.files.length === 1) { 288 288 const file = e.clipboardData.files[0] 289 - if (allowedMimes.includes(file.type) && file.path !== '' && file.path) { 290 - replaceCover(file.path) 291 - } else if (allowedMimes.includes(file.type)) { 289 + if (allowed_mimes.includes(file.type) && file.path !== '' && file.path) { 290 + replace_cover(file.path) 291 + } else if (allowed_mimes.includes(file.type)) { 292 292 const reader = new FileReader() 293 293 reader.onload = (e) => { 294 294 if (e.target?.result && e.target.result instanceof ArrayBuffer) { 295 - replaceCoverData(e.target.result, file.type) 295 + replace_cover_data(e.target.result, file.type) 296 296 } 297 297 } 298 298 reader.readAsArrayBuffer(file) ··· 302 302 </script> 303 303 304 304 <svelte:window on:keydown={keydown} /> 305 - <svelte:body on:keydown|self={keydownNoneSelected} on:paste={coverPaste} /> 306 - <Modal onCancel={cancel} cancelOnEscape form={save}> 305 + <svelte:body on:keydown|self={keydown_none_selected} on:paste={cover_paste} /> 306 + <Modal on_cancel={cancel} cancel_on_escape form={save}> 307 307 <main class="modal"> 308 308 <div class="header" class:has-subtitle={image && image.totalImages >= 2}> 309 309 <div 310 310 class="cover-area" 311 311 class:droppable 312 312 tabindex="0" 313 - on:keydown={coverKeydown} 313 + on:keydown={cover_keydown} 314 314 role="button" 315 315 aria-label="Cover artwork" 316 316 > 317 317 <!-- svelte-ignore a11y-no-static-element-interactions --> 318 318 <div 319 319 class="cover" 320 - on:dragenter={dragEnterOrOver} 321 - on:dragover={dragEnterOrOver} 322 - on:dragleave={dragLeave} 320 + on:dragenter={drag_enter_or_over} 321 + on:dragover={drag_enter_or_over} 322 + on:dragleave={drag_leave} 323 323 on:drop={drop} 324 - on:dblclick={pickCover} 324 + on:dblclick={pick_cover} 325 325 > 326 326 {#if image} 327 327 <img class="outline-element" alt="" src={image.objectUrl} /> ··· 342 342 {/if} 343 343 </div> 344 344 {#if image && image.totalImages >= 2} 345 - {@const imageIndex = image.index} 345 + {@const image_index = image.index} 346 346 <div class="cover-subtitle"> 347 - <div class="arrow" class:unclickable={imageIndex <= 0}> 347 + <div class="arrow" class:unclickable={image_index <= 0}> 348 348 <!-- svelte-ignore a11y-click-events-have-key-events --> 349 349 <svg 350 - on:click={prevImage} 350 + on:click={prev_image} 351 351 tabindex="-1" 352 352 role="button" 353 353 aria-label="Previous image" ··· 368 368 <div class="subtitle-text"> 369 369 {image.index + 1} / {image.totalImages} 370 370 </div> 371 - <div class="arrow" class:unclickable={imageIndex >= image.totalImages - 1}> 371 + <div class="arrow" class:unclickable={image_index >= image.totalImages - 1}> 372 372 <!-- svelte-ignore a11y-click-events-have-key-events --> 373 373 <svg 374 - on:click={nextImage} 374 + on:click={next_image} 375 375 tabindex="-1" 376 376 role="button" 377 377 aria-label="Next image" ··· 409 409 </div> 410 410 <div class="row"> 411 411 <div class="label">Album</div> 412 - <input type="text" bind:value={albumName} /> 412 + <input type="text" bind:value={album_name} /> 413 413 </div> 414 414 <div class="row"> 415 415 <div class="label">Album artist</div> 416 - <input type="text" bind:value={albumArtist} /> 416 + <input type="text" bind:value={album_artist} /> 417 417 </div> 418 418 <div class="row"> 419 419 <div class="label">Composer</div> ··· 433 433 </div> 434 434 <div class="row num"> 435 435 <div class="label">Track</div> 436 - <input class="num" type="text" bind:value={trackNum} class:big={big(trackNum)} /> 436 + <input class="num" type="text" bind:value={track_num} class:big={big(track_num)} /> 437 437 <div class="midtext">of</div> 438 - <input class="num" type="text" bind:value={trackCount} class:big={big(trackCount)} /> 438 + <input class="num" type="text" bind:value={track_count} class:big={big(track_count)} /> 439 439 </div> 440 440 <div class="row num"> 441 441 <div class="label">Disc number</div> 442 - <input class="num" type="text" bind:value={discNum} class:big={big(discNum)} /> 442 + <input class="num" type="text" bind:value={disc_num} class:big={big(disc_num)} /> 443 443 <div class="midtext">of</div> 444 - <input class="num" type="text" bind:value={discCount} class:big={big(discCount)} /> 444 + <input class="num" type="text" bind:value={disc_count} class:big={big(disc_count)} /> 445 445 </div> 446 446 <div class="row"> 447 447 <div class="label">Compilation</div> ··· 457 457 </div> 458 458 <div class="row"> 459 459 <div class="label">Play count</div> 460 - <p>{playCount}</p> 460 + <p>{play_count}</p> 461 461 </div> 462 462 <div class="row"> 463 463 <div class="label">Comments</div>
+104 -98
src/components/TrackList.svelte
··· 1 1 <script lang="ts"> 2 - import { page, removeFromOpenPlaylist, filter, deleteTracksInOpen, paths } from '../lib/data' 3 - import { newPlaybackInstance, playingId } from '../lib/player' 4 2 import { 5 - getDuration, 6 - formatDate, 7 - checkMouseShortcut, 8 - checkShortcut, 9 - assertUnreachable, 3 + page, 4 + remove_from_open_playlist, 5 + filter, 6 + delete_tracks_in_open, 7 + paths, 8 + } from '../lib/data' 9 + import { new_playback_instance, playing_id } from '../lib/player' 10 + import { 11 + get_duration, 12 + format_date, 13 + check_mouse_shortcut, 14 + check_shortcut, 15 + assert_unreachable, 10 16 } from '../lib/helpers' 11 - import { appendToUserQueue, prependToUserQueue } from '../lib/queue' 17 + import { append_to_user_queue, prepend_to_user_queue } from '../lib/queue' 12 18 import { selection, tracklist_actions } from '../lib/page' 13 - import { ipcListen, ipcRenderer } from '../lib/window' 19 + import { ipc_listen, ipc_renderer } from '../lib/window' 14 20 import { onDestroy, onMount } from 'svelte' 15 21 import { dragged } from '../lib/drag-drop' 16 22 import * as dragGhost from './DragGhost.svelte' 17 23 import type { TrackID } from 'ferrum-addon/addon' 18 24 import VirtualListBlock, { scroll_container_keydown } from './VirtualListBlock.svelte' 19 25 20 - export let onTrackInfo: (allIds: TrackID[], index: number) => void 26 + export let on_track_info: (allIds: TrackID[], index: number) => void 21 27 22 - let tracklistElement: HTMLDivElement 28 + let tracklist_element: HTMLDivElement 23 29 24 - const trackActionUnlisten = ipcListen('selectedTracksAction', (_, action) => { 25 - let firstIndex = selection.findFirst() 26 - if (firstIndex === null || !tracklistElement.contains(document.activeElement)) { 30 + const track_action_unlisten = ipc_listen('selectedTracksAction', (_, action) => { 31 + let first_index = selection.findFirst() 32 + if (first_index === null || !tracklist_element.contains(document.activeElement)) { 27 33 return 28 34 } 29 35 if (action === 'Play Next') { 30 - const ids = selection.getSelectedIndexes().map((i) => page.getTrackId(i)) 31 - prependToUserQueue(ids) 36 + const ids = selection.getSelectedIndexes().map((i) => page.get_track_id(i)) 37 + prepend_to_user_queue(ids) 32 38 } else if (action === 'Add to Queue') { 33 - const ids = selection.getSelectedIndexes().map((i) => page.getTrackId(i)) 34 - appendToUserQueue(ids) 39 + const ids = selection.getSelectedIndexes().map((i) => page.get_track_id(i)) 40 + append_to_user_queue(ids) 35 41 } else if (action === 'Get Info') { 36 - onTrackInfo(page.getTrackIds(), firstIndex) 42 + on_track_info(page.get_track_ids(), first_index) 37 43 } else if (action === 'revealTrackFile') { 38 - const track = page.getTrack(firstIndex) 39 - ipcRenderer.invoke('revealTrackFile', paths.tracksDir, track.file) 44 + const track = page.get_track(first_index) 45 + ipc_renderer.invoke('revealTrackFile', paths.tracksDir, track.file) 40 46 } else if (action === 'Remove from Playlist') { 41 - removeFromOpenPlaylist(selection.getSelectedIndexes()) 47 + remove_from_open_playlist(selection.getSelectedIndexes()) 42 48 } else if (action === 'Delete from Library') { 43 - deleteIndexes(selection.getSelectedIndexes()) 49 + delete_indexes(selection.getSelectedIndexes()) 44 50 } else { 45 - assertUnreachable(action) 51 + assert_unreachable(action) 46 52 } 47 53 }) 48 - onDestroy(trackActionUnlisten) 54 + onDestroy(track_action_unlisten) 49 55 50 - const sortBy = page.sortBy 56 + const sort_by = page.sort_by 51 57 $: sortKey = $page.sortKey 52 58 53 - ipcRenderer.on('Group Album Tracks', (_, checked) => { 59 + ipc_renderer.on('Group Album Tracks', (_, checked) => { 54 60 page.set_group_album_tracks(checked) 55 61 }) 56 62 57 - function doubleClick(e: MouseEvent, index: number) { 58 - if (e.button === 0 && checkMouseShortcut(e)) { 59 - playRow(index) 63 + function double_click(e: MouseEvent, index: number) { 64 + if (e.button === 0 && check_mouse_shortcut(e)) { 65 + play_row(index) 60 66 } 61 67 } 62 - async function deleteIndexes(indexes: number[]) { 68 + async function delete_indexes(indexes: number[]) { 63 69 const s = $selection.count > 1 ? 's' : '' 64 - const result = await ipcRenderer.invoke('showMessageBox', false, { 70 + const result = await ipc_renderer.invoke('showMessageBox', false, { 65 71 type: 'info', 66 72 message: `Delete ${$selection.count} song${s} from library?`, 67 73 buttons: [`Delete Song${s}`, 'Cancel'], 68 74 defaultId: 0, 69 75 }) 70 76 if (result.response === 0) { 71 - deleteTracksInOpen(indexes) 77 + delete_tracks_in_open(indexes) 72 78 } 73 79 } 74 80 async function keydown(e: KeyboardEvent) { 75 - if (checkShortcut(e, 'Enter')) { 76 - let firstIndex = selection.findFirst() 77 - if (firstIndex !== null) { 78 - playRow(firstIndex) 79 - } else if (!$playingId) { 80 - playRow(0) 81 + if (check_shortcut(e, 'Enter')) { 82 + let first_index = selection.findFirst() 83 + if (first_index !== null) { 84 + play_row(first_index) 85 + } else if (!$playing_id) { 86 + play_row(0) 81 87 } 82 88 } else if ( 83 - checkShortcut(e, 'Backspace') && 89 + check_shortcut(e, 'Backspace') && 84 90 $selection.count > 0 && 85 91 !$filter && 86 92 $page.tracklist.type === 'playlist' 87 93 ) { 88 94 e.preventDefault() 89 95 const s = $selection.count > 1 ? 's' : '' 90 - const result = ipcRenderer.invoke('showMessageBox', false, { 96 + const result = ipc_renderer.invoke('showMessageBox', false, { 91 97 type: 'info', 92 98 message: `Remove ${$selection.count} song${s} from the list?`, 93 99 buttons: ['Remove Song' + s, 'Cancel'], ··· 95 101 }) 96 102 const indexes = selection.getSelectedIndexes() 97 103 if ((await result).response === 0) { 98 - removeFromOpenPlaylist(indexes) 104 + remove_from_open_playlist(indexes) 99 105 } 100 - } else if (checkShortcut(e, 'Backspace', { cmdOrCtrl: true }) && $selection.count > 0) { 106 + } else if (check_shortcut(e, 'Backspace', { cmd_or_ctrl: true }) && $selection.count > 0) { 101 107 e.preventDefault() 102 - deleteIndexes(selection.getSelectedIndexes()) 108 + delete_indexes(selection.getSelectedIndexes()) 103 109 } else { 104 110 selection.handleKeyDown(e) 105 111 return ··· 107 113 e.preventDefault() 108 114 } 109 115 110 - function playRow(index: number) { 111 - newPlaybackInstance(page.getTrackIds(), index) 116 + function play_row(index: number) { 117 + new_playback_instance(page.get_track_ids(), index) 112 118 } 113 119 114 - let dragLine: HTMLElement 115 - let dragIndexes: number[] = [] 116 - function onDragStart(e: DragEvent) { 120 + let drag_line: HTMLElement 121 + let drag_indexes: number[] = [] 122 + function on_drag_start(e: DragEvent) { 117 123 if (e.dataTransfer) { 118 - dragIndexes = [] 124 + drag_indexes = [] 119 125 for (let i = 0; i < $selection.list.length; i++) { 120 126 if ($selection.list[i]) { 121 - dragIndexes.push(i) 127 + drag_indexes.push(i) 122 128 } 123 129 } 124 130 e.dataTransfer.effectAllowed = 'move' 125 - if (dragIndexes.length === 1) { 126 - const track = page.getTrack(dragIndexes[0]) 127 - dragGhost.setInnerText(track.artist + ' - ' + track.name) 131 + if (drag_indexes.length === 1) { 132 + const track = page.get_track(drag_indexes[0]) 133 + dragGhost.set_inner_text(track.artist + ' - ' + track.name) 128 134 } else { 129 - dragGhost.setInnerText(dragIndexes.length + ' items') 135 + dragGhost.set_inner_text(drag_indexes.length + ' items') 130 136 } 131 137 dragged.tracks = { 132 - ids: dragIndexes.map((i) => page.getTrackId(i)), 133 - playlistIndexes: dragIndexes, 138 + ids: drag_indexes.map((i) => page.get_track_id(i)), 139 + playlist_indexes: drag_indexes, 134 140 } 135 - e.dataTransfer.setDragImage(dragGhost.dragEl, 0, 0) 141 + e.dataTransfer.setDragImage(dragGhost.drag_el, 0, 0) 136 142 e.dataTransfer.setData('ferrum.tracks', '') 137 143 } 138 144 } 139 - let dragToIndex: null | number = null 140 - function onDragOver(e: DragEvent, index: number) { 145 + let drag_to_index: null | number = null 146 + function on_drag_over(e: DragEvent, index: number) { 141 147 if ( 142 148 !$page.sortDesc || 143 149 $page.sortKey !== 'index' || 144 150 $filter || 145 151 $page.tracklist.type !== 'playlist' 146 152 ) { 147 - dragToIndex = null 153 + drag_to_index = null 148 154 return 149 155 } 150 156 if ( 151 - dragged.tracks?.playlistIndexes && 157 + dragged.tracks?.playlist_indexes && 152 158 e.currentTarget && 153 159 e.dataTransfer?.types[0] === 'ferrum.tracks' 154 160 ) { 155 161 e.preventDefault() 156 162 const rect = (e.currentTarget as HTMLElement).getBoundingClientRect() 157 163 if (e.pageY < rect.bottom - rect.height / 2) { 158 - dragLine.style.top = rect.top - 1 + 'px' 159 - dragToIndex = index 164 + drag_line.style.top = rect.top - 1 + 'px' 165 + drag_to_index = index 160 166 } else { 161 - dragLine.style.top = rect.bottom - 1 + 'px' 162 - dragToIndex = index + 1 167 + drag_line.style.top = rect.bottom - 1 + 'px' 168 + drag_to_index = index + 1 163 169 } 164 170 } 165 171 } 166 - async function dropHandler() { 167 - if (dragToIndex !== null) { 168 - page.moveTracks(dragIndexes, dragToIndex) 169 - dragToIndex = null 172 + async function drop_handler() { 173 + if (drag_to_index !== null) { 174 + page.move_tracks(drag_indexes, drag_to_index) 175 + drag_to_index = null 170 176 } 171 177 } 172 - function dragEndHandler() { 173 - dragToIndex = null 178 + function drag_end_handler() { 179 + drag_to_index = null 174 180 } 175 181 176 - function getItem(index: number) { 182 + function get_item(index: number) { 177 183 try { 178 - const track = page.getTrack(index) 179 - return { ...track, id: page.getTrackId(index) } 184 + const track = page.get_track(index) 185 + return { ...track, id: page.get_track_id(index) } 180 186 } catch (_) { 181 187 return null 182 188 } ··· 200 206 </script> 201 207 202 208 <div 203 - bind:this={tracklistElement} 209 + bind:this={tracklist_element} 204 210 class="tracklist" 205 211 role="table" 206 - on:dragleave={() => (dragToIndex = null)} 212 + on:dragleave={() => (drag_to_index = null)} 207 213 class:no-selection={$selection.count === 0} 208 214 > 209 215 <div ··· 216 222 <div 217 223 class="c index" 218 224 class:sort={sortKey === 'index'} 219 - on:click={() => sortBy('index')} 225 + on:click={() => sort_by('index')} 220 226 role="button" 221 227 > 222 228 <span>#</span> ··· 226 232 <div 227 233 class="c name" 228 234 class:sort={sortKey === 'name'} 229 - on:click={() => sortBy('name')} 235 + on:click={() => sort_by('name')} 230 236 role="button" 231 237 > 232 238 <span>Name</span> ··· 236 242 <div 237 243 class="c playCount" 238 244 class:sort={sortKey === 'playCount'} 239 - on:click={() => sortBy('playCount')} 245 + on:click={() => sort_by('playCount')} 240 246 role="button" 241 247 > 242 248 <span>Plays</span> ··· 246 252 <div 247 253 class="c duration" 248 254 class:sort={sortKey === 'duration'} 249 - on:click={() => sortBy('duration')} 255 + on:click={() => sort_by('duration')} 250 256 role="button" 251 257 > 252 258 <span>Time</span> ··· 256 262 <div 257 263 class="c artist" 258 264 class:sort={sortKey === 'artist'} 259 - on:click={() => sortBy('artist')} 265 + on:click={() => sort_by('artist')} 260 266 role="button" 261 267 > 262 268 <span>Artist</span> ··· 266 272 <div 267 273 class="c albumName" 268 274 class:sort={sortKey === 'albumName'} 269 - on:click={() => sortBy('albumName')} 275 + on:click={() => sort_by('albumName')} 270 276 role="button" 271 277 > 272 278 <span>Album</span> ··· 276 282 <div 277 283 class="c comments" 278 284 class:sort={sortKey === 'comments'} 279 - on:click={() => sortBy('comments')} 285 + on:click={() => sort_by('comments')} 280 286 role="button" 281 287 > 282 288 <span>Comments</span> ··· 286 292 <div 287 293 class="c genre" 288 294 class:sort={sortKey === 'genre'} 289 - on:click={() => sortBy('genre')} 295 + on:click={() => sort_by('genre')} 290 296 role="button" 291 297 > 292 298 <span>Genre</span> ··· 296 302 <div 297 303 class="c dateAdded" 298 304 class:sort={sortKey === 'dateAdded'} 299 - on:click={() => sortBy('dateAdded')} 305 + on:click={() => sort_by('dateAdded')} 300 306 role="button" 301 307 > 302 308 <span>Date Added</span> ··· 306 312 <div 307 313 class="c year" 308 314 class:sort={sortKey === 'year'} 309 - on:click={() => sortBy('year')} 315 + on:click={() => sort_by('year')} 310 316 role="button" 311 317 > 312 318 <span>Year</span> ··· 330 336 {scroll_container} 331 337 let:item={i} 332 338 > 333 - {@const track = getItem(i)} 339 + {@const track = get_item(i)} 334 340 {#if track !== null} 335 341 <!-- svelte-ignore a11y-click-events-have-key-events --> 336 342 <!-- svelte-ignore a11y-interactive-supports-focus --> 337 343 <div 338 344 class="row" 339 345 role="row" 340 - on:dblclick={(e) => doubleClick(e, i)} 346 + on:dblclick={(e) => double_click(e, i)} 341 347 on:mousedown={(e) => selection.handleMouseDown(e, i)} 342 348 on:contextmenu={(e) => selection.handleContextMenu(e, i)} 343 349 on:click={(e) => selection.handleClick(e, i)} 344 350 draggable="true" 345 - on:dragstart={onDragStart} 346 - on:dragover={(e) => onDragOver(e, i)} 347 - on:drop={dropHandler} 348 - on:dragend={dragEndHandler} 351 + on:dragstart={on_drag_start} 352 + on:dragover={(e) => on_drag_over(e, i)} 353 + on:drop={drop_handler} 354 + on:dragend={drag_end_handler} 349 355 class:odd={i % 2 === 0} 350 356 class:selected={$selection.list[i] === true} 351 - class:playing={track.id === $playingId} 357 + class:playing={track.id === $playing_id} 352 358 > 353 359 <div class="c index"> 354 - {#if track.id === $playingId} 360 + {#if track.id === $playing_id} 355 361 <svg 356 362 class="playing-icon inline" 357 363 xmlns="http://www.w3.org/2000/svg" ··· 370 376 </div> 371 377 <div class="c name">{track.name}</div> 372 378 <div class="c playCount">{track.playCount || ''}</div> 373 - <div class="c duration">{track.duration ? getDuration(track.duration) : ''}</div> 379 + <div class="c duration">{track.duration ? get_duration(track.duration) : ''}</div> 374 380 <div class="c artist">{track.artist}</div> 375 381 <div class="c albumName">{track.albumName || ''}</div> 376 382 <div class="c comments">{track.comments || ''}</div> 377 383 <div class="c genre">{track.genre || ''}</div> 378 - <div class="c dateAdded">{formatDate(track.dateAdded)}</div> 384 + <div class="c dateAdded">{format_date(track.dateAdded)}</div> 379 385 <div class="c year">{track.year || ''}</div> 380 386 </div> 381 387 {/if} 382 388 </VirtualListBlock> 383 389 </div> 384 - <div class="drag-line" class:show={dragToIndex !== null} bind:this={dragLine} /> 390 + <div class="drag-line" class:show={drag_to_index !== null} bind:this={drag_line} /> 385 391 </div> 386 392 387 393 <style lang="sass">
+21 -21
src/electron/ipc.ts
··· 1 1 import { dialog, Menu, shell, BrowserWindow } from 'electron' 2 - import { ipcMain } from './typed_ipc' 2 + import { ipc_main } from './typed_ipc' 3 3 import path from 'path' 4 4 import is from './is' 5 5 6 - ipcMain.handle('showMessageBox', async (e, attached, options) => { 6 + ipc_main.handle('showMessageBox', async (e, attached, options) => { 7 7 const window = BrowserWindow.fromWebContents(e.sender) 8 8 if (attached && window) { 9 9 return await dialog.showMessageBox(window, options) ··· 12 12 } 13 13 }) 14 14 15 - ipcMain.handle('showOpenDialog', async (e, attached, options) => { 15 + ipc_main.handle('showOpenDialog', async (e, attached, options) => { 16 16 const window = BrowserWindow.fromWebContents(e.sender) 17 17 if (attached && window) { 18 18 return await dialog.showOpenDialog(window, options) ··· 21 21 } 22 22 }) 23 23 24 - ipcMain.handle('revealTrackFile', async (_e, ...paths) => { 24 + ipc_main.handle('revealTrackFile', async (_e, ...paths) => { 25 25 shell.showItemInFolder(path.join(...paths)) 26 26 }) 27 27 28 - ipcMain.handle('volume_change', async (_e, up) => { 28 + ipc_main.handle('volume_change', async (_e, up) => { 29 29 if (up) { 30 30 Menu.getApplicationMenu()?.getMenuItemById('Volume Up')?.click() 31 31 } else { ··· 33 33 } 34 34 }) 35 35 36 - ipcMain.handle('showTrackMenu', (e, options) => { 37 - let queueMenu: Electron.MenuItemConstructorOptions[] = [] 36 + ipc_main.handle('showTrackMenu', (e, options) => { 37 + let queue_menu: Electron.MenuItemConstructorOptions[] = [] 38 38 if (options.queue) { 39 - queueMenu = [ 39 + queue_menu = [ 40 40 { 41 41 label: 'Remove from Queue', 42 42 click: () => { ··· 46 46 { type: 'separator' }, 47 47 ] 48 48 } 49 - const selectedIds = options.selectedIndexes.map((i) => options.allIds[i]) 49 + const selected_ids = options.selectedIndexes.map((i) => options.allIds[i]) 50 50 const menu = Menu.buildFromTemplate([ 51 - ...queueMenu, 51 + ...queue_menu, 52 52 { 53 53 label: 'Play Next', 54 54 click: () => { 55 - e.sender.send('context.Play Next', selectedIds) 55 + e.sender.send('context.Play Next', selected_ids) 56 56 }, 57 57 }, 58 58 { 59 59 label: 'Add to Queue', 60 60 click: () => { 61 - e.sender.send('context.Add to Queue', selectedIds) 61 + e.sender.send('context.Add to Queue', selected_ids) 62 62 }, 63 63 }, 64 64 { ··· 66 66 submenu: options.lists.map((item) => { 67 67 return { 68 68 ...item, 69 - click: () => e.sender.send('context.Add to Playlist', item.id, selectedIds), 69 + click: () => e.sender.send('context.Add to Playlist', item.id, selected_ids), 70 70 } 71 71 }), 72 72 }, ··· 84 84 else if (is.windows) return 'Reveal in File Explorer' 85 85 else return 'Reveal in File Manager' 86 86 })(), 87 - click: () => e.sender.send('context.revealTrackFile', selectedIds[0]), 87 + click: () => e.sender.send('context.revealTrackFile', selected_ids[0]), 88 88 }, 89 89 { type: 'separator', visible: options.playlist?.editable === true }, 90 90 { ··· 96 96 menu.popup() 97 97 }) 98 98 99 - ipcMain.handle('showTracklistMenu', (e, args) => { 100 - const editMenu = [ 99 + ipc_main.handle('showTracklistMenu', (e, args) => { 100 + const edit_menu = [ 101 101 { 102 102 label: 'Edit Details', 103 103 click: () => e.sender.send('context.playlist.edit', args.id), ··· 107 107 click: () => e.sender.send('context.playlist.delete', args.id), 108 108 }, 109 109 ] 110 - const newMenu = [ 110 + const new_menu = [ 111 111 { 112 112 label: 'New Playlist', 113 113 click: () => e.sender.send('newPlaylist', args.id, false), ··· 117 117 click: () => e.sender.send('newPlaylist', args.id, true), 118 118 }, 119 119 ] 120 - let menuItems: Electron.MenuItemConstructorOptions[] = editMenu 120 + let menu_items: Electron.MenuItemConstructorOptions[] = edit_menu 121 121 if (args.isRoot) { 122 - menuItems = newMenu 122 + menu_items = new_menu 123 123 } else if (args.isFolder) { 124 - menuItems = [...editMenu, { type: 'separator' }, ...newMenu] 124 + menu_items = [...edit_menu, { type: 'separator' }, ...new_menu] 125 125 } 126 126 127 - const menu = Menu.buildFromTemplate(menuItems) 127 + const menu = Menu.buildFromTemplate(menu_items) 128 128 menu.popup() 129 129 })
+30 -30
src/electron/main.ts
··· 4 4 5 5 if (is.dev) app.setName('Ferrum Dev') 6 6 7 - import { initMenuBar } from './menubar' 8 - import { initMediaKeys } from './shortcuts' 7 + import { init_menu_bar } from './menubar' 8 + import { init_media_keys } from './shortcuts' 9 9 import('./ipc') 10 10 import path from 'path' 11 11 12 - async function errHandler(msg: string, error: Error) { 12 + async function err_handler(msg: string, error: Error) { 13 13 app.whenReady().then(() => { 14 14 dialog.showMessageBoxSync({ 15 15 type: 'error', ··· 17 17 detail: error.stack, 18 18 title: 'Error', 19 19 }) 20 - errHandler(msg, error) 20 + err_handler(msg, error) 21 21 }) 22 22 } 23 23 process.on('uncaughtException', (error) => { 24 - errHandler('Unhandled Error', error) 24 + err_handler('Unhandled Error', error) 25 25 }) 26 26 process.on('unhandledRejection', (error: Error) => { 27 - errHandler('Unhandled Promise Rejection', error) 27 + err_handler('Unhandled Promise Rejection', error) 28 28 }) 29 29 30 - const appData = app.getPath('appData') 30 + const app_data = app.getPath('appData') 31 31 if (is.dev) { 32 - const electronDataPath = path.join(appData, 'space.kasper.ferrum.dev', 'Electron Data') 33 - app.setPath('userData', electronDataPath) 32 + const electron_data_path = path.join(app_data, 'space.kasper.ferrum.dev', 'Electron Data') 33 + app.setPath('userData', electron_data_path) 34 34 } else { 35 - const electronDataPath = path.join(appData, 'space.kasper.ferrum', 'Electron Data') 36 - app.setPath('userData', electronDataPath) 35 + const electron_data_path = path.join(app_data, 'space.kasper.ferrum', 'Electron Data') 36 + app.setPath('userData', electron_data_path) 37 37 } 38 38 39 39 let quitting = false 40 - let appLoaded = false 40 + let app_loaded = false 41 41 42 42 app.on('window-all-closed', () => { 43 43 app.quit() 44 44 }) 45 45 46 46 app.whenReady().then(async () => { 47 - let mainWindow: BrowserWindow | null = new BrowserWindow({ 47 + let main_window: BrowserWindow | null = new BrowserWindow({ 48 48 width: 1305, 49 49 height: 1000, 50 50 minWidth: 850, ··· 60 60 }) 61 61 62 62 if (!is.dev) { 63 - await initMediaKeys(mainWindow) 63 + await init_media_keys(main_window) 64 64 } 65 65 66 66 protocol.registerFileProtocol('track', (request, callback) => { ··· 92 92 }) 93 93 }) 94 94 95 - if (is.dev) mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL || 'missing') 96 - else mainWindow.loadFile(path.resolve(__dirname, '../web/index.html')) 95 + if (is.dev) main_window.loadURL(process.env.VITE_DEV_SERVER_URL || 'missing') 96 + else main_window.loadFile(path.resolve(__dirname, '../web/index.html')) 97 97 98 - if (is.dev) mainWindow.webContents.openDevTools() 98 + if (is.dev) main_window.webContents.openDevTools() 99 99 100 - mainWindow.once('ready-to-show', () => { 101 - mainWindow?.show() 100 + main_window.once('ready-to-show', () => { 101 + main_window?.show() 102 102 }) 103 103 104 - mainWindow.on('close', (e) => { 104 + main_window.on('close', (e) => { 105 105 if (!quitting) { 106 106 e.preventDefault() 107 107 app.hide() 108 108 } 109 109 }) 110 - mainWindow.on('closed', () => { 111 - mainWindow = null 110 + main_window.on('closed', () => { 111 + main_window = null 112 112 }) 113 113 ipcMain.once('appLoaded', () => { 114 - appLoaded = true 114 + app_loaded = true 115 115 }) 116 116 117 117 // doesn't fire on Windows :( 118 118 app.on('before-quit', () => { 119 - if (appLoaded) { 120 - mainWindow?.webContents.send('gonnaQuit') 119 + if (app_loaded) { 120 + main_window?.webContents.send('gonnaQuit') 121 121 ipcMain.once('readyToQuit', () => { 122 122 quitting = true 123 - mainWindow?.close() 123 + main_window?.close() 124 124 }) 125 125 } else { 126 126 quitting = true 127 - mainWindow?.close() 127 + main_window?.close() 128 128 } 129 129 }) 130 130 131 131 app.on('activate', () => { 132 - if (mainWindow !== null) { 133 - mainWindow.show() 132 + if (main_window !== null) { 133 + main_window.show() 134 134 } 135 135 }) 136 136 137 - initMenuBar(app, mainWindow) 137 + init_menu_bar(app, main_window) 138 138 })
+29 -29
src/electron/menubar.ts
··· 1 1 import { type App, BrowserWindow, Menu, shell, dialog } from 'electron' 2 - import { ipcMain } from './typed_ipc' 2 + import { ipc_main } from './typed_ipc' 3 3 import type { MenuItemConstructorOptions } from 'electron/common' 4 4 import is from './is' 5 5 import type { WebContents } from './typed_ipc' ··· 13 13 }) 14 14 } 15 15 16 - export function initMenuBar(app: App, mainWindow: BrowserWindow) { 17 - const webContents = mainWindow.webContents as WebContents 16 + export function init_menu_bar(app: App, main_window: BrowserWindow) { 17 + const web_contents = main_window.webContents as WebContents 18 18 const template: MenuItemConstructorOptions[] = [ 19 19 { 20 20 label: app.name, ··· 38 38 label: 'New Playlist', 39 39 accelerator: 'CmdOrCtrl+N', 40 40 click: () => { 41 - webContents.send('newPlaylist', 'root', false) 41 + web_contents.send('newPlaylist', 'root', false) 42 42 }, 43 43 }, 44 44 { 45 45 label: 'New Playlist Folder', 46 46 click: () => { 47 - webContents.send('newPlaylist', 'root', true) 47 + web_contents.send('newPlaylist', 'root', true) 48 48 }, 49 49 }, 50 50 { type: 'separator' }, ··· 52 52 label: 'Import...', 53 53 accelerator: 'CmdOrCtrl+O', 54 54 click: () => { 55 - webContents.send('import') 55 + web_contents.send('import') 56 56 }, 57 57 }, 58 58 { type: 'separator' }, 59 59 { 60 60 label: 'Import iTunes Library...', 61 61 click: () => { 62 - webContents.send('itunesImport') 62 + web_contents.send('itunesImport') 63 63 }, 64 64 }, 65 65 { type: 'separator' }, ··· 88 88 label: 'Filter', 89 89 accelerator: 'CmdOrCtrl+F', 90 90 click: () => { 91 - webContents.send('filter') 91 + web_contents.send('filter') 92 92 }, 93 93 }) 94 94 return menu ··· 101 101 label: 'Play Next', 102 102 accelerator: '', 103 103 click: () => { 104 - webContents.send('selectedTracksAction', 'Play Next') 104 + web_contents.send('selectedTracksAction', 'Play Next') 105 105 }, 106 106 }, 107 107 { 108 108 label: 'Add to Queue', 109 109 accelerator: '', 110 110 click: () => { 111 - webContents.send('selectedTracksAction', 'Add to Queue') 111 + web_contents.send('selectedTracksAction', 'Add to Queue') 112 112 }, 113 113 }, 114 114 { type: 'separator' }, ··· 116 116 label: 'Get Info', 117 117 accelerator: 'CmdOrCtrl+I', 118 118 click: () => { 119 - webContents.send('selectedTracksAction', 'Get Info') 119 + web_contents.send('selectedTracksAction', 'Get Info') 120 120 }, 121 121 }, 122 122 { type: 'separator' }, ··· 128 128 })(), 129 129 accelerator: 'Shift+CmdOrCtrl+R', 130 130 click: () => { 131 - webContents.send('selectedTracksAction', 'revealTrackFile') 131 + web_contents.send('selectedTracksAction', 'revealTrackFile') 132 132 }, 133 133 }, 134 134 { type: 'separator' }, ··· 136 136 label: 'Remove from Playlist', 137 137 accelerator: '', 138 138 click: () => { 139 - webContents.send('selectedTracksAction', 'Remove from Playlist') 139 + web_contents.send('selectedTracksAction', 'Remove from Playlist') 140 140 }, 141 141 }, 142 142 { 143 143 label: 'Delete from Library', 144 144 accelerator: 'CmdOrCtrl+Backspace', 145 145 click: () => { 146 - webContents.send('selectedTracksAction', 'Delete from Library') 146 + web_contents.send('selectedTracksAction', 'Delete from Library') 147 147 }, 148 148 }, 149 149 ], ··· 157 157 type: 'checkbox', 158 158 accelerator: 'CmdOrCtrl+U', 159 159 click: () => { 160 - webContents.send('Show Queue') 160 + web_contents.send('Show Queue') 161 161 }, 162 162 }, 163 163 { ··· 165 165 type: 'checkbox', 166 166 accelerator: 'CmdOrCtrl+K', 167 167 click: () => { 168 - webContents.send('ToggleQuickNav') 168 + web_contents.send('ToggleQuickNav') 169 169 }, 170 170 }, 171 171 { ··· 173 173 type: 'checkbox', 174 174 checked: true, 175 175 click: (item) => { 176 - webContents.send('Group Album Tracks', item.checked) 176 + web_contents.send('Group Album Tracks', item.checked) 177 177 }, 178 178 }, 179 179 { type: 'separator' }, ··· 195 195 label: 'Pause', 196 196 // no accelerator because it's unreliable 197 197 click: () => { 198 - webContents.send('playPause') 198 + web_contents.send('playPause') 199 199 }, 200 200 }, 201 201 { 202 202 label: 'Next', 203 203 accelerator: 'CmdOrCtrl+Right', 204 204 click: () => { 205 - webContents.send('Next') 205 + web_contents.send('Next') 206 206 }, 207 207 }, 208 208 { 209 209 label: 'Previous', 210 210 accelerator: 'CmdOrCtrl+Left', 211 211 click: () => { 212 - webContents.send('Previous') 212 + web_contents.send('Previous') 213 213 }, 214 214 }, 215 215 { type: 'separator' }, ··· 219 219 type: 'checkbox', 220 220 accelerator: 'CmdOrCtrl+S', 221 221 click: () => { 222 - webContents.send('Shuffle') 222 + web_contents.send('Shuffle') 223 223 }, 224 224 }, 225 225 { ··· 228 228 type: 'checkbox', 229 229 accelerator: 'CmdOrCtrl+R', 230 230 click: () => { 231 - webContents.send('Repeat') 231 + web_contents.send('Repeat') 232 232 }, 233 233 }, 234 234 { type: 'separator' }, ··· 237 237 label: 'Volume Up', 238 238 accelerator: 'CmdOrCtrl+Up', 239 239 click: () => { 240 - webContents.send('volumeUp') 240 + web_contents.send('volumeUp') 241 241 }, 242 242 }, 243 243 { ··· 245 245 label: 'Volume Down', 246 246 accelerator: 'CmdOrCtrl+Down', 247 247 click: () => { 248 - webContents.send('volumeDown') 248 + web_contents.send('volumeDown') 249 249 }, 250 250 }, 251 251 ], ··· 258 258 label: 'Select Next List', 259 259 accelerator: 'Ctrl+Tab', 260 260 click: () => { 261 - webContents.send('Select Next List') 261 + web_contents.send('Select Next List') 262 262 }, 263 263 }, 264 264 { ··· 266 266 label: 'Select Previous List', 267 267 accelerator: 'Ctrl+Shift+Tab', 268 268 click: () => { 269 - webContents.send('Select Previous List') 269 + web_contents.send('Select Previous List') 270 270 }, 271 271 }, 272 272 { type: 'separator', visible: is.mac }, ··· 293 293 const menu = Menu.buildFromTemplate(template) 294 294 Menu.setApplicationMenu(menu) 295 295 296 - ipcMain.handle('update:Shuffle', (_, checked) => { 296 + ipc_main.handle('update:Shuffle', (_, checked) => { 297 297 const item = menu.getMenuItemById('Shuffle') 298 298 if (!item) return handle_missing('Shuffle') 299 299 item.checked = checked 300 300 }) 301 - ipcMain.handle('update:Repeat', (_, checked) => { 301 + ipc_main.handle('update:Repeat', (_, checked) => { 302 302 const item = menu.getMenuItemById('Repeat') 303 303 if (!item) return handle_missing('Repeat') 304 304 item.checked = checked 305 305 }) 306 - ipcMain.handle('update:Show Queue', (_, checked) => { 306 + ipc_main.handle('update:Show Queue', (_, checked) => { 307 307 const item = menu.getMenuItemById('Show Queue') 308 308 if (!item) return handle_missing('Show Queue') 309 309 item.checked = checked
+7 -7
src/electron/preload.ts
··· 3 3 import addon from '../../ferrum-addon' 4 4 5 5 window.addon = addon 6 - window.isDev = is.dev 7 - window.libraryPath = process.env.LIBRARY 8 - window.isMac = is.mac 9 - window.isWindows = is.windows 6 + window.is_dev = is.dev 7 + window.library_path = process.env.LIBRARY 8 + window.is_mac = is.mac 9 + window.is_windows = is.windows 10 10 11 - window.joinPaths = (...args) => { 12 - const combinedPath = path.join(...args) 13 - return combinedPath 11 + window.join_paths = (...args) => { 12 + const combined_path = path.join(...args) 13 + return combined_path 14 14 }
+20 -20
src/electron/shortcuts.ts
··· 2 2 import is from './is' 3 3 import type { WebContents } from './typed_ipc' 4 4 5 - function tryRegistering(mainWindow: BrowserWindow) { 6 - const webContents = mainWindow.webContents as WebContents 5 + function try_registering(main_window: BrowserWindow) { 6 + const web_contents = main_window.webContents as WebContents 7 7 const success1 = globalShortcut.register('MediaPlayPause', () => { 8 - if (mainWindow !== null) webContents.send('playPause') 8 + if (main_window !== null) web_contents.send('playPause') 9 9 }) 10 10 if (!success1) return false 11 11 globalShortcut.register('MediaNextTrack', () => { 12 - if (mainWindow !== null) webContents.send('Next') 12 + if (main_window !== null) web_contents.send('Next') 13 13 }) 14 14 globalShortcut.register('MediaPreviousTrack', () => { 15 - if (mainWindow !== null) webContents.send('Previous') 15 + if (main_window !== null) web_contents.send('Previous') 16 16 }) 17 17 globalShortcut.register('MediaStop', () => { 18 - if (mainWindow !== null) webContents.send('Stop') 18 + if (main_window !== null) web_contents.send('Stop') 19 19 }) 20 20 return true 21 21 } 22 22 23 - async function requestLoop(mainWindow: BrowserWindow) { 24 - let firstRequest = true 23 + async function request_loop(main_window: BrowserWindow) { 24 + let first_request = true 25 25 for (;;) { 26 26 let msg = 'No accessibility permissions detected.' 27 - if (!firstRequest) { 27 + if (!first_request) { 28 28 msg = 'Click Done when you have granted Ferrum accessibility permissions.' 29 29 } else { 30 - firstRequest = true 30 + first_request = true 31 31 } 32 - const result = await dialog.showMessageBox(mainWindow, { 32 + const result = await dialog.showMessageBox(main_window, { 33 33 type: 'info', 34 34 message: `${msg} To grant them, open System Preferences, click Security & Privacy, click Privacy, click Accessibility, then select Ferrum's checkbox`, 35 35 buttons: ['Done', 'Cancel'], ··· 41 41 } 42 42 } 43 43 44 - async function getTrustedAccessibilityMacOS(mainWindow: BrowserWindow) { 45 - const result = await dialog.showMessageBox(mainWindow, { 44 + async function get_trusted_accesibility_macos(main_window: BrowserWindow) { 45 + const result = await dialog.showMessageBox(main_window, { 46 46 type: 'info', 47 47 message: 'Ferrum needs accessibility permissions to exclusively take over the media keys', 48 48 buttons: ['Continue', 'Ignore'], ··· 52 52 setTimeout(() => { 53 53 systemPreferences.isTrustedAccessibilityClient(true) // prompt 54 54 }, 500) 55 - await requestLoop(mainWindow) 56 - tryRegistering(mainWindow) 55 + await request_loop(main_window) 56 + try_registering(main_window) 57 57 } 58 58 } 59 59 60 - export async function initMediaKeys(mainWindow: BrowserWindow) { 61 - const success = tryRegistering(mainWindow) 60 + export async function init_media_keys(main_window: BrowserWindow) { 61 + const success = try_registering(main_window) 62 62 if (!success) { 63 63 if (is.mac) { 64 64 if (systemPreferences.isTrustedAccessibilityClient(false)) { 65 - await dialog.showMessageBox(mainWindow, { 65 + await dialog.showMessageBox(main_window, { 66 66 type: 'info', 67 67 message: 'Could not register media key shortcuts', 68 68 }) 69 69 } else { 70 - await getTrustedAccessibilityMacOS(mainWindow) 70 + await get_trusted_accesibility_macos(main_window) 71 71 } 72 72 } else { 73 - await dialog.showMessageBox(mainWindow, { 73 + await dialog.showMessageBox(main_window, { 74 74 type: 'info', 75 75 message: 'Could not register media key shortcuts', 76 76 })
+1 -1
src/electron/typed_ipc.ts
··· 164 164 export type IpcRenderer = TypedIpcRenderer<Events, Commands> 165 165 export type WebContents = TypedWebContents<Events> 166 166 167 - export const ipcMain = electronIpcMain as IpcMain 167 + export const ipc_main = electronIpcMain as IpcMain 168 168 169 169 interface TypedIpcFunctions<IpcEvents extends InputMap> extends ElectronIpcRenderer { 170 170 ipcListen<K extends keyof IpcEvents>(
+84 -83
src/lib/data.ts
··· 1 1 import { writable } from 'svelte/store' 2 - import { ipcRenderer } from '@/lib/window' 2 + import { ipc_renderer } from '@/lib/window' 3 3 import type { 4 4 MsSinceUnixEpoch, 5 5 TrackID, ··· 11 11 import { selection as pageSelection } from './page' 12 12 import { queue } from './queue' 13 13 14 - export const isDev = window.isDev 15 - export const libraryPath = window.libraryPath 16 - export const isMac = window.isMac 17 - export const isWindows = window.isWindows 18 - const innerAddon = window.addon 19 - export const ItunesImport = innerAddon.ItunesImport 14 + export const is_dev = window.is_dev 15 + export const library_path = window.library_path 16 + export const is_mac = window.is_mac 17 + export const is_windws = window.is_windows 18 + const inner_addon = window.addon 19 + // eslint-disable-next-line @typescript-eslint/naming-convention 20 + export const ItunesImport = inner_addon.ItunesImport 20 21 21 - call((addon) => addon.load_data(isDev, libraryPath)) 22 + call((addon) => addon.load_data(is_dev, library_path)) 22 23 23 24 export const view_as_songs: ViewAs.Songs = 0 24 25 export const view_as_artists: ViewAs.Artists = 1 25 26 26 - function getErrorMessage(err: unknown): string { 27 + function get_error_message(err: unknown): string { 27 28 if (typeof err === 'object' && err !== null) { 28 29 const obj = err as { [key: string]: unknown } 29 30 if (obj.message) { ··· 34 35 } 35 36 return 'No reason or code provided' 36 37 } 37 - function getErrorStack(err: unknown): string { 38 + function get_error_stack(err: unknown): string { 38 39 if (typeof err === 'object' && err !== null) { 39 40 const obj = err as { [key: string]: unknown } 40 41 if (obj.stack) { ··· 43 44 } 44 45 return '' 45 46 } 46 - function errorPopup(err: unknown) { 47 - ipcRenderer.invoke('showMessageBox', false, { 47 + function error_popup(err: unknown) { 48 + ipc_renderer.invoke('showMessageBox', false, { 48 49 type: 'error', 49 - message: getErrorMessage(err), 50 - detail: getErrorStack(err), 50 + message: get_error_message(err), 51 + detail: get_error_stack(err), 51 52 }) 52 53 } 53 54 54 - export function call<T, P extends T | Promise<T>>(cb: (addon: typeof innerAddon) => P): P { 55 + export function call<T, P extends T | Promise<T>>(cb: (addon: typeof inner_addon) => P): P { 55 56 try { 56 - const result = cb(innerAddon) 57 + const result = cb(inner_addon) 57 58 if (result instanceof Promise) { 58 59 return result.catch((err) => { 59 - errorPopup(err) 60 + error_popup(err) 60 61 throw err 61 62 }) as P 62 63 } else { ··· 65 66 } catch (err) { 66 67 console.log('errorPopup') 67 68 68 - errorPopup(err) 69 + error_popup(err) 69 70 throw err 70 71 } 71 72 } 72 73 73 - export const trackListsDetailsMap = (() => { 74 + export const track_lists_details_map = (() => { 74 75 const initial = call((addon) => addon.get_track_lists_details()) 75 76 76 77 const { subscribe, set } = writable(initial) ··· 81 82 }, 82 83 } 83 84 })() 84 - export async function addTracksToPlaylist( 85 - playlistId: TrackListID, 86 - trackIds: TrackID[], 87 - checkDuplicates = true, 85 + export async function add_track_to_playlist( 86 + playlist_id: TrackListID, 87 + track_ids: TrackID[], 88 + check_duplicates = true, 88 89 ) { 89 - if (checkDuplicates) { 90 - const filteredIds = call((addon) => addon.playlist_filter_duplicates(playlistId, trackIds)) 91 - const duplicates = trackIds.length - filteredIds.length 90 + if (check_duplicates) { 91 + const filtered_ids = call((addon) => addon.playlist_filter_duplicates(playlist_id, track_ids)) 92 + const duplicates = track_ids.length - filtered_ids.length 92 93 93 94 if (duplicates > 0) { 94 - const result = await ipcRenderer.invoke('showMessageBox', false, { 95 + const result = await ipc_renderer.invoke('showMessageBox', false, { 95 96 type: 'question', 96 97 message: 'Already added', 97 98 detail: ··· 104 105 if (result.response === 1) { 105 106 return 106 107 } else if (result.response === 2) { 107 - trackIds = filteredIds 108 + track_ids = filtered_ids 108 109 } 109 110 } 110 111 } 111 - if (trackIds.length >= 1) { 112 - call((addon) => addon.add_tracks_to_playlist(playlistId, trackIds)) 113 - if (page.get().tracklist.id === playlistId) { 114 - page.refreshIdsAndKeepSelection() 112 + if (track_ids.length >= 1) { 113 + call((addon) => addon.add_tracks_to_playlist(playlist_id, track_ids)) 114 + if (page.get().tracklist.id === playlist_id) { 115 + page.refresh_ids_and_keep_selection() 115 116 } 116 117 methods.save() 117 118 } 118 119 } 119 - export function removeFromOpenPlaylist(indexes: number[]) { 120 + export function remove_from_open_playlist(indexes: number[]) { 120 121 call((addon) => addon.remove_from_open_playlist(indexes)) 121 - page.refreshIdsAndKeepSelection() 122 + page.refresh_ids_and_keep_selection() 122 123 pageSelection.clear() 123 124 methods.save() 124 125 } 125 - export function deleteTracksInOpen(indexes: number[]) { 126 + export function delete_tracks_in_open(indexes: number[]) { 126 127 call((addon) => addon.delete_tracks_in_open(indexes)) 127 - page.refreshIdsAndKeepSelection() 128 + page.refresh_ids_and_keep_selection() 128 129 pageSelection.clear() 129 130 queue.removeDeleted() 130 131 methods.save() ··· 137 138 id: string 138 139 editMode: boolean 139 140 } 140 - export function newPlaylist(info: PlaylistInfo) { 141 + export function new_playlist(info: PlaylistInfo) { 141 142 call((addon) => addon.new_playlist(info.name, info.description, info.isFolder, info.id)) 142 - trackListsDetailsMap.refresh() 143 + track_lists_details_map.refresh() 143 144 methods.save() 144 145 } 145 - export function updatePlaylist(id: string, name: string, description: string) { 146 + export function update_playlist(id: string, name: string, description: string) { 146 147 call((addon) => addon.update_playlist(id, name, description)) 147 - trackListsDetailsMap.refresh() 148 - page.refreshIdsAndKeepSelection() 148 + track_lists_details_map.refresh() 149 + page.refresh_ids_and_keep_selection() 149 150 methods.save() 150 151 } 151 - export function movePlaylist( 152 + export function move_playlist( 152 153 id: TrackListID, 153 - fromParent: TrackListID, 154 - toParent: TrackListID, 155 - toIndex: number, 154 + from_parent: TrackListID, 155 + to_parent: TrackListID, 156 + to_index: number, 156 157 ) { 157 - call((addon) => addon.move_playlist(id, fromParent, toParent, toIndex)) 158 - trackListsDetailsMap.refresh() 158 + call((addon) => addon.move_playlist(id, from_parent, to_parent, to_index)) 159 + track_lists_details_map.refresh() 159 160 methods.save() 160 161 } 161 162 162 163 export const paths = call((addon) => addon.get_paths()) 163 164 164 - export async function importTracks(paths: string[]) { 165 - let errState = null 165 + export async function import_tracks(paths: string[]) { 166 + let err_state = null 166 167 const now = Date.now() 167 168 for (const path of paths) { 168 169 try { 169 - innerAddon.import_file(path, now) 170 + inner_addon.import_file(path, now) 170 171 } catch (err) { 171 - if (errState === 'skip') continue 172 - const result = await ipcRenderer.invoke('showMessageBox', false, { 172 + if (err_state === 'skip') continue 173 + const result = await ipc_renderer.invoke('showMessageBox', false, { 173 174 type: 'error', 174 175 message: 'Error importing track ' + path, 175 - detail: getErrorMessage(err), 176 - buttons: errState ? ['OK', 'Skip all errors'] : ['OK'], 176 + detail: get_error_message(err), 177 + buttons: err_state ? ['OK', 'Skip all errors'] : ['OK'], 177 178 defaultId: 0, 178 179 }) 179 - if (result.response === 1) errState = 'skip' 180 - else errState = 'skippable' 180 + if (result.response === 1) err_state = 'skip' 181 + else err_state = 'skippable' 181 182 } 182 183 } 183 - page.refreshIdsAndKeepSelection() 184 + page.refresh_ids_and_keep_selection() 184 185 pageSelection.clear() 185 186 methods.save() 186 187 } ··· 200 201 }, 201 202 deleteTrackList: (id: TrackListID) => { 202 203 call((data) => data.delete_track_list(id)) 203 - page.refreshIdsAndKeepSelection() 204 + page.refresh_ids_and_keep_selection() 204 205 pageSelection.clear() 205 - trackListsDetailsMap.refresh() 206 + track_lists_details_map.refresh() 206 207 methods.save() 207 208 }, 208 209 save: () => { ··· 210 211 }, 211 212 addPlay: (id: TrackID) => { 212 213 call((data) => data.add_play(id)) 213 - page.refreshIdsAndKeepSelection() 214 + page.refresh_ids_and_keep_selection() 214 215 methods.save() 215 216 }, 216 217 addSkip: (id: TrackID) => { 217 218 call((data) => data.add_skip(id)) 218 - page.refreshIdsAndKeepSelection() 219 + page.refresh_ids_and_keep_selection() 219 220 methods.save() 220 221 }, 221 - addPlayTime: (id: TrackID, startTime: MsSinceUnixEpoch, durationMs: number) => { 222 - call((data) => data.add_play_time(id, startTime, durationMs)) 223 - page.refreshIdsAndKeepSelection() 222 + addPlayTime: (id: TrackID, start_time: MsSinceUnixEpoch, duration_ms: number) => { 223 + call((data) => data.add_play_time(id, start_time, duration_ms)) 224 + page.refresh_ids_and_keep_selection() 224 225 methods.save() 225 226 }, 226 227 readCoverAsync(id: TrackID, index: number) { 227 - return innerAddon.read_cover_async(id, index).catch((error) => { 228 + return inner_addon.read_cover_async(id, index).catch((error) => { 228 229 console.log('Could not read cover', error) 229 230 throw error 230 231 }) 231 232 }, 232 233 updateTrackInfo: (id: TrackID, md: TrackMd) => { 233 234 call((data) => data.update_track_info(id, md)) 234 - trackMetadataUpdated.emit() 235 - page.refreshIdsAndKeepSelection() 235 + track_metadata_updated.emit() 236 + page.refresh_ids_and_keep_selection() 236 237 methods.save() 237 238 }, 238 239 loadTags: (id: TrackID) => { ··· 271 272 } 272 273 })() 273 274 274 - function createRefreshStore() { 275 + function create_refresh_store() { 275 276 const store = writable(0) 276 277 return { 277 278 subscribe: store.subscribe, ··· 280 281 }, 281 282 } 282 283 } 283 - export const trackMetadataUpdated = createRefreshStore() 284 + export const track_metadata_updated = create_refresh_store() 284 285 285 286 export const page = (() => { 286 287 function get() { 287 288 const info = call((addon) => addon.get_page_info()) 288 289 return info 289 290 } 290 - function refreshIdsAndKeepSelection() { 291 + function refresh_ids_and_keep_selection() { 291 292 call((addon) => addon.refresh_page()) 292 293 set(get()) 293 294 } ··· 298 299 get, 299 300 set, 300 301 update, 301 - refreshIdsAndKeepSelection, 302 - openPlaylist(id: string, view_as: ViewAs) { 302 + refresh_ids_and_keep_selection: refresh_ids_and_keep_selection, 303 + open_playlist(id: string, view_as: ViewAs) { 303 304 call((data) => data.open_playlist(id, view_as)) 304 - refreshIdsAndKeepSelection() 305 + refresh_ids_and_keep_selection() 305 306 pageSelection.clear() 306 307 filter.set('') 307 308 }, 308 - sortBy(key: string) { 309 + sort_by(key: string) { 309 310 call((addon) => addon.sort(key, true)) 310 - refreshIdsAndKeepSelection() 311 + refresh_ids_and_keep_selection() 311 312 pageSelection.clear() 312 313 }, 313 314 set_group_album_tracks(value: boolean) { 314 315 call((addon) => addon.set_group_album_tracks(value)) 315 - refreshIdsAndKeepSelection() 316 + refresh_ids_and_keep_selection() 316 317 pageSelection.clear() 317 318 }, 318 319 get_artists() { 319 320 return call((addon) => addon.get_artists()) 320 321 }, 321 - getTrack(index: number) { 322 + get_track(index: number) { 322 323 return call((addon) => addon.get_page_track(index)) 323 324 }, 324 - getTrackId(index: number) { 325 + get_track_id(index: number) { 325 326 return call((data) => data.get_page_track_id(index)) 326 327 }, 327 - getTrackIds() { 328 + get_track_ids() { 328 329 return call((data) => data.get_page_track_ids()) 329 330 }, 330 - moveTracks: (indexes: number[], toIndex: number) => { 331 - const newSelection = call((data) => data.move_tracks(indexes, toIndex)) 331 + move_tracks: (indexes: number[], to_index: number) => { 332 + const new_selection = call((data) => data.move_tracks(indexes, to_index)) 332 333 call((data) => data.refresh_page()) 333 - refreshIdsAndKeepSelection() 334 + refresh_ids_and_keep_selection() 334 335 pageSelection.clear() 335 - for (let i = newSelection.from; i <= newSelection.to; i++) { 336 + for (let i = new_selection.from; i <= new_selection.to; i++) { 336 337 pageSelection.add(i) 337 338 } 338 339 methods.save()
+3 -3
src/lib/drag-drop.ts
··· 1 1 type DraggedTracks = { 2 2 ids: string[] 3 - playlistIndexes?: number[] 4 - queueIndexes?: number[] 3 + playlist_indexes?: number[] 4 + queue_indexes?: number[] 5 5 } 6 6 type DraggedPlaylist = { 7 7 id: string 8 - fromFolder: string 8 + from_folder: string 9 9 level: number 10 10 } 11 11
+38 -38
src/lib/helpers.ts
··· 1 1 import { type Updater, type Writable, writable } from 'svelte/store' 2 2 import type { TrackListDetails } from '../../ferrum-addon' 3 3 4 - export function getDuration(dur: number) { 4 + export function get_duration(dur: number) { 5 5 dur = Math.round(dur) 6 6 const secs = dur % 60 7 - let secsText = String(secs) 8 - if (secs < 10) secsText = '0' + secs 7 + let secs_text = String(secs) 8 + if (secs < 10) secs_text = '0' + secs 9 9 const mins = (dur - secs) / 60 10 - return mins + ':' + secsText 10 + return mins + ':' + secs_text 11 11 } 12 12 13 - export function formatDate(timestamp: number) { 13 + export function format_date(timestamp: number) { 14 14 const date = new Date(timestamp) 15 15 const month = date.getMonth() + 1 16 - const monthText = (month < 10 ? '0' : '') + month 16 + const month_text = (month < 10 ? '0' : '') + month 17 17 const day = date.getDate() 18 - const dayText = (day < 10 ? '0' : '') + day 18 + const day_text = (day < 10 ? '0' : '') + day 19 19 const clock = date.toLocaleTimeString(undefined, { 20 20 hour: '2-digit', 21 21 minute: '2-digit', 22 22 }) 23 - return date.getFullYear() + '/' + monthText + '/' + dayText + ' ' + clock 23 + return date.getFullYear() + '/' + month_text + '/' + day_text + ' ' + clock 24 24 } 25 25 26 26 type ShortcutOptions = { 27 27 shift?: boolean 28 28 alt?: boolean 29 - cmdOrCtrl?: boolean 29 + cmd_or_ctrl?: boolean 30 30 } 31 - const isMac = navigator.userAgent.indexOf('Mac') != -1 31 + const is_mac = navigator.userAgent.indexOf('Mac') != -1 32 32 33 - function checkModifiers(e: KeyboardEvent | MouseEvent, options: ShortcutOptions) { 33 + function check_modifiers(e: KeyboardEvent | MouseEvent, options: ShortcutOptions) { 34 34 const target = { 35 35 shift: options.shift || false, 36 36 alt: options.alt || false, 37 - ctrl: (!isMac && options.cmdOrCtrl) || false, 38 - meta: (isMac && options.cmdOrCtrl) || false, 37 + ctrl: (!is_mac && options.cmd_or_ctrl) || false, 38 + meta: (is_mac && options.cmd_or_ctrl) || false, 39 39 } 40 40 41 41 const pressed = { ··· 45 45 meta: !!e.metaKey, 46 46 } 47 47 48 - const ignoreCtrl = isMac && e instanceof MouseEvent 48 + const ignore_ctrl = is_mac && e instanceof MouseEvent 49 49 50 50 return ( 51 51 pressed.shift === target.shift && 52 52 pressed.alt === target.alt && 53 - (pressed.ctrl === target.ctrl || ignoreCtrl) && 53 + (pressed.ctrl === target.ctrl || ignore_ctrl) && 54 54 pressed.meta === target.meta 55 55 ) 56 56 } 57 57 58 - export function checkShortcut(e: KeyboardEvent, key: string, options: ShortcutOptions = {}) { 58 + export function check_shortcut(e: KeyboardEvent, key: string, options: ShortcutOptions = {}) { 59 59 if (e.key.toUpperCase() !== key.toUpperCase()) return false 60 - return checkModifiers(e, options) 60 + return check_modifiers(e, options) 61 61 } 62 - export function checkMouseShortcut(e: MouseEvent, options: ShortcutOptions = {}) { 63 - return checkModifiers(e, options) 62 + export function check_mouse_shortcut(e: MouseEvent, options: ShortcutOptions = {}) { 63 + return check_modifiers(e, options) 64 64 } 65 65 66 66 export function clamp(min: number, max: number, value: number) { ··· 70 70 } 71 71 72 72 type FlattenedListMenuItem = { label: string; enabled: boolean; id: string } 73 - export function flattenChildLists( 74 - trackList: TrackListDetails, 75 - trackLists: Record<string, TrackListDetails>, 76 - indentPrefix: string, 73 + export function flatten_child_lists( 74 + track_list: TrackListDetails, 75 + track_lists: Record<string, TrackListDetails>, 76 + indent_prefix: string, 77 77 ) { 78 78 let flat: FlattenedListMenuItem[] = [] 79 - for (const childListId of trackList.children || []) { 80 - const childList = trackLists[childListId] 81 - if (childList.kind === 'folder') { 82 - const childFlat = flattenChildLists(childList, trackLists, indentPrefix + ' ') 79 + for (const child_list_id of track_list.children || []) { 80 + const child_list = track_lists[child_list_id] 81 + if (child_list.kind === 'folder') { 82 + const child_flat = flatten_child_lists(child_list, track_lists, indent_prefix + ' ') 83 83 flat.push({ 84 - label: indentPrefix + childList.name, 84 + label: indent_prefix + child_list.name, 85 85 enabled: false, 86 - id: childList.id, 86 + id: child_list.id, 87 87 }) 88 - flat = flat.concat(childFlat) 89 - } else if (childList.kind === 'playlist') { 88 + flat = flat.concat(child_flat) 89 + } else if (child_list.kind === 'playlist') { 90 90 flat.push({ 91 - label: indentPrefix + childList.name, 91 + label: indent_prefix + child_list.name, 92 92 enabled: true, 93 - id: childList.id, 93 + id: child_list.id, 94 94 }) 95 95 } 96 96 } ··· 98 98 } 99 99 100 100 type GetterWritable<T> = Writable<T> & { get(): T } 101 - export function getterWritable<T>(value: T): GetterWritable<T> { 101 + export function getter_writable<T>(value: T): GetterWritable<T> { 102 102 const { subscribe, set } = writable(value) 103 103 return { 104 104 subscribe: subscribe, 105 - set(newValue: T) { 106 - value = newValue 107 - set(newValue) 105 + set(new_value: T) { 106 + value = new_value 107 + set(new_value) 108 108 }, 109 109 update(updater: Updater<T>) { 110 110 value = updater(value) ··· 114 114 } 115 115 } 116 116 117 - export function assertUnreachable(x: never) { 117 + export function assert_unreachable(x: never) { 118 118 console.error('Unreachable reached', x) 119 119 }
+24 -24
src/lib/menus.ts
··· 1 1 import { 2 - addTracksToPlaylist, 2 + add_track_to_playlist, 3 3 methods, 4 4 paths, 5 - removeFromOpenPlaylist, 6 - trackListsDetailsMap, 5 + remove_from_open_playlist, 6 + track_lists_details_map, 7 7 } from '@/lib/data' 8 - import { flattenChildLists } from '@/lib/helpers' 9 - import { ipcRenderer } from '@/lib/window' 8 + import { flatten_child_lists } from '@/lib/helpers' 9 + import { ipc_renderer } from '@/lib/window' 10 10 import type { TrackID } from '../../ferrum-addon' 11 11 import { get } from 'svelte/store' 12 - import { appendToUserQueue, prependToUserQueue } from './queue' 12 + import { append_to_user_queue, prepend_to_user_queue } from './queue' 13 13 import type { ShowTrackMenuOptions } from '@/electron/typed_ipc' 14 14 15 - export async function showTrackMenu( 16 - allIds: string[], 17 - selectedIndexes: number[], 15 + export async function show_track_menu( 16 + all_id: string[], 17 + selected_indexes: number[], 18 18 playlist?: { editable: boolean }, 19 19 queue = false, 20 20 ) { 21 - const trackLists = get(trackListsDetailsMap) 22 - const flat = flattenChildLists(trackLists.root, trackLists, '') 21 + const track_lists = get(track_lists_details_map) 22 + const flat = flatten_child_lists(track_lists.root, track_lists, '') 23 23 const args: ShowTrackMenuOptions = { 24 - allIds, 25 - selectedIndexes, 24 + allIds: all_id, 25 + selectedIndexes: selected_indexes, 26 26 playlist, 27 27 queue, 28 28 lists: flat, 29 29 } 30 30 31 - await ipcRenderer.invoke('showTrackMenu', args) 31 + await ipc_renderer.invoke('showTrackMenu', args) 32 32 } 33 33 34 - ipcRenderer.on('context.Play Next', (e, ids: TrackID[]) => { 35 - prependToUserQueue(ids) 34 + ipc_renderer.on('context.Play Next', (e, ids: TrackID[]) => { 35 + prepend_to_user_queue(ids) 36 36 }) 37 - ipcRenderer.on('context.Add to Queue', (e, ids: TrackID[]) => { 38 - appendToUserQueue(ids) 37 + ipc_renderer.on('context.Add to Queue', (e, ids: TrackID[]) => { 38 + append_to_user_queue(ids) 39 39 }) 40 - ipcRenderer.on('context.Add to Playlist', (e, id: TrackID, trackIds: TrackID[]) => { 41 - addTracksToPlaylist(id, trackIds) 40 + ipc_renderer.on('context.Add to Playlist', (e, id: TrackID, track_ids: TrackID[]) => { 41 + add_track_to_playlist(id, track_ids) 42 42 }) 43 - ipcRenderer.on('context.revealTrackFile', (e, id: TrackID) => { 43 + ipc_renderer.on('context.revealTrackFile', (e, id: TrackID) => { 44 44 const track = methods.getTrack(id) 45 - ipcRenderer.invoke('revealTrackFile', paths.tracksDir, track.file) 45 + ipc_renderer.invoke('revealTrackFile', paths.tracksDir, track.file) 46 46 }) 47 - ipcRenderer.on('context.Remove from Playlist', (e, indexes: number[]) => { 48 - removeFromOpenPlaylist(indexes) 47 + ipc_renderer.on('context.Remove from Playlist', (e, indexes: number[]) => { 48 + remove_from_open_playlist(indexes) 49 49 })
+8 -8
src/lib/page.ts
··· 1 1 import { get } from 'svelte/store' 2 2 import { page } from './data' 3 - import { showTrackMenu } from './menus' 4 - import { newSelection } from './selection' 3 + import { show_track_menu } from './menus' 4 + import { new_selection } from './selection' 5 5 6 6 export const tracklist_actions = { 7 7 scroll_to_index(_index: number) {}, 8 8 focus() {}, 9 9 } 10 - export const selection = newSelection({ 11 - getItemCount() { 10 + export const selection = new_selection({ 11 + get_item_count() { 12 12 return get(page).length 13 13 }, 14 14 // scrollToItem: scroll_to_index, 15 - scrollToItem(i) { 15 + scroll_to_item(i) { 16 16 tracklist_actions.scroll_to_index?.(i) 17 17 }, 18 - async onContextMenu() { 18 + async on_context_menu() { 19 19 const indexes = selection.getSelectedIndexes() 20 - const ids = page.getTrackIds() 21 - await showTrackMenu(ids, indexes, { editable: get(page).tracklist.type === 'playlist' }) 20 + const ids = page.get_track_ids() 21 + await show_track_menu(ids, indexes, { editable: get(page).tracklist.type === 'playlist' }) 22 22 }, 23 23 })
+106 -106
src/lib/player.ts
··· 4 4 import quit from './quit' 5 5 import { methods, paths } from './data' 6 6 import type { Track, TrackID } from '../../ferrum-addon' 7 - import { ipcRenderer, joinPaths } from './window' 8 - import { queue, setNewQueue, next as queueNext, prev as queuePrev } from './queue' 7 + import { ipc_renderer, join_paths } from './window' 8 + import { queue, set_new_queue, next as queueNext, prev as queuePrev } from './queue' 9 9 10 10 const audio = new Audio() 11 - let isStopped = true 11 + let is_stopped = true 12 12 export const stopped = (() => { 13 13 const { subscribe, set } = writable(true) 14 14 return { 15 15 subscribe, 16 16 set: (value: boolean) => { 17 - isStopped = value 17 + is_stopped = value 18 18 set(value) 19 19 }, 20 20 } 21 21 })() 22 - export const timeRecord = writable({ 22 + export const time_record = writable({ 23 23 elapsed: 0, 24 24 at_timestamp: Date.now(), 25 25 paused: true, 26 26 duration: 0, 27 27 }) 28 - function updateTimeDetails() { 29 - timeRecord.update((record) => { 28 + function update_time_details() { 29 + time_record.update((record) => { 30 30 record.elapsed = audio.currentTime 31 31 record.at_timestamp = Date.now() 32 32 record.paused = audio.paused ··· 34 34 return record 35 35 }) 36 36 } 37 - export const playingTrack: Writable<Track | null> = writable(null) 38 - export const playingId = derived(queue, () => { 39 - const currentId = queue.getCurrent()?.id 40 - if (currentId) { 41 - coverSrc.newFromTrackId(currentId) 37 + export const playing_track: Writable<Track | null> = writable(null) 38 + export const playing_id = derived(queue, () => { 39 + const current_id = queue.getCurrent()?.id 40 + if (current_id) { 41 + cover_src.newFromTrackId(current_id) 42 42 } 43 - return currentId 43 + return current_id 44 44 }) 45 - let waitingToPlay = false 46 - const mediaSession = navigator.mediaSession 45 + let waiting_to_play = false 46 + const media_session = navigator.mediaSession 47 47 48 48 export const volume = (() => { 49 - let lastVolume = 1 49 + let last_volume = 1 50 50 const store = writable(1) 51 51 audio.addEventListener('volumechange', () => { 52 52 store.set(audio.volume) 53 53 }) 54 54 function set(value: number) { 55 - lastVolume = audio.volume 55 + last_volume = audio.volume 56 56 audio.volume = clamp(0, 1, value) 57 57 store.set(clamp(0, 1, value)) 58 58 } ··· 60 60 set, 61 61 toggle() { 62 62 if (audio.volume > 0) set(0) 63 - else set(lastVolume || 1) 63 + else set(last_volume || 1) 64 64 }, 65 65 subscribe: store.subscribe, 66 66 } 67 67 })() 68 - ipcRenderer.on('volumeUp', () => { 68 + ipc_renderer.on('volumeUp', () => { 69 69 volume.set(audio.volume + 0.05) 70 70 }) 71 - ipcRenderer.on('volumeDown', () => { 71 + ipc_renderer.on('volumeDown', () => { 72 72 volume.set(audio.volume - 0.05) 73 73 }) 74 74 75 - export const coverSrc = (() => { 75 + export const cover_src = (() => { 76 76 const { set, subscribe }: Writable<string | null> = writable(null) 77 77 return { 78 78 async newFromTrackId(id: TrackID) { ··· 88 88 } 89 89 })() 90 90 91 - audio.onplay = updateTimeDetails 92 - audio.onpause = updateTimeDetails 93 - audio.ontimeupdate = updateTimeDetails 91 + audio.onplay = update_time_details 92 + audio.onpause = update_time_details 93 + audio.ontimeupdate = update_time_details 94 94 95 95 audio.addEventListener('error', async (e) => { 96 96 stop() ··· 106 106 } 107 107 } 108 108 } 109 - await ipcRenderer.invoke('showMessageBox', false, { type: 'error', message, detail }) 109 + await ipc_renderer.invoke('showMessageBox', false, { type: 'error', message, detail }) 110 110 }) 111 111 112 - let startTime = Date.now() 113 - function getPlayTime() { 114 - return Date.now() - (startTime ?? Date.now()) 112 + let start_time = Date.now() 113 + function set_play_time() { 114 + return Date.now() - (start_time ?? Date.now()) 115 115 } 116 116 117 - function startPlayback() { 117 + function start_playback() { 118 118 audio.play() 119 - updateTimeDetails() 120 - startTime = Date.now() 121 - if (mediaSession) mediaSession.playbackState = 'playing' 119 + update_time_details() 120 + start_time = Date.now() 121 + if (media_session) media_session.playbackState = 'playing' 122 122 } 123 123 124 - function setPlayingFile(id: TrackID, paused = false) { 124 + function set_playing_file(id: TrackID, paused = false) { 125 125 const track = methods.getTrack(id) 126 - const fileUrl = 'track:' + joinPaths(paths.tracksDir, track.file) 127 - waitingToPlay = !paused 128 - audio.src = fileUrl 129 - playingTrack.set(track) 130 - if (mediaSession) { 131 - mediaSession.metadata = new MediaMetadata({ 126 + const file_url = 'track:' + join_paths(paths.tracksDir, track.file) 127 + waiting_to_play = !paused 128 + audio.src = file_url 129 + playing_track.set(track) 130 + if (media_session) { 131 + media_session.metadata = new MediaMetadata({ 132 132 title: track.name, 133 133 artist: track.artist, 134 134 album: track.albumName || '', ··· 138 138 } 139 139 140 140 audio.oncanplay = () => { 141 - if (waitingToPlay) { 142 - waitingToPlay = false 143 - startPlayback() 144 - startTime = Date.now() 141 + if (waiting_to_play) { 142 + waiting_to_play = false 143 + start_playback() 144 + start_time = Date.now() 145 145 stopped.set(false) 146 146 } 147 147 } 148 148 149 - audio.ondurationchange = updateTimeDetails 149 + audio.ondurationchange = update_time_details 150 150 151 151 /** Saves play time if needed */ 152 - function resetAndSavePlayTime() { 153 - const currentId = queue.getCurrent()?.id 154 - if (getPlayTime() >= 1000 && currentId) { 155 - methods.addPlayTime(currentId, startTime, getPlayTime()) 152 + function reset_and_save_play_time() { 153 + const current_id = queue.getCurrent()?.id 154 + if (set_play_time() >= 1000 && current_id) { 155 + methods.addPlayTime(current_id, start_time, set_play_time()) 156 156 } 157 - startTime = Date.now() 157 + start_time = Date.now() 158 158 } 159 159 160 - function pausePlayback() { 161 - waitingToPlay = false 160 + function pause_playback() { 161 + waiting_to_play = false 162 162 audio.pause() 163 - updateTimeDetails() 164 - resetAndSavePlayTime() 165 - if (mediaSession) mediaSession.playbackState = 'paused' 163 + update_time_details() 164 + reset_and_save_play_time() 165 + if (media_session) media_session.playbackState = 'paused' 166 166 } 167 167 168 - export function newPlaybackInstance(newQueue: TrackID[], index: number) { 169 - if (!isStopped) pausePlayback() 170 - setNewQueue(newQueue, index) 168 + export function new_playback_instance(new_queue: TrackID[], index: number) { 169 + if (!is_stopped) pause_playback() 170 + set_new_queue(new_queue, index) 171 171 const current = queue.getCurrent() 172 172 if (current) { 173 - setPlayingFile(current.id) 173 + set_playing_file(current.id) 174 174 } 175 175 } 176 176 177 - export function playPause() { 178 - if (isStopped) return 179 - else if (audio.paused) startPlayback() 180 - else pausePlayback() 177 + export function play_pause() { 178 + if (is_stopped) return 179 + else if (audio.paused) start_playback() 180 + else pause_playback() 181 181 } 182 182 183 183 export function reload() { 184 184 const id = queue.getCurrent()?.id 185 - const wasPaused = audio.paused 186 - if (id && !isStopped) { 187 - const currentTime = audio.currentTime 185 + const was_paused = audio.paused 186 + if (id && !is_stopped) { 187 + const current_time = audio.currentTime 188 188 audio.src = '' 189 189 audio.load() 190 - setPlayingFile(id, wasPaused) 191 - audio.currentTime = currentTime 190 + set_playing_file(id, was_paused) 191 + audio.currentTime = current_time 192 192 } 193 193 } 194 194 195 195 export function stop() { 196 - waitingToPlay = false 196 + waiting_to_play = false 197 197 audio.pause() 198 - updateTimeDetails() 199 - resetAndSavePlayTime() 198 + update_time_details() 199 + reset_and_save_play_time() 200 200 stopped.set(true) 201 201 seek(0) 202 - if (mediaSession) { 203 - mediaSession.playbackState = 'none' 204 - mediaSession.metadata = null 202 + if (media_session) { 203 + media_session.playbackState = 'none' 204 + media_session.metadata = null 205 205 } 206 206 } 207 207 ··· 213 213 next(false) 214 214 } 215 215 216 - export function skipToNext() { 216 + export function skip_to_next() { 217 217 next(true) 218 218 } 219 219 220 220 function next(skip: boolean) { 221 - const currentId = queue.getCurrent()?.id 222 - if (currentId) { 221 + const current_id = queue.getCurrent()?.id 222 + if (current_id) { 223 223 if (skip) { 224 - methods.addSkip(currentId) 224 + methods.addSkip(current_id) 225 225 } else { 226 - methods.addPlay(currentId) 226 + methods.addPlay(current_id) 227 227 } 228 - resetAndSavePlayTime() 228 + reset_and_save_play_time() 229 229 queueNext() 230 - const newCurrentId = queue.getCurrent()?.id 231 - if (newCurrentId) { 232 - setPlayingFile(newCurrentId) 230 + const new_current_id = queue.getCurrent()?.id 231 + if (new_current_id) { 232 + set_playing_file(new_current_id) 233 233 } else { 234 234 stop() 235 235 } 236 236 } 237 237 } 238 238 export function previous() { 239 - const currentId = queue.getCurrent()?.id 240 - if (currentId) { 241 - resetAndSavePlayTime() 239 + const current_id = queue.getCurrent()?.id 240 + if (current_id) { 241 + reset_and_save_play_time() 242 242 queuePrev() 243 - const newCurrentId = queue.getCurrent()?.id 244 - if (newCurrentId) { 245 - setPlayingFile(newCurrentId) 243 + const new_current_id = queue.getCurrent()?.id 244 + if (new_current_id) { 245 + set_playing_file(new_current_id) 246 246 } else { 247 247 // this should never happen because the first track will play again 248 248 stop() ··· 250 250 } 251 251 } 252 252 253 - export function seek(to: number, fastSeek = false) { 254 - const newTime = Math.min(to, audio.duration || 0) 255 - if (fastSeek && audio.fastSeek) { 253 + export function seek(to: number, fast_seek = false) { 254 + const new_time = Math.min(to, audio.duration || 0) 255 + if (fast_seek && audio.fastSeek) { 256 256 audio.fastSeek(to) 257 257 } else { 258 - audio.currentTime = newTime 258 + audio.currentTime = new_time 259 259 } 260 - updateTimeDetails() 260 + update_time_details() 261 261 } 262 262 263 263 if (navigator.mediaSession) { 264 - const mediaSession = navigator.mediaSession 265 - mediaSession.setActionHandler('play', startPlayback) 266 - mediaSession.setActionHandler('pause', pausePlayback) 267 - mediaSession.setActionHandler('stop', stop) 268 - mediaSession.setActionHandler('seekbackward', (details) => { 264 + const media_sesison = navigator.mediaSession 265 + media_sesison.setActionHandler('play', start_playback) 266 + media_sesison.setActionHandler('pause', pause_playback) 267 + media_sesison.setActionHandler('stop', stop) 268 + media_sesison.setActionHandler('seekbackward', (details) => { 269 269 seek(audio.currentTime - (details.seekOffset || 5)) 270 270 }) 271 - mediaSession.setActionHandler('seekforward', (details) => { 271 + media_sesison.setActionHandler('seekforward', (details) => { 272 272 seek(audio.currentTime + (details.seekOffset || 5)) 273 273 }) 274 - mediaSession.setActionHandler('seekto', (details) => { 274 + media_sesison.setActionHandler('seekto', (details) => { 275 275 if (details.seekTime && details.fastSeek) { 276 276 seek(details.seekTime, details.fastSeek) 277 277 } 278 278 }) 279 - mediaSession.setActionHandler('previoustrack', previous) 280 - mediaSession.setActionHandler('nexttrack', skipToNext) 279 + media_sesison.setActionHandler('previoustrack', previous) 280 + media_sesison.setActionHandler('nexttrack', skip_to_next) 281 281 } 282 282 283 - ipcRenderer.on('playPause', playPause) 284 - ipcRenderer.on('Next', skipToNext) 285 - ipcRenderer.on('Previous', previous) 286 - ipcRenderer.on('Stop', stop) 283 + ipc_renderer.on('playPause', play_pause) 284 + ipc_renderer.on('Next', skip_to_next) 285 + ipc_renderer.on('Previous', previous) 286 + ipc_renderer.on('Stop', stop)
+108 -108
src/lib/queue.ts
··· 1 1 import type { TrackID } from '../../ferrum-addon' 2 2 import { get, writable } from 'svelte/store' 3 3 import { methods } from './data' 4 - import { getterWritable } from './helpers' 5 - import { ipcRenderer } from './window' 4 + import { getter_writable } from './helpers' 5 + import { ipc_renderer } from './window' 6 6 7 - export const queueVisible = writable(false) 8 - export function toggleQueueVisibility() { 9 - queueVisible.update((v) => !v) 7 + export const queue_visible = writable(false) 8 + export function toggle_queue_visibility() { 9 + queue_visible.update((v) => !v) 10 10 } 11 11 12 12 export type QueueItem = { 13 13 qId: number 14 14 id: TrackID 15 - nonShufflePos?: number 15 + non_shuffle_pos?: number 16 16 } 17 - let lastQId = -1 18 - function newQueueItem(id: TrackID): QueueItem { 19 - lastQId++ 17 + let last_qid = -1 18 + function new_queue_item(id: TrackID): QueueItem { 19 + last_qid++ 20 20 return { 21 - qId: lastQId, 21 + qId: last_qid, 22 22 id, 23 23 } 24 24 } ··· 30 30 past: QueueItem[] 31 31 current: { 32 32 item: QueueItem 33 - fromAutoQueue: boolean 33 + from_auto_queue: boolean 34 34 } | null 35 - userQueue: QueueItem[] 36 - autoQueue: QueueItem[] 35 + user_queue: QueueItem[] 36 + auto_queue: QueueItem[] 37 37 } 38 38 export const queue = (() => { 39 - const store = getterWritable({ 39 + const store = getter_writable({ 40 40 past: [], 41 - userQueue: [], 41 + user_queue: [], 42 42 current: null, 43 - autoQueue: [], 43 + auto_queue: [], 44 44 } as Queue) 45 45 return { 46 46 subscribe: store.subscribe, 47 47 set: store.set, 48 48 update: store.update, 49 49 get: store.get, 50 - getCurrent, 51 - getQueueLength, 52 - getByQueueIndex, 53 - prependToUserQueue, 54 - appendToUserQueue, 55 - removeIndexes, 56 - removeDeleted, 50 + getCurrent: get_current, 51 + getQueueLength: get_queue_length, 52 + getByQueueIndex: get_by_queue_index, 53 + prependToUserQueue: prepend_to_user_queue, 54 + appendToUserQueue: append_to_user_queue, 55 + removeIndexes: remove_indexes, 56 + removeDeleted: remove_deleted, 57 57 } 58 58 })() 59 59 60 60 /** Fisher-Yates shuffle */ 61 - function shuffleArray<T>(array: Array<T>) { 62 - let currentIndex = array.length, 63 - randomIndex 61 + function shuffle_array<T>(array: Array<T>) { 62 + let current_index = array.length, 63 + random_index 64 64 65 65 // While there remain elements to shuffle. 66 - while (currentIndex != 0) { 66 + while (current_index != 0) { 67 67 // Pick a remaining element. 68 - randomIndex = Math.floor(Math.random() * currentIndex) 69 - currentIndex-- 68 + random_index = Math.floor(Math.random() * current_index) 69 + current_index-- 70 70 71 71 // And swap it with the current element. 72 - ;[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]] 72 + ;[array[current_index], array[random_index]] = [array[random_index], array[current_index]] 73 73 } 74 74 75 75 return array 76 76 } 77 77 78 78 export const shuffle = writable(false) 79 - function applyShuffle(shuffle: boolean) { 79 + function apply_shuffle(shuffle: boolean) { 80 80 queue.update((q) => { 81 81 if (shuffle) { 82 - for (let i = 0; i < q.autoQueue.length; i++) { 83 - q.autoQueue[i].nonShufflePos = i 82 + for (let i = 0; i < q.auto_queue.length; i++) { 83 + q.auto_queue[i].non_shuffle_pos = i 84 84 } 85 - q.autoQueue = shuffleArray(q.autoQueue) 85 + q.auto_queue = shuffle_array(q.auto_queue) 86 86 return q 87 87 } else { 88 - const oldItems: Partial<QueueItem[]> = [] 89 - const newItems: QueueItem[] = [] 90 - for (const item of q.autoQueue) { 91 - if (item.nonShufflePos !== undefined) { 92 - oldItems[item.nonShufflePos] = item 88 + const old_items: Partial<QueueItem[]> = [] 89 + const new_items: QueueItem[] = [] 90 + for (const item of q.auto_queue) { 91 + if (item.non_shuffle_pos !== undefined) { 92 + old_items[item.non_shuffle_pos] = item 93 93 } else { 94 - newItems.push(item) 94 + new_items.push(item) 95 95 } 96 96 } 97 - const oldItemsClean = oldItems.filter((i) => i !== undefined) as QueueItem[] 98 - q.autoQueue = newItems.concat(oldItemsClean) 97 + const old_items_clean = old_items.filter((i) => i !== undefined) as QueueItem[] 98 + q.auto_queue = new_items.concat(old_items_clean) 99 99 return q 100 100 } 101 101 }) 102 102 } 103 103 shuffle.subscribe(($shuffle) => { 104 - applyShuffle($shuffle) 105 - ipcRenderer.invoke('update:Shuffle', $shuffle) 104 + apply_shuffle($shuffle) 105 + ipc_renderer.invoke('update:Shuffle', $shuffle) 106 106 }) 107 - ipcRenderer.on('Shuffle', () => { 107 + ipc_renderer.on('Shuffle', () => { 108 108 shuffle.update((value) => !value) 109 109 }) 110 110 111 - export const repeat = getterWritable(false) 112 - ipcRenderer.on('Repeat', () => { 111 + export const repeat = getter_writable(false) 112 + ipc_renderer.on('Repeat', () => { 113 113 repeat.update((value) => !value) 114 114 }) 115 115 repeat.subscribe(($repeat) => { 116 - ipcRenderer.invoke('update:Repeat', $repeat) 116 + ipc_renderer.invoke('update:Repeat', $repeat) 117 117 }) 118 118 119 - export function getCurrent() { 119 + export function get_current() { 120 120 return queue.get().current?.item ?? null 121 121 } 122 - export function getQueueLength() { 123 - const { past, current, userQueue, autoQueue } = queue.get() 124 - return past.length + Number(!!current) + userQueue.length + autoQueue.length 122 + export function get_queue_length() { 123 + const { past, current, user_queue, auto_queue } = queue.get() 124 + return past.length + Number(!!current) + user_queue.length + auto_queue.length 125 125 } 126 - export function getByQueueIndex(index: number) { 127 - const { past, current, userQueue, autoQueue } = queue.get() 126 + export function get_by_queue_index(index: number) { 127 + const { past, current, user_queue, auto_queue } = queue.get() 128 128 if (index < past.length) { 129 129 return past[index] 130 130 } ··· 133 133 return current.item 134 134 } 135 135 index -= Number(!!current) 136 - if (index < userQueue.length) { 137 - return userQueue[index] 136 + if (index < user_queue.length) { 137 + return user_queue[index] 138 138 } 139 - index -= userQueue.length 140 - return autoQueue[index] 139 + index -= user_queue.length 140 + return auto_queue[index] 141 141 } 142 142 143 - export function prependToUserQueue(trackIds: TrackID[]) { 144 - const items = trackIds.map(newQueueItem) 143 + export function prepend_to_user_queue(track_ids: TrackID[]) { 144 + const items = track_ids.map(new_queue_item) 145 145 queue.update((q) => { 146 - q.userQueue.unshift(...items) 146 + q.user_queue.unshift(...items) 147 147 return q 148 148 }) 149 149 } 150 - export function appendToUserQueue(trackIds: TrackID[]) { 151 - const items = trackIds.map(newQueueItem) 150 + export function append_to_user_queue(track_ids: TrackID[]) { 151 + const items = track_ids.map(new_queue_item) 152 152 queue.update((q) => { 153 - q.userQueue.push(...items) 153 + q.user_queue.push(...items) 154 154 return q 155 155 }) 156 156 } 157 157 158 - export function moveIndexes(indexes: number[], newIndex: number, to_user_queue: boolean) { 158 + export function move_indexes(indexes: number[], new_index: number, to_user_queue: boolean) { 159 159 const items: QueueItem[] = [] 160 160 queue.update((q) => { 161 161 // Sort descending. We need to remove the last indexes first to not mess up the indexes 162 162 for (const index of indexes.sort((a, b) => b - a)) { 163 - const removed_item = removeIndex(q, index) 163 + const removed_item = remove_index(q, index) 164 164 if (removed_item) { 165 165 items.push(removed_item) 166 - if (index < newIndex) { 167 - newIndex-- 166 + if (index < new_index) { 167 + new_index-- 168 168 } 169 169 } else { 170 - items.push(newQueueItem(getByQueueIndex(index).id)) 170 + items.push(new_queue_item(get_by_queue_index(index).id)) 171 171 } 172 172 } 173 173 return q 174 174 }) 175 175 // We sorted the indexes descending, so now reverse them 176 - return insertItems(items.reverse(), newIndex, to_user_queue) 176 + return insert_items(items.reverse(), new_index, to_user_queue) 177 177 } 178 178 179 - function insertItems(items: QueueItem[], index: number, to_user_queue: boolean) { 179 + function insert_items(items: QueueItem[], index: number, to_user_queue: boolean) { 180 180 queue.update((q) => { 181 181 const user_queue_index = q.past.length + Number(!!q.current) 182 182 index -= user_queue_index 183 183 184 - if (index < q.userQueue.length || to_user_queue) { 185 - q.userQueue.splice(index, 0, ...items) 184 + if (index < q.user_queue.length || to_user_queue) { 185 + q.user_queue.splice(index, 0, ...items) 186 186 return q 187 187 } 188 - index -= q.userQueue.length 189 - q.autoQueue.splice(index, 0, ...items) 188 + index -= q.user_queue.length 189 + q.auto_queue.splice(index, 0, ...items) 190 190 return q 191 191 }) 192 192 return { ··· 195 195 } 196 196 } 197 197 198 - export function insertIds(ids: TrackID[], index: number, bias_top = false) { 199 - return insertItems(ids.map(newQueueItem), index, bias_top) 198 + export function insert_ids(ids: TrackID[], index: number, bias_top = false) { 199 + return insert_items(ids.map(new_queue_item), index, bias_top) 200 200 } 201 201 202 - function removeIndex(q: Queue, index: number): QueueItem | null { 202 + function remove_index(q: Queue, index: number): QueueItem | null { 203 203 const up_next_index = q.past.length + Number(!!q.current) 204 204 if (index < up_next_index) { 205 205 return null 206 206 } 207 207 index -= up_next_index 208 - if (index < q.userQueue.length) { 209 - const [removed] = q.userQueue.splice(index, 1) 208 + if (index < q.user_queue.length) { 209 + const [removed] = q.user_queue.splice(index, 1) 210 210 return removed 211 211 } 212 - index -= q.userQueue.length 213 - const [removed] = q.autoQueue.splice(index, 1) 212 + index -= q.user_queue.length 213 + const [removed] = q.auto_queue.splice(index, 1) 214 214 return removed 215 215 } 216 216 217 - export function removeIndexes(indexes: number[]) { 217 + export function remove_indexes(indexes: number[]) { 218 218 queue.update((q) => { 219 219 // we need to remove the last indexes first to not mess up the indexes 220 220 for (const index of indexes.sort((a, b) => a - b).reverse()) { 221 - removeIndex(q, index) 221 + remove_index(q, index) 222 222 } 223 223 return q 224 224 }) 225 225 } 226 - export function removeDeleted() { 226 + export function remove_deleted() { 227 227 const q = queue.get() 228 228 const past = q.past.filter((qi) => methods.trackExists(qi.id)) 229 229 const current = q.current && methods.trackExists(q.current.item.id) ? q.current : null 230 - const userQueue = q.userQueue.filter((qi) => methods.trackExists(qi.id)) 231 - const autoQueue = q.autoQueue.filter((qi) => methods.trackExists(qi.id)) 230 + const user_queue = q.user_queue.filter((qi) => methods.trackExists(qi.id)) 231 + const auto_queue = q.auto_queue.filter((qi) => methods.trackExists(qi.id)) 232 232 if ( 233 233 past.length !== q.past.length || 234 234 current !== null || 235 - userQueue.length !== q.userQueue.length || 236 - autoQueue.length !== q.autoQueue.length 235 + user_queue.length !== q.user_queue.length || 236 + auto_queue.length !== q.auto_queue.length 237 237 ) { 238 - queue.set({ past, current, userQueue, autoQueue }) 238 + queue.set({ past, current, user_queue, auto_queue }) 239 239 } 240 240 } 241 241 ··· 244 244 if (q.current) { 245 245 q.past.push(q.current.item) 246 246 } 247 - if (q.userQueue.length) { 247 + if (q.user_queue.length) { 248 248 q.current = { 249 - item: q.userQueue.shift()!, 250 - fromAutoQueue: false, 249 + item: q.user_queue.shift()!, 250 + from_auto_queue: false, 251 251 } 252 - } else if (q.autoQueue.length) { 252 + } else if (q.auto_queue.length) { 253 253 if (repeat.get() && q.current) { 254 - q.autoQueue.push(newQueueItem(q.current.item.id)) 254 + q.auto_queue.push(new_queue_item(q.current.item.id)) 255 255 } 256 256 q.current = { 257 - item: q.autoQueue.shift()!, 258 - fromAutoQueue: true, 257 + item: q.auto_queue.shift()!, 258 + from_auto_queue: true, 259 259 } 260 260 } else { 261 261 q.current = null ··· 266 266 const q = queue.get() 267 267 if (q.past.length) { 268 268 if (q.current) { 269 - q.userQueue.unshift(q.current.item) 269 + q.user_queue.unshift(q.current.item) 270 270 } 271 271 q.current = { 272 272 item: q.past.pop()!, 273 - fromAutoQueue: false, 273 + from_auto_queue: false, 274 274 } 275 275 queue.set(q) 276 276 } 277 277 } 278 278 279 279 // TODO: Preserve userQueue when setting a new queue. Before we do that, a clear user queue button would be nice 280 - export function setNewQueue(newIds: TrackID[], newCurrentIndex: number) { 281 - const autoQueue = newIds.splice(newCurrentIndex + 1) 282 - const current = newIds.pop() 280 + export function set_new_queue(new_ids: TrackID[], new_current_index: number) { 281 + const auto_queue = new_ids.splice(new_current_index + 1) 282 + const current = new_ids.pop() 283 283 queue.set({ 284 - past: newIds.map(newQueueItem), 284 + past: new_ids.map(new_queue_item), 285 285 current: current 286 286 ? { 287 - item: newQueueItem(current), 288 - fromAutoQueue: true, 287 + item: new_queue_item(current), 288 + from_auto_queue: true, 289 289 } 290 290 : null, 291 - userQueue: [], 292 - autoQueue: autoQueue.map(newQueueItem), 291 + user_queue: [], 292 + auto_queue: auto_queue.map(new_queue_item), 293 293 }) 294 294 queue.removeDeleted() 295 - applyShuffle(get(shuffle)) 295 + apply_shuffle(get(shuffle)) 296 296 }
+15 -15
src/lib/quit.ts
··· 1 - import { ipcRenderer } from './window' 1 + import { ipc_renderer } from './window' 2 2 3 - let unfinishedTasks = 0 4 - export function notReady() { 5 - unfinishedTasks++ 3 + let unfinished_tasks = 0 4 + export function not_ready() { 5 + unfinished_tasks++ 6 6 } 7 7 export function ready() { 8 - unfinishedTasks-- 9 - tryToQuit() 8 + unfinished_tasks-- 9 + try_to_quit() 10 10 } 11 11 12 12 type Callback = () => void | Promise<void> ··· 14 14 [key: string]: Callback 15 15 } 16 16 const handlers: Handlers = {} 17 - function setHandler(name: string, callback: Callback) { 17 + function set_handler(name: string, callback: Callback) { 18 18 handlers[name] = callback 19 19 } 20 20 21 - export default { notReady, ready, setHandler } 21 + export default { notReady: not_ready, ready, setHandler: set_handler } 22 22 23 - function tryToQuit() { 24 - if (unfinishedTasks === 0) { 25 - ipcRenderer.send('readyToQuit') 23 + function try_to_quit() { 24 + if (unfinished_tasks === 0) { 25 + ipc_renderer.send('readyToQuit') 26 26 } 27 27 } 28 28 29 - notReady() 30 - ipcRenderer.on('gonnaQuit', async function () { 29 + not_ready() 30 + ipc_renderer.on('gonnaQuit', async function () { 31 31 for (const key of Object.keys(handlers)) { 32 32 const handler = handlers[key] 33 - const asyncHandler = await Promise.resolve(handler) 34 - await asyncHandler() 33 + const async_handler = await Promise.resolve(handler) 34 + await async_handler() 35 35 } 36 36 ready() 37 37 })
+95 -95
src/lib/selection.ts
··· 1 - import { checkMouseShortcut, checkShortcut, getterWritable } from './helpers' 1 + import { check_mouse_shortcut, check_shortcut, getter_writable } from './helpers' 2 2 3 3 /** 4 4 * The selection object. ··· 20 20 selection.lastAdded = null 21 21 selection.shiftAnchor = null 22 22 } 23 - function addIndex(selection: Selection, index: number) { 23 + function add_index(selection: Selection, index: number) { 24 24 if (index < selection.minimumIndex) { 25 25 return 26 26 } ··· 30 30 } 31 31 selection.lastAdded = index 32 32 } 33 - function addRange(selection: Selection, from: number, to: number) { 33 + function add_range(selection: Selection, from: number, to: number) { 34 34 if (from < selection.minimumIndex) { 35 35 if (to < selection.minimumIndex) { 36 36 return ··· 42 42 } 43 43 if (from < to) { 44 44 for (let i = from; i <= to; i++) { 45 - addIndex(selection, i) 45 + add_index(selection, i) 46 46 } 47 47 } else { 48 48 for (let i = from; i >= to; i--) { 49 - addIndex(selection, i) 49 + add_index(selection, i) 50 50 } 51 51 } 52 52 } ··· 55 55 selection.list[index] = false 56 56 selection.count-- 57 57 } 58 - function removeRange(selection: Selection, from: number, to: number) { 58 + function remove_range(selection: Selection, from: number, to: number) { 59 59 if (from < to) { 60 60 for (let i = from; i <= to; i++) { 61 61 remove(selection, i) ··· 67 67 } 68 68 } 69 69 70 - function getShiftAnchor(selection: Selection) { 70 + function get_shift_anchor(selection: Selection) { 71 71 if (selection.shiftAnchor !== null) return selection.shiftAnchor 72 72 else return selection.lastAdded 73 73 } 74 74 75 - function selectTo(selection: Selection, toIndex: number) { 76 - const anchor = getShiftAnchor(selection) 77 - const lastAdded = selection.lastAdded 78 - if (lastAdded === null || anchor === null) { 75 + function select_to(selection: Selection, to_index: number) { 76 + const anchor = get_shift_anchor(selection) 77 + const last_added = selection.lastAdded 78 + if (last_added === null || anchor === null) { 79 79 return selection 80 80 } 81 - if (anchor < toIndex) { 82 - if (toIndex < lastAdded) { 81 + if (anchor < to_index) { 82 + if (to_index < last_added) { 83 83 // new shift selection is closer to anchor 84 - removeRange(selection, toIndex + 1, lastAdded) 85 - } else if (lastAdded < anchor) { 84 + remove_range(selection, to_index + 1, last_added) 85 + } else if (last_added < anchor) { 86 86 // new shift selection is on the other side of anchor 87 - removeRange(selection, anchor - 1, lastAdded) 88 - addRange(selection, anchor, toIndex) 87 + remove_range(selection, anchor - 1, last_added) 88 + add_range(selection, anchor, to_index) 89 89 } else { 90 - addRange(selection, lastAdded, toIndex) 90 + add_range(selection, last_added, to_index) 91 91 } 92 - selection.lastAdded = toIndex 92 + selection.lastAdded = to_index 93 93 } else { 94 - if (toIndex > lastAdded) { 94 + if (to_index > last_added) { 95 95 // new shift selection is closer to anchor 96 - removeRange(selection, toIndex - 1, lastAdded) 97 - } else if (lastAdded > anchor) { 96 + remove_range(selection, to_index - 1, last_added) 97 + } else if (last_added > anchor) { 98 98 // new shift selection is on the other side of anchor 99 - removeRange(selection, anchor + 1, lastAdded) 100 - addRange(selection, anchor, toIndex) 99 + remove_range(selection, anchor + 1, last_added) 100 + add_range(selection, anchor, to_index) 101 101 } else { 102 - addRange(selection, lastAdded, toIndex) 102 + add_range(selection, last_added, to_index) 103 103 } 104 - selection.lastAdded = toIndex 104 + selection.lastAdded = to_index 105 105 } 106 106 selection.shiftAnchor = anchor 107 107 } 108 108 109 109 type SelectOptions = { 110 - getItemCount: () => number 111 - scrollToItem: (index: number) => void 112 - onContextMenu: () => void 113 - minimumIndex?: number 110 + get_item_count: () => number 111 + scroll_to_item: (index: number) => void 112 + on_context_menu: () => void 113 + minimum_index?: number 114 114 } 115 - export function newSelection(options: SelectOptions) { 116 - const store = getterWritable<Selection>({ 115 + export function new_selection(options: SelectOptions) { 116 + const store = getter_writable<Selection>({ 117 117 list: [], 118 118 count: 0, 119 119 lastAdded: null, 120 120 shiftAnchor: null, 121 - minimumIndex: options.minimumIndex || 0, 121 + minimumIndex: options.minimum_index || 0, 122 122 }) 123 - let possibleRowClick = false 123 + let possible_row_click = false 124 124 125 - function mouseDownSelect(e: MouseEvent, index: number) { 126 - const isSelected = store.get().list[index] 127 - if (checkMouseShortcut(e) && !isSelected) { 125 + function mouse_down_select(e: MouseEvent, index: number) { 126 + const is_selected = store.get().list[index] 127 + if (check_mouse_shortcut(e) && !is_selected) { 128 128 selection.clear() 129 129 selection.add(index) 130 - } else if (checkMouseShortcut(e, { cmdOrCtrl: true }) && !isSelected) { 130 + } else if (check_mouse_shortcut(e, { cmd_or_ctrl: true }) && !is_selected) { 131 131 selection.add(index) 132 - } else if (checkMouseShortcut(e, { shift: true })) { 132 + } else if (check_mouse_shortcut(e, { shift: true })) { 133 133 selection.shiftSelectTo(index) 134 134 } 135 135 } ··· 149 149 store.update((selection) => { 150 150 selection.minimumIndex = index 151 151 if (index > 0) { 152 - removeRange(selection, 0, index - 1) 152 + remove_range(selection, 0, index - 1) 153 153 } 154 154 return selection 155 155 }) ··· 165 165 return indexes 166 166 }, 167 167 /** Add an index to the selection */ 168 - add(fromIndex: number, toIndex?: number) { 168 + add(from_index: number, to_index?: number) { 169 169 store.update((selection) => { 170 - if (toIndex === undefined) { 171 - addIndex(selection, fromIndex) 170 + if (to_index === undefined) { 171 + add_index(selection, from_index) 172 172 } else { 173 - addRange(selection, fromIndex, toIndex) 173 + add_range(selection, from_index, to_index) 174 174 } 175 175 selection.shiftAnchor = null 176 176 return selection ··· 180 180 * Replace selection with the previous index. 181 181 * - `maxIndex`: The max index that can be selected. 182 182 */ 183 - goBackward(maxIndex: number) { 183 + goBackward(max_index: number) { 184 184 store.update((selection) => { 185 185 if (selection.count === 0) { 186 - addIndex(selection, maxIndex) 186 + add_index(selection, max_index) 187 187 } else if (selection.lastAdded !== null) { 188 - const newIndex = selection.lastAdded - 1 188 + const new_index = selection.lastAdded - 1 189 189 clear(selection) 190 - addIndex(selection, Math.max(selection.minimumIndex, newIndex)) 190 + add_index(selection, Math.max(selection.minimumIndex, new_index)) 191 191 } 192 192 return selection 193 193 }) ··· 196 196 * Replace selection with the next index. 197 197 * - `maxIndex`: The max index that can be selected. 198 198 */ 199 - goForward(maxIndex: number) { 199 + goForward(max_index: number) { 200 200 store.update((selection) => { 201 201 if (selection.count === 0) { 202 - addIndex(selection, selection.minimumIndex) 202 + add_index(selection, selection.minimumIndex) 203 203 } else if (selection.lastAdded !== null) { 204 - const newIndex = selection.lastAdded + 1 204 + const new_index = selection.lastAdded + 1 205 205 clear(selection) 206 - addIndex(selection, Math.min(newIndex, maxIndex)) 206 + add_index(selection, Math.min(new_index, max_index)) 207 207 } 208 208 return selection 209 209 }) ··· 211 211 /** Expand or shrink selection backwards (shift+up) */ 212 212 shiftSelectBackward() { 213 213 store.update((selection) => { 214 - const anchor = getShiftAnchor(selection) 214 + const anchor = get_shift_anchor(selection) 215 215 selection.shiftAnchor = anchor 216 216 if (anchor === null || selection.lastAdded === null) { 217 217 return selection ··· 220 220 // add prev to selection 221 221 for (let i = selection.lastAdded; i >= selection.minimumIndex; i--) { 222 222 if (selection.list[i] !== true) { 223 - addIndex(selection, i) 223 + add_index(selection, i) 224 224 return selection 225 225 } 226 226 } ··· 236 236 * Expand or shrink selection forwards (shift+down). 237 237 * - `maxIndex`: The maximum index to expand to 238 238 */ 239 - shiftSelectForward(maxIndex: number) { 239 + shiftSelectForward(max_index: number) { 240 240 store.update((selection) => { 241 - const anchor = getShiftAnchor(selection) 241 + const anchor = get_shift_anchor(selection) 242 242 selection.shiftAnchor = anchor 243 243 if (anchor === null || selection.lastAdded === null) { 244 244 return selection 245 245 } 246 246 if (selection.lastAdded >= anchor) { 247 247 // add next to selection 248 - for (let i = selection.lastAdded; i <= maxIndex; i++) { 248 + for (let i = selection.lastAdded; i <= max_index; i++) { 249 249 if (selection.list[i] !== true) { 250 - addIndex(selection, i) 250 + add_index(selection, i) 251 251 return selection 252 252 } 253 253 } ··· 264 264 * Selects in either upwards or downwards order. 265 265 * Selects only `toIndex` if there's no existing selection. 266 266 */ 267 - shiftSelectTo(toIndex: number) { 267 + shiftSelectTo(to_index: number) { 268 268 store.update((selection) => { 269 - selectTo(selection, toIndex) 269 + select_to(selection, to_index) 270 270 return selection 271 271 }) 272 272 }, ··· 278 278 } 279 279 remove(selection, index) 280 280 } else { 281 - addIndex(selection, index) 281 + add_index(selection, index) 282 282 } 283 283 selection.shiftAnchor = null 284 284 return selection ··· 296 296 return 297 297 } 298 298 if (store.get().list[index]) { 299 - possibleRowClick = true 299 + possible_row_click = true 300 300 } 301 - mouseDownSelect(e, index) 301 + mouse_down_select(e, index) 302 302 }, 303 303 handleContextMenu(e: MouseEvent, index: number) { 304 - mouseDownSelect(e, index) 305 - options.onContextMenu() 304 + mouse_down_select(e, index) 305 + options.on_context_menu() 306 306 }, 307 307 handleClick(e: MouseEvent, index: number) { 308 - if (possibleRowClick && e.button === 0) { 309 - if (checkMouseShortcut(e)) { 308 + if (possible_row_click && e.button === 0) { 309 + if (check_mouse_shortcut(e)) { 310 310 selection.clear() 311 311 selection.add(index) 312 - } else if (checkMouseShortcut(e, { cmdOrCtrl: true })) { 312 + } else if (check_mouse_shortcut(e, { cmd_or_ctrl: true })) { 313 313 selection.toggle(index) 314 314 } 315 315 } 316 - possibleRowClick = false 316 + possible_row_click = false 317 317 }, 318 318 handleKeyDown(e: KeyboardEvent) { 319 - const itemCount = options.getItemCount() 319 + const item_count = options.get_item_count() 320 320 const { minimumIndex } = store.get() 321 - if (itemCount === 0 || itemCount < minimumIndex) { 321 + if (item_count === 0 || item_count < minimumIndex) { 322 322 return 323 323 } 324 - if (checkShortcut(e, 'Escape')) { 324 + if (check_shortcut(e, 'Escape')) { 325 325 selection.clear() 326 - } else if (checkShortcut(e, 'A', { cmdOrCtrl: true })) { 327 - selection.add(minimumIndex, itemCount - 1) 328 - } else if (checkShortcut(e, 'ArrowUp')) { 329 - selection.goBackward(itemCount - 1) 330 - options.scrollToItem(store.get().lastAdded || minimumIndex) 331 - } else if (checkShortcut(e, 'ArrowUp', { shift: true })) { 326 + } else if (check_shortcut(e, 'A', { cmd_or_ctrl: true })) { 327 + selection.add(minimumIndex, item_count - 1) 328 + } else if (check_shortcut(e, 'ArrowUp')) { 329 + selection.goBackward(item_count - 1) 330 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 331 + } else if (check_shortcut(e, 'ArrowUp', { shift: true })) { 332 332 selection.shiftSelectBackward() 333 - options.scrollToItem(store.get().lastAdded || minimumIndex) 334 - } else if (checkShortcut(e, 'ArrowUp', { alt: true })) { 333 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 334 + } else if (check_shortcut(e, 'ArrowUp', { alt: true })) { 335 335 selection.clear() 336 336 selection.add(minimumIndex) 337 - options.scrollToItem(minimumIndex) 338 - } else if (checkShortcut(e, 'ArrowUp', { shift: true, alt: true })) { 337 + options.scroll_to_item(minimumIndex) 338 + } else if (check_shortcut(e, 'ArrowUp', { shift: true, alt: true })) { 339 339 selection.shiftSelectTo(minimumIndex) 340 - options.scrollToItem(store.get().lastAdded || minimumIndex) 341 - } else if (checkShortcut(e, 'ArrowDown')) { 342 - selection.goForward(itemCount - 1) 343 - options.scrollToItem(store.get().lastAdded || minimumIndex) 344 - } else if (checkShortcut(e, 'ArrowDown', { shift: true })) { 345 - selection.shiftSelectForward(itemCount - 1) 346 - options.scrollToItem(store.get().lastAdded || minimumIndex) 347 - } else if (checkShortcut(e, 'ArrowDown', { alt: true })) { 340 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 341 + } else if (check_shortcut(e, 'ArrowDown')) { 342 + selection.goForward(item_count - 1) 343 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 344 + } else if (check_shortcut(e, 'ArrowDown', { shift: true })) { 345 + selection.shiftSelectForward(item_count - 1) 346 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 347 + } else if (check_shortcut(e, 'ArrowDown', { alt: true })) { 348 348 selection.clear() 349 - selection.add(itemCount - 1) 350 - options.scrollToItem(store.get().lastAdded || minimumIndex) 351 - } else if (checkShortcut(e, 'ArrowDown', { shift: true, alt: true })) { 352 - selection.shiftSelectTo(itemCount - 1) 353 - options.scrollToItem(store.get().lastAdded || minimumIndex) 349 + selection.add(item_count - 1) 350 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 351 + } else if (check_shortcut(e, 'ArrowDown', { shift: true, alt: true })) { 352 + selection.shiftSelectTo(item_count - 1) 353 + options.scroll_to_item(store.get().lastAdded || minimumIndex) 354 354 } else { 355 355 return 356 356 }
+10 -10
src/lib/window.ts
··· 2 2 import type Addon from 'ferrum-addon/addon' 3 3 const electron = window.require('electron') 4 4 5 - export const ipcRenderer = electron.ipcRenderer as IpcRenderer 5 + export const ipc_renderer = electron.ipcRenderer as IpcRenderer 6 6 7 - export const ipcListen: IpcFunctions['ipcListen'] = (channel, listener) => { 8 - ipcRenderer.on(channel, listener) 7 + export const ipc_listen: IpcFunctions['ipcListen'] = (channel, listener) => { 8 + ipc_renderer.on(channel, listener) 9 9 return () => { 10 - ipcRenderer.removeListener(channel, listener) 10 + ipc_renderer.removeListener(channel, listener) 11 11 } 12 12 } 13 13 14 14 declare global { 15 15 interface Window { 16 16 addon: typeof Addon 17 - isDev: boolean 18 - libraryPath?: string 19 - isMac: boolean 20 - isWindows: boolean 21 - joinPaths: (...args: string[]) => string 17 + is_dev: boolean 18 + library_path?: string 19 + is_mac: boolean 20 + is_windows: boolean 21 + join_paths: (...args: string[]) => string 22 22 } 23 23 } 24 24 25 - export const joinPaths = window.joinPaths 25 + export const join_paths = window.join_paths