| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 14 | 8 | 0% |
| Commands | 5 | 19 | 13 | 14% |
| Section tags | 2 | 7 | 3 | 17% |
What each file covers
Sections
0 shared · 14 only in A · 8 only in B- − Instructions for GitHub Copilot
- − Purpose
- − Single Sources of Truth (SOT)
- − Build & Run (local dev; use Makefile first)
- − Tests & Lint
- − Project Structure & Languages
- − Code Review Instructions (for Copilot)
- − Style & Patterns
- − Commits & Pull Requests
- − Performance & Reliability
- − When Unsure
- − References To Help Copilot Answer Questions
- − Output Expectations
- − Safety Checklist Before Proposing a CLI Command
- + Frontend Guidelines
- + Dependencies & Pins
- + Frontend Linting & Test Entry Points
- + Templates, Session Bootstrap & Browser Baseline
- + Translations
- + Focus Management
- + Playwright MCP Usage
- + Frontend Test Gotchas
Commands
5 shared · 19 only in A · 13 only in B- − make help
- − make list
- − make docker-build
- − docker compose up
- − docker compose logs -f --tail=100 photoprism
- − make dep
- − make build-go
- − make watch-js
- − make test
- − make test-go
- − make test-short
- − go test ./internal/<pkg> -run '<TestName>' -count=1
- − make fmt-go
- − make lint-go
- − make fmt-js
- − make lint-js
- − make fmt-go swag-fmt swag
- − go.mod
- − go.sum
- + git log -p -S "<pkg>" -- frontend/package.json
- + npm install --ignore-scripts --no-audit --no-fund --no-update-notifier
- + make audit
- + npm ls <pkg> --all
- + make -C frontend help
- + make -C frontend list
- + make storage/acceptance
- + make acceptance-sqlite-restart
- + make wait-2
- + make acceptance-sqlite-stop
- + make gettext-extract
- + make -C plus gettext-extract
- + npx
- make build-js
- make test-js
- make vitest-watch
- make vitest-coverage
- make notice
Section tags
2 shared · 7 only in A · 3 only in B- − build
- − lint-format
- − architecture
- − git-pr
- − performance
- − do-not
- − agent-behaviour
- + setup
- + testing-strategy
- + dependencies
- test
- code-style
Line diff
photoprism/photoprism · .github/copilot-instructions.md
@@ −1 @@
1# Instructions for GitHub Copilot
2
3**Last Updated:** July 28, 2026
4
5## Purpose
6
7- Provide Copilot with the single sources of truth for building, testing, and contributing to PhotoPrism.
8- Improve PR reviews and code suggestions by aligning them with our documented workflows and style.
9- Path-specific rules that apply on top of this file live in `.github/instructions/*.instructions.md`.
10
11## Single Sources of Truth (SOT)
12
13- Makefile targets (always prefer existing targets): https://github.com/photoprism/photoprism/blob/develop/Makefile
14- Developer Guide – Setup: https://docs.photoprism.app/developer-guide/setup/
15- Developer Guide – Tests: https://docs.photoprism.app/developer-guide/tests/
16- Contributing: https://github.com/photoprism/photoprism/blob/develop/CONTRIBUTING.md
17- Security: https://github.com/photoprism/photoprism/blob/develop/SECURITY.md
18- REST API (Swagger): https://docs.photoprism.dev/
19- REST API Guide: https://docs.photoprism.app/developer-guide/api/
20- Agents reference for tools/commands: https://github.com/photoprism/photoprism/blob/develop/AGENTS.md
21- Code maps: https://github.com/photoprism/photoprism/blob/develop/CODEMAP.md (backend) and https://github.com/photoprism/photoprism/blob/develop/frontend/CODEMAP.md (frontend)
22- Terminology: https://github.com/photoprism/photoprism/blob/develop/GLOSSARY.md
23
24## Build & Run (local dev; use Makefile first)
25
26- Show common tasks: `make help` (`make list` shows all targets)
27- Build local image: `make docker-build`
28- Start dev env: `docker compose up` (add `-d` for detached, or run `make up`)
29- Logs: `docker compose logs -f --tail=100 photoprism`
30- Open app: http://localhost:2342/ (HTTP) / https://app.localssl.dev/ (TLS via Traefik when enabled)
31- From the dev container:
32 - Install deps: `make dep`
33 - Build frontend: `make build-js` (or `cd frontend && npm run build`)
34 - Build backend: `make build-go`
35 - Watch frontend: `make watch-js` (stop with Ctrl+C)
36 - Run server binary: `./photoprism start`
37
38## Tests & Lint
39
40- Full tests: `make test`
41- Frontend tests (Vitest): `make test-js` (watch: `make vitest-watch`, coverage: `make vitest-coverage`)
42- Backend tests: `make test-go` (slow); `make test-short` for a fast parallel run
43- Targeted Go tests: `go test ./internal/<pkg> -run '<TestName>' -count=1`
44- Formatting and linting:
45 - Go: `make fmt-go` (gofmt + goimports) and `make lint-go` (golangci-lint)
46 - JS/Vue: `make fmt-js` and `make lint-js`. ESLint owns JS and Vue; Prettier owns CSS/SCSS/SASS only. ESLint does not run Prettier, so do not propose reflowing JS to satisfy Prettier.
47- Swagger: after changing API handlers or annotations, run `make fmt-go swag-fmt swag`; never edit `internal/api/swagger.json` by hand.
48- Dependencies: when `go.mod`, `go.sum`, or `package-lock.json` change, regenerate the license reports with `make notice`; never edit `NOTICE` or `frontend/NOTICE` manually.
49
50## Project Structure & Languages
51
52- Backend: Go (`internal/`, `pkg/`, `cmd/`) + MariaDB/SQLite
53- Frontend: Vue 3 + Vuetify 3 (`frontend/`)
54- Docker/compose for dev/CI; Traefik used for local TLS in dev profile when enabled.
55- A maintainer's working copy may contain private subdirectories (`plus/`, `pro/`, `portal/`, `specs/`) that are not part of this repository. Never reference their paths from public artifacts such as pull request descriptions, issue comments, or code comments — external readers only see a broken link.
56
57## Code Review Instructions (for Copilot)
58
59- Respect SOT above; do not invent flags, env vars, or Compose options. If a command/env var is not in the docs/Makefile/CLI help, say "not documented" and suggest checking the SOT.
60- Prefer minimal, surgical diffs. Propose changes as concrete patches and reference the relevant Makefile target or doc section.
61- Before suggesting refactors, check tests and build tasks exist and can pass with the change. If tests are missing, suggest specific Vitest/Go test snippets.
62- Security: never suggest committing secrets; prefer env vars and `.env` in dev only. Point to SECURITY.md for disclosures.
63- Data safety: never run or recommend destructive CLI operations in examples without explicit backups and `--yes`. Avoid `photoprism reset`, `photoprism users reset`, `photoprism auth reset`, or `photoprism audit reset` in PR comments unless the change is specifically about those commands; if unavoidable, add bold warnings and backup steps.
64- Database/schema: if a change touches persistence, check for migrations and mention `photoprism migrate` / `migrations` commands.
65- API changes: align with the REST API docs/spec; include curl examples only if they match current endpoints and auth notes.
66- UX/i18n: keep UI strings concise, translatable, and consistent; avoid hard-coded language constructs; prefer existing patterns/components.
67
68## Style & Patterns
69
70- Go: idiomatic Go, clear error handling, small functions, packages with focused responsibilities. Keep the public surface minimal.
71 - Every added function, including unexported helpers and helpers extracted by a refactor, needs focused coverage in a sibling `*_test.go`.
72 - Code in `pkg/*` must not import from `internal/*`; new code that needs config, entity, or DB access belongs under `internal/`.
73 - Use the permission constants in `pkg/fs` (`fs.ModeDir`, `fs.ModeFile`, `fs.ModeConfigFile`, `fs.ModeSecretFile`) instead of literal file modes.
74 - Doc comments start with the name and stay compact — one line for the "what", plus a line or two only when the "why" cannot be inferred from the code. No issue numbers or change history in comments.
75- Vue/JS: Options API only. Do not introduce TypeScript (no `.ts` files, no `<script lang="ts">`), Composition API, or `<script setup>`.
76 - Shared state lives in reactive singleton modules under `src/common/` and `src/app/`. Do not propose Vuex or Pinia.
77 - Every user-visible string must go through `$gettext`, so it reaches `frontend/src/locales/translations.pot`. Standardized technical identifiers (`Client ID`, `OIDC`, `UUID`) stay literal.
78 - Prefer existing components and model methods over raw `$api` calls in components.
79- Config & flags: suggest `photoprism --help`, `photoprism show config-options` or `photoprism show config-yaml` to verify names before using them.
80
81## Commits & Pull Requests
82
83- Commit subjects use the imperative mood with a one-word scope prefix, for example `Config: Add tests for "darktable-cli" path detection`, and must not exceed 80 characters.
84- Reference related issue or PR IDs in the message when applicable, for example `Docker: Use two stage build to reduce image size #123 #5632`.
85- Do not add AI-authorship trailers such as `Co-Authored-By: Copilot` — this repository uses no commit trailers.
86- Issue titles follow the same prefix style; descriptions open with a bold user story (`**As a <role>, I want <goal>, so that <outcome>.**`) and close with a checklist of acceptance criteria using MUST, SHOULD, or MAY.
87- Commit messages, code comments, and public issue or PR text must not describe exploitable details of a security fix; present such changes as general hardening.
88
89## Performance & Reliability
90
91- Prefer using existing caches, workers, and batching strategies referenced in code and Makefile. Consider memory/CPU impact; suggest benchmarks or profiling only when justified.
92
93## When Unsure
94
95- Ask for the exact Makefile target or doc link you need, then proceed. Defer to SOT if any conflict arises.
96
97## References To Help Copilot Answer Questions
98
99- Show supported formats/filters: run `photoprism show file-formats` and `photoprism show search-filters` (use results rather than guessing).
100- For CI/dev containers, assume a Linux/Unix shell on amd64 or arm64 by default; for Windows specifics, link https://docs.photoprism.app/developer-guide/faq/
101
102## Output Expectations
103
104- Prefer short, actionable comments with code blocks that pass tests locally: `make test-js` (frontend) / `make test-go` (backend)
105- If a suggestion requires additional context (e.g., DB access, external service), call it out explicitly.
106
107## Safety Checklist Before Proposing a CLI Command
108
109- Include a dry-run or non-destructive variant if possible.
110- Recommend creating/using backups before any reset/migrate.
111
photoprism/photoprism · frontend/AGENTS.md
@@ +1 @@
1# Frontend Guidelines
2
3**Last Updated:** July 28, 2026
4
5## Dependencies & Pins
6
7- [`frontend/README.md`](README.md) is the canonical doc for dependency pin rationale, the `overrides` layer, ESM-only upgrade blockers, and the orphan-audit pattern.
8- **Pins are intentional.** When a version has no caret (e.g., `"axios": "1.17.0"`, `"vuetify": "3.12.2"`), check `frontend/README.md` and `git log -p -S "<pkg>" -- frontend/package.json` for the reason before changing it.
9- npm is a workspace; run `npm install --ignore-scripts --no-audit --no-fund --no-update-notifier` from the **repo root** (not `frontend/`) so the root `package-lock.json` updates.
10- After dep changes run `make audit`, `make build-js`, `make test-js`, and `make notice`.
11- Before adding a new dep — and especially before declaring an existing one "unused" — verify with `rg -nF "<pkg>" frontend …` plus `npm ls <pkg> --all` that no consumer or peer-dep needs it.
12
13## Frontend Linting & Test Entry Points
14
15- Run `make -C frontend help` for an overview of the most common frontend targets, and `make -C frontend list` to see all of them.
16- Use the lint and format scripts declared in `frontend/package.json`; all added JS, Vue, and frontend tests must follow those standards.
17- Frontend unit tests use Vitest. Common entry points are `make test-js`, `make vitest-watch`, and `make vitest-coverage`.
18- New JavaScript functions, including helpers, should be tested whenever practical; update existing tests or add new ones as needed.
19- New Vue components should have component-test coverage, and existing component tests should be updated as needed when component behavior changes.
20- Acceptance tests use the `acceptance-*` targets in the root `Makefile`.
21- For one-off TestCafe checks, keep startup and cleanup in the repository root: `make storage/acceptance`, `make acceptance-sqlite-restart`, `make wait-2`, then `(cd frontend && npm run testcafe -- "chrome --headless=new --use-gl=angle --use-angle=swiftshader --disable-features=LocalNetworkAccessChecks" --config-file ./testcaferc.json --test-meta mode=public,type=short,testID=components-001 "tests/acceptance")`, then `make acceptance-sqlite-stop`.
22- If a command temporarily changes into `frontend/`, return to the repository root before running `make acceptance-sqlite-stop`.
23
24## Templates, Session Bootstrap & Browser Baseline
25
26- HTML entry points live under `assets/templates/`; the key files are `index.gohtml`, `app.gohtml`, `app.js.gohtml`, and `splash.gohtml`.
27- Browser checks live in `assets/static/js/browser-check.js` and must load before the main bundle from `app.js.gohtml`. Do not add `defer` or `async` unless you restore guarded loading.
28- OIDC completion is bridged through `assets/templates/auth.gohtml` and must stay aligned with `frontend/src/common/session.js`, `frontend/src/common/storage.js`, and `frontend/src/page/auth/login.vue`. Preserve the `session` storage preference across the callback so `sessionStorage` logins survive redirect.
29- When touching frontend session bootstrap, verify that `frontend/src/common/session.js` resolves `storageNamespace` from the real client config shape (`window.__CONFIG__` or `config.values`), not only from simplified mocks. Include a focused test that would fail if restore fell back to `pp:root:`.
30- The loader partial is reused in `pro/assets/templates/index.gohtml`, `plus/assets/templates/index.gohtml`, and `portal/assets/templates/index.gohtml`; whenever you change `app.js.gohtml` or bundle loading, verify those files still include the shared partial.
31- Splash styles live in `frontend/src/css/splash.css`; add new splash elements there so public and private editions stay aligned.
32- Browser baseline: the `browserslist` query in `frontend/package.json` is authoritative — `.babelrc` sets no explicit `targets`, so `@babel/preset-env` compiles to that set. Resolve it with `(cd frontend && npx browserslist)` rather than quoting fixed versions, which go stale as caniuse data updates. Update the message in `assets/templates/app.js.gohtml` and matching CSS if the support matrix changes.
33
34## Translations
35
36- Translation extraction source of truth is the root `make gettext-extract`, which runs `scripts/gettext-extract.sh` across `frontend/src` and any available `plus`, `pro`, or `portal` overlays.
37- Compatibility targets such as `make -C plus gettext-extract` delegate to the root target.
38- Avoid punctuation-only gettext keys such as `$gettext("—")`; they create noisy entries in `frontend/src/locales/translations.pot`.
39- Case conventions: tooltips, labels, buttons, placeholders, and short imperative phrases use **Title Case** (`Zoom In`, `Toggle Thumbnails`, `Add to Album`); running prose, full sentences, and notifications use **sentence case** (`Failed to save changes`). Lowercase only articles, short conjunctions, and ≤3-letter prepositions when not first. The forced-as-is Vuetify 3 UI messages in `frontend/src/locales.js` are adopted verbatim — exempt, and not a casing reference to copy. Full rules: `specs/frontend/translations.md` §"Case Conventions".
40
41## Focus Management
42
43- Dialogs must follow the shared focus pattern documented in `frontend/src/common/README.md`.
44- Always expose `ref="dialog"` on `<v-dialog>` overlays, call `$view.enter` and `$view.leave` in `@after-enter` and `@after-leave`, and avoid positive `tabindex` values.
45- Persistent dialogs must handle Escape via `@keydown.esc.exact` so Vuetify's rejection animation is suppressed; keep other shortcuts on `@keyup` so inner inputs can cancel them first.
46- Global shortcuts flow through `onShortCut(ev)` in `common/view.js`; it forwards only Escape and `ctrl` or `meta` combinations.
47- When a dialog opens nested menus such as combobox suggestion lists, verify they still cooperate with the global trap.
48
49## Playwright MCP Usage
50
51- Default endpoint is `http://localhost:2342/`; default login routes are `/library/login` for CE, Plus, and Pro, and `/portal/login` for Portal.
52- Use the local compose admin credentials; if login fails, inspect the active compose environment.
53- Desktop sessions default to `1280x900`; mobile sessions should use the mobile Playwright server with `375x667`.
54- Close the browser tab after scripted interactions.
55- Prefer waits over sleeps, click only visible and enabled elements, and use role, label, or text selectors instead of brittle XPath selectors.
56- Keep screenshots small and reproducible: prefer JPEG, visible viewport, deterministic `.local/screenshots/<case>/<step>__<viewport>.jpg` names, and no large inline screenshots.
57- If `npx` fetches an MCP server at runtime, add `--yes` or preinstall it to avoid prompts.
58
59## Frontend Test Gotchas
60
61- Hidden-route UI checks under `/library/hidden` or `/portal/hidden` require both `files.file_error` and `photos.photo_quality = -1`; `file_error` alone will not surface the row.
62
@@ −1 +1 @@
1−# Instructions for GitHub Copilot
1+# Frontend Guidelines
22
33 **Last Updated:** July 28, 2026
44
5−## Purpose
5+## Dependencies & Pins
66
7−- Provide Copilot with the single sources of truth for building, testing, and contributing to PhotoPrism.
8−- Improve PR reviews and code suggestions by aligning them with our documented workflows and style.
9−- Path-specific rules that apply on top of this file live in `.github/instructions/*.instructions.md`.
7+- [`frontend/README.md`](README.md) is the canonical doc for dependency pin rationale, the `overrides` layer, ESM-only upgrade blockers, and the orphan-audit pattern.
8+- **Pins are intentional.** When a version has no caret (e.g., `"axios": "1.17.0"`, `"vuetify": "3.12.2"`), check `frontend/README.md` and `git log -p -S "<pkg>" -- frontend/package.json` for the reason before changing it.
9+- npm is a workspace; run `npm install --ignore-scripts --no-audit --no-fund --no-update-notifier` from the **repo root** (not `frontend/`) so the root `package-lock.json` updates.
10+- After dep changes run `make audit`, `make build-js`, `make test-js`, and `make notice`.
11+- Before adding a new dep — and especially before declaring an existing one "unused" — verify with `rg -nF "<pkg>" frontend …` plus `npm ls <pkg> --all` that no consumer or peer-dep needs it.
1012
11−## Single Sources of Truth (SOT)
13+## Frontend Linting & Test Entry Points
1214
13−- Makefile targets (always prefer existing targets): https://github.com/photoprism/photoprism/blob/develop/Makefile
14−- Developer Guide – Setup: https://docs.photoprism.app/developer-guide/setup/
15−- Developer Guide – Tests: https://docs.photoprism.app/developer-guide/tests/
16−- Contributing: https://github.com/photoprism/photoprism/blob/develop/CONTRIBUTING.md
17−- Security: https://github.com/photoprism/photoprism/blob/develop/SECURITY.md
18−- REST API (Swagger): https://docs.photoprism.dev/
19−- REST API Guide: https://docs.photoprism.app/developer-guide/api/
20−- Agents reference for tools/commands: https://github.com/photoprism/photoprism/blob/develop/AGENTS.md
21−- Code maps: https://github.com/photoprism/photoprism/blob/develop/CODEMAP.md (backend) and https://github.com/photoprism/photoprism/blob/develop/frontend/CODEMAP.md (frontend)
22−- Terminology: https://github.com/photoprism/photoprism/blob/develop/GLOSSARY.md
15+- Run `make -C frontend help` for an overview of the most common frontend targets, and `make -C frontend list` to see all of them.
16+- Use the lint and format scripts declared in `frontend/package.json`; all added JS, Vue, and frontend tests must follow those standards.
17+- Frontend unit tests use Vitest. Common entry points are `make test-js`, `make vitest-watch`, and `make vitest-coverage`.
18+- New JavaScript functions, including helpers, should be tested whenever practical; update existing tests or add new ones as needed.
19+- New Vue components should have component-test coverage, and existing component tests should be updated as needed when component behavior changes.
20+- Acceptance tests use the `acceptance-*` targets in the root `Makefile`.
21+- For one-off TestCafe checks, keep startup and cleanup in the repository root: `make storage/acceptance`, `make acceptance-sqlite-restart`, `make wait-2`, then `(cd frontend && npm run testcafe -- "chrome --headless=new --use-gl=angle --use-angle=swiftshader --disable-features=LocalNetworkAccessChecks" --config-file ./testcaferc.json --test-meta mode=public,type=short,testID=components-001 "tests/acceptance")`, then `make acceptance-sqlite-stop`.
22+- If a command temporarily changes into `frontend/`, return to the repository root before running `make acceptance-sqlite-stop`.
2323
24−## Build & Run (local dev; use Makefile first)
24+## Templates, Session Bootstrap & Browser Baseline
2525
26−- Show common tasks: `make help` (`make list` shows all targets)
27−- Build local image: `make docker-build`
28−- Start dev env: `docker compose up` (add `-d` for detached, or run `make up`)
29−- Logs: `docker compose logs -f --tail=100 photoprism`
30−- Open app: http://localhost:2342/ (HTTP) / https://app.localssl.dev/ (TLS via Traefik when enabled)
31−- From the dev container:
32− - Install deps: `make dep`
33− - Build frontend: `make build-js` (or `cd frontend && npm run build`)
34− - Build backend: `make build-go`
35− - Watch frontend: `make watch-js` (stop with Ctrl+C)
36− - Run server binary: `./photoprism start`
26+- HTML entry points live under `assets/templates/`; the key files are `index.gohtml`, `app.gohtml`, `app.js.gohtml`, and `splash.gohtml`.
27+- Browser checks live in `assets/static/js/browser-check.js` and must load before the main bundle from `app.js.gohtml`. Do not add `defer` or `async` unless you restore guarded loading.
28+- OIDC completion is bridged through `assets/templates/auth.gohtml` and must stay aligned with `frontend/src/common/session.js`, `frontend/src/common/storage.js`, and `frontend/src/page/auth/login.vue`. Preserve the `session` storage preference across the callback so `sessionStorage` logins survive redirect.
29+- When touching frontend session bootstrap, verify that `frontend/src/common/session.js` resolves `storageNamespace` from the real client config shape (`window.__CONFIG__` or `config.values`), not only from simplified mocks. Include a focused test that would fail if restore fell back to `pp:root:`.
30+- The loader partial is reused in `pro/assets/templates/index.gohtml`, `plus/assets/templates/index.gohtml`, and `portal/assets/templates/index.gohtml`; whenever you change `app.js.gohtml` or bundle loading, verify those files still include the shared partial.
31+- Splash styles live in `frontend/src/css/splash.css`; add new splash elements there so public and private editions stay aligned.
32+- Browser baseline: the `browserslist` query in `frontend/package.json` is authoritative — `.babelrc` sets no explicit `targets`, so `@babel/preset-env` compiles to that set. Resolve it with `(cd frontend && npx browserslist)` rather than quoting fixed versions, which go stale as caniuse data updates. Update the message in `assets/templates/app.js.gohtml` and matching CSS if the support matrix changes.
3733
38−## Tests & Lint
34+## Translations
3935
40−- Full tests: `make test`
41−- Frontend tests (Vitest): `make test-js` (watch: `make vitest-watch`, coverage: `make vitest-coverage`)
42−- Backend tests: `make test-go` (slow); `make test-short` for a fast parallel run
43−- Targeted Go tests: `go test ./internal/<pkg> -run '<TestName>' -count=1`
44−- Formatting and linting:
45− - Go: `make fmt-go` (gofmt + goimports) and `make lint-go` (golangci-lint)
46− - JS/Vue: `make fmt-js` and `make lint-js`. ESLint owns JS and Vue; Prettier owns CSS/SCSS/SASS only. ESLint does not run Prettier, so do not propose reflowing JS to satisfy Prettier.
47−- Swagger: after changing API handlers or annotations, run `make fmt-go swag-fmt swag`; never edit `internal/api/swagger.json` by hand.
48−- Dependencies: when `go.mod`, `go.sum`, or `package-lock.json` change, regenerate the license reports with `make notice`; never edit `NOTICE` or `frontend/NOTICE` manually.
36+- Translation extraction source of truth is the root `make gettext-extract`, which runs `scripts/gettext-extract.sh` across `frontend/src` and any available `plus`, `pro`, or `portal` overlays.
37+- Compatibility targets such as `make -C plus gettext-extract` delegate to the root target.
38+- Avoid punctuation-only gettext keys such as `$gettext("—")`; they create noisy entries in `frontend/src/locales/translations.pot`.
39+- Case conventions: tooltips, labels, buttons, placeholders, and short imperative phrases use **Title Case** (`Zoom In`, `Toggle Thumbnails`, `Add to Album`); running prose, full sentences, and notifications use **sentence case** (`Failed to save changes`). Lowercase only articles, short conjunctions, and ≤3-letter prepositions when not first. The forced-as-is Vuetify 3 UI messages in `frontend/src/locales.js` are adopted verbatim — exempt, and not a casing reference to copy. Full rules: `specs/frontend/translations.md` §"Case Conventions".
4940
50−## Project Structure & Languages
41+## Focus Management
5142
52−- Backend: Go (`internal/`, `pkg/`, `cmd/`) + MariaDB/SQLite
53−- Frontend: Vue 3 + Vuetify 3 (`frontend/`)
54−- Docker/compose for dev/CI; Traefik used for local TLS in dev profile when enabled.
55−- A maintainer's working copy may contain private subdirectories (`plus/`, `pro/`, `portal/`, `specs/`) that are not part of this repository. Never reference their paths from public artifacts such as pull request descriptions, issue comments, or code comments — external readers only see a broken link.
43+- Dialogs must follow the shared focus pattern documented in `frontend/src/common/README.md`.
44+- Always expose `ref="dialog"` on `<v-dialog>` overlays, call `$view.enter` and `$view.leave` in `@after-enter` and `@after-leave`, and avoid positive `tabindex` values.
45+- Persistent dialogs must handle Escape via `@keydown.esc.exact` so Vuetify's rejection animation is suppressed; keep other shortcuts on `@keyup` so inner inputs can cancel them first.
46+- Global shortcuts flow through `onShortCut(ev)` in `common/view.js`; it forwards only Escape and `ctrl` or `meta` combinations.
47+- When a dialog opens nested menus such as combobox suggestion lists, verify they still cooperate with the global trap.
5648
57−## Code Review Instructions (for Copilot)
49+## Playwright MCP Usage
5850
59−- Respect SOT above; do not invent flags, env vars, or Compose options. If a command/env var is not in the docs/Makefile/CLI help, say "not documented" and suggest checking the SOT.
60−- Prefer minimal, surgical diffs. Propose changes as concrete patches and reference the relevant Makefile target or doc section.
61−- Before suggesting refactors, check tests and build tasks exist and can pass with the change. If tests are missing, suggest specific Vitest/Go test snippets.
62−- Security: never suggest committing secrets; prefer env vars and `.env` in dev only. Point to SECURITY.md for disclosures.
63−- Data safety: never run or recommend destructive CLI operations in examples without explicit backups and `--yes`. Avoid `photoprism reset`, `photoprism users reset`, `photoprism auth reset`, or `photoprism audit reset` in PR comments unless the change is specifically about those commands; if unavoidable, add bold warnings and backup steps.
64−- Database/schema: if a change touches persistence, check for migrations and mention `photoprism migrate` / `migrations` commands.
65−- API changes: align with the REST API docs/spec; include curl examples only if they match current endpoints and auth notes.
66−- UX/i18n: keep UI strings concise, translatable, and consistent; avoid hard-coded language constructs; prefer existing patterns/components.
51+- Default endpoint is `http://localhost:2342/`; default login routes are `/library/login` for CE, Plus, and Pro, and `/portal/login` for Portal.
52+- Use the local compose admin credentials; if login fails, inspect the active compose environment.
53+- Desktop sessions default to `1280x900`; mobile sessions should use the mobile Playwright server with `375x667`.
54+- Close the browser tab after scripted interactions.
55+- Prefer waits over sleeps, click only visible and enabled elements, and use role, label, or text selectors instead of brittle XPath selectors.
56+- Keep screenshots small and reproducible: prefer JPEG, visible viewport, deterministic `.local/screenshots/<case>/<step>__<viewport>.jpg` names, and no large inline screenshots.
57+- If `npx` fetches an MCP server at runtime, add `--yes` or preinstall it to avoid prompts.
6758
68−## Style & Patterns
59+## Frontend Test Gotchas
6960
70−- Go: idiomatic Go, clear error handling, small functions, packages with focused responsibilities. Keep the public surface minimal.
71− - Every added function, including unexported helpers and helpers extracted by a refactor, needs focused coverage in a sibling `*_test.go`.
72− - Code in `pkg/*` must not import from `internal/*`; new code that needs config, entity, or DB access belongs under `internal/`.
73− - Use the permission constants in `pkg/fs` (`fs.ModeDir`, `fs.ModeFile`, `fs.ModeConfigFile`, `fs.ModeSecretFile`) instead of literal file modes.
74− - Doc comments start with the name and stay compact — one line for the "what", plus a line or two only when the "why" cannot be inferred from the code. No issue numbers or change history in comments.
75−- Vue/JS: Options API only. Do not introduce TypeScript (no `.ts` files, no `<script lang="ts">`), Composition API, or `<script setup>`.
76− - Shared state lives in reactive singleton modules under `src/common/` and `src/app/`. Do not propose Vuex or Pinia.
77− - Every user-visible string must go through `$gettext`, so it reaches `frontend/src/locales/translations.pot`. Standardized technical identifiers (`Client ID`, `OIDC`, `UUID`) stay literal.
78− - Prefer existing components and model methods over raw `$api` calls in components.
79−- Config & flags: suggest `photoprism --help`, `photoprism show config-options` or `photoprism show config-yaml` to verify names before using them.
80−
81−## Commits & Pull Requests
82−
83−- Commit subjects use the imperative mood with a one-word scope prefix, for example `Config: Add tests for "darktable-cli" path detection`, and must not exceed 80 characters.
84−- Reference related issue or PR IDs in the message when applicable, for example `Docker: Use two stage build to reduce image size #123 #5632`.
85−- Do not add AI-authorship trailers such as `Co-Authored-By: Copilot` — this repository uses no commit trailers.
86−- Issue titles follow the same prefix style; descriptions open with a bold user story (`**As a <role>, I want <goal>, so that <outcome>.**`) and close with a checklist of acceptance criteria using MUST, SHOULD, or MAY.
87−- Commit messages, code comments, and public issue or PR text must not describe exploitable details of a security fix; present such changes as general hardening.
88−
89−## Performance & Reliability
90−
91−- Prefer using existing caches, workers, and batching strategies referenced in code and Makefile. Consider memory/CPU impact; suggest benchmarks or profiling only when justified.
92−
93−## When Unsure
94−
95−- Ask for the exact Makefile target or doc link you need, then proceed. Defer to SOT if any conflict arises.
96−
97−## References To Help Copilot Answer Questions
98−
99−- Show supported formats/filters: run `photoprism show file-formats` and `photoprism show search-filters` (use results rather than guessing).
100−- For CI/dev containers, assume a Linux/Unix shell on amd64 or arm64 by default; for Windows specifics, link https://docs.photoprism.app/developer-guide/faq/
101−
102−## Output Expectations
103−
104−- Prefer short, actionable comments with code blocks that pass tests locally: `make test-js` (frontend) / `make test-go` (backend)
105−- If a suggestion requires additional context (e.g., DB access, external service), call it out explicitly.
106−
107−## Safety Checklist Before Proposing a CLI Command
108−
109−- Include a dry-run or non-destructive variant if possible.
110−- Recommend creating/using backups before any reset/migrate.
61+- Hidden-route UI checks under `/library/hidden` or `/portal/hidden` require both `files.file_error` and `photos.photo_quality = -1`; `file_error` alone will not surface the row.
11162
