25d5302
feat(web): style Markdown code blocks with border and tinted background
Click to expand commit body
Add subtle border, bg-muted/40 background, and rounded corners to
fenced code blocks. Reset inline code styles inside highlighted
blocks so Shiki colors show through cleanly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
18ca38f
fix(web): pre-load common languages in shared Shiki highlighter
Click to expand commit body
The rehype plugin needs languages already loaded (it's synchronous).
Pre-load 16 common languages (JS/TS, Go, Python, Rust, etc.) so
Markdown code blocks get highlighted. FileViewer continues to load
additional languages on demand.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
d52ca8b
feat(web): add Shiki syntax highlighting to Markdown code blocks
Click to expand commit body
Share the Shiki highlighter between FileViewer and Markdown via a
singleton in src/lib/shiki.ts. The Markdown component uses
@shikijs/rehype/core with a pre-resolved highlighter (react-markdown
doesn't support async plugins).
The highlighter loads in a useEffect — Markdown renders without
highlighting initially, then re-renders with syntax colors once
the highlighter is ready.
Also update sanitize schema to allow Shiki's style/class attributes
on pre/code/span elements, and adjust prose styles so inline code
inside highlighted blocks doesn't get bg-muted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
597e01a
fix(web): don't apply Shiki background-color to token spans
Click to expand commit body
Only the root .shiki element gets background-color. Token spans only
get color/font-style/font-weight/text-decoration. This prevents
white blocks showing through the yellow line highlight.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
a171a0f
fix(web): make line number gutter background transparent
Click to expand commit body
The Shiki global CSS rule '.shiki span { background-color: ... }'
was applying to the gutter span, showing a white block through the
yellow line highlight. Force transparent background on .line-number.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
075f73d
fix(web): only allow line selection via gutter click
Click to expand commit body
Replace ::before pseudo-element with an actual <span> for line numbers,
injected by the Shiki transformer. Only the gutter span has
data-line-number, so clicking code text no longer triggers selection.
Gutter gets a hover state (opacity 0.4 → 0.8) for affordance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
f24aa74
refactor(web): use Shiki defaultColor:false for CSS-only theming
Click to expand commit body
Shiki now outputs only CSS variables (--shiki-light, --shiki-dark)
on each span with no inline color/background. The CSS in index.css
maps the variables to the active theme — no !important needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
b35e620
refactor(web): replace :global(.line) with CSS module scoped class
Click to expand commit body
Use a Shiki transformer to replace the default "line" class with our
CSS module's scoped .line class. No more :global() needed — everything
is properly module-scoped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
f29d46b
fix(web): use :global() for Shiki's .line class in CSS module
Click to expand commit body
CSS modules were mangling .line in selectors like
.code-content code > .line, but .line is generated by Shiki and
must stay as-is. Wrap with :global(.line) so the selector targets
the actual DOM class.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
aa39076
fix(web): use kebab-case CSS classes and Tailwind font-mono var
Click to expand commit body
Rename code_block → code-block, code_content → code-content.
Use var(--font-mono) from the Tailwind theme instead of hardcoded
font-family stack.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
4b8cb70
refactor(web): move FileViewer code styles to CSS module
Click to expand commit body
Extract the wall of Tailwind arbitrary selectors into
file-viewer.module.css with snake_case class names. The scoped
<style> tag for line highlighting uses the module's generated
class name for proper scoping.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
fd059a0
fix(web): fix code block background stripes and dark mode highlight
Click to expand commit body
- Remove !bg-transparent override — let Shiki control the background
so there's no mismatch between padding areas and line backgrounds
- Move vertical padding from <code> to first/last .line elements so
every visible pixel has Shiki's background
- Use separate light/dark highlight colors (yellow 30% light, 15% dark)
- Fix line number opacity to work with both themes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
0ff322c
fix(web): fix line highlight full-width and visibility
Click to expand commit body
- Switch lines from table-row to block display so highlight
background spans the full container width
- Use GitHub-style yellow highlight (rgba(255,235,59,0.25))
instead of subtle accent color
- Fix line count (was showing 0 — use text split instead of
hast tree traversal)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
c485a03
fix(web): preserve newlines in code copy-paste
Click to expand commit body
Append a \n text node inside each .line span so copy-pasting from
the code block includes proper line breaks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
a5b0f3d
fix(web): fix FileViewer line rendering issues
Click to expand commit body
- Use Shiki transformer to inject data-line-number on each .line span
and strip whitespace text nodes between lines (caused empty rows
with table-row display)
- Render line numbers via CSS ::before + content:attr(data-line-number)
so gutter is always in sync with code lines
- Count lines from hast tree instead of splitting text by \n
- Use event delegation on the code block for line click handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
e82762c
feat(web): add line numbers with GitHub-style line highlighting
Click to expand commit body
Replace dangerouslySetInnerHTML with native React rendering via
Shiki's codeToHast → hast-util-to-jsx-runtime pipeline.
Features:
- Clickable line numbers in a sticky gutter
- Click to select a line (#L12 in URL hash)
- Shift+click to select a range (#L12:25)
- Selected lines highlighted with accent background
- URL hash syncs both ways (navigation + popstate)
- Scrolls to selected line on initial load
Also fix dark mode theme sync — add CSS rule to swap Shiki's
--shiki-dark variables when .dark class is active.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
884afdf
refactor(web): replace highlight.js with Shiki for code highlighting
Click to expand commit body
Shiki uses VS Code's TextMate grammar engine (Oniguruma WASM) for
accurate syntax highlighting that matches what developers see in
their editor.
- Lazy singleton highlighter with on-demand language loading
- github-light/github-dark dual themes with automatic dark mode
- Inline styles — no external CSS needed (removed hljs theme + overrides)
- Fine-grained imports via @shikijs/langs and @shikijs/themes
Skip FileViewer from Vitest browser tests (Shiki WASM doesn't load
in that context). Snapshot tests still cover it via happy-dom.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
c7a2f42
fix(web): add proper disabled state to Pagination Previous/Next
Click to expand commit body
Apply opacity-50, pointer-events-none, aria-disabled, and tabIndex=-1
when disabled. Prevents navigation and gives visual feedback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
dcaecd8
fix(web): constrain search icon size in QueryInput
Click to expand commit body
The Icon wrapper's size-4 didn't constrain the child SVG.
Add [&>svg]:size-4 to force the icon to 16px.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
8d2cf62
docs(web): update README for current architecture
Click to expand commit body
Reflect the component layer system (ui/shared/bugs/code), colocated
GraphQL fragments, Storybook + Vitest testing setup, a11y testing,
and all current tooling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
c0e6846
feat(web): add interaction tests with play functions
Click to expand commit body
Add play functions to stories for behavior testing:
- QueryInput: type "label:" → suggestions appear → Enter selects first
- WritePreview: click Preview → content switches → click Write → back
These run as real browser tests via the Storybook Vitest addon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
d4c58b6
refactor(web): migrate StatusTabs into issues list and user profile
Click to expand commit body
Replace ~45 lines of inline status toggle code in each consumer with
the StatusTabs composition component. Indicators accept an `active`
prop for explicit state control (needed because active state comes
from query parsing, not router URL matching).
Also remove unused cn/CircleDot/CircleCheck imports from issues list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
54189d4
refactor(web): move IssueFilters to shared/
Click to expand commit body
IssueFilters has zero GraphQL dependencies — it's purely presentational.
Belongs in shared/ with other reusable, domain-aware components.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
571ee43
refactor(web): extract query utilities to src/lib/query-utils.ts
Click to expand commit body
Move tokenizeQuery, parseQueryString, buildBaseQuery, buildQueryString,
SortValue, StatusFilter, and SORT_OPTIONS from the route file and
issue-filters into a shared module. These are pure functions with no
React deps — removes ~80 lines from the issues route.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
52bf712
refactor(web): standardize all component files to kebab-case
Click to expand commit body
Rename all PascalCase component files to kebab-case across the
codebase for consistency with the ui/ (shadcn) convention:
- shared/: IssueRow→issue-row, LabelBadge→label-badge, StatusBadge→status-badge
- bugs/: CommentBox→comment-box, IssueFilters→issue-filters, etc.
- code/: CodeBreadcrumb→code-breadcrumb, FileTree→file-tree, etc.
- content/: Markdown→markdown
- layout/: Header→header, Shell→shell
All imports updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
f22cab2
refactor(web): reorganize component hierarchy and fix shadcn config
Click to expand commit body
Restructure components into three clear layers:
ui/ — generic shadcn primitives (button, input, avatar, etc.)
Managed by shadcn CLI. No domain knowledge.
shared/ — app-level reusable components with domain awareness but
no data fetching. Typed against GraphQL fragments.
(IssueRow, LabelBadge, StatusBadge, CommentCard,
Pagination, QueryInput, StatusTabs, WritePreview,
EmptyState, SectionHeading)
bugs/ — feature components with GraphQL mutations and auth
(CommentBox, Timeline, TitleEditor, LabelEditor,
IssueFilters)
Also update components.json for Tailwind v4 (drop tailwind.config
reference, set config to empty string per shadcn docs).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
10e6081
refactor(web): type components against GraphQL fragment types
Click to expand commit body
- LabelBadge: props typed as LabelFieldsFragment (spread fragment
data directly onto the component)
- CommentCard.AuthorAvatar: props typed as Pick<IdentitySummaryFragment>
with avatarUrl/displayName fields
- IssueRow stories: mock data typed as BugSummaryFragment
- CommentCard stories: mock data typed as IdentitySummaryFragment
- Update Timeline and CommentBox for new AuthorAvatar prop names
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
Define reusable fragments next to the components that consume them:
- IdentitySummary: id, humanId, displayName, avatarUrl
- LabelFields: name, color { R G B }
- BugSummary: composes IdentitySummary + LabelFields for bug list nodes
- Timeline fragments: BugCreateCommentFields, BugAddCommentFields,
LabelChangeFields, StatusChangeFields, TitleChangeFields
Rewrite BugList, BugDetail, and UserProfile queries to compose
fragments instead of duplicating field selections. Codegen now
includes src/components/**/*.graphql for fragment discovery.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
6124a94
fix(web): make FileViewer skeleton widths deterministic
Click to expand commit body
Replace Math.random() with a deterministic formula so snapshot tests
don't flake on every run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
4a56564
refactor(web): migrate consumers to EmptyState, SectionHeading, Pagination, CommentCard
Four new shared UI components with composition APIs:
- EmptyState: styled "no results" message
- SectionHeading: uppercase sidebar/section heading
- Pagination: createLink-wrapped Previous/Next with Info
- CommentCard: avatar + bordered card (Root/AuthorAvatar/Card/CardHeader/CardBody)
Each has stories and snapshot tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
3676d2e
feat(web): add snapshot tests for all story files
Click to expand commit body
Every story file now has a corresponding snapshot test using the
portable stories API. The pattern auto-generates a test per exported
story — adding a new story automatically adds a snapshot test.
63 snapshot tests across 18 component files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
5466d11
refactor(web): drop vite-tsconfig-paths in favor of resolve.alias
Click to expand commit body
Vite 8 no longer needs the plugin. Use resolve.alias to map @/* to
src/* directly, silencing the deprecation warning on every build/test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
269a31a
feat(web): add @storybook/addon-a11y for accessibility testing
Click to expand commit body
Integrates axe-core via the a11y addon. Violations fail tests by
default (test: "error"). The addon adds an accessibility panel in
Storybook UI and a vision impairment simulator in the toolbar.
Also fix preview.ts import to use @storybook/react-vite.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
c6f3106
fix(web): align vitest setup with storybook docs
Click to expand commit body
- Restore setupFiles in storybook project (addon detects it, skips
auto-provisioning, but it's still needed per docs)
- Add storybookScript for watch mode DX (story links in failures)
- Switch snapshot tests from render() to run() which goes through
the full Storybook lifecycle (loaders, decorators, play functions)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
d4c83d9
feat(web): add @storybook/addon-vitest for browser smoke tests
Click to expand commit body
Set up the official Storybook Vitest addon which auto-transforms every
story into a Vitest test running in a real Chromium browser via
Playwright. Works alongside the existing snapshot tests.
Two vitest projects:
- storybook: browser-mode smoke/interaction tests (Playwright)
- snapshot: portable stories snapshot tests (happy-dom)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
6c3b7c8
feat(web): set up vitest with storybook for snapshot testing
Click to expand commit body
Configure vitest with happy-dom and the portable stories API to
run snapshot tests against Storybook stories. Adding a story
automatically adds a snapshot test.
Includes Button snapshot tests as the initial example.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
7b39409
refactor(web): remove old QueryInput component
Click to expand commit body
Replaced by the provider-based ui/query-input composition component.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
84e923c
refactor(web): extract QueryInput as provider-based composition component
Click to expand commit body
New generic QueryInput with pluggable CompletionProviders:
- Root manages state (cursor tracking, keyboard nav, suggestions) via context
- Input renders the two-layer syntax-highlighted input
- Completions renders the autocomplete dropdown
- Icon is a positioned slot
Providers define prefix, highlight color, and getSuggestions (sync or async).
Adding a new filter type means adding a provider — zero component changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
All consumers now use the IssueRow composition components.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
484e74c
refactor(web): migrate CommentBox to WritePreview (controlled)
Click to expand commit body
Replace inline write/preview tab rendering with WritePreview
composition component in controlled mode, since CommentBox resets
preview state after submitting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
11a54fb
refactor(web): migrate new issue form to WritePreview
Click to expand commit body
Replace inline write/preview tab logic with WritePreview composition
component in uncontrolled mode. Removes manual preview state management.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
15629cf
refactor(web): migrate user profile to IssueRow composition
Click to expand commit body
Replace 40-line inline bug row rendering with IssueRow compound
components. No hover, no label links, no author — expressed naturally
by what the consumer composes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
18e4ab8
refactor(web): migrate issues list to IssueRow + LabelBadgeLink
Click to expand commit body
Replace BugRow with IssueRow composition components in the issues list.
Labels now use LabelBadgeLink (createLink-wrapped) instead of onClick.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
eb6798b
feat(web): add IssueRow, StatusTabs, and WritePreview composition components
Click to expand commit body
IssueRow: compound component (Root/StatusIcon/TitleArea/Meta/CommentCount)
replacing the flat prop-heavy BugRow.
StatusTabs: createLink-wrapped tab with OpenIndicator/ClosedIndicator/Count
sub-components for the open/closed toggle pattern.
WritePreview: context-based write/preview editor with controlled and
uncontrolled modes, replacing duplicated tab logic.
Also refactor LabelBadge: remove onClick prop, add LabelBadgeLink via
createLink for navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
336eb68
feat(web): add stories for all presentational components
Click to expand commit body
Add Storybook stories for:
- UI primitives: Badge, Input, Textarea, Separator, Skeleton, Avatar
- Bug components: StatusBadge, LabelBadge, BugRow
- Code components: RefSelector, FileTree, FileViewer, CodeBreadcrumb
- Content: Markdown
Also add a TanStack Router decorator for components using <Link>.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
9995c1f
fix(web): remove unnecessary type assertion in _code route
Click to expand commit body
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
4a75d14
feat(web): add @tanstack/eslint-plugin-router via oxlint jsPlugins
Click to expand commit body
Enforce proper property ordering when creating routes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
30a03de
feat(web): add eslint-plugin-storybook via oxlint jsPlugins
Click to expand commit body
Load the Storybook ESLint plugin through oxlint's jsPlugins support,
scoped to *.stories.tsx files via an override. Also fix the Button
story import to use @storybook/react-vite per no-renderer-packages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created
219a715
feat(web): bootstrap Storybook 10 for component development
Click to expand commit body
Set up Storybook with @storybook/react-vite for isolated component
development and visual testing. Includes a Button story showcasing
all variants and sizes as a starting point.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quentin Gliech
and
Claude Opus 4.6 (1M context)
created