AGENTS.md
Rama main · réplica de lectura del repositorio original.
# BetterShot contributor and agent rules
These rules apply to changes in this repository. Read the relevant implementation
and [CONTRIBUTING.md](CONTRIBUTING.md) before editing. Preserve the current UI and
behavior unless the task explicitly asks to change them. This file is the shared
source of instructions for coding agents; `CLAUDE.md` imports it.
For maintainer tasks, push completed changes directly to `main`; do not create
a PR unless explicitly requested.
## Stack and scope
- The macOS app in `Sources/` uses SwiftUI, AppKit, CoreGraphics, AVFoundation,
and ScreenCaptureKit. Use native controls and existing project primitives.
- The deployment target is macOS 14.0, set by `minimumOS` in `version.json`.
Gate macOS 15 and 26 APIs with `#available` and give each a working fallback
on macOS 14. Read `project.yml` for actual compiler
settings: the app currently uses Swift 5 language compatibility with main-actor
default isolation and approachable concurrency. Standalone checks use Swift 6.
Do not change toolchain settings as a side effect of a feature or UI fix.
- `bettershot-landing/` is a separate Next.js/React/Tailwind website. Web libraries
and CSS rules do not belong in the native app.
- Search for existing callers and shared rendering paths before adding code.
Fix the shared cause, keep the diff focused, and avoid speculative abstractions
or new dependencies when existing code or the platform covers the need.
## Required native UI patterns
### Identity and icons
- Preserve BetterShot's bundled clover identity: `AppIcon` for the app and
`MenuBarIcon` for the menu tray. Do not replace either with a generic camera,
a website logo file, or a newly generated icon unless explicitly requested.
- Use Apple SF Symbols for action, tool, settings, and navigation icons:
`Image(systemName:)`, `Label(_:systemImage:)`, or AppKit's
`NSImage(systemSymbolName:accessibilityDescription:)`.
- Do not reintroduce Nucleo, third-party UI icon packs, or asset-mapping wrappers.
Choose symbols available on the deployment target; installing the SF Symbols
design app is not an application dependency. `scripts/check-sf-symbols.py`
enforces this in `scripts/run-checks.sh`.
- Keep cloud sharing recognizable with `icloud.and.arrow.up`. Use the same
symbol for the same action across editors, menus, settings, and capture bars.
### Layout and chrome
- Both editors keep their inspector on the left. Image tools belong in one
toolbar; do not duplicate tool, aspect-ratio, or redaction controls in the
image sidebar.
- Video inspector tabs are Background, Cursor, Camera, Effects, and Zoom & Clips.
Keep its effect cards expanded with no outer disclosure chevrons. Advanced
controls may use the existing Advanced section.
- Reuse `EditorChrome`, `EditorButtonStyle`, `studioGlass`, `studioEffectCard`,
`InspectorMetrics`, and the existing inspector components.
- Preserve classic frosted chrome, subtle borders, and compact spacing. In dark
appearance, effect cards are lighter grey than the darker sidebar. Use system
colors, readable disabled states, and the existing blue selection accent.
- Respect both light and dark appearances, Reduce Transparency, and Reduce Motion.
Do not introduce a new Liquid Glass redesign as an incidental cleanup.
- Use `InspectorSlider` for all app sliders, including Settings and timeline zoom.
Its 0.4.0 compact track contains
the label and has a separate editable value on the right. Hide the TextField
label in Forms to prevent duplicate labels and clipped values. Preserve keyboard input, units,
bounds, precision, and undo grouping; do not substitute another slider style.
- Hide scroll indicators in both editors while preserving scrolling. Check narrow
windows so tools, labels, values, and actions are not clipped or unreachable.
- Keep one shared capture bar. Reuse `BarMetrics`: capture height 64 pt, recording
height 38 pt. The recording row contains Stop/timer, Pause, Restart, and Discard.
### Interactions and accessibility
- A second click on an active image tool returns to Select. Clicking video Crop
again cancels the uncommitted crop; do not destroy the previously applied crop.
Blur and Pixelate must be visible and toggle out of mask editing on a second click.
- Video mask scope uses Crop Only for the selected rectangle and Full Frame for
the whole source frame. Keep preview, export, and project persistence consistent.
- Scissors starts unselected. Once selected, it stays active across repeated cuts
until deselected by clicking it or pressing S. Preserve cut badges below the
filmstrip, removed-duration labels, and hover previews of source footage.
- Keep native keyboard/focus behavior for standard controls. Icon-only actions
need accessible names; tooltips alone are insufficient. Preserve selected and
disabled states, text entry, copy/paste, and keyboard navigation.
- Confirm destructive actions using the existing native alerts. Show recoverable
errors beside the action with a useful retry or next step.
- Reuse `TransferStatusCard` for export/share progress, completion, and failures.
Present it through `TransferToast` in a separate panel at the screen-top toast
position on the editor's display, outside the editor canvas. Preserve native
progress and Copy/Open/Reveal/Retry actions.
- Do not add animation unless requested. For new interaction feedback, prefer
opacity or transforms, ease-out, and at most 200 ms. Never animate large blur
surfaces, make progress dependent on a decorative animation, or add perpetual
motion. Respect reduced-motion settings. Existing specialized capture/video
motion is not a reason to redesign unrelated UI.
## Capture, image quality, and recording
- Shortcut defaults: `⌘⇧4` region screenshot, `⌘⇧3` fullscreen screenshot,
`⌘⇧2` shared capture bar, `⌘⇧5` Recording options, `⌘⇧O` OCR, `⌘⇧C` color picker,
`⌘⇧1` Capture Previous Region. `⌘⇧2` always opens the capture bar.
Use `ShortcutService` as the source for UI labels and settings. Migrations must
preserve customized bindings and disabled states.
- Region screenshots default to BetterShot's adjustable AppKit selector. Use native
macOS region selector in Settings > Capture > Region (off by default) routes
them through `screencapture -i` without activating BetterShot, so open menus and
popovers survive. Native selection always captures on release and shows the live
screen, so Capture as soon as I let go and Freeze the screen while selecting are
disabled there. It does not update the remembered rectangle; Capture Previous
Region keeps the last area chosen with BetterShot's selector, and scrolling
capture and recording areas still need it. With BetterShot's selector, Capture
as soon as I let go chooses capture on mouse release or adjustment before
confirmation. Window screenshots use macOS's native window selector. Recording
areas use the adjustable AppKit selector and system crosshair. By default it
opens with the previous area selected, so Return captures it again; Start with
the last area selected in Settings > Capture > Region turns that off for every
use of the selector without forgetting the area. Its edges resize it,
and a drag anywhere, even inside it, draws a new area. OCR keeps macOS's native `screencapture` selector. Do not describe the
recording selector as Apple's system recording picker.
- Preserve capture-window exclusion, correct display/coordinate conversion,
cancellation, and focus restoration. Never start a recording after cancellation.
Check Screen Recording access at capture time through `ScreenCapturePermission`,
never at launch.
- Keep untouched capture pixels and editable project sources. Full-resolution
image previews are the default; an optional low-resolution preview must never
become the source for saving, copying, or export. Keep screenshot framing aligned
to integer pixels and preserve lossless PNG behavior.
- All screenshot modes stage privately. Copy never exports to the configured
folder; Edit, Pin, Share, and drag-out retain working files internally. Only
explicit Save/Export, capture-and-save, or enabled automatic saving of normal
captures writes a deliverable. Automatic screenshot saving defaults on for new
installs; upgrades preserve both explicit choices and the previous unset/off
behavior. Explicit Copy, Edit, and Pin shortcuts bypass it. Keeping previews
open is independent of automatic saving. Preserve preview/editor behavior and retain
failed automatic saves for retry. Editor Save
must work for untouched screenshots, and clipboard file references must
survive dismissing the capture card.
- The Arrow cursor style uses the shared high-resolution, white-outlined stemless vector artwork with an arrow-tip hotspot. Keep the legacy `macOS` storage key. Hand is no longer offered, but
legacy projects still render it with `NSCursor.pointingHand`. Preserve native
hotspots and highest-resolution representations through `PointerArtworkCapture`.
- Custom cursor raster resolution is independent of its logical size and hotspot.
Preserve transparent backgrounds, contrasting outlines, the highest-resolution
recorded representation, and cached decoding. Do not regenerate cursor PNGs per frame.
- Keep Recorded/Arrow/Dark/Light/Dot (and legacy Hand), visibility, Natural/Smooth motion, press/ripple,
and idle hiding consistent in preview, export, and saved projects. Imported
footage with a baked-in cursor cannot be restyled; explain that in the UI.
- Camera frame ratios are independent of the video canvas ratio. Use
`RecordingStudioLayout` for preview and export, keep the frame within the canvas,
and default older projects/presets without a camera ratio to 1:1. Screen/camera
layout presets apply to the whole video, default legacy projects to Bubble,
and fall back to the screen when camera footage is unavailable or hidden.
- Plain typing is not recorded. Do not expand input capture as a shortcut to a
cursor or keyboard-overlay feature.
- `ScreenshotFileNaming` is the only source for deliverable file names. Keep its
renderer pure and Foundation-only, keep the default template rendering the
pre-0.5.0 name, and never let a template reach the file system without the
existing sanitizing. Recording package directory names are project identity,
not deliverable names; do not rename them.
## Backgrounds and persistence
- `GradientPreset.presets` in `Sources/Models/BackgroundStyle.swift` is the source
for the ten approved soft gradients: Blush, Peach, Mint, Powder Blue, Butter,
Lilac, Sage, Coral, Aqua, and Mauve. Their gradients are media backgrounds,
not a license to add decorative gradients to controls or chrome.
- Reuse the shared gradient drawing, stop positions, and highlights in Settings,
both editors, and exports. Do not duplicate palettes or flatten them into thumbnails.
- Preserve old project decoding and stored artwork when replacing available presets.
- General > Default Look supplies background, padding, corner radius, and shadow
for new images and videos. Do not add a separate video background default or
let project edits overwrite General's defaults. Saved projects retain their look.
- Preserve raw captures, annotations, source movies, masks, crop, undo/redo, and
saved share links. Use existing history/project resolvers and atomic writes.
## Verification and documentation
- Run checks appropriate to the change; use `make test` for native logic and
rendering changes. Use existing standalone checks or editor/export integration
checks against production code, not a copied implementation.
- Check compact layouts and both appearances for UI changes. Offscreen snapshots
cannot prove live AVPlayer rendering, native window toolbars, global shortcuts,
permissions, or interactive capture; report what was actually checked.
- Tests must use `BETTERSHOT_TESTING=1` through the provided runners and must not
access real R2 credentials. Never log credentials or weaken release signing.
- `version.json` is the version source. The 0.5.1 changelog entry is dated
2026-09-10 at the maintainer’s request. Publish release binaries only when explicitly
requested; no 0.4.3 release.
Preserve historical release entries and contributor credit.
- Update README and contributor guidance when behavior or workflows change.
Keep `CLAUDE.md` a short import of shared instructions rather than a second rulebook.
- Do not modify unrelated work, commit local signing identities, publish releases,
or remove installed apps/user data as a side effect of routine editing.
## Website-only rules
- Landing-page-only changes must not update or create README files or changelog
entries unless explicitly requested. Report website changes and validation in
the task response instead. This overrides the general documentation rule above.
For `bettershot-landing/`, use existing Tailwind tokens and the `cn` utility.
Reuse the installed Radix components; do not mix primitive systems within one
surface. Use accessible labels, existing alert dialogs, `h-dvh` and safe-area
insets for full-height/fixed layouts, and tabular numbers for data. Keep headings
balanced and body text readable without arbitrary letter spacing. Use
`motion/react` only when animation is requested; animate opacity/transforms,
respect reduced motion, and avoid layout or backdrop-filter animation. Do not
add effects solely for decoration or copy website branding into native app assets.