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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .claude/skills/contribute/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: contribute
description: Implement design from a screenshot or a definition. Use model Claude Sonnet 4.5 for this task.
---

# Contribute

## Overview

Implements a design from a screenshot or a definition in a Extend Apps Directory structure.

## When to Use

Use this skill when:

- You want to contribute to Extend Apps Directory.
- You are not familiar on how to contribute to Extend Apps Directory.

## Implementation Principles

1. Always follow semantic HTML.
2. DO NOT SKEW FROM THE DESIGN. Use exactly the same style _per element_. There may be flaws in the design, but it is what it is.
3. DO NOT CREATE ADDITIONAL FILES that are otherwise not required. Focus on the given task at hand.
4. If the prompt specifies a file (or files) that already exists, do not recreate them.
5. When running a code snippet (command line), use the following as terminal preference:
1. Windows: Powershell 7.
2. \*nix: Zsh.
6. Implement responsive design with mobile-first approach using CSS media queries.
7. For texts in the design, COPY THEM WORD FOR WORD. Do not deviate the text content from the design. Ensure that this not only covers a "free text", but also texts inside cards and text inside buttons. Ensure that there are no missing elements.

## Instructions

Make sure to keep the implementation principles above in mind when doing these steps.

### Step 0: Verify requirements

Ensure that you can establish connection to Figma MCP Server according [to the guide](https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/#claude-code). If there is no MCP server named "figma", terminate the action.

### Step 1: Implement the features

Use the resources obtained from Figma MCP server to implement each of the sections. Ensure high quality of the implementation and it's pixel-perfect to what's provided in the design.
26 changes: 26 additions & 0 deletions .claude/skills/create-plan/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: create-plan
description: "Create a detailed design plan from a raw plan."
---

# Plan

## Overview

Reads a file from `.claude/raw-plans` and creates a detailed plan in `.claude/plans` with the same file base name.

## When to Use

Use this skill when:

- You want to contribute to Extend Apps Directory.
- You are not familiar on how to contribute to Extend Apps Directory.
- You want to make sure that your AI-assisted development can be as accurate as possible (reducing back and forth).

## Flow

Given a file name in the format of `.claude/raw-plans/{name}.md`, read that high-level plan and convert it into a detailed plan `.claude/plans/{name}.md`. Use all of the information in the raw plans and use it into the final plan. The final plan should contain these sections:

1. Overview: an outline of the file.
2. Module breakdown (non-technical): visual elements, assets required, questions.
3. Module breakdown (technical): per-section high-level implementation detail.
5 changes: 3 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: Validate Extend Apps Directory

on:
pull_request:
paths:
- 'app/data/extend-apps-directory.json'

jobs:
validate:
Expand All @@ -25,3 +23,6 @@ jobs:

- name: Run prebuild script
run: yarn prebuild

- name: Build
run: yarn build
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.yarn/install-state.gz
.yarn/install-state.gz

.claude/settings.local.json
.claude/plans
.claude/raw-plans
41 changes: 41 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Extend Apps Directory

## Prerequisites

- Node.js LTS version, ideally v24
- Yarn modern (_with_ Corepack)

Always start with `yarn` in the root project to install the dependencies before you run any scripts in `package.json`, if you haven't already.

## Conventions

- Use React for the UI library.
- Use Bootstrap utility classes, such as `d-flex` or `m-4` to style components.
- Use Ant Design library for the UI components.
- Always generate formatted code with Prettier.
- When generating JavaScript syntaxes, use the syntaxes available from the `module` field from the currently used `tsconfig.json`.
- For styling, follow these order:
- If possible to be styled built-in with Ant Design, use it.
- If the Ant Design style can't be overridden with Bootstrap class, then create a `*.module.css` file CLOSE TO THE component that uses it (in the same folder as the component). Sometimes it needs `!important` to override Ant Design styles, only use it when it can't be overridden in a "normal way" (e.g. using higher CSS selector specificity).
- When running a code snippet (command line), use the following as terminal preference:
- Windows: Powershell 7.
- \*nix: Bash/Zsh.
- When it comes to naming variables:
- Make sure booleans always start with `is`, `has`, or `can`. For example: `isModalOpen`, `hasQueryParameter`, `canUpdate`.
- Make sure functions always start with a verb or if it returns boolean, use boolean-y name. For example: `showModal()`, `hasQueryParameter()`.

### React-specific conventions

- When hiding modals, prefer using `open` prop as opposed to returning `null`. For example:

```tsx

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

just curious, will this convention be applied to this repo only or we can apply it to our other repos? because i've seen a lot of modals in AP are like {isOpen && }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm kinda hoping we can apply it to other repos because it seems awkward (from my perspective, anyway) if we already define open prop but we don't want to define open={isOpen}.

But I'd say it's case-by-case, if the component contains other components, I think return null is fine. But if it only contains the modal itself, I think we can just inline it with the prop.

Also, Ant Design docs seem to prefer that way, too: https://ant.design/components/modal#modal-demo-basic

// Don't do this:
if (!isOpen) return null

// Instead:
<Modal open={isOpen} />
```

## Testing

Run `yarn prebuild && yarn typecheck` from the root project.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,30 @@ yarn start

## Project Structure

- `app/`: Contains the main application code.
- `assets/`: Static assets like images and logos.
- `components/`: Reusable React components.
- `data/`: JSON data for the Extend apps.
- `routes/`: Route components for different pages.
- `types/`: TypeScript type definitions.
- `app.css`: Global CSS styles.
- `root.tsx`: The root component of the application.
- `routes.ts`: Route configuration.
- `public/`: Public assets that are not processed by Vite.
- `vite.config.ts`: Vite configuration file.
- `package.json`: Project dependencies and scripts.
- `tsconfig.json`: TypeScript configuration.
- `app/`: Contains the main application code.
- `assets/`: Static assets like images and logos.
- `components/`: Reusable React components.
- `data/`: JSON data for the Extend apps.
- `routes/`: Route components for different pages.
- `types/`: TypeScript type definitions.
- `app.css`: Global CSS styles.
- `root.tsx`: The root component of the application.
- `routes.ts`: Route configuration.
- `public/`: Public assets that are not processed by Vite.
- `vite.config.ts`: Vite configuration file.
- `package.json`: Project dependencies and scripts.
- `tsconfig.json`: TypeScript configuration.

## AI integration

### Requirements

- Download Claude Code: https://code.claude.com/docs/en/quickstart
- Set up Figma MCP server: https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/#claude-code.

### How-to

- Copy `resources/plan.md` into `.claude/raw-plans/{page_name}.md`. For example: `.claude/raw-plans/{feature_name}.md`.
- Open Claude Code, make sure the model is Sonnet 4.5 (or higher). After that, run this: `/create-plan from .claude/raw-plans/{feature_name}.md.`.
- Answer the questions provided by the AI agent in the new plan document `.claude/plans/{feature_name}.md`.
- Inside Claude Code, run `/contribute with the context of .claude/plans/{feature_name}-sonnet.md.`.
73 changes: 73 additions & 0 deletions app/components/ContributingGuideModal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.modal {
max-width: 100%;
}

@media (min-width: 768px) {
.modal {
width: 640px !important;
}
}

.modalHeader {
gap: 12px;
padding: 20px 24px;
border-bottom: 1px solid #f0f0f0;
}

.modalTitle {
margin: 0px !important;
}

.modalContent {
padding: 24px;
}

.instructionsList {
margin: 0;
padding-left: 20px;
color: #000000a6;
}

.instructionsList li {
margin-bottom: 0;
line-height: 32px;
}

.instructionsList ol {
margin-top: 0;
margin-bottom: 0;
}

.instructionsList ol li {
line-height: 32px;
}

.instructionsList a {
text-decoration: underline;
}

@media (max-width: 480px) {
:global(.ant-modal),
:global(.ant-modal-content) {
height: 100%;
width: 100vw;
margin: 0px !important;
top: 0px;
}

:global(.ant-modal-content) {
border-radius: 0px !important;
}

:global(.ant-modal-body) {
height: 100%;
}

.modalTitle {
font-size: 20px !important;
}

.modalContent {
min-height: 100vh;
}
}
129 changes: 129 additions & 0 deletions app/components/ContributingGuideModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { CloseOutlined, ExportOutlined, VideoCameraFilled } from '@ant-design/icons'
import { Alert, Button, Modal, Typography } from 'antd'
import { useEffect } from 'react'
import { useSearchParams } from 'react-router-dom'
import styles from './ContributingGuideModal.module.css'
import { CONTRIBUTING_GUIDELINE_MODAL_QUERY_PARAMETER } from '~/types/ui'

const { Title, Paragraph, Text } = Typography

// External link constants
const LINKS = {
PUBLIC_FOLDER: 'https://github.com/AccelByte/extend-apps-directory/tree/main/public',
JSON_DATA: 'https://github.com/AccelByte/extend-apps-directory/blob/main/app/data/extend-apps-directory.json',
REPOSITORY: 'https://github.com/AccelByte/extend-apps-directory',
VIDEO: 'https://www.youtube.com/watch?v=Y3YU5g6ri5I'
} as const

export function ContributingGuideModal() {
const [searchParams, setSearchParams] = useSearchParams()
const isModalOpen = searchParams.get(CONTRIBUTING_GUIDELINE_MODAL_QUERY_PARAMETER) === 'true'

useEffect(() => {
// Track modal view with Google Analytics
if (isModalOpen && window.gtag) {
window.gtag('event', 'view_contributing_guide', {
event_category: 'engagement',
event_label: 'Contributing Guide Modal Opened'
})
}
}, [isModalOpen])

const handleClose = () => {
searchParams.delete(CONTRIBUTING_GUIDELINE_MODAL_QUERY_PARAMETER)
setSearchParams(searchParams, { preventScrollReset: true })
}

return (
<Modal
open={isModalOpen}
className={styles.modal}
styles={{ content: { padding: 0 } }}
okButtonProps={{ hidden: true }}
cancelButtonProps={{ hidden: true }}
onCancel={handleClose}
closeIcon={null}
width={640}
>
<div className={`d-flex align-items-center justify-content-between ${styles.modalHeader}`}>
<Title level={4} className={styles.modalTitle}>
Submit your app
</Title>
<Button style={{ width: 32, height: 32 }} onClick={handleClose}>
<CloseOutlined aria-label="Close modal" />
</Button>
</div>
<div className={styles.modalContent}>
<div className="d-flex flex-column" style={{ gap: 24 }}>
<div className="d-flex flex-column" style={{ gap: 8 }}>
<Paragraph style={{ color: '#000000A6', marginBottom: 0 }}>
Follow these steps to submit a Pull Request (PR) to our team for review and approval:
</Paragraph>
<ol className={styles.instructionsList}>
<li>
<Text strong>Go public:</Text> Ensure your repository (e.g., GitHub) is set to public.
</li>
<li>
<Text strong>Add app icon:</Text> Upload your icon{' '}
<a href={LINKS.PUBLIC_FOLDER} target="_blank" rel="noopener noreferrer">
<Text strong style={{ color: '#0b6cff' }}>
here
</Text>
</a>
. Recommended: 512x512px.
</li>
<li>
<Text strong>Register app or suite:</Text> Add your app's details in <Text strong>extend-apps-directory.json</Text>{' '}
<a href={LINKS.JSON_DATA} target="_blank" rel="noopener noreferrer">
<Text strong style={{ color: '#0b6cff' }}>
here
</Text>
</a>
.
<ol type="a" style={{ marginTop: 0 }}>
<li>
<Text strong>App:</Text> A single app hosted in one repository.
</li>
<li>
<Text strong>Suite:</Text> A collection of multiple related apps (e.g., Gacha Suite).
</li>
</ol>
</li>
<li>
<Text strong>Submit PR:</Text> Open a Pull Request to this{' '}
<a href={LINKS.REPOSITORY} target="_blank" rel="noopener noreferrer">
<Text strong style={{ color: '#0b6cff' }}>
repository
</Text>
</a>
, which our team will review.
</li>
<li>
<Text strong>Go live:</Text> Once approved and merged, your app will automatically appear in the directory.
</li>
</ol>
</div>
<Alert
type="info"
icon={<VideoCameraFilled />}
message={
<div className="d-flex align-items-center justify-content-between" style={{ gap: 8 }}>
<Text>Build your first Extend app in minutes using AI.</Text>
<Button
type="primary"
size="small"
href={LINKS.VIDEO}
target="_blank"
rel="noopener noreferrer"
>
Watch video <ExportOutlined aria-hidden />
</Button>
</div>
}
showIcon
/>
</div>
</div>
</Modal>
)
}
Loading