diff --git a/.cargo/config.toml b/.cargo/config.toml index 17380505b..bdeac036f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,9 +1,6 @@ -[build] -rustflags = ["--cfg", "tokio_unstable"] - # Windows has stack overflows when calling from Tauri, so we increase the default stack size used by the compiler [target.'cfg(windows)'] -rustflags = ["--cfg", "tokio_unstable", "-C", "link-args=/STACK:16777220"] +rustflags = ["-C", "link-args=/STACK:16777220"] [target.x86_64-pc-windows-msvc] linker = "rust-lld" diff --git a/.claude/skills/api-module/SKILL.md b/.claude/skills/api-module/SKILL.md new file mode 100644 index 000000000..37b4ac778 --- /dev/null +++ b/.claude/skills/api-module/SKILL.md @@ -0,0 +1,18 @@ +--- +name: api-module +description: Add a new API endpoint module to packages/api-client from an OpenAPI schema. Use when adding new backend endpoints, creating API client modules, or when an openapi.yml is provided. +argument-hint: +--- + +Refer to the standard: @standards/frontend/ADDING_API_MODULES.md + +## Steps + +1. **Read the OpenAPI schema** at `$ARGUMENTS` — identify the endpoints, request/response shapes, and path parameters. +2. **Read the standard above** for naming conventions, type rules, and the module registration pattern. +3. **Determine the service and version** — the URL path prefix tells you which service directory and version namespace to use (e.g. `/v3/projects` → `labrinth/v3/`). +4. **Define types in `types.ts`** — types must match the API response 1:1. Use the OpenAPI schema as the source of truth. Do not reshape or rename fields. +5. **Create the module class** — extend `BaseModule`, implement each endpoint as a method. Use the correct HTTP verb and request options pattern from the standard. +6. **Register in `MODULE_REGISTRY`** — add the module entry so it's auto-instantiated on the client. +7. **Export types** from the service's barrel `index.ts`. +8. **Verify** — check that the module compiles and the types are accessible from `@modrinth/api-client`. diff --git a/.claude/skills/cross-platform-pages/SKILL.md b/.claude/skills/cross-platform-pages/SKILL.md new file mode 100644 index 000000000..6558eed3c --- /dev/null +++ b/.claude/skills/cross-platform-pages/SKILL.md @@ -0,0 +1,26 @@ +--- +name: cross-platform-pages +description: Convert a page to the cross-platform page system so it works in both the website and the desktop app. Use when moving a page into packages/ui/src/layouts/, creating shared or wrapped layouts, or setting up DI contracts for platform abstraction. +argument-hint: +--- + +Refer to the standards: @standards/frontend/CROSS_PLATFORM_PAGES.md and @standards/frontend/DEPENDENCY_INJECTION.md + +## Steps + +1. **Read the target page** at `$ARGUMENTS` and understand its data sources, mutations, and navigation. +2. **Read the standards above** to understand the shared vs wrapped distinction and the DI pattern. +3. **Decide the category:** + - **Wrapped** (`layouts/wrapped/`) — if the page uses the same API source on both platforms (e.g. web requests, not Tauri plugins). Just move the page component into `packages/ui` and import it from both frontends. + - **Shared** (`layouts/shared/`) — if the page has different data-fetching logic per platform (e.g. website uses `api-client`, app uses Tauri `invoke`). Requires a DI contract. +4. **For shared layouts:** + - Define a DI contract interface in `providers/` capturing all platform-specific operations. + - Create the layout component that injects the context and handles all UI logic. + - Extract reusable stateful logic (search, filtering, selection) into `composables/`. + - Implement the contract separately in each frontend (`apps/frontend/`, `apps/app-frontend/`). +5. **For wrapped pages:** + - Move the page component into `packages/ui/src/layouts/wrapped/` matching the route structure. + - Replace any platform-specific imports with shared utilities. + - Import and render the wrapped page from both frontends as a simple component. + - If the layout uses TanStack Query for initial route paint with `ReadyTransition` / `useReadyState`, each platform route shell must call `ensureQueryData` for those queries with matching keys and fetchers — see **Platform route shells: prefetch with `ensureQueryData`** in `standards/frontend/CROSS_PLATFORM_PAGES.md`. +6. **Verify** the page renders correctly by checking for missing imports and that all DI contracts are satisfied. diff --git a/.claude/skills/figma-mcp/SKILL.md b/.claude/skills/figma-mcp/SKILL.md new file mode 100644 index 000000000..defabf8d2 --- /dev/null +++ b/.claude/skills/figma-mcp/SKILL.md @@ -0,0 +1,22 @@ +--- +name: figma-mcp +description: Use the Figma MCP server to translate a Figma design into a Vue page or component layout. Use when the user provides a Figma URL, asks to implement a design, or wants to draft a page layout from Figma. +argument-hint: +--- + +Refer to the standard: @standards/frontend/FIGMA_MCP_USAGE.md +Also read @packages/ui/CLAUDE.md for color token mapping and component conventions. + +## Steps + +1. **Parse the Figma URL** from `$ARGUMENTS` — extract the `fileKey` and `nodeId`. Convert `-` to `:` in the node ID. +2. **Read the standards above** for the available tools, adaptation rules, and color usage. +3. **Call `get_design_context`** with the extracted `nodeId` and `fileKey`, using `clientLanguages: "typescript,html,css"` and `clientFrameworks: "vue"`. This is always the first tool to call. +5. **Adapt the output to the Modrinth codebase:** + - Map Figma color variables to `surface-*` / `text-*` tokens — never use Figma's aliased names directly. + - Check `packages/ui/src/components/` for existing components that match elements in the design (buttons, cards, modals, inputs, etc.). + - Check `packages/assets/styles/variables.scss` for tokens not exposed in Figma. + - Match spacing values exactly from the design. +6. **Use `get_screenshot`** if you need a closer visual reference of specific nodes. +7. **Use `get_variable_defs`** to verify which design tokens are applied to ambiguous elements. +8. **Build the component** as a Vue SFC using Tailwind classes and the project's existing component library. diff --git a/.claude/skills/i18n-pass/SKILL.md b/.claude/skills/i18n-pass/SKILL.md new file mode 100644 index 000000000..7edd69996 --- /dev/null +++ b/.claude/skills/i18n-pass/SKILL.md @@ -0,0 +1,24 @@ +--- +name: i18n-pass +description: Perform an i18n localization pass on changed files or a pull request, converting hard-coded English strings to the @modrinth/ui i18n system. Use when internationalizing a set of changes, reviewing a PR for untranslated strings, or converting a specific component. +argument-hint: [file-path-or-pr-number] +--- + +Refer to the standard: @standards/frontend/INTERNATIONALIZATION.md + +## Steps + +1. **Identify the scope of changes:** + - If `$ARGUMENTS` is a PR number, run `gh pr diff $ARGUMENTS` to get the changed files. + - If `$ARGUMENTS` is a file path, use that directly. + - If no argument, check `git diff` for uncommitted changes. +2. **Read the standard above** for the message definition pattern, ICU format rules, and `IntlFormatted` usage. +3. **Filter to Vue SFCs** — only `.vue` files need i18n passes. Skip non-component files. +4. **For each file, scan for hard-coded strings:** + - `