This commit is contained in:
Moon.eth
2026-07-29 12:44:15 +09:00
parent cd77720a4f
commit 4f682857ac
3 changed files with 82 additions and 0 deletions
+3
View File
@@ -6,6 +6,7 @@
import SiteHeader from '$components/chrome/SiteHeader.svelte'
import SiteNav from '$components/chrome/SiteNav.svelte'
import SiteFooter from '$components/chrome/SiteFooter.svelte'
import RefreshHotkey from '$components/common/RefreshHotkey.svelte'
import Home from '$routes/Home.svelte'
import Profile from '$routes/Profile.svelte'
import Timeline from '$routes/Timeline.svelte'
@@ -56,6 +57,8 @@
)
</script>
<RefreshHotkey />
<div class="site">
<SiteHeader />
<SiteNav />
@@ -0,0 +1,43 @@
<script lang="ts">
/**
* App-wide, deliberately invisible refresh shortcut.
*
* The action is injectable so this component can be tested without actually
* navigating, and editable controls are ignored so composing text is safe.
*/
interface Props {
onrefresh?: () => void
}
let { onrefresh = () => window.location.reload() }: Props = $props()
function isEditable(target: EventTarget | null): boolean {
if (!(target instanceof Element)) return false
return Boolean(
target.closest(
'input, textarea, select, [contenteditable]:not([contenteditable="false"])',
),
)
}
function handleKeydown(event: KeyboardEvent): void {
if (
event.defaultPrevented ||
event.repeat ||
event.isComposing ||
event.key.toLowerCase() !== 'r' ||
!event.shiftKey ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
isEditable(event.target)
) {
return
}
event.preventDefault()
onrefresh()
}
</script>
<svelte:window onkeydown={handleKeydown} />
@@ -0,0 +1,36 @@
import { fireEvent, render } from '@testing-library/svelte'
import { describe, expect, it, vi } from 'vitest'
import RefreshHotkey from './RefreshHotkey.svelte'
describe('RefreshHotkey', () => {
it('refreshes for Shift+R from anywhere on the page', async () => {
const onrefresh = vi.fn()
render(RefreshHotkey, { props: { onrefresh } })
const event = new KeyboardEvent('keydown', {
key: 'R',
shiftKey: true,
bubbles: true,
cancelable: true,
})
window.dispatchEvent(event)
expect(onrefresh).toHaveBeenCalledOnce()
expect(event.defaultPrevented).toBe(true)
})
it('ignores typing, repeats, and other modifier combinations', async () => {
const onrefresh = vi.fn()
render(RefreshHotkey, { props: { onrefresh } })
const input = document.createElement('input')
document.body.append(input)
await fireEvent.keyDown(input, { key: 'R', shiftKey: true })
await fireEvent.keyDown(window, { key: 'r' })
await fireEvent.keyDown(window, { key: 'R', shiftKey: true, repeat: true })
await fireEvent.keyDown(window, { key: 'R', shiftKey: true, ctrlKey: true })
await fireEvent.keyDown(window, { key: 'R', shiftKey: true, metaKey: true })
expect(onrefresh).not.toHaveBeenCalled()
})
})