Skip to content

code cleanup#1581

Merged
simo6529 merged 3 commits intomainfrom
clean-v3
Oct 29, 2025
Merged

code cleanup#1581
simo6529 merged 3 commits intomainfrom
clean-v3

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Oct 29, 2025

Summary by CodeRabbit

  • Refactor
    • Optimized internal architecture and module exports for traits system and API routes to improve code maintainability and reduce circular dependencies.

Signed-off-by: Simo <simo@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 29, 2025

Walkthrough

This PR refactors the memes trait system to decouple type definitions and change initialization patterns. Initial trait values transition from a constant export to a function-based export. Trait component prop types move from centralized exports to local component definitions. API routes have revalidate constants removed and ts-prune-ignore comments added. Public API surfaces are narrowed across multiple index files.

Changes

Cohort / File(s) Summary
Test Updates
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts, __tests__/components/waves/memes/traits/schema.test.ts
Updated mocks and imports from constant initialTraits to function getInitialTraitsValues()
Schema & Initialization
components/waves/memes/traits/schema.ts, components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts
initialTraits constant replaced with exported getInitialTraitsValues() function; hook updated to call function instead of requiring constant
Trait Component Type Decentralization
components/waves/memes/traits/TextTrait.tsx, components/waves/memes/traits/BooleanTrait.tsx, components/waves/memes/traits/DropdownTrait.tsx, components/waves/memes/traits/NumberTrait.tsx, components/waves/memes/traits/Section.tsx, components/waves/memes/traits/TraitWrapper.tsx
Prop type interfaces moved from centralized types.ts into individual component files as local declarations
Trait Module Exports
components/waves/memes/traits/types.ts, components/waves/memes/traits/index.ts
Removed all exported trait prop interfaces from types.ts; narrowed index.ts exports to only Section and TraitField
Submission Validation Exports
components/waves/memes/submission/validation/index.ts
Reduced re-exports to only validationHooks, removing validationTypes, validationRules, and traitsValidation
API Route Configuration
app/api/farcaster/route.ts, app/api/open-graph/route.ts, app/api/open-graph/proxy-image/route.ts, app/api/pepe/resolve/route.ts, app/api/tiktok/route.ts, app/api/wikimedia-card/route.ts
Removed export const revalidate = 0 declarations; added ts-prune-ignore comments on route handlers
WebSocket Service Exports
services/websocket/index.ts
Narrowed re-export from all WebSocketTypes to single type export WebSocketConfig

Sequence Diagram(s)

sequenceDiagram
    participant Consumer
    participant Schema as traits/schema.ts
    participant Hook as useArtworkSubmissionForm.ts
    
    rect rgb(230, 245, 250)
    Note over Consumer,Hook: Previous Pattern (Constant)
    Consumer->>Schema: import { initialTraits }
    Schema-->>Consumer: return constant object
    end
    
    rect rgb(245, 250, 230)
    Note over Consumer,Hook: New Pattern (Function)
    Consumer->>Schema: import { getInitialTraitsValues }
    Hook->>Schema: call getInitialTraitsValues()
    Schema-->>Hook: return TraitsData object
    Hook-->>Consumer: provide initialized traits
    end
Loading
sequenceDiagram
    participant External as External types.ts
    participant Component as Individual Components
    participant Local as Local Definitions
    
    rect rgb(230, 245, 250)
    Note over External,Local: Before: Centralized Types
    Component->>External: import TextTraitProps
    Component->>External: import NumberTraitProps
    Component->>External: import BooleanTraitProps
    end
    
    rect rgb(245, 250, 230)
    Note over External,Local: After: Decentralized Types
    Component->>Local: define TextTraitProps locally
    Component->>Local: define NumberTraitProps locally
    Component->>Local: define BooleanTraitProps locally
    Note over Local: Each component self-contained
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20-25 minutes

  • API routes: Verify revalidate removal is intentional and aligns with dynamic export behavior; check ts-prune annotations are appropriate for all handlers
  • Type decentralization: Ensure each component's locally-defined prop types match original signatures exactly; verify no type imports are missed in components
  • Schema/initialization function: Confirm all call sites updated from initialTraits constant to getInitialTraitsValues() function; check for circular dependency improvements
  • Export surface reductions: Verify that narrowed exports in validation, traits, and websocket modules don't break external consumers

Possibly related PRs

  • #1569: Modifies the same traits schema initialization pattern, transitioning from initialTraits constant to getInitialTraitsValues() function with corresponding test updates
  • #1509: Related trait component restructuring affecting components/waves/memes/traits and associated hook/test updates

Suggested reviewers

  • ragnep

Poem

🐰 The traits hop from central homes to cozy nooks,
No more constants frozen in old-fashioned books!
Functions now dance where objects once lay still,
Exports trim their gardens with careful will,
Decentralized and refactored—what a sight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "code cleanup" is highly generic and uses a non-descriptive term that fails to convey meaningful information about the specific changes in the pull request. While the changeset does involve cleanup activities—including consolidation of exports, migration of prop types from shared files into individual components, removal of unused exports, and simplification of public APIs—the title provides no indication of these key changes. A teammate reviewing the commit history would not understand what was actually modified or refactored without examining the detailed changes. Consider using a more specific title that captures the primary changes, such as "Refactor: Consolidate trait type definitions into individual components and simplify public exports" or "Refactor: Move trait prop types from shared types.ts to individual component files." This would provide clearer context about the primary objective of the changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch clean-v3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
services/websocket/index.ts (1)

29-66: Update documentation to match actual exports.

The usage examples reference WebSocketProvider (line 35) and useWebSocketMessage (line 49), but these are not exported from this module. The actual export is useWebSocket (line 18), and there's no WebSocketProvider export visible. This documentation will mislead developers and cause import errors.

Update the examples to use the actual exported APIs, or export the referenced components if they should be part of the public interface.

🧹 Nitpick comments (2)
app/api/open-graph/proxy-image/route.ts (1)

113-116: Remove redundant revalidate export.

When dynamic = "force-dynamic" is set, Next.js forces the route to be fully dynamic on every request, which makes the revalidate = 0 export redundant. Next.js ignores revalidate settings when dynamic is set to "force-dynamic". This redundancy can create confusion about which setting controls the caching behavior.

Apply this diff to remove the redundant export:

-// ts-prune-ignore-next: Next.js framework consumes this export via route conventions.
 export const dynamic = "force-dynamic";
-// ts-prune-ignore-next: Next.js framework consumes this export via route conventions.
-export const revalidate = 0;
components/waves/memes/traits/schema.ts (1)

498-522: LGTM! Function export is clean and correct.

The conversion from a static constant to an exported function is appropriate. The implementation correctly builds initial values from the schema definitions.

If this function is called frequently in hot paths (e.g., on every render), consider caching the result since traitDefinitions is static:

let cachedInitialValues: TraitsData | null = null;

export function getInitialTraitsValues(): TraitsData {
  if (cachedInitialValues) {
    return cachedInitialValues;
  }

  const initialValues: Record<string, any> = {
    title: "",
    description: "",
  };

  traitDefinitions.forEach((section) => {
    section.fields.forEach((field) => {
      if (field.field !== "title" && field.field !== "description") {
        initialValues[field.field] =
          field.initialValue !== undefined
            ? field.initialValue
            : field.type === FieldType.BOOLEAN
            ? false
            : field.type === FieldType.NUMBER
            ? 0
            : "";
      }
    });
  });

  cachedInitialValues = initialValues as TraitsData;
  return cachedInitialValues;
}

Alternatively, consumers could use useMemo to cache the result if needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22b8e2a and 552157c.

📒 Files selected for processing (20)
  • __tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (1 hunks)
  • __tests__/components/waves/memes/traits/schema.test.ts (2 hunks)
  • app/api/farcaster/route.ts (1 hunks)
  • app/api/open-graph/proxy-image/route.ts (2 hunks)
  • app/api/open-graph/route.ts (0 hunks)
  • app/api/pepe/resolve/route.ts (2 hunks)
  • app/api/tiktok/route.ts (0 hunks)
  • app/api/wikimedia-card/route.ts (0 hunks)
  • components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (2 hunks)
  • components/waves/memes/submission/validation/index.ts (1 hunks)
  • components/waves/memes/traits/BooleanTrait.tsx (1 hunks)
  • components/waves/memes/traits/DropdownTrait.tsx (1 hunks)
  • components/waves/memes/traits/NumberTrait.tsx (1 hunks)
  • components/waves/memes/traits/Section.tsx (1 hunks)
  • components/waves/memes/traits/TraitWrapper.tsx (1 hunks)
  • components/waves/memes/traits/index.ts (0 hunks)
  • components/waves/memes/traits/schema.ts (1 hunks)
  • components/waves/memes/traits/types.ts (0 hunks)
  • proxy.ts (0 hunks)
  • services/websocket/index.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • app/api/wikimedia-card/route.ts
  • app/api/tiktok/route.ts
  • components/waves/memes/traits/index.ts
  • app/api/open-graph/route.ts
  • components/waves/memes/traits/types.ts
  • proxy.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props

Use TypeScript across the codebase

Files:

  • components/waves/memes/traits/schema.ts
  • __tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
  • app/api/farcaster/route.ts
  • services/websocket/index.ts
  • components/waves/memes/traits/NumberTrait.tsx
  • app/api/pepe/resolve/route.ts
  • components/waves/memes/traits/Section.tsx
  • components/waves/memes/traits/TraitWrapper.tsx
  • components/waves/memes/traits/DropdownTrait.tsx
  • components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts
  • app/api/open-graph/proxy-image/route.ts
  • __tests__/components/waves/memes/traits/schema.test.ts
  • components/waves/memes/submission/validation/index.ts
  • components/waves/memes/traits/BooleanTrait.tsx
__tests__/**

📄 CodeRabbit inference engine (tests/AGENTS.md)

Place Jest test suites under the __tests__ directory mirroring source folders (e.g., components, contexts, hooks, utils)

Files:

  • __tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
  • __tests__/components/waves/memes/traits/schema.test.ts
__tests__/components/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use @testing-library/react and @testing-library/user-event for React component tests

Files:

  • __tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
  • __tests__/components/waves/memes/traits/schema.test.ts
**/__tests__/**

📄 CodeRabbit inference engine (AGENTS.md)

Place tests in __tests__ directories when organizing test suites

Files:

  • __tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
  • __tests__/components/waves/memes/traits/schema.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
  • __tests__/components/waves/memes/traits/schema.test.ts
{app,pages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use NextJS features that match the current version

Files:

  • app/api/farcaster/route.ts
  • app/api/pepe/resolve/route.ts
  • app/api/open-graph/proxy-image/route.ts
app/api/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (app/api/AGENTS.md)

app/api/**/*.{ts,tsx,js,jsx}: Never call fetch directly with user-controlled or scraped URLs in API code; use @/lib/security/urlGuard helpers (parsePublicUrl, assertPublicUrl, fetchPublicUrl, fetchPublicJson) so every hop is validated
When custom headers or timeouts are needed for external requests, pass them via urlGuard helper options instead of rolling a custom wrapper

Files:

  • app/api/farcaster/route.ts
  • app/api/pepe/resolve/route.ts
  • app/api/open-graph/proxy-image/route.ts
app/api/**/route.{ts,js}

📄 CodeRabbit inference engine (app/api/AGENTS.md)

app/api/**/route.{ts,js}: Catch UrlGuardError explicitly in route handlers if returning a tailored response; otherwise allow it to propagate so the correct status code surfaces
Export HTTP verb handlers (e.g., GET) from route.ts files
Keep route.ts logic in small internal functions when it grows beyond ~200 lines
For edge caching behavior, prefer export const dynamic = "force-dynamic" or revalidate constants rather than inline headers
Follow project default responses (NextResponse.json) and reuse existing util modules instead of duplicating logic

Files:

  • app/api/farcaster/route.ts
  • app/api/pepe/resolve/route.ts
  • app/api/open-graph/proxy-image/route.ts
app/api/**/*.ts

📄 CodeRabbit inference engine (app/api/AGENTS.md)

Use TypeScript types for request parameters and responses; avoid any unless a third-party payload has no shape guarantees

Files:

  • app/api/farcaster/route.ts
  • app/api/pepe/resolve/route.ts
  • app/api/open-graph/proxy-image/route.ts
app/**

📄 CodeRabbit inference engine (AGENTS.md)

Add all new Next.js production routes under the app/ router (the pages/ directory is fully migrated)

Files:

  • app/api/farcaster/route.ts
  • app/api/pepe/resolve/route.ts
  • app/api/open-graph/proxy-image/route.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks for UI components

Files:

  • components/waves/memes/traits/NumberTrait.tsx
  • components/waves/memes/traits/Section.tsx
  • components/waves/memes/traits/TraitWrapper.tsx
  • components/waves/memes/traits/DropdownTrait.tsx
  • components/waves/memes/traits/BooleanTrait.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-28T12:33:56.329Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:56.329Z
Learning: Applies to app/api/**/route.{ts,js} : For edge caching behavior, prefer export const dynamic = "force-dynamic" or revalidate constants rather than inline headers

Applied to files:

  • app/api/farcaster/route.ts
  • app/api/pepe/resolve/route.ts
📚 Learning: 2025-09-28T12:33:56.329Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:56.329Z
Learning: Applies to app/api/**/route.{ts,js} : Export HTTP verb handlers (e.g., GET) from route.ts files

Applied to files:

  • app/api/pepe/resolve/route.ts
🧬 Code graph analysis (4)
app/api/pepe/resolve/route.ts (1)
components/waves/pepe/PepeCard.tsx (1)
  • PepeKind (5-5)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (1)
components/waves/memes/traits/schema.ts (1)
  • getInitialTraitsValues (498-522)
app/api/open-graph/proxy-image/route.ts (5)
app/api/farcaster/route.ts (1)
  • dynamic (759-759)
app/api/pepe/resolve/route.ts (1)
  • dynamic (867-867)
app/api/tiktok/route.ts (1)
  • dynamic (441-441)
app/api/open-graph/route.ts (1)
  • dynamic (355-355)
app/api/wikimedia-card/route.ts (1)
  • dynamic (1023-1023)
__tests__/components/waves/memes/traits/schema.test.ts (1)
components/waves/memes/traits/schema.ts (1)
  • getInitialTraitsValues (498-522)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
app/api/open-graph/proxy-image/route.ts (1)

97-98: LGTM!

The ts-prune-ignore comment appropriately documents that Next.js consumes this export via its routing convention.

components/waves/memes/submission/validation/index.ts (1)

1-1: API surface narrowing verified—no breaking changes detected.

Verification confirms the export narrowing is safe. The only import through this index is useTraitsValidation (used in ArtworkStep.tsx), which remains exported. All imports of the removed exports (validationTypes, validationRules, traitsValidation) use direct file paths, not the index path, so they're unaffected by this change.

app/api/farcaster/route.ts (1)

758-759: LGTM!

The ts-prune comment appropriately documents the Next.js convention, and keeping only dynamic = "force-dynamic" (without the redundant revalidate = 0) is the correct approach for disabling caching.

Based on learnings

app/api/pepe/resolve/route.ts (2)

817-817: LGTM!

The ts-prune comment appropriately documents the Next.js convention for route handlers. The removal of the redundant revalidate = 0 export (while keeping dynamic = "force-dynamic") correctly eliminates duplication.

Based on learnings


14-14: Change is safe to proceed - no external imports depend on this route's export.

Verification confirms zero files import PepeKind from app/api/pepe/resolve/route.ts. The only external usage imports it from @/components/waves/pepe/PepeCard.tsx (the canonical export), while the route file uses its own local type definition internally. This change safely narrows the API surface as intended.

services/websocket/index.ts (1)

14-14: LGTM!

The type-only export correctly narrows the public API surface and improves tree-shaking. This aligns well with the PR's goal of reducing public module surfaces.

components/waves/memes/traits/BooleanTrait.tsx (1)

4-15: LGTM! Clean refactor to local prop types.

The migration from external prop type imports to a local BooleanTraitProps interface follows the coding guidelines with proper readonly modifiers and type imports. This aligns with the PR's objective to localize trait component type definitions.

components/waves/memes/traits/Section.tsx (1)

3-7: LGTM! Consistent with the localization pattern.

The local SectionProps interface follows the same pattern as other trait components, with proper readonly modifiers as required by coding guidelines.

components/waves/memes/traits/DropdownTrait.tsx (1)

3-16: LGTM! Follows the established localization pattern.

The local DropdownTraitProps interface with TraitsData type import is consistent with the refactoring approach across all trait components. Proper use of type imports and readonly modifiers.

components/waves/memes/traits/NumberTrait.tsx (1)

5-19: LGTM! Consistent localization of prop types.

The local NumberTraitProps interface follows the same pattern as other trait components. Proper type imports and readonly modifiers throughout.

components/waves/memes/traits/TraitWrapper.tsx (1)

5-14: LGTM! Completes the localization refactor.

The local TraitWrapperProps interface rounds out the consistent pattern of moving prop types from centralized exports to local definitions. Proper readonly modifiers throughout.

components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (2)

7-7: LGTM! Import aligns with the new initialization approach.

The direct import of getInitialTraitsValues replaces the previous dynamic require pattern, supporting the shift to a function-based initialization strategy.


85-86: LGTM! Addresses circular dependency concerns.

Calling getInitialTraitsValues() directly eliminates the runtime circular dependency risk mentioned in the comment. This is a cleaner and more maintainable approach than the previous dynamic require pattern.

__tests__/components/waves/memes/traits/schema.test.ts (2)

1-1: LGTM! Test imports updated for new API.

The import now includes getInitialTraitsValues to match the function-based initialization approach.


10-14: LGTM! Test properly exercises the new function-based API.

The test correctly validates that getInitialTraitsValues() returns the expected initial trait structure, confirming the refactor from a static constant to a function works as intended.

__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (1)

4-11: LGTM! Mock properly updated for function-based API.

The mock now exports getInitialTraitsValues as a function instead of a static initialTraits constant, correctly matching the implementation changes and ensuring tests validate the new behavior.

Comment thread services/websocket/index.ts
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
components/waves/memes/traits/TextTrait.tsx (1)

20-23: Remove code comments per coding guidelines.

The coding guidelines specify "Do not include any comments in the code" for TypeScript/TSX files. Since this is a code cleanup PR, consider removing all comments throughout this file (lines 20-23, 36, 39, 44, 47, 58, 68, 73, 79, 87, 93, 102).

As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 552157c and 34e0816.

📒 Files selected for processing (2)
  • components/waves/memes/traits/TextTrait.tsx (1 hunks)
  • components/waves/memes/traits/types.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • components/waves/memes/traits/types.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props

Use TypeScript across the codebase

Files:

  • components/waves/memes/traits/TextTrait.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks for UI components

Files:

  • components/waves/memes/traits/TextTrait.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
components/waves/memes/traits/TextTrait.tsx (1)

8-18: LGTM! Type definition properly decoupled.

The inline type definition correctly implements all properties with the readonly modifier, following the coding guidelines. This change successfully decouples the component from external type dependencies as intended by the PR.

@simo6529 simo6529 merged commit 14cc0db into main Oct 29, 2025
9 checks passed
@simo6529 simo6529 deleted the clean-v3 branch October 29, 2025 11:08
@coderabbitai coderabbitai Bot mentioned this pull request Nov 2, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Mar 18, 2026
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