Skip to content

Commit 3b743c7

Browse files
committed
feat: update mcp.json server configuration and add Copilot instructions
1 parent 235bfa8 commit 3b743c7

3 files changed

Lines changed: 381 additions & 2 deletions

File tree

.github/copilot-instructions.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copilot Instructions
2+
3+
This project is a web application that allows users to create and manage tasks. The application is built using React, Vite, Mantine, and Storybook. It uses static JSON files as the database.
4+
5+
## Coding Standards
6+
7+
- Use camelCase for variable and function names.
8+
- Use PascalCase for component names.
9+
- Use single quotes for strings.
10+
- Use 2 spaces for indentation.
11+
- Use arrow functions for callbacks.
12+
- Use async/await for asynchronous code.
13+
- Use const for constants and let for variables that will be reassigned.
14+
- Use destructuring for objects and arrays.
15+
- Use template literals for strings that contain variables.
16+
- Use the latest JavaScript features (ES6+) where possible.
17+
- When running terminal commands, do not use "&&" to combine as this is not recognized by Windows PowerShell.
18+
- Use design patterns that favor unit testing, such as dependency injection and separation of concerns.
19+
- Use OpenAPI 3.x standards when creating backend web-based applications and APIs.
20+
- Use JSDoc comments for functions and classes to describe their purpose and parameters.
21+
- Use "TODO:" comments to indicate areas of the code that can benefit from unit testing.
22+
- Use "FIXME:" comments to indicate areas of the code that need to be fixed or improved.

.vscode/mcp.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"servers": {
33
"nx-mcp": {
4-
"type": "http",
5-
"url": "http://localhost:9429/mcp"
4+
"type": "stdio",
5+
"command": "npx",
6+
"args": ["nx-mcp@latest", "/path/to/your/workspace"]
67
},
78
"my-development-projects": {
89
"command": "npx",
Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import { useState, useEffect, useCallback } from 'react';
3+
import { MantineProvider, ColorSchemeScript, localStorageColorSchemeManager } from '@mantine/core';
4+
import { ThemeProvider } from '../context/ThemeContext';
5+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
6+
import { baseTheme } from '../theme/mantineTheme';
7+
import { AbilityManualsContext } from '../context/AbilityManualsContext';
8+
import { ModalsProvider } from '@mantine/modals';
9+
import { Notifications } from '@mantine/notifications';
10+
import { useDisclosure } from '@mantine/hooks';
11+
import { Ability } from '../models/abilities.zod';
12+
import { useStyles } from '../hooks/useStyles';
13+
14+
// Import components used in the actual AbilitiesPage
15+
import {
16+
Container,
17+
Title,
18+
Text,
19+
SimpleGrid,
20+
Group,
21+
useMantineColorScheme,
22+
Loader,
23+
Center,
24+
Paper
25+
} from '@mantine/core';
26+
import { AbilityCard } from '../components/AbilityCard';
27+
import { AbilityDetailsModal } from '../components/AbilityDetailsModal';
28+
import { AddToAbilityManualModal } from '../components/AddToAbilityManualModal';
29+
import { AbilitiesFilter } from '../components/AbilitiesFilter';
30+
import { ExportButton } from '../components/ExportButton';
31+
32+
// Create a mocked version of the page that doesn't require hooks
33+
function MockedAbilitiesPage({ isLoading = false, hasError = false }) {
34+
const { isDark } = useStyles();
35+
const [selectedAbility, setSelectedAbility] = useState<Ability | null>(null);
36+
const [filteredAbilities, setFilteredAbilities] = useState<Ability[]>([]);
37+
const [detailsModalOpened, { open: openDetailsModal, close: closeDetailsModal }] = useDisclosure(false);
38+
const [abilityManualModalOpened, { open: openAbilityManualModal, close: closeAbilityManualModal }] = useDisclosure(false); // Using sample data directly from sampleAbilities// Initialize filtered abilities
39+
useEffect(() => {
40+
if (!isLoading && !hasError) {
41+
const sortedAbilities = [...sampleAbilities].sort((a, b) => a.abilityName.localeCompare(b.abilityName));
42+
setFilteredAbilities(sortedAbilities);
43+
}
44+
}, [isLoading, hasError]);
45+
46+
const handleViewDetails = (ability: Ability) => {
47+
setSelectedAbility(ability);
48+
openDetailsModal();
49+
};
50+
51+
const handleAddToAbilityManual = (ability: Ability) => {
52+
setSelectedAbility(ability);
53+
openAbilityManualModal();
54+
}; // Use useCallback to memoize the filter change handler
55+
const handleFilterChange = useCallback((filtered: Ability[]) => {
56+
// Create a new sorted array rather than modifying the input
57+
const sortedAbilities = [...filtered].sort((a, b) => a.abilityName.localeCompare(b.abilityName));
58+
setFilteredAbilities(sortedAbilities);
59+
}, []);
60+
61+
if (isLoading) {
62+
return (
63+
<Center h="70vh">
64+
<Loader size="xl" color={isDark ? 'blue.4' : 'blue.6'} />
65+
</Center>
66+
);
67+
}
68+
69+
if (hasError) {
70+
return (
71+
<Container size="md" py="xl">
72+
<Paper p="xl" withBorder radius="md" bg={isDark ? 'dark.6' : 'white'}>
73+
<Title order={2} ta="center" c="red" mb="xl">
74+
Error Loading Abilities
75+
</Title>
76+
<Text ta="center" c={isDark ? 'gray.2' : 'dark.7'}>
77+
Failed to fetch abilities. Please try again later.
78+
</Text>
79+
</Paper>
80+
</Container>
81+
);
82+
}
83+
84+
return (
85+
<Container size="xl" py="xl">
86+
<Group justify="space-between" mb="xl">
87+
<Title order={1} c={isDark ? 'gray.1' : 'dark.8'}>Abilities Library</Title>
88+
<ExportButton abilities={filteredAbilities} label="Export Abilities" />
89+
</Group> <AbilitiesFilter
90+
abilities={sampleAbilities}
91+
onFilterChange={handleFilterChange}
92+
/>
93+
94+
<Text mt="md" mb="md" c={isDark ? 'gray.3' : 'dark.7'}>
95+
{filteredAbilities.length} {filteredAbilities.length === 1 ? 'ability' : 'abilities'} found
96+
</Text>
97+
98+
<SimpleGrid
99+
cols={{ base: 1, xs: 2, md: 3, lg: 4 }}
100+
spacing="md"
101+
mt="xl"
102+
>
103+
{filteredAbilities.map((ability) => (
104+
<AbilityCard
105+
key={`${ability.abilityName} (${ability.abilityDiscipline})`}
106+
ability={ability}
107+
onViewDetails={handleViewDetails}
108+
onAddToAbilityManual={handleAddToAbilityManual}
109+
/>
110+
))}
111+
</SimpleGrid>
112+
113+
<AbilityDetailsModal
114+
ability={selectedAbility}
115+
opened={detailsModalOpened}
116+
onClose={closeDetailsModal}
117+
/>
118+
119+
<AddToAbilityManualModal
120+
ability={selectedAbility}
121+
opened={abilityManualModalOpened}
122+
onClose={closeAbilityManualModal}
123+
/>
124+
</Container>
125+
);
126+
}
127+
128+
// Create a client for React Query
129+
const queryClient = new QueryClient({
130+
defaultOptions: {
131+
queries: {
132+
staleTime: Infinity,
133+
refetchOnWindowFocus: false,
134+
},
135+
},
136+
});
137+
138+
// Sample abilities for our mock data
139+
const sampleAbilities: Ability[] = [
140+
{
141+
abilityName: "Deflecting Edge",
142+
abilityCp: 40,
143+
abilityDiscipline: "Balanced_Sword",
144+
abilityLevel: "Intermediate",
145+
abilityType: "Active",
146+
abilityDescription: "When an opponent attacks you with a melee weapon, you may spend a degree of success to attempt to redirect their blow. Make an opposed Balanced Sword roll against the attacker. If you succeed, their attack is redirected to another target within your weapon's reach, and your opponent must apply their attack result to the new target instead."
147+
},
148+
{
149+
abilityName: "Elemental Attunement",
150+
abilityCp: 30,
151+
abilityDiscipline: "Elementalism",
152+
abilityLevel: "Basic",
153+
abilityType: "Passive",
154+
abilityDescription: "You have an intuitive understanding of elemental magic. You gain a +1 bonus to all spellcasting rolls involving elemental magic and reduce the complexity of all elemental spells by 1.",
155+
complexityPyromancy: 1,
156+
complexityHydromancy: 1,
157+
complexityAeromancy: 1,
158+
complexityGeomancy: 1
159+
},
160+
{
161+
abilityName: "Whirlwind Strike",
162+
abilityCp: 60,
163+
abilityDiscipline: "Unbalanced_Sword",
164+
abilityLevel: "Advanced",
165+
abilityType: "Active",
166+
abilityDescription: "You spin in a deadly arc, striking all enemies within your reach. Make a single Unbalanced Sword roll and apply the result against the defense of all targets within your weapon's reach. This ability can only be used once per scene and requires a full turn to execute."
167+
},
168+
{
169+
abilityName: "Inner Strength",
170+
abilityCp: 25,
171+
abilityDiscipline: "Inner_Pillar",
172+
abilityLevel: "Basic",
173+
abilityType: "Passive",
174+
abilityDescription: "Through rigorous training, you have developed exceptional mental resilience. You gain a +2 bonus to all willpower checks to resist mental manipulation or control."
175+
},
176+
{
177+
abilityName: "Strategic Command",
178+
abilityCp: 45,
179+
abilityDiscipline: "Strategist",
180+
abilityLevel: "Intermediate",
181+
abilityType: "Active",
182+
abilityDescription: "As an action, you can analyze the battlefield and provide tactical guidance to your allies. Up to three allies of your choice gain a +1 bonus to their next attack or defense roll, provided they can hear and understand you."
183+
},
184+
{
185+
abilityName: "Shadow Step",
186+
abilityCp: 35,
187+
abilityDiscipline: "Sneak",
188+
abilityLevel: "Intermediate",
189+
abilityType: "Active",
190+
abilityDescription: "You can move silently from shadow to shadow. When in dim light or darkness, you can move up to half your movement speed without making any noise and without requiring a stealth check."
191+
}
192+
];
193+
194+
// Sample ability manual for context
195+
const sampleAbilityManual = {
196+
id: "123456",
197+
name: "Wizard's Spellbook",
198+
character: "Gandalf the Grey",
199+
description: "A collection of magical abilities for my wizard character",
200+
abilities: [sampleAbilities[0], sampleAbilities[1]],
201+
createdAt: new Date("2025-05-01"),
202+
updatedAt: new Date("2025-05-20"),
203+
};
204+
205+
// Mock for AbilityManualsContext
206+
const mockAbilityManualsContext = {
207+
AbilityManuals: [sampleAbilityManual],
208+
addAbilityManual: () => { },
209+
updateAbilityManual: () => { },
210+
deleteAbilityManual: () => { },
211+
getAbilityManual: (id: string) => {
212+
if (id === "123456") {
213+
return sampleAbilityManual;
214+
}
215+
return undefined;
216+
},
217+
addAbilityToAbilityManual: () => { },
218+
removeAbilityFromAbilityManual: () => { },
219+
};
220+
221+
// Mock abilities tags for the useAbilityTags hook
222+
queryClient.setQueryData(['abilityTags'], {
223+
data: {
224+
tags: [
225+
{
226+
tag: "offensive",
227+
name: "Offensive",
228+
description: "Abilities focused on attacking",
229+
abilities: ["Deflecting Edge", "Whirlwind Strike"]
230+
},
231+
{
232+
tag: "defensive",
233+
name: "Defensive",
234+
description: "Abilities focused on defense",
235+
abilities: ["Deflecting Edge"]
236+
},
237+
{
238+
tag: "magic",
239+
name: "Magic",
240+
description: "Magic-related abilities",
241+
abilities: ["Elemental Attunement"]
242+
},
243+
{
244+
tag: "strategy",
245+
name: "Strategy",
246+
description: "Strategic abilities",
247+
abilities: ["Strategic Command"]
248+
},
249+
{
250+
tag: "stealth",
251+
name: "Stealth",
252+
description: "Stealth abilities",
253+
abilities: ["Shadow Step"]
254+
},
255+
{
256+
tag: "mental",
257+
name: "Mental",
258+
description: "Mental abilities",
259+
abilities: ["Inner Strength"]
260+
}
261+
]
262+
}
263+
});
264+
265+
// Mock abilities data for the useAbilities hook
266+
queryClient.setQueryData(['abilities'], sampleAbilities);
267+
268+
const meta: Meta<typeof MockedAbilitiesPage> = {
269+
component: MockedAbilitiesPage,
270+
title: 'Pages/AbilitiesPage',
271+
tags: ['autodocs'],
272+
parameters: {
273+
layout: 'fullscreen',
274+
},
275+
decorators: [
276+
(Story) => {
277+
// Create a color scheme manager for storybook
278+
const colorSchemeManager = localStorageColorSchemeManager({ key: 'saga-abilities-storybook-color-scheme' });
279+
280+
return (
281+
<QueryClientProvider client={queryClient}>
282+
<ColorSchemeScript defaultColorScheme="light" />
283+
<MantineProvider theme={baseTheme} defaultColorScheme="light" colorSchemeManager={colorSchemeManager}>
284+
<Notifications position="top-right" />
285+
<ModalsProvider>
286+
<ThemeProvider>
287+
<AbilityManualsContext.Provider value={mockAbilityManualsContext}>
288+
<Story />
289+
</AbilityManualsContext.Provider>
290+
</ThemeProvider>
291+
</ModalsProvider>
292+
</MantineProvider>
293+
</QueryClientProvider>
294+
);
295+
},
296+
],
297+
};
298+
299+
export default meta;
300+
type Story = StoryObj<typeof MockedAbilitiesPage>;
301+
302+
// Default story showing the abilities page
303+
export const Default: Story = {
304+
args: {
305+
isLoading: false,
306+
hasError: false
307+
}
308+
};
309+
310+
// Loading state story
311+
export const Loading: Story = {
312+
args: {
313+
isLoading: true,
314+
hasError: false
315+
}
316+
};
317+
318+
// Error state story
319+
export const Error: Story = {
320+
args: {
321+
isLoading: false,
322+
hasError: true
323+
}
324+
};
325+
326+
// Dark mode variant
327+
export const DarkMode: Story = {
328+
args: {
329+
isLoading: false,
330+
hasError: false
331+
},
332+
parameters: {
333+
backgrounds: { default: 'dark' },
334+
},
335+
decorators: [
336+
(Story) => {
337+
const colorSchemeManager = localStorageColorSchemeManager({ key: 'saga-abilities-storybook-color-scheme' });
338+
339+
return (
340+
<QueryClientProvider client={queryClient}>
341+
<ColorSchemeScript defaultColorScheme="dark" />
342+
<MantineProvider theme={baseTheme} defaultColorScheme="dark" colorSchemeManager={colorSchemeManager}>
343+
<Notifications position="top-right" />
344+
<ModalsProvider>
345+
<ThemeProvider>
346+
<AbilityManualsContext.Provider value={mockAbilityManualsContext}>
347+
<Story />
348+
</AbilityManualsContext.Provider>
349+
</ThemeProvider>
350+
</ModalsProvider>
351+
</MantineProvider>
352+
</QueryClientProvider>
353+
);
354+
},
355+
],
356+
};

0 commit comments

Comments
 (0)