vicinae is a multi purpose launcher built with QtQuick. This file focuses on explaining general rules. For everything else, just look at the codebase.

It uses QML for presentation and C++ for business logic.
React/Typescript is used to power the extension API.

For development: 
- On UNIXes, use `make debug`.
- On Windows use `cmake --preset windows-relwithdebinfo` (full debug builds only when needed)

## Separation of concerns

QML is exclusively used for presentation. DO NOT put business logic inside QML files.
It's okay to make small JavaScript functions and utilities as long as their purpose is to help deal with presentation concerns.

To implement views, we use a bridge pattern: a C++ "view host" class exposes data and actions as `Q_PROPERTY`s and `Q_INVOKABLE`s, and a QML component binds to them.

Remain pragmatic about this, we don't want to go back and forth between QML and C++ to execute 2 lines of logic. But most of the time, the separation is clear.

## C++/QT coding rules

We use C++23 so we have access to most modern C++ features. 
Here are a few rules to keep in mind:

- Lack of value: use `std::optional` instead of arbitrary value discriminants such as the empty string. If this is not possible or goes against a commonly used convention, respect the convention first, no shoehorning. If we are dealing with raw pointers, the nullable component is already part of it so no need to add a layer of indirection.
- Prefer `std::array` to C arrays where it makes sense. You can use `std::to_array` to initialize them automatically sized from an initializer list.
- C symbols: always use the `std::` prefix for symbols coming from C headers (`std::uint64_t`, `std::size_t`, `std::memset`, `std::getenv`, ...) and include the `<c*>` header form. POSIX-only symbols (`ssize_t`, `recv`, ...) have no `std::` counterpart and stay unprefixed.
- use `constexpr` and `consteval` as much as possible, we want to move what we can at compile time.
- Avoid raw pointers: unless we are dealing with QT's ownership model or a C API. For QT classes that are not QObjects, you should probably use standard smart pointers as recommended in modern QT. 
- QObjects must be deleted using `deleteLater` and never raw `delete`, as this can cause memory corruption with signal and slots. Calling `delete` on a `QObject` is a **STRONG** code smell.
- Watch for implicit copies: avoid copies as much as possible, use non owning containers when you can (`std::span`, `std::string_view`, `QStringView`) and just `std::move` the data when applicable. When copy is the safest option, use copy: don't go out of your way to respect this rule.
- QT vs STL classes: prefer STL containers over QT counterparts.
- vectors: always reserve `std::vector`s and use `emplace_back` to push new elements.
- STL ranges: we like to use `<ranges>` where it works well. If you need to use a `for` loop, prioritize ranged ones or use `std::views::enumerate` if you need to deal with indexes and data.
- casts: as you may notice from the clang-tidy rules, using `static_cast` is generally discouraged for classes that are related by inheritance, as it is very easy to make mistakes. Unless the logic is in a hot path (e.g a QT event filter), it's preferrable to use `dynamic_cast` as a safety net when there is no safer option. Note however that `dynamic_cast` is STILL a code smell and should generally be avoided in favor of better polymorphism.
- Platform guards: use Qt's `Q_OS_*` macros (`Q_OS_MACOS`, `Q_OS_LINUX`, `Q_OS_WIN`) rather than `Q_OS_DARWIN` or compiler-defined ones like `__APPLE__`. `Q_OS_MACOS` specifically targets desktop macOS (excluding iOS/tvOS) and keeps guards consistent across the codebase.
- As Vicinae becomes cross-platform, it is really important that we implement functionality the way it is intended to on the target platform. We should avoid using weird hacks and always use native APIs when possible.

## Coding style

- For QObject classes: always put `Q_OBJECT`, `Q_PROPERTY`, `Q_INVOKABLE`, and `signals` on top of the class, in this order.
- Do not add unnecessary comments. Use them sparingly when something really needs to be made explicit.
- Include order: system headers before local headers.
- Header guards: use `#pragma once`.
- String types: use `std::string` internally, `QString` only at the Qt boundary.
- Constants: use `UPPER_SNAKE_CASE` for `constexpr` and `static const` values.
- Do not overuse comments, only add them when they provide actual value

## Linting and formatting

We format all our code using `clang-format`. We have a `make format` rule that will automatically format the entire codebase if necessary.

To format the codebase:

- On UNIXes, run `make format`
- On Windows, run the PowerShell script at `.\scripts\format.ps1`

ALWAYS run `make format` at the end of a development session to make sure formatting is properly applied to all files.

All our code is also linted with `clang-tidy` in order to make detecting common mistakes easier. `clang-tidy` violations may be acceptable under some circumstances, and should be implemented using `//NOLINTBEGIN(<rule>)` and `NOLINTEND(<rule>)`. comments. Inline `//NOLINT` comments are generally discouraged because they can break after formatting. If a nolint directive does not work (e.g we're dealing with an internal STL false positive) then you can do a local override of the `clang-tidy` configuration to explicitly disable the faulty check.

## QML rules

When writing JavaScript inside QML files, use ES6 syntax to the largest extent possible.

Try to keep the amount of logic in these files small. Logic in QML is only for presentation concerns: metrics computation, hover on signal, etc...

Some configuration and theming options may need to be accessed directly in QML. We expose a global config and theme bridges for this use case.

## Internationalization

User-visible natural-language strings must be marked for translation. English source text is the translation key.

- QML: wrap literals in `qsTr("...")`.
- C++ classes with `Q_OBJECT`: use `tr("...")`.
- C++ classes without `Q_OBJECT`: NEVER call the inherited `tr()` (it resolves to the wrong context at runtime). Add `Q_DECLARE_TR_FUNCTIONS(ClassName)` at the top of the class, or for a single string use `QCoreApplication::translate("ClassName", "...")`.
- Never store translated strings in `static`/`constexpr` data. Plurals use `tr("%n item(s)", nullptr, n)`. Never concatenate translated fragments: use `%1` placeholders with `.arg()`.
- Preserve brand and product names such as "Raycast Store", "Raycast Compat", and "Extension Store". Translate the surrounding prose, not the name itself.
- Never translate stable technical notation such as ids, acronyms, unit symbols (`MB`, `GB`), filenames, protocol names, icon names (`ImageURL::builtin`), log output, storage keys, or strings compared as discriminants.
- Use natural, concise language appropriate for a desktop launcher. Prefer familiar UI wording over literal translations, bureaucratic language, or an excessively formal or polite tone.
- Use the platform's official localized terminology for operating-system features and settings pages.
- Use stable explicit translation contexts for shared domain strings. Add translator notes or disambiguation only when the source text is genuinely ambiguous.
- Source text and location metadata in `src/server/translations/*.ts` are generated: never hand-edit them. Edit
  translations with Qt Linguist. Contributors are not required to translate new strings or include regenerated catalogs in
  feature PRs. Maintainers periodically run `make update-translations` in dedicated localization updates.

## General guidelines

- Search should always be fuzzy, unless it has a good reason not to. We have fuzzy utilities already, and we like to use our fuzzy trait system by specializing the `FuzzySearchable` template
- We are building Vicinae with cross-platform compatibility in mind (Linux, MacOS, Windows) so abstractions need to be designed accordingly. While Linux is our main development target, we should avoid making abstractions that are too platform specific.

## React/TypeScript extensions

Everything related to the Typescript SDK can be found under `src/typescript`. More information can be obtained by reading `README.md` files under this directory if you need to work on this part. 
