Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions site/src/lib/components/Tour.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@
title: 'About + methodology',
text: 'The "About" link in the header opens a page describing how the data + the AI surfaces are produced — a short lay paragraph per stage, plus a "Technical details" toggle that expands code-grounded specifics (algorithms, parameters, file paths, cache keys). Every external citation is HEAD-checked at build time so the references stay live.',
attachTo: { element: '[data-testid="header-about-link"]', on: place('bottom') }
},
{
id: 'home-feedback',
title: 'Spot something broken? Have an idea?',
text: 'The speech-bubble icon next to the theme switch opens a pre-filled GitHub issue (page URL + deploy SHA + user-agent templated in for you). Use it for bug reports, missing features, or "this abstract looks wrong" notes — issues land under the `feedback` label.',
attachTo: { element: '[data-testid="header-feedback"]', on: place('bottom') }
}
);
} else if (kind === 'detail') {
Expand Down
85 changes: 85 additions & 0 deletions site/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,53 @@
import { onMount } from 'svelte';
import { base } from '$app/paths';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { buildInfoFromEnv, loadManifest, type BuildInfo, type Manifest } from '$lib/shards';
import BuildInfoFooter from '$lib/components/BuildInfo.svelte';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
import Tour from '$lib/components/Tour.svelte';
import { tourStore, tourFlags } from '$lib/stores/tour';

const FEEDBACK_REPO = 'sensein/ohbm2026';

let manifest: Manifest | null = null;
const envBuildInfo: BuildInfo | null = buildInfoFromEnv();
$: dataBuildInfo = manifest?.build_info ?? null;

/**
* Reactive GitHub-issue pre-fill URL. Recomputes on route change so
* the body reflects whichever page the user was on when they clicked.
*/
$: feedbackUrl = (() => {
const sha =
envBuildInfo?.code_revision_short ??
dataBuildInfo?.code_revision_short ??
'(unknown)';
const here = $page.url.href;
const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For SvelteKit applications, it is more idiomatic to use the browser boolean from $app/environment to guard client-side only globals like navigator. This ensures consistent behavior across SSR and hydration.

const ua = browser ? navigator.userAgent : '';

const body = [
'<!-- Replace this template with your bug report or feature request. -->',
'',
'## What were you doing?',
'',
'## What happened?',
'',
'## What did you expect?',
'',
'---',
'',
`- page: ${here}`,
`- build: \`${sha}\``,
`- user-agent: ${ua}`
].join('\n');
const params = new URLSearchParams({
labels: 'feedback',
title: '[feedback] ',
body
});
return `https://github.com/${FEEDBACK_REPO}/issues/new?${params.toString()}`;
})();
Comment on lines +23 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The logic for constructing the GitHub issue URL is currently embedded directly within a reactive block in the layout. While functional, this logic is quite verbose and could be extracted into a dedicated helper function or a derived store in a separate utility file (e.g., $lib/feedback.ts). This would improve the maintainability and testability of the layout component, which already handles complex SPA redirect logic.


const SPA_REDIRECT_KEY = 'ohbm2026.spa.redirect';

onMount(async () => {
Expand Down Expand Up @@ -102,6 +139,40 @@
<a class="header-link" href={`${base}/about/`} data-testid="header-about-link">
About
</a>
<!--
Feedback icon — opens a pre-filled GitHub issue in a new tab.
The body templates the current page URL, the deploy SHA, and
the user-agent so a maintainer has the minimum context to
reproduce. Repo owner / project link from a const so it's
trivial to swap.
-->
<a
class="header-feedback"
target="_blank"
rel="noopener noreferrer"
title="Report a bug or request a feature"
aria-label="Report a bug or request a feature on GitHub"
data-testid="header-feedback"
href={feedbackUrl}
>
<!-- Lucide-style "message-square-warning" icon — keeps the meaning
unambiguous: a comment bubble with an exclamation. -->
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
</a>
<ThemeToggle />
</div>
</div>
Expand Down Expand Up @@ -220,6 +291,20 @@
button.header-link:hover {
background: var(--accent-soft-bg);
}
.header-feedback {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--text-muted);
border-radius: 4px;
text-decoration: none;
}
.header-feedback:hover {
color: var(--accent);
background: var(--accent-soft-bg);
}
.tour-cta {
display: flex;
align-items: center;
Expand Down
Loading