Skip to content

Add allowedHosts configuration for dev server tunnel preview - #21

Merged
korpseluv merged 10 commits into
mainfrom
cutting-edge
Jan 2, 2026
Merged

Add allowedHosts configuration for dev server tunnel preview#21
korpseluv merged 10 commits into
mainfrom
cutting-edge

Conversation

@korpseluv

Copy link
Copy Markdown
Owner

Summary

/

Why

Why is this change needed?

Testing

  • npm run build
  • npm run typecheck
  • Manual check (screenshots optional)

Security / privacy

  • No secrets added
  • No new replay parsing/network behavior without opt-in
  • No new endpoints exposed in production unintentionally

Notes

Anything reviewers should know.

Copilot AI review requested due to automatic review settings January 2, 2026 14:01
@korpseluv
korpseluv merged commit bb984e2 into main Jan 2, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds configuration for allowed hosts in the Vite dev server to support tunnel-based preview environments. However, the PR includes several additional unrelated changes that extend beyond the stated purpose, including favicon updates, tab navigation with URL hash support, SVG-based graph interaction improvements, accessibility enhancements, and UI styling refinements.

Key Changes:

  • Added allowedHosts configuration to Vite server settings with environment variable support
  • Implemented URL hash-based tab navigation with browser history integration
  • Enhanced graph interaction with SVG coordinate space mapping for improved accuracy
  • Added keyboard accessibility and ARIA attributes to interactive score cards

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.

File Description
nuxt.config.ts Added Vite server allowedHosts configuration with environment variable fallback; updated favicon references from PNG to ICO format
app/app.vue Added URL hash-based tab navigation; improved SVG graph coordinate mapping; enhanced accessibility with keyboard support; refined badge and timeline styling; made fallback username configurable via runtime config
.env.example Added example environment variables for OSU_TARGET_USER and ALLOWED_HOSTS configuration
.github/PULL_REQUEST_TEMPLATE.md Added typecheck step to testing checklist

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/app.vue
Comment on lines +1174 to +1178
class="group relative overflow-hidden rounded-2xl border border-white/10 bg-zinc-900/40 p-4 backdrop-blur transition-all duration-500 ease-out hover:-translate-y-px hover:border-white/30 cursor-pointer"
role="button"
tabindex="0"
@click="score.deep_stats && openDetail(score)"
@keyup.enter="score.deep_stats && openDetail(score)"

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional rendering logic 'score.deep_stats && openDetail(score)' on both click and keyup handlers will silently do nothing if deep_stats is falsy. For better accessibility and user feedback, consider disabling the interactive elements (removing role, tabindex, cursor-pointer) when deep_stats is not available, or providing visual feedback that the item is not clickable.

Copilot uses AI. Check for mistakes.
Comment thread app/app.vue
badge.tone === 'badge-s' ? 'bg-white/6 text-white ring-2 ring-white/10 shadow-[0_0_8px_rgba(255,255,255,0.04)]' : '',
badge.label === 'F' ? 'line-through opacity-60 border border-white/20 text-white' : '',
!['badge-ss','badge-s','badge-a'].includes(badge.tone) && badge.label !== 'F' ? 'border border-white/30 text-white' : ''
!['badge-ss','badge-s'].includes(badge.tone) && badge.label !== 'F' ? 'border border-white/30 text-white' : ''

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 838 checks if badge.tone is NOT in the array ['badge-ss','badge-s'], but line 835-836 define styles for 'badge-ss' and 'badge-s'. The removed line 837 previously had a check for 'badge-a', which is now missing from line 838. If 'badge-a' is still a valid tone value, it will now incorrectly fall through to the default border styling instead of having its own specific styling.

Suggested change
!['badge-ss','badge-s'].includes(badge.tone) && badge.label !== 'F' ? 'border border-white/30 text-white' : ''
!['badge-ss','badge-s','badge-a'].includes(badge.tone) && badge.label !== 'F' ? 'border border-white/30 text-white' : ''

Copilot uses AI. Check for mistakes.
Comment thread app/app.vue
class="relative mb-5 last:mb-0"
>
<span class="absolute -left-1.5 mt-0.5 h-3 w-3 rounded-full border border-white bg-black" />
<span class="absolute -left-4 top-1/2 -translate-y-1/2 h-3 w-3 rounded-full border border-white bg-black" />

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeline dot positioning changed from '-left-1.5 mt-0.5' to '-left-4 top-1/2 -translate-y-1/2'. This change shifts the dot 16px to the left (from -6px to -16px) and centers it vertically. Verify that there is sufficient left padding or margin in the parent container to accommodate the additional -10px shift, otherwise the dot may be clipped or overflow outside the visible area.

Copilot uses AI. Check for mistakes.
Comment thread nuxt.config.ts
plugins: [tsconfigPaths()],
server: {
// allow hosts for previewing the dev server through a tunnel
// Can be set via env: `NUXT_ALLOWED_HOSTS` or `VITE_ALLOWED_HOSTS` (comma-separated).

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions "VITE_ALLOWED_HOSTS" as an option, but based on the environment variable naming convention used in the project (NUXT_*), using VITE_ALLOWED_HOSTS might not work as expected. Consider clarifying in the comment whether VITE_ALLOWED_HOSTS is actually supported, or if only NUXT_ALLOWED_HOSTS should be documented.

Copilot uses AI. Check for mistakes.
Comment thread .env.example
OSU_REPLAY_PATH="/path/to/your/osu/exports"
NUXT_ENABLE_REPLAY_WATCHER="false" No newline at end of file
NUXT_ENABLE_REPLAY_WATCHER="false"
NUXT_ALLOWED_HOSTS="osu.korpseluv.com" No newline at end of file

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value 'osu.korpseluv.com' in the .env.example file should match the intention of being an example. Consider using a placeholder like 'your-tunnel-domain.com' or 'example.com' instead of what appears to be a specific/personal domain.

Suggested change
NUXT_ALLOWED_HOSTS="osu.korpseluv.com"
NUXT_ALLOWED_HOSTS="your-tunnel-domain.com"

Copilot uses AI. Check for mistakes.
Comment thread app/app.vue
Comment on lines +394 to +400
onMounted(() => {
// initialize from the fragment when the component mounts
setTabFromHash()
window.addEventListener('hashchange', setTabFromHash)
})

onBeforeUnmount(() => {

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tab navigation logic uses window.location.hash directly without checking if window is defined on line 397, but the check is present on line 390. While line 390 guards setTabFromHash's initial read, line 397 is inside onMounted which only runs client-side, so the additional check on line 390 may be redundant. Consider removing the typeof window check on line 390 for consistency, or document why it's needed.

Suggested change
onMounted(() => {
// initialize from the fragment when the component mounts
setTabFromHash()
window.addEventListener('hashchange', setTabFromHash)
})
onBeforeUnmount(() => {
onMounted(() => {
if (typeof window === 'undefined') return
// initialize from the fragment when the component mounts
setTabFromHash()
window.addEventListener('hashchange', setTabFromHash)
})
onBeforeUnmount(() => {
if (typeof window === 'undefined') return

Copilot uses AI. Check for mistakes.
Comment thread app/app.vue

<div class="flex flex-row flex-wrap gap-2">
<div
<div

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation appears to have changed from the original alignment. The opening div tag now has inconsistent indentation (extra spaces) compared to the surrounding code, which may indicate a formatting issue.

Copilot uses AI. Check for mistakes.
Comment thread nuxt.config.ts
server: {
// allow hosts for previewing the dev server through a tunnel
// Can be set via env: `NUXT_ALLOWED_HOSTS` or `VITE_ALLOWED_HOSTS` (comma-separated).
allowedHosts: (process.env.NUXT_ALLOWED_HOSTS || process.env.VITE_ALLOWED_HOSTS || 'osu.korpseluv.com')

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded default value 'osu.korpseluv.com' should not be in the production configuration. This appears to be a personal or development-specific hostname. The default should either be empty, use 'localhost', or be documented that it must be overridden via environment variables. Having a specific external domain as the default could cause security issues or unexpected behavior for other developers.

Suggested change
allowedHosts: (process.env.NUXT_ALLOWED_HOSTS || process.env.VITE_ALLOWED_HOSTS || 'osu.korpseluv.com')
allowedHosts: (process.env.NUXT_ALLOWED_HOSTS || process.env.VITE_ALLOWED_HOSTS || 'localhost')

Copilot uses AI. Check for mistakes.
Comment thread app/app.vue
const svg = chartRef.value
if (svg && typeof svg.getScreenCTM === 'function') {
try {
const pt = (svg.createSVGPoint ? svg.createSVGPoint() : (new DOMPoint() as any)) as any

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback for DOMPoint when createSVGPoint is unavailable casts to 'any', which bypasses type safety. Consider defining a proper interface or type for the point object to maintain type safety, ensuring it has x, y properties and a matrixTransform method.

Copilot uses AI. Check for mistakes.
Comment thread app/app.vue
Comment on lines +405 to +412
watch(activeTab, (val) => {
const frag = val === 'deep' ? 'deep-stats' : val
try {
history.replaceState(null, '', `#${frag}`)
} catch (e) {
if (typeof window !== 'undefined') window.location.hash = `#${frag}`
}
})

Copilot AI Jan 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The watch callback for activeTab changes the URL without checking if we're in a browser environment. While the watch will only run client-side in practice, the explicit window check on line 410 suggests defensive programming, but it's inconsistent with line 408 which doesn't check before calling history.replaceState. Consider adding a guard at the start of the watch callback to ensure window/history are available.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants