|
| 1 | +# Gemini Project Helper |
| 2 | + |
| 3 | +This document provides guidance for the Gemini AI assistant on how to interact with the `outfoxweb-astro` project. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is a modern website built using the Astro framework. It appears to be a portfolio or agency website, with a focus on web development, design, and SEO services. The project uses Svelte for interactive UI components and TypeScript for type safety. |
| 8 | + |
| 9 | +## Technologies |
| 10 | + |
| 11 | +- **Framework**: [Astro](https://astro.build/) |
| 12 | +- **UI Components**: [Svelte](https://svelte.dev/) |
| 13 | +- **Styling**: [Tailwind CSS](https://tailwindcss.com/) |
| 14 | +- **Language**: [TypeScript](https://www.typescriptlang.org/) |
| 15 | +- **Package Manager**: [Yarn](https://yarnpkg.com/) |
| 16 | +- **Content**: Astro Content Collections (for the blog) |
| 17 | + |
| 18 | +## Key Directories |
| 19 | + |
| 20 | +- `src/pages`: Contains the site's pages and API routes. Each file corresponds to a URL endpoint. |
| 21 | +- `src/components`: Houses reusable UI components, primarily written in Svelte (`.svelte`). |
| 22 | +- `src/layouts`: Defines the overall page structure, like the main `Layout.astro`. |
| 23 | +- `src/content`: Manages content collections, such as blog posts in `src/content/blog/`. |
| 24 | +- `src/styles`: Contains global CSS and Tailwind CSS configuration. |
| 25 | +- `public/`: Stores static assets like images, fonts, and favicons that are copied directly to the build output. |
| 26 | + |
| 27 | +## Common Commands |
| 28 | + |
| 29 | +The project uses `yarn` as its package manager. The following commands are likely configured in `package.json`: |
| 30 | + |
| 31 | +- **Start development server**: `yarn dev` |
| 32 | +- **Create a production build**: `yarn build` |
| 33 | +- **Preview the production build**: `yarn preview` |
| 34 | +- **Run Astro's type-checker**: `yarn astro check` |
| 35 | +- **Linting**: Check `package.json` for a `lint` script. |
| 36 | + |
| 37 | +When making changes, please adhere to the existing coding style, including TypeScript best practices and the conventions used in the Astro and Svelte components. |
| 38 | + |
| 39 | +## Framework and Technology Guidelines |
| 40 | + |
| 41 | +For specific guidelines on the technologies used in this project, please refer to the following documents in the `instructions/` directory. These files contain detailed rules, conventions, and examples that are critical for maintaining code quality and consistency. |
| 42 | + |
| 43 | +- **Svelte 5:** Refer to `instructions/svelte_rules.md` for comprehensive rules on Svelte 5 development, including the mandatory use of the Runes system, state management patterns, and component best practices. |
| 44 | +- **Convex:** Refer to `instructions/convex_rules.md` for guidelines on schema design, queries, mutations, actions, and other Convex-specific features. |
| 45 | +- **Svelte + Convex Integration:** Refer to `instructions/svelte_and_convex.md` for information on how to use Svelte with Convex, including a link to the `convex-svelte` client-side package documentation. |
| 46 | + |
| 47 | +# General Coding Style Rules |
| 48 | + |
| 49 | +Keep your explanations brief and to the point. |
| 50 | + |
| 51 | +- Use descriptive variable and function names |
| 52 | +- Use functional and declarative programming patterns; avoid unnecessary classes except for state machines |
| 53 | +- Prefer iteration and modularization over code duplication |
| 54 | +- Use TypeScript for all code; prefer types over interfaces |
| 55 | +- Implement proper lazy loading for images and other assets |
| 56 | +- Wherever appropriate, opt for early returns as opposed to multiple or nested if/else conditions or switch statements |
| 57 | +- Prefer to abstract complicated logic into small pure functions with descriptive names and few parameters as necessary |
| 58 | + |
| 59 | +### Accessibility |
| 60 | + |
| 61 | +- Ensure proper semantic HTML structure |
| 62 | +- Implement ARIA attributes where necessary |
| 63 | +- Ensure keyboard navigation support for interactive elements |
| 64 | + |
| 65 | +## Prefer to "early out" in functions rather than nesting statements |
| 66 | + |
| 67 | +Instead of this: |
| 68 | + |
| 69 | +```ts |
| 70 | +let theResult = ""; |
| 71 | +if (someVariable == 42) { |
| 72 | + theResult = "the answer"; |
| 73 | +} |
| 74 | +else if (someVariable == 69) { |
| 75 | + theResult = "nice"; |
| 76 | +} |
| 77 | +else { |
| 78 | + theResult = "nope"; |
| 79 | +} |
| 80 | +return theResult |
| 81 | +``` |
| 82 | + |
| 83 | +You should write this: |
| 84 | + |
| 85 | +```ts |
| 86 | +if (someVariable == 42) |
| 87 | + return "the answer"; |
| 88 | +if (someVariable == 69) |
| 89 | + return "nice"; |
| 90 | +return "nope"; |
| 91 | +``` |
| 92 | + |
| 93 | +Another example. Instead of this: |
| 94 | + |
| 95 | +```ts |
| 96 | +const showAdminPanel = () => { |
| 97 | + if (wifi) { |
| 98 | + if (login) { |
| 99 | + if (admin) { |
| 100 | + seeAdminPanel(); |
| 101 | + } else { |
| 102 | + console.log('must be an admin') |
| 103 | + } |
| 104 | + } else { |
| 105 | + console.log('must be logged in') |
| 106 | + } |
| 107 | + } else { |
| 108 | + console.log('must be connected to wifi') |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +Do this: |
| 114 | + |
| 115 | +```ts |
| 116 | +const showAdminPanel = () => { |
| 117 | + if (!wifi) return handleNoWifi() |
| 118 | + |
| 119 | + if (!login) return handleNotLoggedIn() |
| 120 | + |
| 121 | + if (!admin) return handleNotAdmin() |
| 122 | + |
| 123 | + seeAdminPanel(); |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +## Prefer to use the "object in object out" pattern when writing typescript functions |
| 128 | + |
| 129 | +So instead of writing this: |
| 130 | + |
| 131 | +```ts |
| 132 | +function myFunction(firstArg: string, second: number, isSomething?: boolean) { |
| 133 | + // ... |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +You should write: |
| 138 | + |
| 139 | +```ts |
| 140 | +function myFunction({ firstArg, second, isSomething }: { firstArg: string, second: number, isSomething?: boolean }) { |
| 141 | + // ... |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +If the function needs to return multiple values then return an object: |
| 146 | + |
| 147 | +```ts |
| 148 | +function calculateSomething() { |
| 149 | + return { |
| 150 | + theAnswer: 42, |
| 151 | + reason: "the computer said so" |
| 152 | + } |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +## Prefer if-statements with early returns over switch statements |
| 157 | + |
| 158 | +Instead of this: |
| 159 | + |
| 160 | +```ts |
| 161 | +function doSomething(kind: MyKind) { |
| 162 | + switch (kind) { |
| 163 | + case "a": |
| 164 | + return "it was A"; |
| 165 | + case "b": |
| 166 | + return "it was B"; |
| 167 | + case "c": |
| 168 | + return "it was C"; |
| 169 | + } |
| 170 | +} |
| 171 | +``` |
| 172 | + |
| 173 | +Prefer this: |
| 174 | + |
| 175 | +```ts |
| 176 | +function doSomething(kind: MyKind) { |
| 177 | + if (kind === "a") return "it was A"; |
| 178 | + if (kind === "b") return "it was B"; |
| 179 | + if (kind === "c") return "it was C"; |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +## You should generally never use the non-null assertion operator to trick the typescript compiler |
| 184 | + |
| 185 | +You should never do this: |
| 186 | + |
| 187 | +```ts |
| 188 | +function doSomething(myObj: { value: string } | null) { |
| 189 | + console.log(myObj!.value); |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +Instead do this: |
| 194 | + |
| 195 | +```ts |
| 196 | +function doSomething(myObj: { value: string } | null) { |
| 197 | + if (!myObj) throw new Error("myObj is null"); |
| 198 | + console.log(myObj.value); |
| 199 | +} |
| 200 | +``` |
| 201 | + |
| 202 | +## Error Handling |
| 203 | + |
| 204 | +- Prefer try/catch blocks for error handling, even multiple within the same function if it keeps the code clean |
| 205 | +- Use explicit error checking rather than ignoring potential failures |
| 206 | + |
| 207 | +Example: |
| 208 | + |
| 209 | +```ts |
| 210 | +async function processData({ input, backup }: { input: string, backup?: string }) { |
| 211 | + try { |
| 212 | + return await primaryProcessor(input); |
| 213 | + } catch (primaryError) { |
| 214 | + if (!backup) throw new Error("Primary processing failed and no backup provided"); |
| 215 | + |
| 216 | + try { |
| 217 | + return await backupProcessor(backup); |
| 218 | + } catch (backupError) { |
| 219 | + throw new Error(`Both primary and backup processing failed: ${primaryError.message}, ${backupError.message}`); |
| 220 | + } |
| 221 | + } |
| 222 | +} |
| 223 | +``` |
| 224 | + |
| 225 | +## Type Definitions |
| 226 | + |
| 227 | +- Prefer type unions over enums |
| 228 | +- Use utility types like `Pick`, `Omit`, `Partial` when they improve code clarity |
| 229 | + |
| 230 | +Instead of this: |
| 231 | + |
| 232 | +```ts |
| 233 | +enum Status { |
| 234 | + PENDING = "pending", |
| 235 | + COMPLETED = "completed", |
| 236 | + FAILED = "failed" |
| 237 | +} |
| 238 | +``` |
| 239 | + |
| 240 | +Prefer this: |
| 241 | + |
| 242 | +```ts |
| 243 | +type Status = "pending" | "completed" | "failed"; |
| 244 | +``` |
| 245 | + |
| 246 | +Example using utility types: |
| 247 | + |
| 248 | +```ts |
| 249 | +type User = { |
| 250 | + id: string; |
| 251 | + name: string; |
| 252 | + email: string; |
| 253 | + role: "admin" | "user"; |
| 254 | + createdAt: Date; |
| 255 | +} |
| 256 | + |
| 257 | +type CreateUser = Omit<User, "id" | "createdAt">; |
| 258 | +type UpdateUser = Partial<Pick<User, "name" | "email">>; |
| 259 | +``` |
| 260 | + |
| 261 | +## Import/Export Patterns |
| 262 | + |
| 263 | +- Prefer named exports over default exports |
| 264 | +- Use barrel exports (index.ts files) for grouping related exports like images or components |
| 265 | +- Follow generally accepted import ordering conventions |
| 266 | + |
| 267 | +Example barrel export: |
| 268 | + |
| 269 | +```ts |
| 270 | +import home from "@/assets/icons/home.png"; |
| 271 | +import search from "@/assets/icons/search.png"; |
| 272 | +import person from "@/assets/icons/person.png"; |
| 273 | +import logo from "@/assets/icons/logo.png"; |
| 274 | +import save from "@/assets/icons/save.png"; |
| 275 | +import star from "@/assets/icons/star.png"; |
| 276 | +import play from "@/assets/icons/play.png"; |
| 277 | +import arrow from "@/assets/icons/arrow.png"; |
| 278 | + |
| 279 | +export const icons = { |
| 280 | + home, |
| 281 | + search, |
| 282 | + person, |
| 283 | + logo, |
| 284 | + save, |
| 285 | + star, |
| 286 | + play, |
| 287 | + arrow, |
| 288 | +}; |
| 289 | +``` |
| 290 | + |
| 291 | +Example import usage: |
| 292 | + |
| 293 | +```ts |
| 294 | +import { icons } from '@/constants/icons' |
| 295 | +// |
| 296 | +<img src={icons.home} /> |
| 297 | +``` |
0 commit comments