This is a web application written using the Phoenix web framework. ## Project guidelines - Use `mix precommit` alias when you are done with all changes and fix any pending issues - Use the already included and available `:req` (`Req`) library for HTTP requests, **avoid** `:httpoison`, `:tesla`, and `:httpc`. Req is included by default and is the preferred HTTP client for Phoenix apps ### Phoenix v1.8 guidelines - **Always** begin your LiveView templates with `` which wraps all inner content - The `MyAppWeb.Layouts` module is aliased in the `my_app_web.ex` file, so you can use it without needing to alias it again - Anytime you run into errors with no `current_scope` assign: - You failed to follow the Authenticated Routes guidelines, or you failed to pass `current_scope` to `` - **Always** fix the `current_scope` error by moving your routes to the proper `live_session` and ensure you pass `current_scope` as needed - Phoenix v1.8 moved the `<.flash_group>` component to the `Layouts` module. You are **forbidden** from calling `<.flash_group>` outside of the `layouts.ex` module - Out of the box, `core_components.ex` imports an `<.icon name="hero-x-mark" class="w-5 h-5"/>` component for hero icons. **Always** use the `<.icon>` component for icons, **never** use `Heroicons` modules or similar - **Always** use the imported `<.input>` component for form inputs from `core_components.ex` when available. `<.input>` is imported and using it will save steps and prevent errors - If you override the default input classes (`<.input class="myclass px-2 py-1 rounded-lg">)`) class with your own values, no default classes are inherited, so your custom classes must fully style the input ### JS and CSS guidelines - **Use Tailwind CSS classes and custom CSS rules** to create polished, responsive, and visually stunning interfaces. - Tailwindcss v4 **no longer needs a tailwind.config.js** and uses a new import syntax in `app.css`: @import "tailwindcss" source(none); @source "../css"; @source "../js"; @source "../../lib/my_app_web"; - **Always use and maintain this import syntax** in the app.css file for projects generated with `phx.new` - **Never** use `@apply` when writing raw css - **Always** manually write your own tailwind-based components instead of using daisyUI for a unique, world-class design - Out of the box **only the app.js and app.css bundles are supported** - You cannot reference an external vendor'd script `src` or link `href` in the layouts - You must import the vendor deps into app.js and app.css to use them - **Never write inline tags within templates** ### UI/UX & design guidelines - **Produce world-class UI designs** with a focus on usability, aesthetics, and modern design principles - Implement **subtle micro-interactions** (e.g., button hover effects, and smooth transitions) - Ensure **clean typography, spacing, and layout balance** for a refined, premium look - Focus on **delightful details** like hover effects, loading states, and smooth page transitions ## usage_rules usage _A config-driven dev tool for Elixir projects to manage AGENTS.md files and agent skills from dependencies_ ## Using Usage Rules Many packages have usage rules, which you should *thoroughly* consult before taking any action. These usage rules contain guidelines and rules *directly from the package authors*. They are your best source of knowledge for making decisions. ## Modules & functions in the current app and dependencies When looking for docs for modules & functions that are dependencies of the current project, or for Elixir itself, use `mix usage_rules.docs` ``` # Search a whole module mix usage_rules.docs Enum # Search a specific function mix usage_rules.docs Enum.zip # Search a specific function & arity mix usage_rules.docs Enum.zip/1 ``` ## Searching Documentation You should also consult the documentation of any tools you are using, early and often. The best way to accomplish this is to use the `usage_rules.search_docs` mix task. Once you have found what you are looking for, use the links in the search results to get more detail. For example: ``` # Search docs for all packages in the current application, including Elixir mix usage_rules.search_docs Enum.zip # Search docs for specific packages mix usage_rules.search_docs Req.get -p req # Search docs for multi-word queries mix usage_rules.search_docs "making requests" -p req # Search only in titles (useful for finding specific functions/modules) mix usage_rules.search_docs "Enum.zip" --query-by title ``` ## usage_rules:elixir usage # Elixir Core Usage Rules ## Pattern Matching - Use pattern matching over conditional logic when possible - Prefer to match on function heads instead of using `if`/`else` or `case` in function bodies - `%{}` matches ANY map, not just empty maps. Use `map_size(map) == 0` guard to check for truly empty maps ## Error Handling - Use `{:ok, result}` and `{:error, reason}` tuples for operations that can fail - Avoid raising exceptions for control flow - Use `with` for chaining operations that return `{:ok, _}` or `{:error, _}` ## Common Mistakes to Avoid - Elixir has no `return` statement, nor early returns. The last expression in a block is always returned. - Don't use `Enum` functions on large collections when `Stream` is more appropriate - Avoid nested `case` statements - refactor to a single `case`, `with` or separate functions - Don't use `String.to_atom/1` on user input (memory leak risk) - Lists and enumerables cannot be indexed with brackets. Use pattern matching or `Enum` functions - Prefer `Enum` functions like `Enum.reduce` over recursion - When recursion is necessary, prefer to use pattern matching in function heads for base case detection - Using the process dictionary is typically a sign of unidiomatic code - Only use macros if explicitly requested - There are many useful standard library functions, prefer to use them where possible ## Function Design - Use guard clauses: `when is_binary(name) and byte_size(name) > 0` - Prefer multiple function clauses over complex conditional logic - Name functions descriptively: `calculate_total_price/2` not `calc/2` - Predicate function names should not start with `is` and should end in a question mark. - Names like `is_thing` should be reserved for guards ## Data Structures - Use structs over maps when the shape is known: `defstruct [:name, :age]` - Prefer keyword lists for options: `[timeout: 5000, retries: 3]` - Use maps for dynamic key-value data - Prefer to prepend to lists `[new | list]` not `list ++ [new]` ## Mix Tasks - Use `mix help` to list available mix tasks - Use `mix help task_name` to get docs for an individual task - Read the docs and options fully before using tasks ## Testing - Run tests in a specific file with `mix test test/my_test.exs` and a specific test with the line number `mix test path/to/test.exs:123` - Limit the number of failed tests with `mix test --max-failures n` - Use `@tag` to tag specific tests, and `mix test --only tag` to run only those tests - Use `assert_raise` for testing expected exceptions: `assert_raise ArgumentError, fn -> invalid_function() end` - Use `mix help test` to for full documentation on running tests ## Debugging - Use `dbg/1` to print values while debugging. This will display the formatted value and other relevant information in the console. ## usage_rules:otp usage # OTP Usage Rules ## GenServer Best Practices - Keep state simple and serializable - Handle all expected messages explicitly - Use `handle_continue/2` for post-init work - Implement proper cleanup in `terminate/2` when necessary ## Process Communication - Use `GenServer.call/3` for synchronous requests expecting replies - Use `GenServer.cast/2` for fire-and-forget messages. - When in doubt, use `call` over `cast`, to ensure back-pressure - Set appropriate timeouts for `call/3` operations ## Fault Tolerance - Set up processes such that they can handle crashing and being restarted by supervisors - Use `:max_restarts` and `:max_seconds` to prevent restart loops ## Task and Async - Use `Task.Supervisor` for better fault tolerance - Handle task failures with `Task.yield/2` or `Task.shutdown/2` - Set appropriate task timeouts - Use `Task.async_stream/3` for concurrent enumeration with back-pressure