Skip to content

Phase G: design tokens and restyled pages - #7

Merged
Bonobo791 merged 4 commits into
mainfrom
phase-g-design
Jul 30, 2026
Merged

Phase G: design tokens and restyled pages#7
Bonobo791 merged 4 commits into
mainfrom
phase-g-design

Conversation

@Bonobo791

Copy link
Copy Markdown
Owner

Automated PR. All checks green locally. Verify checklist per plan (v3 steps 24–26):

  • app.css replaced with the verbatim design tokens + base stylesheet (--brand: #4f46e5 present), plus the code selector required by step 26; AGPL header prepended per AGENTS.md
  • src/lib/EmptyState.svelte and src/lib/Skeleton.svelte created verbatim
  • Four pages restyled, presentation only (no load/action/route changes):
    • h1 + .page-sub heading pattern, svelte:head titles 'Moderaty — …'
    • EmptyState with the verbatim copy on all four pages
    • form errors render in .error-box
    • badge semantics on dashboard counts and log actions (ok/danger/neutral/brand)
    • queue cards: 600-weight author, blockquote comment, single flex row of the four action forms, Skeleton fallback when data.pending is undefined
    • I13 aria-labels on all rule form controls (3) and target-naming action buttons; log table caption
  • grep -rc 'yt-mod' src/ → 0 everywhere
  • npm run check: 0 errors/0 warnings; npm run build: exits 0; npm run test: 58/58 passed
  • Codacy: no local analyzer installed (cloud CLI only) — analysis runs on this PR

Do not merge if any step's Verify failed.

@cla-bot cla-bot Bot added the cla-signed label Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed 28b845c Jul 30, 2026 · 11:13 11:15
✅ Reviewed your PR 5f0f7d5 Jul 30, 2026 · 00:57 00:59

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0aae0be8-d55f-4126-867d-5a6aad99d3ad

📥 Commits

Reviewing files that changed from the base of the PR and between 5f0f7d5 and 28b845c.

📒 Files selected for processing (3)
  • src/routes/channels/[id]/queue/+page.svelte
  • src/routes/channels/[id]/queue/page-states.test.ts
  • src/routes/channels/[id]/rules/+page.svelte

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added shared empty-state UI for dashboard, activity log, moderation queue, and rules pages.
    • Added skeleton loading placeholders and clearer loading/empty/error handling on the moderation queue.
    • Added document titles and section subtitles to relevant pages.
  • Accessibility
    • Improved aria-labels for moderation actions, form controls, and delete buttons.
    • Added accessible error-box markup and reduced-motion support (disables animations/transitions).
  • UI Improvements
    • Refreshed the theme using design tokens (colors, radii, shadows, typography).
    • Upgraded buttons, forms, tables, badges, and moderation/audit status styling.
  • Tests
    • Added page-state assertions for queue/rules error-box and guidance copy.

Walkthrough

The PR introduces a token-based visual theme, reusable empty/loading components, and updated dashboard, queue, audit log, and rules page states, metadata, badges, form presentation, and accessibility attributes.

Changes

UI refinements

Layer / File(s) Summary
Theme and shared state components
src/app.css, src/lib/EmptyState.svelte, src/lib/Skeleton.svelte
Adds design tokens, refreshed component styling, reduced-motion handling, and reusable empty-state and skeleton-loading components.
Dashboard presentation
src/routes/+page.svelte
Adds a page title and subtitle, replaces the no-channel message with EmptyState, and applies semantic badge variants.
Audit log states and badges
src/routes/channels/[id]/log/+page.svelte
Maps actions to badge classes, adds page metadata and subtitles, and renders separate empty and populated states.
Queue loading and actions
src/routes/channels/[id]/queue/+page.svelte, src/routes/channels/[id]/queue/page-states.test.ts
Adds loading, error, and empty states, updates comment card markup, adds author-specific button labels, and verifies queue and rules error markup.
Rules form and empty state
src/routes/channels/[id]/rules/+page.svelte
Adds page metadata, externalizes form errors, improves control labels, and uses EmptyState for empty rules.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: a design-token refresh and page restyling.
Description check ✅ Passed The description is directly related to the changeset and accurately summarizes the UI and component updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch phase-g-design

Comment @coderabbitai help to get the list of available commands.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

User description

Automated PR. All checks green locally. Verify checklist per plan (v3 steps 24–26):

  • app.css replaced with the verbatim design tokens + base stylesheet (--brand: #4f46e5 present), plus the code selector required by step 26; AGPL header prepended per AGENTS.md
  • src/lib/EmptyState.svelte and src/lib/Skeleton.svelte created verbatim
  • Four pages restyled, presentation only (no load/action/route changes):
    • h1 + .page-sub heading pattern, svelte:head titles 'Moderaty — …'
    • EmptyState with the verbatim copy on all four pages
    • form errors render in .error-box
    • badge semantics on dashboard counts and log actions (ok/danger/neutral/brand)
    • queue cards: 600-weight author, blockquote comment, single flex row of the four action forms, Skeleton fallback when data.pending is undefined
    • I13 aria-labels on all rule form controls (3) and target-naming action buttons; log table caption
  • grep -rc 'yt-mod' src/ → 0 everywhere
  • npm run check: 0 errors/0 warnings; npm run build: exits 0; npm run test: 58/58 passed
  • Codacy: no local analyzer installed (cloud CLI only) — analysis runs on this PR

Do not merge if any step's Verify failed.


CodeAnt-AI Description

Refresh the moderation dashboard with clearer states, actions, and visual feedback

What Changed

  • Updated the dashboard, rules, review queue, and audit log with a consistent visual design, clearer headings, and page titles
  • Added clear empty-state messages for unconnected channels, missing rules, an empty review queue, and an empty audit log
  • Shows loading placeholders while the review queue is being loaded
  • Highlights moderation statuses and audit actions with distinct success, warning, and danger badges
  • Groups review actions together and presents comments in a more readable format
  • Displays form errors in prominent error panels and adds accessible labels to controls and actions
  • Adds visible keyboard focus styles and respects reduced-motion preferences

Impact

✅ Clearer moderation status at a glance
✅ More understandable empty and loading screens
✅ Easier keyboard and screen-reader navigation

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Phase G Design Pass Review

This PR successfully implements a comprehensive design system overhaul with proper AGPL licensing headers, design tokens, and improved UI components. The changes include:

Approved Changes:

  • ✅ AGPL license headers properly added to all files
  • ✅ CSS design tokens centralized with proper naming (--brand, --danger, --ok, etc.)
  • ✅ New EmptyState and Skeleton components for better UX
  • ✅ Consistent page structure with svelte:head titles
  • ✅ Accessibility improvements with aria-labels on forms and buttons
  • ✅ Semantic badge styling for status indicators
  • ✅ Proper focus-visible states and reduced-motion support

Critical Issue Identified:
One blocking issue found: grammatically incorrect user-facing text in the review queue that could cause user confusion about moderation states. Please review the suggested fix.

The overall design implementation is solid and follows accessibility best practices. Once the text clarity issue is resolved, this will be ready to merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

{:else}
<p class="muted">Queue is empty.</p>
{/each}
<p class="muted">Nothing here is public-facing yet only if previously held; rejected/approved comments already have their final state. Your action is final.</p>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Logic Error: Rewrite this sentence to be grammatically correct and understandable. The current text "Nothing here is public-facing yet only if previously held" is confusing and grammatically incorrect, which could cause user confusion about the actual moderation state.

Suggested change
<p class="muted">Nothing here is public-facing yet only if previously held; rejected/approved comments already have their final state. Your action is final.</p>
<p class="muted">Comments here are held for review. Rejected/approved comments already have their final state. Your action is final.</p>

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Phase G: Apply design tokens and restyle core pages

✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Replace global CSS with Moderaty design tokens and updated base component styles.
• Add shared EmptyState and Skeleton UI components for empty/loading states.
• Restyle dashboard, rules, queue, and audit log pages with consistent headings, badges, and a11y
 labels.
Diagram

graph TD
  CSS["src/app.css\nDesign tokens + base styles"] --> Dash["routes/+page.svelte\nDashboard"]
  CSS --> Rules["routes/.../rules/+page.svelte\nRules"]
  CSS --> Queue["routes/.../queue/+page.svelte\nReview queue"]
  CSS --> Log["routes/.../log/+page.svelte\nAudit log"]
  Empty["lib/EmptyState.svelte"] --> Dash
  Empty --> Rules
  Empty --> Queue
  Empty --> Log
  Skel["lib/Skeleton.svelte"] --> Queue
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Extract repeated page header pattern into a shared layout component
  • ➕ Reduces repetition of title + h1 + .page-sub across pages
  • ➕ Makes future design passes less error-prone (single place to change)
  • ➖ Adds an extra abstraction for a small app; may conflict with the “verbatim” step-by-step plan constraints
2. Replace inline styles with CSS utility classes (or component-scoped classes)
  • ➕ Improves consistency and maintainability (queue card blockquote + action row styling)
  • ➕ Avoids style drift across future edits
  • ➖ Slightly more CSS surface area; may be intentionally avoided for this phase’s “presentation-only/verbatim” requirement
3. Use a design system / utility framework (e.g., Tailwind) for tokens and components
  • ➕ Faster iteration on consistent spacing/typography/states
  • ➕ Encourages reusable patterns by default
  • ➖ Bigger dependency and migration step; not aligned with the plan’s requirement to install specific tokens/styles verbatim

Recommendation: The PR’s approach (global tokens + minimal shared components + page-level markup updates) is appropriate for a controlled design pass and matches the stated “presentation only” constraint. If maintainability becomes a concern, the best follow-up is extracting the repeated page header pattern and replacing the remaining inline styles with named classes/components (non-blocking, separate PR).

Files changed (7) +332 / -68

Enhancement (7) +332 / -68
app.cssIntroduce Moderaty design tokens and refreshed global base styles +158/-14

Introduce Moderaty design tokens and refreshed global base styles

• Replaces prior minimal styling with a tokenized design system (colors, radius, shadow, typography). Updates base component styles for navigation, cards, buttons, forms, badges, and tables, and adds shared state styles for empty/skeleton/error/flash UI.

src/app.css

EmptyState.svelteAdd reusable EmptyState component +28/-0

Add reusable EmptyState component

• Adds a shared EmptyState component that renders a standardized empty-state container with required title and optional hint text, matching the new global .empty styles.

src/lib/EmptyState.svelte

Skeleton.svelteAdd reusable Skeleton loading placeholder component +32/-0

Add reusable Skeleton loading placeholder component

• Adds a Skeleton component that renders a configurable number of placeholder cards with shimmer animation and appropriate aria attributes for loading states.

src/lib/Skeleton.svelte

+page.svelteRestyle dashboard with headings, badge semantics, and empty state +14/-4

Restyle dashboard with headings, badge semantics, and empty state

• Adds a consistent document title and subheading pattern. Updates status badges to semantic variants (danger/ok) and replaces the “no channels” text with the shared EmptyState copy.

src/routes/+page.svelte

+page.svelteRestyle audit log with semantic badges, caption, and empty state +42/-18

Restyle audit log with semantic badges, caption, and empty state

• Adds page title/subheading, introduces a badgeClass helper to map actions to semantic badge styles, and adds a table caption for accessibility. Renders EmptyState when there are no log entries.

src/routes/channels/[id]/log/+page.svelte

+page.svelteRestyle review queue with skeleton fallback and accessible action buttons +40/-25

Restyle review queue with skeleton fallback and accessible action buttons

• Adds page title/subheading and uses Skeleton when pending data is undefined. Restyles queue cards (author weight, comment blockquote, single-row actions) and adds aria-labels to action buttons; shows EmptyState when the queue is empty.

src/routes/channels/[id]/queue/+page.svelte

+page.svelteRestyle rules page with error-box, a11y labels, and empty state +18/-7

Restyle rules page with error-box, a11y labels, and empty state

• Adds page title/subheading and moves form error rendering into a shared .error-box. Adds aria-labels to rule inputs/selects and to the delete button, updates rule type badge styling, and renders EmptyState when no rules exist.

src/routes/channels/[id]/rules/+page.svelte

@coderabbitai coderabbitai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to commit 5f0f7d5
CategorySuggestion                                                                                                                                    Severity
Api mismatch
The loading fallback never renders because the page data contract always supplies an array

The loading branch is unreachable because the server load function always awaits the
database query and returns pending as an array, including when the array is empty.
As a result, Skeleton is never rendered for this route; use an actually deferred
promise/loading state or remove this misleading fallback.

src/routes/channels/[id]/queue/+page.svelte [35]

Why it matters? 🤔
  • ⚠️ Review queue loading fallback is unreachable.
  • ⚠️ Users receive no skeleton during database loading.
  • ⚠️ Empty queues render directly after the awaited query.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/channels/[id]/queue/+page.svelte
**Line:** 35:35
**Comment:**
	*Api Mismatch: The loading branch is unreachable because the server load function always awaits the database query and returns `pending` as an array, including when the array is empty. As a result, `Skeleton` is never rendered for this route; use an actually deferred promise/loading state or remove this misleading fallback.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major
Incorrect condition logic
Held moderation actions are incorrectly rendered with the neutral badge

The audit schema and pipeline emit hold actions, but badgeClass does not classify
hold as a status-specific badge. Those entries fall through to badge neutral, so
held-for-review actions lose their warning/status styling in the audit log. Add an
explicit hold classification consistent with the intended badge semantics.

src/routes/channels/[id]/log/+page.svelte [26-33]

Why it matters? 🤔
  • ⚠️ Audit log hides held-for-review status styling.
  • ⚠️ Operators may miss pending moderation warnings.
  • ⚠️ Hold actions are explicitly emitted by the production pipeline.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/channels/[id]/log/+page.svelte
**Line:** 26:33
**Comment:**
	*Incorrect Condition Logic: The audit schema and pipeline emit `hold` actions, but `badgeClass` does not classify `hold` as a status-specific badge. Those entries fall through to `badge neutral`, so held-for-review actions lose their warning/status styling in the audit log. Add an explicit `hold` classification consistent with the intended badge semantics.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major

@codacy-production

codacy-production Bot commented Jul 30, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 25 minor

Results:
25 new issues

Category Results
CodeStyle 25 minor

View in Codacy

🟢 Metrics 5 complexity · 0 duplication

Metric Results
Complexity 5 (≤ 400 complexity)
Duplication 0 (≤ 1 duplication)

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@qodo-code-review

qodo-code-review Bot commented Jul 30, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 46 rules

Grey Divider


Remediation recommended

1. Queue page missing error-box ✓ Resolved 📘 Rule violation ✧ Quality
Description
src/routes/channels/[id]/queue/+page.svelte implements loading/empty/populated rendering but does
not render an .error-box error state for failed actions. As a result, users can hit action
failures (e.g., invalid/missing commentId) without any visible error feedback on the page.
Code

src/routes/channels/[id]/queue/+page.svelte[R35-66]

+{#if data.pending === undefined}
+	<Skeleton rows={3} />
{:else}
-	<p class="muted">Queue is empty.</p>
-{/each}
+	<p class="muted">Nothing here is public-facing yet only if previously held; rejected/approved comments already have their final state. Your action is final.</p>
+
+	{#each data.pending as c}
+		<div class="card">
+			<p style="margin-top:0"><span style="font-weight:600">{c.authorName}</span> <span class="muted">{c.publishedAt}</span></p>
+			<blockquote style="margin:8px 0; padding:8px 12px; border-left:3px solid var(--border); color: var(--ink-2)">{c.text}</blockquote>
+			<div style="display:flex; gap:8px">
+				<form class="inline" method="POST" action="?/approve">
+					<input type="hidden" name="commentId" value={c.id} />
+					<button class="btn secondary small" aria-label="Approve comment by {c.authorName}">Approve</button>
+				</form>
+				<form class="inline" method="POST" action="?/reject">
+					<input type="hidden" name="commentId" value={c.id} />
+					<button class="btn small" aria-label="Reject comment by {c.authorName}">Reject</button>
+				</form>
+				<form class="inline" method="POST" action="?/del">
+					<input type="hidden" name="commentId" value={c.id} />
+					<button class="btn danger small" aria-label="Delete comment by {c.authorName}">Delete</button>
+				</form>
+				<form class="inline" method="POST" action="?/ban">
+					<input type="hidden" name="commentId" value={c.id} />
+					<button class="btn danger small" aria-label="Ban author {c.authorName}">Ban author</button>
+				</form>
+			</div>
+		</div>
+	{:else}
+		<EmptyState title="Queue is clear" hint="Borderline comments will appear here for your review." />
+	{/each}
+{/if}
Relevance

●●● Strong

PR6 accepted adding .error-box error rendering; aligns with their standard UI-state expectations.

PR-#6

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2407796 requires an explicit error state rendering using .error-box. The queue
page only branches on data.pending === undefined and the list-empty {:else} of {#each}, with
no .error-box rendering; meanwhile the server actions can return fail(..., { error: ... }),
creating an error state that must be displayed.

Rule 2407796: Implement all four standard UI states for each page
src/routes/channels/[id]/queue/+page.svelte[35-66]
src/routes/channels/[id]/queue/+page.server.ts[85-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The review queue page lacks the required error UI state: it never renders a container with class `error-box` when an action fails.

## Issue Context
`+page.server.ts` returns `fail(400, { error: ... })` on invalid inputs. SvelteKit surfaces this as `form?.error` on the page, but the page currently doesn't render it.

## Fix Focus Areas
- src/routes/channels/[id]/queue/+page.svelte[21-66]
- src/routes/channels/[id]/queue/+page.server.ts[85-105]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Input focus-visible overridden 🐞 Bug ⚙ Maintainability
Description
src/app.css defines an outline for input:focus-visible/select:focus-visible, but the later
input:focus, select:focus rule sets outline: none, overriding the focus-visible outline for
those elements. Inputs/selects still get a box-shadow focus indicator, but the focus-visible
styling is inconsistent with links/buttons and the earlier rule is effectively negated.
Code

src/app.css[R101-116]

+.btn:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible {
+  outline: 2px solid var(--brand);
+  outline-offset: 2px;
+}
+
+/* ── forms ──────────────────────────────────────────────── */
+input, select {
+  padding: 8px 10px;
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+  font-size: 14px;
+  font-family: var(--font);
+  background: var(--surface);
+  color: var(--ink);
+}
+input:focus, select:focus { border-color: var(--brand); outline: none; box-shadow: 0 0 0 3px var(--brand-soft); }
Relevance

●● Moderate

No prior repo feedback on focus-visible being overridden by later focus rules.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The stylesheet first sets an outline on input:focus-visible/select:focus-visible, then later
applies outline: none on input:focus/select:focus (same specificity, later wins), so the
focus-visible outline does not apply for inputs/selects.

src/app.css[101-116]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The CSS intends to apply a `:focus-visible` outline across controls, but `input:focus, select:focus { outline: none; ... }` comes later and overrides `input:focus-visible` / `select:focus-visible` outlines.

### Issue Context
This creates inconsistent focus-visible behavior: inputs/selects use the later box-shadow ring (and suppress the outline), while buttons/links keep the outline. If the design goal is “outline for keyboard focus only”, separate `:focus-visible` and `:focus:not(:focus-visible)`.

### Fix Focus Areas
- src/app.css[101-116]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. app.css uses space indents 📘 Rule violation ⚙ Maintainability
Description
src/app.css uses leading spaces for indentation in many blocks (e.g., :root variables),
violating the tabs-only indentation requirement. This can cause style inconsistencies and automated
formatting diffs across contributors.
Code

src/app.css[R22-41]

+:root {
+  --bg: #f7f7f5;
+  --surface: #ffffff;
+  --border: #e5e3de;
+  --ink: #1c1b1a;
+  --ink-2: #6f6a63;          /* muted text */
+  --brand: #4f46e5;          /* indigo 600 */
+  --brand-hover: #4338ca;    /* indigo 700 */
+  --brand-soft: #eef2ff;     /* indigo 50  */
+  --danger: #dc2626;
+  --danger-hover: #b91c1c;
+  --danger-soft: #fef2f2;
+  --warn-soft: #fffbeb;
+  --ok: #16a34a;
+  --ok-soft: #f0fdf4;
+  --radius: 10px;
+  --radius-sm: 6px;
+  --shadow: 0 1px 2px rgb(28 27 26 / 0.06), 0 4px 12px rgb(28 27 26 / 0.05);
+  --font: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
+}
Relevance

●● Moderate

No historical evidence enforcing tabs-only in CSS; prior app.css in PR6 used spaces.

PR-#6

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2401137 requires tabs for indentation and forbids leading spaces. The :root block
in src/app.css shows properties indented with spaces (e.g.,   --bg: ...).

Rule 2401137: Use tabs for indentation, never spaces
src/app.css[22-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/app.css` is indented using spaces, but the project requires tabs-only indentation.

## Issue Context
The new design-token and base stylesheet blocks introduced in this PR use 2-space indentation.

## Fix Focus Areas
- src/app.css[22-182]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Dead Skeleton loading state 🐞 Bug ⚙ Maintainability
Description
The review-queue page only renders the new <Skeleton> when data.pending === undefined, but the
page load() always returns pending as an array, so the Skeleton branch never runs. This makes
the intended loading-state behavior ineffective and leaves dead UI logic in the page.
Code

src/routes/channels/[id]/queue/+page.svelte[R35-37]

+{#if data.pending === undefined}
+	<Skeleton rows={3} />
{:else}
-	<p class="muted">Queue is empty.</p>
-{/each}
Relevance

●● Moderate

No historical evidence found about dead Skeleton/loading-state branches on Svelte pages.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The queue page gates <Skeleton> behind data.pending === undefined, but the server load always
defines pending by awaiting .all() and returning it, meaning the condition cannot be true in
normal execution.

src/routes/channels/[id]/queue/+page.svelte[35-40]
src/routes/channels/[id]/queue/+page.server.ts[27-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`src/routes/channels/[id]/queue/+page.svelte` shows a Skeleton only when `data.pending === undefined`, but `src/routes/channels/[id]/queue/+page.server.ts` always returns `pending` from Drizzle `.all()` (an array), so this loading branch never renders.

### Issue Context
If you want a real loading UI, it needs an actual loading signal (e.g., streaming/deferred data) rather than checking for `undefined` on a value that is always defined.

### Fix Focus Areas
- src/routes/channels/[id]/queue/+page.svelte[35-37]
- src/routes/channels/[id]/queue/+page.server.ts[27-37]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/app.css
Comment on lines +22 to +41
:root {
--bg: #f7f7f5;
--surface: #ffffff;
--border: #e5e3de;
--ink: #1c1b1a;
--ink-2: #6f6a63; /* muted text */
--brand: #4f46e5; /* indigo 600 */
--brand-hover: #4338ca; /* indigo 700 */
--brand-soft: #eef2ff; /* indigo 50 */
--danger: #dc2626;
--danger-hover: #b91c1c;
--danger-soft: #fef2f2;
--warn-soft: #fffbeb;
--ok: #16a34a;
--ok-soft: #f0fdf4;
--radius: 10px;
--radius-sm: 6px;
--shadow: 0 1px 2px rgb(28 27 26 / 0.06), 0 4px 12px rgb(28 27 26 / 0.05);
--font: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. app.css uses space indents 📘 Rule violation ⚙ Maintainability

src/app.css uses leading spaces for indentation in many blocks (e.g., :root variables),
violating the tabs-only indentation requirement. This can cause style inconsistencies and automated
formatting diffs across contributors.
Agent Prompt
## Issue description
`src/app.css` is indented using spaces, but the project requires tabs-only indentation.

## Issue Context
The new design-token and base stylesheet blocks introduced in this PR use 2-space indentation.

## Fix Focus Areas
- src/app.css[22-182]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/routes/channels/[id]/queue/+page.svelte
Comment on lines +35 to 37
{#if data.pending === undefined}
<Skeleton rows={3} />
{:else}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

3. Dead skeleton loading state 🐞 Bug ⚙ Maintainability

The review-queue page only renders the new <Skeleton> when data.pending === undefined, but the
page load() always returns pending as an array, so the Skeleton branch never runs. This makes
the intended loading-state behavior ineffective and leaves dead UI logic in the page.
Agent Prompt
### Issue description
`src/routes/channels/[id]/queue/+page.svelte` shows a Skeleton only when `data.pending === undefined`, but `src/routes/channels/[id]/queue/+page.server.ts` always returns `pending` from Drizzle `.all()` (an array), so this loading branch never renders.

### Issue Context
If you want a real loading UI, it needs an actual loading signal (e.g., streaming/deferred data) rather than checking for `undefined` on a value that is always defined.

### Fix Focus Areas
- src/routes/channels/[id]/queue/+page.svelte[35-37]
- src/routes/channels/[id]/queue/+page.server.ts[27-37]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/app.css
Comment on lines +101 to +116
.btn:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible {
outline: 2px solid var(--brand);
outline-offset: 2px;
}

/* ── forms ──────────────────────────────────────────────── */
input, select {
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: 14px;
font-family: var(--font);
background: var(--surface);
color: var(--ink);
}
input:focus, select:focus { border-color: var(--brand); outline: none; box-shadow: 0 0 0 3px var(--brand-soft); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

4. Input focus-visible overridden 🐞 Bug ⚙ Maintainability

src/app.css defines an outline for input:focus-visible/select:focus-visible, but the later
input:focus, select:focus rule sets outline: none, overriding the focus-visible outline for
those elements. Inputs/selects still get a box-shadow focus indicator, but the focus-visible
styling is inconsistent with links/buttons and the earlier rule is effectively negated.
Agent Prompt
### Issue description
The CSS intends to apply a `:focus-visible` outline across controls, but `input:focus, select:focus { outline: none; ... }` comes later and overrides `input:focus-visible` / `select:focus-visible` outlines.

### Issue Context
This creates inconsistent focus-visible behavior: inputs/selects use the later box-shadow ring (and suppress the outline), while buttons/links keep the outline. If the design goal is “outline for keyboard focus only”, separate `:focus-visible` and `:focus:not(:focus-visible)`.

### Fix Focus Areas
- src/app.css[101-116]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@codacy-production codacy-production Bot 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 PR implements a significant design and accessibility overhaul, introducing a tokenized CSS system and reusable components like EmptyState and Skeleton. While the structural changes align with the project goals, there is a complete absence of automated tests for the new UI logic and components, which poses a regression risk.

Key issues to address before merging include fixing potential layout breaks caused by long text strings in rule lists and audit logs, improving text contrast in the review queue for better accessibility, and resolving several CSS formatting and token-consistency issues in the core stylesheet. Although Codacy rates the quality as 'up to standards', the 25 new issues identified in the stylesheet should be addressed to maintain code consistency.

About this PR

  • The PR introduces significant new UI logic, such as the badgeClass helper and reusable components (EmptyState, Skeleton), but does not include any new test files. Automated unit tests should be added to verify these components and logic paths.

Test suggestions

  • EmptyState component correctly renders the provided title and conditionally renders the hint text.
  • Skeleton component renders the requested number of rows with varied widths and shimmer animation.
  • The badgeClass helper in the Audit Log correctly maps various action strings to semantic badge classes (ok, danger, neutral).
  • The Review Queue correctly displays the Skeleton component when data.pending is undefined.
  • Forms on the Rules page correctly display error messages using the .error-box class.
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. EmptyState component correctly renders the provided title and conditionally renders the hint text.
2. Skeleton component renders the requested number of rows with varied widths and shimmer animation.
3. The badgeClass helper in the Audit Log correctly maps various action strings to semantic badge classes (ok, danger, neutral).
4. The Review Queue correctly displays the Skeleton component when data.pending is undefined.
5. Forms on the Rules page correctly display error messages using the .error-box class.

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

<div class="card">
<p style="margin-top:0"><span style="font-weight:600">{c.authorName}</span> <span class="muted">{c.publishedAt}</span></p>
<blockquote style="margin:8px 0; padding:8px 12px; border-left:3px solid var(--border); color: var(--ink-2)">{c.text}</blockquote>
<div style="display:flex; gap:8px">

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 RISK

Suggestion: The primary comment content is currently styled with a muted token (--ink-2). Use a high-contrast text token to improve readability for moderators.

{:else}
<p class="muted">Queue is empty.</p>
{/each}
<p class="muted">Nothing here is public-facing yet only if previously held; rejected/approved comments already have their final state. Your action is final.</p>

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 RISK

Suggestion: The instructional text 'Nothing here is public-facing yet only if previously held' is difficult to parse; consider clarifying the visibility of comments in the queue (e.g., '...except if previously approved').

Comment thread src/app.css
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #e3e6ea; vertical-align: top; }
.badge { display: inline-block; border-radius: 4px; padding: 2px 8px; font-size: 12px; background: #e3e6ea; }
th { color: var(--ink-2); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; }
th, td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }

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 RISK

Suggestion: Ensure table cells handle long, non-breaking strings gracefully using appropriate CSS overflow or wrapping properties.

Comment thread src/app.css

/* ── text & badges ──────────────────────────────────────── */
.muted { color: var(--ink-2); font-size: 13px; }
code { background: var(--brand-soft); padding: 1px 6px; border-radius: 4px; }

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 RISK

Suggestion: Add overflow handling (e.g., word-break or overflow-wrap) to the code blocks in rule lists to prevent long patterns from breaking the container layout.

Comment thread src/app.css
Comment thread src/app.css
Comment thread src/app.css
Comment thread src/app.css

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app.css`:
- Around line 27-36: Update the status foreground variables --ok and --danger in
the root color definitions to use darker AA-compliant colors against --ok-soft
and --danger-soft, respectively. Preserve the existing semantic variables and
apply the same contrast-safe values to all status text usages, including the
badge, error, and flash styles referenced elsewhere.

In `@src/lib/Skeleton.svelte`:
- Line 25: Update the loading container in Skeleton.svelte by adding
role="status" to the div that currently has aria-busy="true" and
aria-label="Loading", preserving its existing attributes and content.

In `@src/routes/channels/`[id]/queue/+page.svelte:
- Line 38: Update the guidance paragraph in the queue page markup to use
grammatically correct wording that clearly explains why the comments are
displayed and that rejecting or approving them is final. Preserve the existing
muted styling and the intended final-action warning.
- Around line 35-37: Replace the ineffective data.pending === undefined
condition in the page’s loading block with a real loading signal, such as
SvelteKit navigation state or streamed-data availability. Preserve the Skeleton
rendering only while that loading state is active, and keep the existing
pending-array rendering for loaded data.

In `@src/routes/channels/`[id]/rules/+page.svelte:
- Line 34: Update the form error container in the page markup to include
alert/live-region semantics, such as role="alert", while preserving the existing
conditional rendering and form.error content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 32bd9391-513c-4dd0-a38d-92231b1dd477

📥 Commits

Reviewing files that changed from the base of the PR and between a5c6038 and 5f0f7d5.

📒 Files selected for processing (7)
  • src/app.css
  • src/lib/EmptyState.svelte
  • src/lib/Skeleton.svelte
  • src/routes/+page.svelte
  • src/routes/channels/[id]/log/+page.svelte
  • src/routes/channels/[id]/queue/+page.svelte
  • src/routes/channels/[id]/rules/+page.svelte

Comment thread src/app.css
Comment on lines +27 to +36
--ink-2: #6f6a63; /* muted text */
--brand: #4f46e5; /* indigo 600 */
--brand-hover: #4338ca; /* indigo 700 */
--brand-soft: #eef2ff; /* indigo 50 */
--danger: #dc2626;
--danger-hover: #b91c1c;
--danger-soft: #fef2f2;
--warn-soft: #fffbeb;
--ok: #16a34a;
--ok-soft: #f0fdf4;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use AA-compliant foreground colors for status text.

--ok on --ok-soft is approximately 3.0:1, and --danger on --danger-soft is approximately 4.4:1. These colors are used for normal-size badge, error, and flash text, so the status messages fail WCAG AA contrast requirements.

Proposed fix
 :root {
-  --ok: `#16a34a`;
+  --ok: `#15803d`;
+  --danger-ink: `#b91c1c`;
 }

-.badge.danger { background: var(--danger-soft); color: var(--danger); }
+.badge.danger { background: var(--danger-soft); color: var(--danger-ink); }

 .error-box {
-  color: var(--danger);
+  color: var(--danger-ink);
 }

Also applies to: 122-133, 160-176

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app.css` around lines 27 - 36, Update the status foreground variables
--ok and --danger in the root color definitions to use darker AA-compliant
colors against --ok-soft and --danger-soft, respectively. Preserve the existing
semantic variables and apply the same contrast-safe values to all status text
usages, including the badge, error, and flash styles referenced elsewhere.

Comment thread src/lib/Skeleton.svelte
Comment thread src/routes/channels/[id]/queue/+page.svelte
Comment thread src/routes/channels/[id]/queue/+page.svelte Outdated
Comment thread src/routes/channels/[id]/rules/+page.svelte Outdated
@Bonobo791 Bonobo791 changed the title Phase G: Design pass Phase G: design tokens and restyled pages Jul 30, 2026
@sonarqubecloud

Copy link
Copy Markdown

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files size:XL This PR changes 500-999 lines, ignoring generated files labels Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

User description

Automated PR. All checks green locally. Verify checklist per plan (v3 steps 24–26):

  • app.css replaced with the verbatim design tokens + base stylesheet (--brand: #4f46e5 present), plus the code selector required by step 26; AGPL header prepended per AGENTS.md
  • src/lib/EmptyState.svelte and src/lib/Skeleton.svelte created verbatim
  • Four pages restyled, presentation only (no load/action/route changes):
    • h1 + .page-sub heading pattern, svelte:head titles 'Moderaty — …'
    • EmptyState with the verbatim copy on all four pages
    • form errors render in .error-box
    • badge semantics on dashboard counts and log actions (ok/danger/neutral/brand)
    • queue cards: 600-weight author, blockquote comment, single flex row of the four action forms, Skeleton fallback when data.pending is undefined
    • I13 aria-labels on all rule form controls (3) and target-naming action buttons; log table caption
  • grep -rc 'yt-mod' src/ → 0 everywhere
  • npm run check: 0 errors/0 warnings; npm run build: exits 0; npm run test: 58/58 passed
  • Codacy: no local analyzer installed (cloud CLI only) — analysis runs on this PR

Do not merge if any step's Verify failed.


CodeAnt-AI Description

Refresh moderation pages with clearer states and accessible feedback

What Changed

  • Restyled the dashboard, rules, review queue, and audit log with consistent navigation, cards, buttons, badges, forms, and page headings.
  • Added clear empty-state guidance for unconnected channels, empty queues, missing rules, and logs with no activity.
  • Added loading placeholders for the review queue and clear alert messages when queue or rule actions fail.
  • Clarified review-queue guidance, grouped comment actions, and added descriptive labels for assistive technology.
  • Added meaningful page titles, status-colored badges, table captions, and reduced-motion support.

Impact

✅ Clearer moderation status and next steps
✅ More understandable queue and form errors
✅ Easier keyboard and screen-reader navigation

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to commit 28b845c
CategorySuggestion                                                                                                                                    Severity
Incomplete implementation
Dashboard status badges omit held comments and show incomplete moderation totals

The dashboard status badges omit the valid held comment status. Rule-based hold
actions store comments with status held, so channels containing held comments
display incomplete moderation counts and users cannot see how many comments are
awaiting review from this page. Add a badge for the held status or otherwise include
it in the displayed totals.

src/routes/+page.svelte [44-47]

Why it matters? 🤔
  • ⚠️ Dashboard moderation totals omit rule-held comments.
  • ⚠️ Users cannot see held-comment counts per channel.
  • ⚠️ Status visibility differs from database-supported moderation states.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/+page.svelte
**Line:** 44:47
**Comment:**
	*Incomplete Implementation: The dashboard status badges omit the valid `held` comment status. Rule-based hold actions store comments with status `held`, so channels containing held comments display incomplete moderation counts and users cannot see how many comments are awaiting review from this page. Add a badge for the held status or otherwise include it in the displayed totals.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major
Incorrect condition logic
Simulation actions are incorrectly presented as dangerous moderation outcomes

dry-run indicates simulation mode rather than a rejected, deleted, or otherwise
dangerous moderation result. Classifying it as badge danger causes simulated actions
in the audit log to be presented as destructive failures, misleading users about
what actually happened. Treat dry-run as neutral or use a distinct non-danger
styling.

src/routes/channels/[id]/log/+page.svelte [29-30]

Why it matters? 🤔
  • ⚠️ Audit logs misrepresent simulation results as dangerous.
  • ⚠️ Dry-run previews appear equivalent to destructive actions.
  • ⚠️ Moderators may misinterpret the audit history.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/routes/channels/[id]/log/+page.svelte
**Line:** 29:30
**Comment:**
	*Incorrect Condition Logic: `dry-run` indicates simulation mode rather than a rejected, deleted, or otherwise dangerous moderation result. Classifying it as `badge danger` causes simulated actions in the audit log to be presented as destructive failures, misleading users about what actually happened. Treat dry-run as neutral or use a distinct non-danger styling.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major

@Bonobo791
Bonobo791 merged commit d76b7e2 into main Jul 30, 2026
4 checks passed
This was referenced Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant