diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a23cca2..c2b6c96 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,79 +1,110 @@
# Contributing to ColorCraft
-Thank you for your interest in contributing to ColorCraft! This document provides guidelines for contributing to the project.
+## Before you start
-## Getting Started
+- Install Python 3.11.
+- Install Node.js 20 or newer.
+- Enable Corepack.
+- Read [ColorCraft Technical English](./docs/writing-style.md) before you change
+ documentation.
-1. Fork the repository
-2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/ColorCraft.git`
-3. Create a new branch: `git checkout -b feature/your-feature-name`
-4. Make your changes
-5. Test your changes thoroughly
-6. Commit your changes: `git commit -m "Add your feature"`
-7. Push to your fork: `git push origin feature/your-feature-name`
-8. Create a Pull Request
+## Set up the repository
-## Development Setup
+### Windows
-### Backend
-```bash
-cd backend
-pip install -r requirements.txt
-python main.py
+```powershell
+py -3.11 -m venv backend\.venv311
+.\backend\.venv311\Scripts\python.exe -m pip install --upgrade pip
+.\backend\.venv311\Scripts\python.exe -m pip install -r backend\requirements-dev.txt
+cd frontend
+corepack pnpm@9.15.9 install
+cd ..
```
-### Frontend
+### macOS and Linux
+
```bash
+python3.11 -m venv backend/.venv
+backend/.venv/bin/python -m pip install --upgrade pip
+backend/.venv/bin/python -m pip install -r backend/requirements-dev.txt
cd frontend
-pnpm install
-pnpm dev
+corepack pnpm@9.15.9 install
+cd ..
```
-## Code Style
-
-### Python
-- Follow PEP 8 guidelines
-- Use type hints where appropriate
-- Add docstrings to functions and classes
-
-### TypeScript/React
-- Use TypeScript for type safety
-- Follow React best practices
-- Use functional components with hooks
-
-## Testing
+## Start ColorCraft
-Before submitting a PR, ensure:
-- All existing tests pass
-- New features include appropriate tests
-- The application builds without errors
-- No console errors or warnings
+Use the one-terminal launcher:
-## Pull Request Guidelines
-
-- Provide a clear description of the changes
-- Reference any related issues
-- Include screenshots for UI changes
-- Ensure your code is well-documented
-- Keep PRs focused on a single feature or fix
+```powershell
+.\backend\.venv311\Scripts\python.exe dev.py
+```
-## Feature Requests
+On macOS or Linux, run `backend/.venv/bin/python dev.py`. See
+[Getting started](./docs/getting-started.md) for manual startup and runtime
+configuration.
-Feature requests are welcome! Please:
-- Check if the feature has already been requested
-- Provide a clear use case
-- Explain how it aligns with the project goals
+## Validate a change
-## Bug Reports
+Install Playwright Chromium once:
-When reporting bugs, please include:
-- Steps to reproduce
-- Expected behavior
-- Actual behavior
-- Screenshots if applicable
-- Environment details (OS, browser, etc.)
+```powershell
+cd frontend
+corepack pnpm@9.15.9 exec playwright install chromium
+cd ..
+```
-## Questions?
+Run the complete gate before you open a pull request:
-Feel free to open an issue for any questions or clarifications.
+```powershell
+.\backend\.venv311\Scripts\python.exe check.py
+git diff --check
+```
+On macOS or Linux, use `backend/.venv/bin/python check.py`.
+
+The gate runs Prettier, ESLint, TypeScript, Vitest with coverage, the frontend
+production build, Ruff, mypy, pytest with coverage, Playwright, and axe
+accessibility checks. Use `python check.py --skip-e2e` only for a fast local
+iteration. Do not use the reduced gate as the final pull request validation.
+
+## Code requirements
+
+- Keep a change focused.
+- Add or update tests for changed behavior.
+- Preserve the Pydantic and Zod contract boundary.
+- Use semantic style tokens for application chrome.
+- Do not describe relationship fit as aesthetic quality.
+- Do not describe a passing contrast pair as proof of complete accessibility.
+
+## Documentation requirements
+
+- Follow [ColorCraft Technical English](./docs/writing-style.md).
+- Use the preferred ColorCraft terminology.
+- Preserve exact UI labels and technical strings.
+- Add a glossary entry for a new stable concept.
+- Update the canonical document instead of copying a large section into the
+ README.
+- Verify algorithm claims against current code and tests.
+- Keep measured relationships separate from aesthetic judgment.
+
+## Pull request requirements
+
+- Explain what changed and why.
+- Reference related issues.
+- List the validation commands and results.
+- Include current screenshots for an intentional UI change.
+- State changes to API contracts, persistence, runtime configuration, or
+ privacy boundaries.
+- Do not commit virtual environments, `node_modules`, build output, temporary
+ screenshots, uploaded test images, credentials, or local configuration.
+
+## Report a bug
+
+Include:
+
+- Reproduction steps
+- Expected result
+- Actual result
+- Operating system and browser
+- Relevant terminal output with secrets and local private data removed
diff --git a/README.md b/README.md
index ac51c85..6eb4925 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
A local-first workspace for extracting, refining, reviewing, saving, and exporting color palettes.
+A local workspace for extracting, editing, reviewing, saving, and exporting color palettes.
- +ColorCraft creates a palette from a source image or from colors that you enter +manually. It detects geometric hue relationships, evaluates contrast, proposes +optional colors, and generates browser-side exports. -ColorCraft turns an image or a hand-built set of colors into practical design evidence. It extracts representative colors, detects geometric harmony relationships, checks WCAG contrast, suggests additions, and exports reusable values. Saved palettes stay in the current browser. Uploaded source images are processed for the active session and are not placed in persistent browser storage. + + +## Current workflow + +1. Create a palette from a JPG, PNG, or WebP source image, or select **Start manually**. +2. Edit, add, duplicate, remove, or sample palette colors. +3. Select **Analyze palette**. +4. Use **Overview**, **Harmony**, **Contrast**, and **Suggestions** in Review. +5. Assign color roles and evaluate the applicable contrast pairs. +6. Use Export to generate **CSS custom properties**, **JSON**, **Tailwind theme colors**, or an **SVG swatch sheet**. +7. Select **Copy** or **Download**. + +Relationship fit measures geometric agreement with documented hue structures, not aesthetic quality. Contrast checks measure one accessibility requirement; they do not prove complete WCAG conformance. + +The API accepts a source image up to 10 MB and 40 million decoded pixels. Extraction returns 3–10 requested colors, or fewer for a limited processing sample. **Save palette** stores a versioned record in IndexedDB. The Library can open, search, rename, duplicate, and delete saved palettes. + +ColorCraft has no accounts or cloud synchronization. Source-image bytes and unsaved changes remain session-only. Browser history stores the active view and Review tab, not palette data. Export generation runs in the browser. + + ## Quick start -Use Python 3.11 and Node.js 20 or newer. From the repository root: +Use Python 3.11 and Node.js 20 or newer: ```powershell py -3.11 -m venv backend\.venv311 @@ -29,58 +49,24 @@ cd .. .\backend\.venv311\Scripts\python.exe dev.py ``` -On macOS or Linux, create `backend/.venv`, install the same requirements, then run `backend/.venv/bin/python dev.py`. One launcher starts both services and opens the app: - -- Web: `http://127.0.0.1:5174` -- API: `http://127.0.0.1:4100` -- Runtime metadata: `http://127.0.0.1:4100/metadata` - -See [Getting started](./docs/getting-started.md) for setup and recovery details. +The web application starts at `http://127.0.0.1:5174`; the API starts at `http://127.0.0.1:4100`. The default configuration accepts loopback traffic only. Trusted LAN access expands the security boundary and requires opt-in. -## Product workflow - -```mermaid -flowchart LR - Start[Upload image or start manually] --> Edit[Refine palette] - Edit --> Save[Save locally] - Save --> Review[Review harmony and contrast] - Review --> Export[Copy or download exports] - Save --> Library[Search, reopen, rename, duplicate, or delete] -``` +Read [Getting started](./docs/getting-started.md) for Windows, macOS, Linux, manual startup, configuration, shutdown, and recovery procedures. -The application makes state explicit: **Unsaved** means no local record exists, **Saved** means the open palette matches its record, and **Modified** means local edits need **Save changes**. Starting another palette or opening a saved palette prompts before meaningful unsaved work is discarded. - -## Capabilities +## Documentation -- Deterministic LAB-space image color extraction -- Manual palette creation and direct HEX editing -- Harmony visualization and explainable relationship fit -- WCAG contrast-role review and color suggestions -- CSS, JSON, Tailwind, and token export -- IndexedDB palette library with search and recent items -- Light, dark, and system themes with responsive navigation -- Dashboard discovery through a static manifest and runtime metadata endpoint +Use the [documentation index](./docs/README.md) for user, technical, operations, and contribution guides. All documentation follows [ColorCraft Technical English](./docs/writing-style.md). ## Validation -After installing the development dependencies and Playwright Chromium: +Run the complete local and CI gate: ```powershell -cd frontend -corepack pnpm@9.15.9 exec playwright install chromium -cd .. .\backend\.venv311\Scripts\python.exe check.py ``` -`check.py` runs formatting, linting, frontend and backend typing, unit and integration coverage, production build, Playwright workflow coverage, and automated accessibility checks. CI runs the same command. See [Testing](./docs/testing.md). - -## Documentation - -The [documentation index](./docs/README.md) links setup, workflow, runtime configuration, architecture, API, persistence and privacy, dashboard manifest, brand, testing, screenshot review, and troubleshooting guides. - -## Privacy and security - -ColorCraft binds to loopback addresses by default. Palette records are stored in the browser's IndexedDB database; theme preference is the only application preference stored in LocalStorage. Source images are sent to the local API for extraction but are not retained by ColorCraft after processing. Clearing site data removes saved palettes. Read [Persistence and privacy](./docs/persistence-and-privacy.md) before enabling LAN access. +The gate checks formatting, linting, types, tests, coverage, the production +build, browser workflows, and representative accessibility states. ## License diff --git a/docs/README.md b/docs/README.md index e85fa55..c2bd631 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,15 +1,20 @@ # ColorCraft documentation -Start with [Getting started](./getting-started.md), then use the guide that matches your task: +| Document | Audience and purpose | +| --- | --- | +| [Getting started](./getting-started.md) | Users and contributors who must install, start, stop, or recover ColorCraft. | +| [User guide](./user-guide.md) | Users who create, review, save, manage, and export palettes. | +| [Color analysis](./color-analysis.md) | Readers who must interpret extraction, relationship, fit, and contrast results. | +| [API contracts](./api-contracts.md) | API consumers who need canonical routes, payloads, validation, and errors. | +| [Architecture](./architecture.md) | Contributors who need component boundaries and data flows. | +| [Runtime configuration](./runtime-configuration.md) | Operators who configure hosts, ports, CORS, browser launch, or trusted LAN access. | +| [Persistence and privacy](./persistence-and-privacy.md) | Users and operators who need storage, retention, migration, and security details. | +| [Dashboard manifest](./dashboard-manifest.md) | Dashboard integrators who discover ColorCraft and resolve runtime endpoints. | +| [Testing](./testing.md) | Contributors who run local and CI validation. | +| [Troubleshooting](./troubleshooting.md) | Users and contributors who must recover from known failures. | +| [Screenshot review](./screenshot-review.md) | Reviewers who generate or curate product screenshots. | +| [Brand system](./brand-system.md) | Designers and UI contributors who apply the visual identity and product voice. | +| [ColorCraft Technical English](./writing-style.md) | All documentation contributors who need the controlled writing and terminology standard. | -- [User workflow](./user-workflow.md) — create, save, review, export, and manage palettes. -- [Runtime configuration](./runtime-configuration.md) — ports, hosts, CORS, LAN opt-in, and launcher behavior. -- [Architecture](./architecture.md) — application boundaries and data flow. -- [API](./api.md) — endpoints, request shapes, metadata, readiness, and errors. -- [Persistence and privacy](./persistence-and-privacy.md) — IndexedDB schema, migration, retention, and deletion. -- [Dashboard manifest](./dashboard-manifest.md) — static discovery defaults and runtime resolution. -- [Testing](./testing.md) — local and CI quality gates. -- [Screenshot review](./screenshot-review.md) — deterministic fixtures, temporary output, and curation. -- [Troubleshooting](./troubleshooting.md) — common Python, pnpm, port, and browser failures. -- [Brand system](./brand-system.md) — identity, tokens, themes, and shell conventions. -- [API contract rationale](./api-contracts.md) — compatibility and validation design notes. +The application code, API schemas, and tests are the source of truth for +behavior. Update the relevant canonical document when behavior changes. diff --git a/docs/api-contracts.md b/docs/api-contracts.md index 341756c..d2d53f4 100644 --- a/docs/api-contracts.md +++ b/docs/api-contracts.md @@ -1,71 +1,146 @@ # ColorCraft API contracts -ColorCraft uses explicit Pydantic response models in the FastAPI service and -matching Zod schemas in the React client. Public JSON fields use camelCase. -Unknown request fields are rejected. +## Contract boundary + +FastAPI Pydantic models define the service contracts. Matching frontend Zod +schemas validate API responses. Public JSON fields use camelCase. Unknown +request fields are rejected. + +The default base URL is `http://127.0.0.1:4100`. FastAPI provides interactive +OpenAPI documentation at `/docs` and the OpenAPI document at `/openapi.json`. + +## Routes + +| Method | Route | Request | Success response | +| --- | --- | --- | --- | +| `GET` | `/` | None | Service identity | +| `GET` | `/health` | None | Service liveness | +| `GET` | `/ready` | None | Readiness and capabilities | +| `GET` | `/metadata` | None | Runtime-resolved application metadata | +| `POST` | `/api/extract-colors?n_colors=5` | Multipart `file` | Extracted colors | +| `POST` | `/api/analyze-colors` | JSON palette | Relationship and all-pairs contrast analysis | +| `POST` | `/api/suggest-colors` | JSON palette | Suggestion approaches for each base color | +| `POST` | `/api/full-analysis?n_colors=5` | Multipart `file` | Extracted colors and analysis | + +`n_colors` must be an integer from 3 through 10. ## Canonical color input -Palette analysis and suggestion requests use a strict full-color input: +Analysis accepts 2–10 colors. Suggestions accept 1–10 colors. ```json { - "hex": "#667eea", - "rgb": { "r": 102, "g": 126, "b": 234 }, - "hsl": { "h": 229, "s": 75, "l": 66 } + "colors": [ + { + "hex": "#667eea", + "rgb": { "r": 102, "g": 126, "b": 234 }, + "hsl": { "h": 229, "s": 75, "l": 66 } + }, + { + "hex": "#f5f0e8", + "rgb": { "r": 245, "g": 240, "b": 232 }, + "hsl": { "h": 37, "s": 39, "l": 94 } + } + ] } ``` HEX must contain exactly six hexadecimal digits. RGB channels must be integers -from 0 through 255. HSL values must be integers with hue from 0 through 360 and -saturation and lightness from 0 through 100. +from 0 through 255. HSL values must be integers. Hue must be from 0 through 360. +Saturation and lightness must be from 0 through 100. -The API derives RGB and HSL from HEX during validation and rejects a request -when the supplied representations contradict one another. It never silently -chooses one representation over another. +The API derives RGB and HSL from HEX during validation. It rejects contradictory +representations. Hue 0 and hue 360 are equivalent when the other HSL values +match. -## Extracted colors +Do not send a bare color array. Send `{ "colors": [...] }`. -Extraction returns between one and the requested maximum number of colors. -Each extracted color adds: +## Extracted color -- `population`: ratio of the deterministic processing sample assigned to the - cluster, from 0 through 1 -- `pixelCount`: number of sampled processing pixels assigned to the cluster +Each extracted color contains: -Colors are sorted by population, largest first. Fully transparent pixels are -ignored. Partially transparent pixels are composited over white before -clustering. Representatives are sampled RGB medoids nearest the LAB cluster -center, rather than converted LAB medians. +```json +{ + "hex": "#667eea", + "rgb": { "r": 102, "g": 126, "b": 234 }, + "hsl": { "h": 229, "s": 75, "l": 66 }, + "population": 0.425, + "pixelCount": 4250 +} +``` -Uploads are limited to 10 MB and 40 million decoded pixels. CPU-heavy -clustering runs in a worker thread instead of blocking the async server loop. +`population` and `pixelCount` describe the deterministic processing sample. +Extraction can return fewer colors than requested. See +[Color analysis](./color-analysis.md). ## Relationship evidence -Every detected relationship includes `type`, `colorIndexes`, -`expectedAngles`, `measuredAngles`, `deviation`, and `confidence`. Hue -calculations are circular. Colors below 10% saturation and duplicate hues do -not provide geometric evidence. +Each detected relationship contains: + +- `type` +- `colorIndexes` +- `expectedAngles` +- `measuredAngles` +- `deviation` +- `confidence` + +`colorTheory` also contains: -`relationshipFit`, `relationshipSummary`, and `relationshipFactors` replace -the former bonus-based harmony score. They describe measured geometric fit, -not subjective design quality. +- `harmonies` +- `temperatureBalance` +- `relationshipFit` +- `relationshipSummary` +- `relationshipFactors` +- `tags` +- `metrics` -The detector uses these inclusive tolerances: +Relationship fit describes measured geometry. It does not describe aesthetic +quality. -| Relationship | Expected structure | Tolerance | -| --- | --- | --- | -| Complementary | one 180 degree separation | 12 degrees | -| Analogous | one 30 degree separation | 15 degrees | -| Triadic | three 120 degree circular gaps | 12 degrees per gap | -| Tetradic | four 90 degree circular gaps | 10 degrees per gap | -| Split-complementary | 150, 150, and 60 degree separations | 12 degrees | -| Monochromatic | meaningful hues around one circular mean | 10 degrees | +## Contrast analysis + +`accessibility` contains: + +- `pairs` +- `issues` +- `summary` + +Each pair contains `color1`, `color2`, `ratio`, `aaNormal`, `aaLarge`, +`aaaNormal`, and `aaaLarge`. These fields report contrast thresholds for the +pair. They do not prove complete accessibility or WCAG conformance. + +## Suggestion response + +Each item in `suggestions` contains: + +- `baseColor` +- `harmonies` + +Each harmony item contains `type`, `angle`, `description`, `useCases`, `mood`, +`examples`, and `suggestions`. Each suggested color contains `name`, +`description`, HEX, RGB, and HSL values. + +## Metadata and readiness + +`GET /metadata` returns schema version 1 with: + +- `id` +- `name` +- `descriptor` +- `version` +- `icon` +- `webUrl` +- `apiUrl` +- `healthUrl` +- `readinessUrl` +- `capabilities` + +`GET /ready` returns HTTP 200 with `status: "ready"` after startup. Before +startup completes, it can return HTTP 503 with `status: "not_ready"`. ## Errors -API failures use a stable error envelope: +Errors use one envelope: ```json { @@ -83,5 +158,18 @@ API failures use a stable error envelope: } ``` -`details` is optional for operational errors. The frontend converts this -envelope into `ColorCraftApiError` and presents a recoverable inline notice. +`details` can be absent or null for operational errors. + +Expected extraction errors include: + +| HTTP status | Code | Recovery | +| ---: | --- | --- | +| 413 | `upload_too_large` | Select a source image that is 10 MB or smaller. | +| 413 | `image_dimensions_too_large` | Reduce the decoded image dimensions below 40 million pixels. | +| 415 | `invalid_file_type` | Select a JPG, PNG, or WebP source image. | +| 422 | `image_decode_error` | Select a valid, decodable source image. | +| 422 | `no_visible_pixels` | Select an image that contains visible pixels. | +| 422 | `validation_error` | Correct the fields listed in `details`. | + +The frontend converts the envelope to `ColorCraftApiError` and displays a +recoverable inline notice. diff --git a/docs/api.md b/docs/api.md deleted file mode 100644 index 5977f71..0000000 --- a/docs/api.md +++ /dev/null @@ -1,49 +0,0 @@ -# API - -The default base URL is `http://127.0.0.1:4100`. Interactive OpenAPI documentation is available at `/docs`. - -| Method | Path | Purpose | -| --- | --- | --- | -| `GET` | `/` | Service identity | -| `GET` | `/health` | Liveness | -| `GET` | `/ready` | Readiness and capability slugs | -| `GET` | `/metadata` | Runtime-resolved dashboard metadata | -| `POST` | `/api/extract-colors?n_colors=5` | Multipart image extraction | -| `POST` | `/api/analyze-colors` | Analyze 2–10 normalized colors | -| `POST` | `/api/suggest-colors` | Suggest colors for 1–10 normalized colors | -| `POST` | `/api/full-analysis?n_colors=5` | Extract and analyze in one request | - -Analysis and suggestion requests wrap colors in an object: - -```json -{ - "colors": [ - { - "hex": "#6A5BCF", - "rgb": { "r": 106, "g": 91, "b": 207 }, - "hsl": { "h": 248, "s": 53, "l": 58 } - }, - { - "hex": "#F5F0E8", - "rgb": { "r": 245, "g": 240, "b": 232 }, - "hsl": { "h": 37, "s": 39, "l": 94 } - } - ] -} -``` - -Passing a bare array produces `422 validation_error`. Error responses are stable envelopes: - -```json -{ - "error": { - "code": "validation_error", - "message": "Request validation failed.", - "details": [] - } -} -``` - -Extraction accepts JPEG, PNG, and WebP files up to 10 MB, limits decoded dimensions, and ignores fully transparent pixels. `n_colors` must be 3–10. Expected client errors use 413, 415, or 422 rather than an unstructured server exception. - -`/metadata` reports the resolved web, API, health, readiness, icon, version, and capabilities. `/ready` returns `503` with `not_ready` until application startup completes. See [Dashboard manifest](./dashboard-manifest.md). diff --git a/docs/architecture.md b/docs/architecture.md index bce7456..b282c87 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,26 +1,137 @@ # Architecture +## System boundary + ```mermaid flowchart TD - Browser[React + TypeScript browser app] - IDB[(IndexedDB palette records)] + Browser[React and TypeScript frontend] + IDB[(IndexedDB saved palettes)] LS[(LocalStorage theme preference)] API[FastAPI service] - Extract[LAB extraction] - Theory[Harmony and suggestions] - Contrast[WCAG contrast] + Extract[Color extraction] + Theory[Relationship analysis] + Suggest[Color suggestions] + Access[All-pairs contrast analysis] + Browser <--> IDB Browser <--> LS - Browser -->|JSON and multipart HTTP| API + Browser -->|multipart and JSON HTTP| API API --> Extract API --> Theory - API --> Contrast + API --> Suggest + API --> Access +``` + +The frontend and API are separate local processes. `dev.py` resolves one runtime +configuration, starts both processes, waits for `/ready`, and stops both +processes on shutdown. + +## Frontend + +The React frontend owns: + +- Create, Review, Export, and Library navigation +- Source-image preview state +- Palette editing and HEX validation +- Color-role assignments +- Stale-analysis invalidation +- Suggestion invalidation +- Browser-side export generation +- Saved palette persistence +- Theme preference + +Pydantic models define API responses. Matching Zod schemas validate API +responses in the frontend. Public JSON fields use camelCase. + +## API + +The FastAPI service is stateless. It validates request data, performs extraction +and analysis, and returns explicit response models. It does not store palette +records or source images. + +The API runs CPU-intensive color extraction in a worker thread. The async +request loop remains available while scikit-learn performs clustering. + +See [API contracts](./api-contracts.md) for the canonical route and field +reference. + +## Extraction flow + +```mermaid +flowchart LR + Upload[Source image] --> Validate[Validate type, bytes, and decoded pixels] + Validate --> Resize[Resize to maximum 400 px dimension] + Resize --> Alpha[Remove transparent pixels and composite partial alpha] + Alpha --> Sample[Create deterministic processing sample] + Sample --> LAB[Convert sampled RGB pixels to LAB] + LAB --> KMeans[Cluster in LAB] + KMeans --> Medoid[Select sampled RGB medoid] + Medoid --> Order[Order by sampled pixel count] ``` -The frontend owns navigation, transient image preview state, explicit save state, role assignments, exports, and the local palette repository. `persistence.ts` validates every stored record with Zod, migrates legacy version-0 records to schema version 1, and ignores malformed or unknown future records without crashing the Library. +See [Color analysis](./color-analysis.md) for exact limits, formulas, and +interpretation constraints. + +## Analysis flow + +The frontend sends 2–10 normalized palette colors to +`POST /api/analyze-colors`. The API: + +1. Verifies that HEX, RGB, and HSL describe the same color. +2. De-duplicates meaningful hue evidence. +3. Detects harmony relationships with circular hue calculations. +4. Calculates relationship confidence and relationship fit. +5. Calculates all-pairs contrast data. +6. Returns a camelCase `analysis` contract. + +The frontend combines the API analysis with current role assignments. Contrast +shows role-specific checks and an advanced all-pairs contrast matrix. + +## Suggestions flow + +The frontend sends 1–10 colors to `POST /api/suggest-colors`. The API generates +suggestion approaches for each base color. Suggestions do not change the +palette automatically. The user must select **Add**. + +The frontend fingerprints the current palette. A palette color change +invalidates the displayed suggestion results. + +## Export flow + +The frontend generates every export without an API request: + +- CSS custom properties +- Schema-versioned JSON +- Tailwind theme colors +- SVG swatch sheet + +CSS and Tailwind comments replace line breaks and the `*/` sequence in the +palette name. SVG output escapes `&`, `<`, `>`, `"`, and `'`. Download uses an +object URL and revokes the URL after the browser starts the download. + +Export does not create or update a saved palette record. + +## Session and persistence + +The current source-image preview, analysis, suggestions, and unsaved changes +remain in memory. The URL records the active application view and Review tab. + +Saved palette records use schema version 1 in the browser's `colorcraft` +IndexedDB database. The frontend validates each record before use. It migrates +unversioned and version-0 records. It ignores malformed records and unknown +future schema versions. + +Source-image bytes are not part of a saved palette record. See +[Persistence and privacy](./persistence-and-privacy.md). + +## Runtime and security boundaries -The backend is stateless. It validates contract models with Pydantic and delegates CPU-heavy extraction to a thread pool. Color extraction uses deterministic sampling, LAB conversion, KMeans clustering, and representative processed pixels. Theory, suggestions, and contrast operate on normalized color contracts. +The default services bind to `127.0.0.1`. CORS accepts the resolved web origin. +Wildcard origins are rejected. Non-loopback hosts and origins require +`COLORCRAFT_ALLOW_LAN_ACCESS=true`. -`dev.py` is a process supervisor rather than an application server. It resolves configuration once, starts the API and Vite with consistent values, checks `/ready`, optionally opens the browser, and forwards shutdown to both processes. +ColorCraft does not provide authentication. Trusted LAN access expands the +network boundary. Do not expose the development API directly to an untrusted +network. -Dashboard integration has two layers: the checked-in static manifest advertises stable defaults, while `/metadata` reports the runtime-resolved addresses. See [API](./api.md) and [Persistence and privacy](./persistence-and-privacy.md). +See [Runtime configuration](./runtime-configuration.md) for exact settings. diff --git a/docs/brand-system.md b/docs/brand-system.md index 734cfb5..4c90fe6 100644 --- a/docs/brand-system.md +++ b/docs/brand-system.md @@ -21,6 +21,11 @@ local utility rather than a dashboard and does not imply accounts or cloud sync. - Avoid generic AI language and inflated claims. - Keep helper text short and place detail beside the result it qualifies. +This document governs product voice, visual identity, and interface tone. +[ColorCraft Technical English](./writing-style.md) governs procedural and +technical documentation. The standards are complementary. Do not rewrite design +rationale as a controlled procedure. + ## Mark The provisional ColorCraft mark is an original double-C aperture: @@ -87,8 +92,10 @@ menus use elevated shadow, and modal dialogs use dialog shadow. - At 1024px and below, the sidebar becomes a mobile top bar and bottom navigation without changing the information architecture. - Navigation is URL-backed so browser history restores the current workflow - stage. Palette data itself remains session-only. -- Do not add Library or Recent Palettes until saved-palette persistence exists. + stage and Review tab. +- Library and Recent palettes show saved palette records from IndexedDB. +- Source-image previews, analysis, suggestions, and unsaved changes remain + session-only. ## Themes and accessibility diff --git a/docs/color-analysis.md b/docs/color-analysis.md new file mode 100644 index 0000000..1cc8fcd --- /dev/null +++ b/docs/color-analysis.md @@ -0,0 +1,192 @@ +# Color analysis + +## Scope + +This document describes the current extraction, relationship, fit, and contrast +calculations. It does not define aesthetic quality. + +## Source-image validation + +The API accepts: + +- `image/jpeg` +- `image/png` +- `image/webp` + +The API reads at most 10 MB plus one byte. It returns HTTP 413 when the upload is +larger than 10 MB. The decoded image can contain at most 40,000,000 pixels. + +The extraction endpoint accepts `n_colors` from 3 through 10. The extraction +function can return fewer colors when the processing sample contains fewer +unique colors. + +## Resize and transparency + +Before clustering, the extraction service: + +1. Loads the decoded source image. +2. Resizes the image when either dimension is larger than 400 pixels. +3. Preserves aspect ratio and uses Pillow LANCZOS resampling. +4. Removes pixels with alpha 0. +5. Composites partial-alpha RGB values over white. +6. Rounds the composited channel values to integers from 0 through 255. + +A source image with no visible pixels returns `no_visible_pixels`. + +The browser source-image picker uses a separate canvas preview with a maximum +dimension of 1600 pixels. That preview is not the backend processing sample. + +## Deterministic processing sample + +If the resized image contains 10,000 visible pixels or fewer, all visible +pixels form the processing sample. Otherwise, NumPy selects 10,000 pixels +without replacement with random seed 42. + +Population and `pixelCount` describe this processing sample. They do not +describe every decoded pixel in the source image. + +## LAB clustering and representative colors + +The extraction service converts the processing-sample RGB pixels to CIE LAB +with a D65 reference white. It performs scikit-learn KMeans clustering with: + +- `random_state=42` +- `n_init=10` +- `max_iter=300` + +The effective cluster count is the minimum of: + +- The requested color count +- The number of unique pixels in the processing sample +- The number of pixels in the processing sample + +For each cluster, the extraction service selects the sampled RGB pixel with the +smallest squared LAB distance to the cluster center. This sampled RGB medoid is +the representative color. The service does not convert a LAB median or cluster +center back to RGB. + +`population` is the cluster's sampled pixel count divided by the processing +sample size. `pixelCount` is the cluster's sampled pixel count. Results are +sorted by descending `pixelCount`, then by HEX value for a deterministic tie. + +## Meaningful hue evidence + +Relationship detection uses circular hue calculations. A palette color provides +meaningful hue evidence when HSL saturation is at least 10%. + +The detector treats hue values less than 1 degree apart as duplicate hue +evidence. Repeated hues cannot create additional relationships. + +## Relationship tolerances + +All limits are inclusive. + +| Harmony relationship | Expected geometry | Maximum angular deviation | +| --- | --- | --- | +| Complementary | One 180 degree separation | 12 degrees | +| Analogous | One 30 degree separation | 15 degrees | +| Triadic | Three 120 degree circular gaps | 12 degrees per gap | +| Tetradic | Four 90 degree circular gaps | 10 degrees per gap | +| Split-complementary | 150, 150, and 60 degree separations | 12 degrees | +| Monochromatic | Hues around one circular mean | 10 degrees | + +Monochromatic detection also requires variation. It rejects a group when both +the saturation range and lightness range are 10 percentage points or less. + +## Relationship confidence + +For one detected relationship: + +```text +confidence = max(0, 1 - deviation / (tolerance × 1.5)) +``` + +The API rounds relationship confidence to three decimal places. + +## Relationship fit + +ColorCraft selects the relationship with the highest confidence for each +detected relationship type. It then calculates: + +```text +relationship fit = + round(70 × mean best confidence + 30 × meaningful-hue coverage) +``` + +Meaningful-hue coverage is the number of meaningful hues involved in any +detected relationship divided by the total meaningful-hue count, with a maximum +of 1. + +The summary labels are: + +- 75–100: `Strong geometric relationship` +- 45–74: `Moderate geometric relationship` +- 0–44: `Limited geometric relationship` +- No detected relationship: `No strong geometric relationship detected` + +Relationship fit is not a harmony score, beauty score, palette-quality score, +accessibility score, or design recommendation. + +## Other palette measurements + +- Hue diversity is circular standard deviation in degrees, capped at 180. +- Average saturation is the arithmetic mean of HSL saturation. +- Lightness range is the maximum HSL lightness minus the minimum. +- Temperature ratios count meaningful hues in the current warm and cool + intervals. They do not evaluate aesthetic balance. + +## Contrast calculation + +ColorCraft converts each sRGB channel to a linear value with the 0.04045 +breakpoint. Relative luminance is: + +```text +L = 0.2126R + 0.7152G + 0.0722B +``` + +Contrast ratio is: + +```text +(lighter luminance + 0.05) / (darker luminance + 0.05) +``` + +The API reports: + +| Result | Minimum ratio | +| --- | ---: | +| WCAG AA normal text | 4.5:1 | +| WCAG AA large text | 3:1 | +| WCAG AAA normal text | 7:1 | +| WCAG AAA large text | 4.5:1 | + +The API evaluates every palette color pair. Review also evaluates assigned +color roles in meaningful interface combinations. + +A passing ratio applies only to the evaluated contrast pair and text category. +It does not prove complete interface accessibility or WCAG conformance. + +## Suggestions + +Suggestions are optional calculations from a selected base color. A suggestion +does not confirm that a detected relationship exists in the current palette. +The frontend invalidates suggestions when any palette color changes. + +## Export accuracy and escaping + +JSON export uses `schemaVersion: 1`. JSON includes ordered colors, normalized +color values, optional population, color-role metadata, and role assignments. + +CSS and Tailwind output sanitize palette names used in comments. SVG output +escapes XML-sensitive characters. Export generation does not alter the palette +and does not save it to IndexedDB. + +## Interpretation limits + +- A representative color comes from the processing sample, not every decoded + source-image pixel. +- A harmony relationship describes geometry, not aesthetic quality. +- Relationship confidence applies to one relationship. +- Relationship fit summarizes detected geometry and coverage. +- Contrast is independent of hue geometry. +- Contrast evaluation covers one accessibility requirement. +- Suggestions are recommendations, not measurements. diff --git a/docs/getting-started.md b/docs/getting-started.md index b4b106d..cab371b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,48 +2,142 @@ ## Requirements -- Python 3.11 (recommended; pinned Pillow wheels are not available for every newer Python release) +- Python 3.11 - Node.js 20 or newer - Corepack - Git -## Windows +Python 3.11 is the supported setup version. The pinned Pillow release does not +provide a wheel for every newer Python release. -From the repository root: +## Install ColorCraft on Windows -```powershell -py -3.11 -m venv backend\.venv311 -.\backend\.venv311\Scripts\python.exe -m pip install --upgrade pip -.\backend\.venv311\Scripts\python.exe -m pip install -r backend\requirements-dev.txt -cd frontend -corepack pnpm@9.15.9 install -cd .. -.\backend\.venv311\Scripts\python.exe dev.py -``` +### Purpose -The launcher supervises the FastAPI and Vite processes, waits for readiness, prints every URL, and shuts both down when you press `Ctrl+C`. +Install the backend and frontend dependencies. -## macOS and Linux +### Procedure -```bash -python3.11 -m venv backend/.venv -backend/.venv/bin/python -m pip install --upgrade pip -backend/.venv/bin/python -m pip install -r backend/requirements-dev.txt -cd frontend -corepack pnpm@9.15.9 install -cd .. -backend/.venv/bin/python dev.py -``` +1. Open PowerShell in the repository root. +2. Create the backend virtual environment: -If the browser does not open, visit `http://127.0.0.1:5174`. The API docs are at `http://127.0.0.1:4100/docs`. + ```powershell + py -3.11 -m venv backend\.venv311 + ``` -## Production build +3. Upgrade pip: -```powershell -cd frontend -corepack pnpm@9.15.9 build -``` + ```powershell + .\backend\.venv311\Scripts\python.exe -m pip install --upgrade pip + ``` -The output is written to `frontend/dist`. This repository's launcher is a development workflow; choose a production web server and API process manager for deployment. +4. Install the backend development dependencies: -For configuration overrides, see [Runtime configuration](./runtime-configuration.md). For setup errors, see [Troubleshooting](./troubleshooting.md). + ```powershell + .\backend\.venv311\Scripts\python.exe -m pip install -r backend\requirements-dev.txt + ``` + +5. Install the frontend dependencies: + + ```powershell + cd frontend + corepack pnpm@9.15.9 install + cd .. + ``` + +### Result + +The repository contains a local backend virtual environment and the frontend +packages. + +## Install ColorCraft on macOS or Linux + +1. Open a terminal in the repository root. +2. Run: + + ```bash + python3.11 -m venv backend/.venv + backend/.venv/bin/python -m pip install --upgrade pip + backend/.venv/bin/python -m pip install -r backend/requirements-dev.txt + cd frontend + corepack pnpm@9.15.9 install + cd .. + ``` + +## Start ColorCraft in one terminal + +### Before you start + +Install both dependency sets. Stop any process that uses port 5174 or 4100. + +### Procedure + +1. Open a terminal in the repository root. +2. On Windows, run: + + ```powershell + .\backend\.venv311\Scripts\python.exe dev.py + ``` + +3. On macOS or Linux, run: + + ```bash + backend/.venv/bin/python dev.py + ``` + +4. Wait for `ColorCraft is ready`. +5. Open `http://127.0.0.1:5174` in a browser. + +### Result + +The web application listens on `http://127.0.0.1:5174`. The API listens on +`http://127.0.0.1:4100`. The launcher also prints health, readiness, and metadata +URLs. + +### Recovery + +If startup reports a port conflict, stop the process that uses the reported +port or configure a different port. If the launcher cannot find a virtual +environment, repeat the installation procedure with Python 3.11. + +## Start the services manually + +Use two terminals when you must inspect each process separately. + +1. Start the API from the `backend` directory: + + ```powershell + cd backend + .\.venv311\Scripts\python.exe -m uvicorn main:app --host 127.0.0.1 --port 4100 + ``` + +2. Start Vite from the `frontend` directory: + + ```powershell + cd frontend + corepack pnpm@9.15.9 dev --host 127.0.0.1 --port 5174 + ``` + +3. Open `http://127.0.0.1:5174`. + +## Stop ColorCraft + +1. Select the terminal that runs the launcher. +2. Press `Ctrl+C`. +3. If Windows asks `Terminate batch job (Y/N)?`, enter `Y`. + +The launcher stops both child services. + +## Configure trusted LAN access + +The default configuration accepts loopback traffic only. Trusted LAN access +requires `COLORCRAFT_ALLOW_LAN_ACCESS=true`, non-loopback bind hosts, and exact +CORS origins. + +**Warning:** ColorCraft does not provide authentication. LAN access exposes the +API and source-image traffic to the configured network path. Do not expose the +development services to an untrusted network. + +See [Runtime configuration](./runtime-configuration.md) for the exact variables. +See [Troubleshooting](./troubleshooting.md) for installation and startup +recovery. diff --git a/docs/persistence-and-privacy.md b/docs/persistence-and-privacy.md index 741a8c8..0c02bdd 100644 --- a/docs/persistence-and-privacy.md +++ b/docs/persistence-and-privacy.md @@ -1,30 +1,64 @@ # Persistence and privacy -ColorCraft is local-first, not account-backed. There is no synchronization service or server-side palette database. +## Storage model -## What is stored +ColorCraft is local-first and does not use accounts, cloud synchronization, or +a server-side palette database. -The `colorcraft` IndexedDB database contains `palettes` records with: +The frontend uses: -- schema version, ID, name, and created/updated timestamps -- manual or image source type and an optional source filename -- 1–10 normalized colors with optional population and pixel-count metadata -- semantic contrast-role assignments +- IndexedDB for saved palette records +- LocalStorage for the theme preference +- Browser memory for source-image previews, analysis, suggestions, and unsaved + changes -Schema version 1 is validated with Zod whenever records are read. Legacy records with no version or version 0 are migrated in memory and become version 1 when next saved. Corrupt records and unknown future versions are skipped so one bad record cannot prevent the Library from opening. +## Saved palette records -LocalStorage contains only the theme preference. Uploaded image bytes, object URLs, analysis responses, and suggestions are not written to LocalStorage or IndexedDB. +The `colorcraft` IndexedDB database contains the `palettes` object store. A +schema-version-1 record contains: -## Image processing +- Record ID and palette name +- Created and updated timestamps +- Manual or image source type +- Optional source filename +- 1–10 normalized palette colors +- Optional population and `pixelCount` +- Color-role assignments -An uploaded file is previewed in browser memory and sent to the configured FastAPI service. The service reads it for that request and does not write it to disk or a database. If LAN access is enabled, image traffic crosses that network path; use a trusted network or HTTPS termination. +The frontend validates each record with Zod. It can migrate an unversioned or +version-0 record to the current shape. The next save writes schema version 1. +The frontend ignores malformed records and unknown future schema versions. + +## Source images + +The browser creates a session-only source-image preview. It sends the source +image to the configured API for extraction. + +The API reads the image for the request. It does not write the image to disk or +a database. A saved palette can contain the source filename and extracted color +metadata. It does not contain the source-image bytes. ## Retention and deletion -Palette records remain until the user deletes them or clears site data for the ColorCraft origin. Library deletion is confirmed and cannot be undone. Different browser profiles, origins, and port combinations have separate browser storage. +A saved palette remains until the user selects **Delete palette** or clears site +data for the ColorCraft origin. Deletion cannot be undone. + +Browser profiles and origins have separate IndexedDB databases. Changing the +web host or port can show a different, empty Palette Library. + +Removing the repository does not necessarily remove browser storage. Use the +browser's site-data controls to remove all saved palettes and the theme +preference. + +## Privacy and network boundary -To remove all ColorCraft browser data, use the browser's site-data controls for the web origin. Uninstalling the repository does not necessarily clear browser data. +The default services bind to loopback addresses. The default configuration +keeps browser-to-API traffic on the current computer. -## Security posture +`COLORCRAFT_ALLOW_LAN_ACCESS=true` permits a larger network boundary. On a LAN, +source-image data traverses the configured network path. ColorCraft does not +provide authentication or transport encryption. Do not expose it directly to +an untrusted network. -Defaults bind both services to loopback. Non-loopback hosts and CORS origins require explicit `COLORCRAFT_ALLOW_LAN_ACCESS=true`; wildcard CORS is rejected. ColorCraft does not provide authentication, so do not expose it directly to an untrusted network. +Local does not automatically mean private, secure, offline, anonymous, or +persistent. diff --git a/docs/runtime-configuration.md b/docs/runtime-configuration.md index e1a4330..8a3b384 100644 --- a/docs/runtime-configuration.md +++ b/docs/runtime-configuration.md @@ -1,6 +1,8 @@ # Runtime configuration -ColorCraft defaults to loopback-only services: +## Defaults + +ColorCraft reads defaults from `runtime-config.json`. | Variable | Default | Purpose | | --- | --- | --- | @@ -8,23 +10,78 @@ ColorCraft defaults to loopback-only services: | `COLORCRAFT_WEB_PORT` | `5174` | Web port | | `COLORCRAFT_API_HOST` | `127.0.0.1` | FastAPI bind host | | `COLORCRAFT_API_PORT` | `4100` | API port | -| `COLORCRAFT_READINESS_TIMEOUT_SECONDS` | `30` | Launcher startup deadline | -| `COLORCRAFT_ALLOWED_ORIGINS` | resolved web origin | Comma-separated exact CORS origins | -| `VITE_API_URL` | resolved API origin | Browser-visible API base URL | -| `COLORCRAFT_ALLOW_LAN_ACCESS` | `false` | Required opt-in for non-loopback hosts/origins | -| `COLORCRAFT_NO_BROWSER` | `false` | Prevent automatic browser launch | +| `COLORCRAFT_READINESS_TIMEOUT_SECONDS` | `30` | Launcher startup deadline in seconds | +| `COLORCRAFT_ALLOWED_ORIGINS` | Resolved web origin | Comma-separated exact CORS origins | +| `VITE_COLORCRAFT_API_URL` | Resolved API origin | API base URL that the frontend uses | +| `COLORCRAFT_ALLOW_LAN_ACCESS` | `false` | Required opt-in for non-loopback hosts and origins | + +Ports must be integers from 1 through 65535. The launcher checks both configured +ports before it starts a child process. + +## Change the local ports + +### Purpose + +Start ColorCraft when a default port is unavailable. + +### Procedure + +1. Open PowerShell in the repository root. +2. Set both port variables: + + ```powershell + $env:COLORCRAFT_WEB_PORT = "5184" + $env:COLORCRAFT_API_PORT = "4110" + ``` + +3. Start the launcher: -Example PowerShell override: + ```powershell + .\backend\.venv311\Scripts\python.exe dev.py + ``` + +### Result + +The launcher prints the resolved web, API, health, readiness, and metadata URLs. + +### Recovery + +If a configured port is still unavailable, select unused port numbers and +repeat the procedure. + +## Configure trusted LAN access + +**Warning:** ColorCraft does not provide authentication. Complete this procedure +only on a trusted network with an appropriate firewall. + +Example: ```powershell -$env:COLORCRAFT_WEB_PORT = "5184" -$env:COLORCRAFT_API_PORT = "4110" -$env:COLORCRAFT_NO_BROWSER = "true" +$env:COLORCRAFT_ALLOW_LAN_ACCESS = "true" +$env:COLORCRAFT_WEB_HOST = "0.0.0.0" +$env:COLORCRAFT_API_HOST = "0.0.0.0" +$env:COLORCRAFT_ALLOWED_ORIGINS = "http://192.168.1.20:5174" +$env:VITE_COLORCRAFT_API_URL = "http://192.168.1.20:4100" .\backend\.venv311\Scripts\python.exe dev.py ``` -Ports must be valid and distinct. The launcher checks availability before starting either child process and reports a focused error if a port is occupied. Wildcard CORS origins are rejected. +Replace `192.168.1.20` with the computer's address on the trusted network. CORS +origins must include the exact scheme, host, and port. Wildcard origins are +rejected. + +Source-image data travels from the browser to the configured API URL. Use HTTPS +termination when the network requires transport encryption. + +## Configuration behavior -LAN binding expands the trust boundary. Set `COLORCRAFT_ALLOW_LAN_ACCESS=true`, configure explicit hosts and origins, and use a firewall appropriate to the network. Uploaded images traverse the configured network path to the API. +- Non-loopback bind hosts require `COLORCRAFT_ALLOW_LAN_ACCESS=true`. +- Non-loopback CORS origins require `COLORCRAFT_ALLOW_LAN_ACCESS=true`. +- An origin cannot contain credentials, a path other than `/`, a query, or a + fragment. +- The launcher does not open a browser automatically. +- `VITE_COLORCRAFT_API_URL` must be reachable from the browser. +- Environment variables override `runtime-config.json`. -Static dashboard defaults stay in [`app-manifest.json`](../app-manifest.json). Consumers that need overrides must read `/metadata`; see [Dashboard manifest](./dashboard-manifest.md). +Static dashboard defaults are in [`app-manifest.json`](../app-manifest.json). +Dashboard consumers must use `/metadata` when they need runtime-resolved URLs. +See [Dashboard manifest](./dashboard-manifest.md). diff --git a/docs/testing.md b/docs/testing.md index 66567b9..2e1c811 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -1,8 +1,11 @@ # Testing -## Complete validation +## Run the complete validation -Install the browser once: +### Before you start + +Install the backend development dependencies and frontend packages. Install +Playwright Chromium once: ```powershell cd frontend @@ -10,37 +13,76 @@ corepack pnpm@9.15.9 exec playwright install chromium cd .. ``` -Then run the same gate used by CI: +### Procedure -```powershell -.\backend\.venv311\Scripts\python.exe check.py -``` +1. Open a terminal in the repository root. +2. On Windows, run: + + ```powershell + .\backend\.venv311\Scripts\python.exe check.py + ``` + +3. On macOS or Linux, run: + + ```bash + backend/.venv/bin/python check.py + ``` -On Unix, use the Python executable from `backend/.venv`. `check.cmd` is a Windows convenience wrapper. +### Result -The gate runs: +The validation runner reports `ColorCraft validation passed.` + +### Recovery + +If a step fails, correct the first reported failure. Run the complete validation +again. + +## Validation layers + +The complete gate runs: 1. Prettier format verification 2. ESLint 3. TypeScript checking -4. Vitest unit/integration tests with V8 coverage +4. Vitest unit and integration tests with V8 coverage 5. Vite production build -6. Ruff format and lint checks -7. mypy on stable backend contracts and the validation runner -8. pytest with backend and launcher coverage -9. Playwright Chromium workflow and axe accessibility checks +6. Ruff format verification +7. Ruff lint +8. mypy on stable backend contracts and the validation runner +9. pytest with backend and launcher coverage +10. Playwright Chromium workflows and axe accessibility checks -Use `python check.py --skip-e2e` for a fast local loop. CI always runs the complete gate. +`check.cmd` selects a known Windows backend virtual environment and runs +`check.py`. ## Focused commands ```powershell cd frontend +corepack pnpm@9.15.9 format:check +corepack pnpm@9.15.9 lint +corepack pnpm@9.15.9 typecheck corepack pnpm@9.15.9 test +corepack pnpm@9.15.9 build corepack pnpm@9.15.9 test:e2e -corepack pnpm@9.15.9 review:screenshots cd .. .\backend\.venv311\Scripts\python.exe -m pytest tests +.\backend\.venv311\Scripts\python.exe -m compileall -q backend dev.py check.py +git diff --check ``` -Persistence tests use an in-memory IndexedDB implementation and cover migration, malformed data, identity-preserving save updates, sorting, rename, duplicate, and delete. Browser tests cover create → save → analyze → contrast → export → reopen → delete plus representative accessibility states. Backend tests cover contracts, upload defenses, analysis, suggestions, metadata, readiness, runtime parsing, and launcher supervision. +Use `python check.py --skip-e2e` for a fast local iteration. CI runs the complete +gate. + +## Test scope + +- Persistence tests cover validation, migration, update identity, sorting, + rename, duplicate, and delete. +- Frontend tests cover navigation, palette state, stale analysis, Save states, + Review, Export, and API contract validation. +- Backend tests cover upload defenses, extraction, relationship analysis, + suggestions, contrast, metadata, readiness, configuration, and launcher + supervision. +- Browser tests cover create, save, analyze, role assignment, export, reopen, + delete, and representative accessibility states. +- Documentation tests verify local Markdown links and curated screenshot names. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 2b990e4..347a678 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,38 +1,150 @@ # Troubleshooting -## Pillow fails to install on Python 3.13 +## Pillow installation fails -The pinned Pillow version may not provide a compatible wheel. Install Python 3.11 and create the environment explicitly: +**Problem:** pip cannot build the pinned Pillow release. -```powershell -py -3.11 -m venv backend\.venv311 -.\backend\.venv311\Scripts\python.exe -m pip install -r backend\requirements-dev.txt -``` +**Cause:** The selected Python release does not have a compatible wheel. + +**Recovery:** + +1. Install Python 3.11. +2. Create a new environment: + + ```powershell + py -3.11 -m venv backend\.venv311 + ``` + +3. Install the development requirements: -If virtual-environment creation reports that `venvlauncher.exe` cannot be copied, close processes using that environment, choose a new directory name, and verify the Python installation outside the Microsoft Store alias. + ```powershell + .\backend\.venv311\Scripts\python.exe -m pip install -r backend\requirements-dev.txt + ``` -## pnpm reports “packages field missing or empty” +If Windows cannot copy `venvlauncher.exe`, close processes that use the target +environment. Create the environment with a new directory name if necessary. -Run pnpm inside `frontend`, not the repository root: +## pnpm reports `packages field missing or empty` + +**Problem:** pnpm cannot find the workspace package. + +**Cause:** The command ran outside `frontend`, or it did not use the pinned pnpm +version. + +**Recovery:** ```powershell cd frontend corepack pnpm@9.15.9 install ``` -The repository pins pnpm 9 for reproducibility. If pnpm blocks an `esbuild` lifecycle script, use the pinned version and the checked-in lockfile rather than approving an unrelated global pnpm policy. +`frontend/pnpm-workspace.yaml` permits the required esbuild installation script. +Do not approve unrelated package build scripts. + +## A port is unavailable + +**Problem:** The launcher reports that the web or API address is unavailable. -## A port is already in use +**Cause:** Another process uses the configured port. -ColorCraft defaults to 5174/4100. Stop the conflicting process or set `COLORCRAFT_WEB_PORT` and `COLORCRAFT_API_PORT`; see [Runtime configuration](./runtime-configuration.md). The launcher prints which address is occupied. +**Recovery:** Stop the other process, or set `COLORCRAFT_WEB_PORT` and +`COLORCRAFT_API_PORT` to unused ports. See +[Runtime configuration](./runtime-configuration.md). + +## The API does not become ready + +**Problem:** The launcher reaches the readiness timeout. + +**Recovery:** + +1. Read the first API error in the terminal. +2. Open the printed `/health` URL. +3. Open the printed `/ready` URL. +4. Verify that the backend virtual environment contains the required packages. +5. Restart the launcher. ## The frontend cannot reach the API -Check `/ready`, confirm the printed API URL, and ensure `VITE_API_URL` matches the browser-reachable address. Custom web origins must appear exactly in `COLORCRAFT_ALLOWED_ORIGINS`. +**Problem:** ColorCraft displays an API connection error. + +**Recovery:** + +1. Verify that `/ready` returns HTTP 200. +2. Verify the printed API URL. +3. If you configured a browser-visible API URL, verify + `VITE_COLORCRAFT_API_URL`. +4. Verify that `COLORCRAFT_ALLOWED_ORIGINS` contains the exact web origin. +5. Restart both services after you change environment variables. + +## A LAN origin is rejected + +**Problem:** Startup or CORS validation rejects a non-loopback host. + +**Recovery:** Set `COLORCRAFT_ALLOW_LAN_ACCESS=true`. Configure explicit bind +hosts, an exact CORS origin, and a browser-reachable +`VITE_COLORCRAFT_API_URL`. + +**Warning:** Do not expose ColorCraft to an untrusted network. + +## A source image is rejected + +Check the API error code: + +- `upload_too_large`: Select a source image that is 10 MB or smaller. +- `image_dimensions_too_large`: Reduce the decoded image below 40 million + pixels. +- `invalid_file_type`: Select a JPG, PNG, or WebP source image. +- `image_decode_error`: Select a valid, decodable source image. +- `no_visible_pixels`: Select an image that contains visible pixels. + +The current Create UI states a 15 MB limit, but the API enforces 10 MB. Use +10 MB as the effective limit. + +## Color extraction returns fewer colors than requested + +**Cause:** The processing sample contains fewer unique colors than the requested +cluster count. + +**Result:** This behavior is expected. Use the returned palette or select +another source image. + +## Review shows stale analysis + +**Problem:** A palette color changed after analysis. + +**Recovery:** Select **Refresh analysis**. ColorCraft does not present the old +analysis as current. + +## Clipboard copy fails + +**Problem:** The browser denies clipboard access. + +**Recovery:** Select **Select preview**, and then copy the selected export text +manually. + +## Download creation fails + +**Problem:** The browser cannot create the exported file. + +**Recovery:** Select **Copy**. If copy also fails, select **Select preview** and +copy the text manually. + +## Saved palettes are missing + +**Cause:** Saved palettes are scoped to a browser profile and exact web origin. +Private browsing, a different port, or cleared site data can show an empty +Palette Library. + +**Recovery:** Return to the original browser profile and origin. ColorCraft does +not have cloud recovery. + +## Browser history does not restore a palette -## Saved palettes disappeared +**Cause:** The URL stores only the application view and Review tab. It does not +store palette data. -Palettes are scoped to the exact browser profile and origin. Changing the web port, using private browsing, or clearing site data creates an empty Library. ColorCraft has no cloud recovery. +**Recovery:** Open the saved palette from **Library**. Unsaved palette state +cannot be recovered after a new session. ## Playwright cannot find Chromium @@ -41,6 +153,8 @@ cd frontend corepack pnpm@9.15.9 exec playwright install chromium ``` -## A 422 response appears for analysis or suggestions +## Analysis or suggestions return HTTP 422 -Send `{ "colors": [...] }`, not a bare array, and include consistent HEX, RGB, and HSL values. Read the structured `error.details` entries or use `/docs`. +Send `{ "colors": [...] }`, not a bare array. Verify that HEX, RGB, and HSL +describe the same colors. Read `error.details`, or open `/docs` for the current +OpenAPI contract. diff --git a/docs/user-guide.md b/docs/user-guide.md new file mode 100644 index 0000000..979242c --- /dev/null +++ b/docs/user-guide.md @@ -0,0 +1,172 @@ +# User guide + +## Create a palette from a source image + +### Before you start + +- Select a JPG, PNG, or WebP source image. +- Use a file that is 10 MB or smaller. +- Use an image with no more than 40 million decoded pixels. +- Saving the palette does not save the source-image bytes. + +### Procedure + +1. Select **Create**. +2. Drop a source image in the drop zone, or select **Choose image**. +3. Wait for the initial color extraction. +4. To change the palette size, set **Requested colors** from 3 through 10, and + then select **Re-extract**. +5. Edit the returned palette colors as required. + +### Result + +ColorCraft shows the source-image preview and the extracted colors. ColorCraft +can return fewer colors than requested when the processing sample contains +fewer distinct colors. + +### Recovery + +If the API rejects the upload, read the inline error. Select a supported image +that is within the byte and decoded-pixel limits. + +## Create a palette manually + +1. Select **Create**. +2. Select **Start manually**. +3. Enter a six-digit HEX value for each palette color. +4. Select **Add color** to add a palette color. + +The palette can contain at most 10 colors. + +## Edit a palette color + +Use one of these controls: + +- Enter a six-digit HEX value. +- Select **Open native color picker for color _n_**. +- Select **Duplicate color**. +- Select **Remove color**. +- For an active source image, select **Pick color _n_ from image**. + +ColorCraft does not currently provide a palette-reorder control. + +## Sample a color from the source image + +1. Select a palette color. +2. Select **Pick from image** or **Pick color _n_ from image**. +3. Point, touch, or click the source image. +4. Select **Use selected color**. + +ColorCraft samples a canvas preview with a maximum dimension of 1600 pixels. +The sampled preview is separate from the backend processing sample. + +## Save the palette + +- **Unsaved** means that the current palette has no saved record. +- **Saved** means that the current palette matches its IndexedDB record. +- **Modified** means that the current palette differs from its saved record. + +Select **Save palette** to create a record. Select **Save changes** to update the +active record. Saved palettes remain in the current browser profile and origin. +They do not synchronize to an account or cloud service. + +## Use the Palette Library + +Select **Library** to list saved palettes. The Library sorts records by the most +recent update. + +You can: + +- Search saved palettes by name. +- Open a saved palette. +- Rename a saved palette. +- Duplicate a saved palette. +- Delete a saved palette. + +**Warning:** **Delete palette** removes the record from IndexedDB. The action +cannot be undone. + +## Analyze a palette + +1. Create or open a palette with at least two valid colors. +2. Select **Analyze palette**. +3. Select **Review**. + +ColorCraft invalidates the current analysis when a palette color changes. +Select **Refresh analysis** before you use a stale result. + +### Overview + +**Overview** shows hue positions, detected connections, population-based +dominant color information, temperature, saturation, lightness, relationship +fit, and a next action. + +### Harmony + +**Harmony** lists detected harmony relationships in descending relationship +confidence. Open **Advanced technical details** to inspect expected angles, +measured angles, angular deviation, and raw confidence. + +Relationship fit and relationship confidence describe geometric evidence. They +do not measure aesthetic quality. + +### Contrast + +1. Select **Contrast**. +2. Assign palette colors to the available color roles. +3. Review each available contrast-role check. +4. Open **Advanced: all-pairs contrast matrix** only when you need exploratory + comparisons. + +The current color-role labels are: + +- **Page background** +- **Surface** +- **Primary text** +- **Secondary text** +- **Primary action** +- **Action text** +- **Border** +- **Focus indicator** + +A passing contrast-role check does not prove that a complete interface is +accessible or conforms to WCAG. + +### Suggestions + +1. Select **Suggestions**. +2. Select a base color. +3. Select **Generate suggestions**. +4. Review the suggested colors. +5. Select **Add** for an optional color. + +Select **Explore all relationships** for the complete set of suggestion +approaches. ColorCraft discards suggestion results when a palette color changes. + +## Export a palette + +1. Select **Export**. +2. Enter a **Palette name**. +3. Select an export format: + - **CSS custom properties** + - **JSON** + - **Tailwind theme colors** + - **SVG swatch sheet** +4. Select **Copy** or **Download**. + +**Copy** places the generated text on the clipboard. **Download** creates an +exported file through the browser. Export does not create or update a saved +palette record. + +If clipboard permission is denied, select **Select preview**, and then copy the +selected text manually. If download creation fails, copy the preview instead. + +## Understand session and history behavior + +The current source-image preview, analysis, suggestions, and unsaved changes +remain in memory for the current session. Saved palette records remain in +IndexedDB until the user deletes them or clears site data. + +The URL stores the active application view and Review tab. Browser Back and +Forward can restore that navigation state. The URL does not contain palette +data and cannot restore lost in-memory work after a new session. diff --git a/docs/user-workflow.md b/docs/user-workflow.md deleted file mode 100644 index 54a197e..0000000 --- a/docs/user-workflow.md +++ /dev/null @@ -1,36 +0,0 @@ -# User workflow - -## Create and refine - -Choose **Upload image** for PNG, JPEG, or WebP files up to 10 MB, or choose **Start manually**. Extraction returns 3–10 representative colors. Select a palette row, type a six-digit HEX value, use the native picker, duplicate a color, or remove it. - -Uploaded image previews exist only for the active page session. Saving an image-derived palette records its filename, colors, population data, and pixel counts—not the image bytes. - -## Save states - -- **Unsaved**: the open palette has no IndexedDB record. **Save palette** creates one. -- **Saved**: the current palette matches its record. The save action is disabled. -- **Modified**: colors, roles, name, or source metadata differ. **Save changes** updates the same record. - -ColorCraft confirms before opening another record or starting over when doing so would discard meaningful unsaved or modified work. - -## Review - -Choose **Analyze palette** to enter Review: - -- **Overview** summarizes temperature, saturation, lightness, geometric relationships, and next actions. -- **Harmony** explains detected relationships on the color wheel. -- **Contrast** assigns semantic interface roles and reports WCAG ratios. -- **Suggestions** proposes complementary, triadic, analogous, split-complementary, tetradic, monochromatic, or accessibility-oriented colors. - -Relationship fit measures geometric hue structure; it is not a judgment of aesthetic quality. - -## Export - -Export the current palette as CSS custom properties, JSON, Tailwind configuration, or design tokens. Copy uses the Clipboard API; download creates a local file. Export does not upload the palette. - -## Library - -The Library lists browser-local records most recently updated first. Search by palette name or source filename. Open, inline rename, duplicate, or delete a record. Deletion always asks for confirmation; deleting the active record returns to the empty Library state and disables Review and Export until another palette opens. - -The sidebar shows recent palettes only when records exist. Clearing browser site data permanently removes the Library. diff --git a/docs/writing-style.md b/docs/writing-style.md new file mode 100644 index 0000000..dd9ebd5 --- /dev/null +++ b/docs/writing-style.md @@ -0,0 +1,269 @@ +# ColorCraft Technical English + +## Purpose + +ColorCraft Technical English is the project-specific standard for technical +writing. The standard is informed by principles from ASD-STE100 Simplified +Technical English. + +ColorCraft does not claim formal ASD-STE100 compliance. This standard does not +reproduce the ASD-STE100 controlled dictionary. + +Use this standard to make instructions clear, consistent, ready for translation, +and safer for AI-assisted maintenance. Current application code, API schemas, +and tests remain the source of truth for behavior. + +Product-specific color-science terms are permitted when this document defines +them and writers use them consistently. Do not rewrite exact commands, API +fields, routes, color values, filenames, paths, formulas, schema versions, HTTP +status codes, or code. + +## Application levels + +### Level 1: Controlled procedural documentation + +Apply the strongest rules to: + +- `docs/getting-started.md` +- `docs/user-guide.md` +- `docs/troubleshooting.md` +- Installation, startup, upload, palette, Review, Export, and recovery + procedures + +### Level 2: Controlled technical reference + +Apply controlled terminology and direct technical prose to: + +- `docs/api-contracts.md` +- `docs/architecture.md` +- `docs/color-analysis.md` +- `docs/testing.md` +- `docs/runtime-configuration.md` +- `docs/persistence-and-privacy.md` +- `CONTRIBUTING.md` + +Preserve formulas, schemas, identifiers, and necessary technical detail. + +### Level 3: Product and brand explanation + +Use clear product language in: + +- `README.md` +- `docs/brand-system.md` +- Product positioning and design rationale +- Changelog entries and pull request descriptions + +Level 3 permits more flexible sentence structure. It does not permit vague, +inflated, or misleading claims. + +## Core rules + +1. Use one preferred term for one concept. +2. Do not change terminology only to avoid repetition. +3. Use active voice in procedures. Write **Select Review**, not **The Review + view should be selected**. +4. Start each procedural step with an action. +5. Put one primary action in each numbered step. +6. Put a condition before its action. Write **If the API is not ready, wait for + the readiness check**. +7. Use short, direct sentences. Review a sentence when it is longer than + approximately 20 words. +8. Avoid ambiguous pronouns. +9. Identify the actor, such as the user, ColorCraft, the frontend, the API, the + extraction service, or the browser. +10. Use **must** for a requirement, **should** for a recommendation, **can** for + capability, and **may** only for permission or possibility. +11. Give a recovery action for each recoverable error. +12. Explain the consequence before a destructive action. +13. Preserve exact technical strings and exact UI labels. +14. Use parallel grammar in lists. +15. Avoid promotional language and generic AI language. +16. Do not present subjective design quality as objective fact. +17. Keep geometric analysis separate from aesthetic judgment. +18. Keep contrast results separate from claims about complete accessibility. +19. Distinguish measured values from recommendations. +20. Label notes, important information, warnings, results, and examples + consistently. + +Use **Warning** only when an action can cause data loss, exposure, or another +material adverse result. Use **Important** for a constraint that can prevent a +procedure from succeeding. Use **Note** for useful additional information. + +## Procedure pattern + +Use this structure when a task needs more than a short command: + +### Purpose + +State what the procedure does. + +### Before you start + +State requirements, limits, permissions, and effects on current work. + +### Procedure + +Use numbered action steps. + +### Result + +State the expected observable result. + +### Recovery + +State what to check when the result does not occur. + +Example: + +> **Purpose** +> +> Start ColorCraft in one terminal. +> +> **Before you start** +> +> - Install Python 3.11. +> - Install Node.js 20 or newer. +> - Install the backend and frontend dependencies. +> +> **Procedure** +> +> 1. Open a terminal in the repository root. +> 2. Run `.\backend\.venv311\Scripts\python.exe dev.py`. +> 3. Wait for the API readiness check. +> 4. Open `http://127.0.0.1:5174`. +> +> **Result** +> +> The web application opens. The API listens on +> `http://127.0.0.1:4100`. +> +> **Recovery** +> +> If startup reports a port conflict, stop the process that uses the reported +> port or configure a different port. + +## Error pattern + +Write errors in this order: + +1. State the problem. +2. State the cause when the cause is known and useful. +3. State the recovery action. + +Examples: + +- ColorCraft cannot reach the API. Start the API, and then retry the action. +- ColorCraft cannot copy the export to the clipboard. Select the generated + preview, and then copy the text manually. +- The source image is larger than the API upload limit. Select an image that is + 10 MB or smaller. + +Do not expose stack traces, local absolute paths, environment values, image +contents, clipboard contents, or unrelated palette data in user-facing errors. + +## Controlled terminology + +| Preferred term | Meaning | Usage constraint | +| --- | --- | --- | +| source image | The image that the user uploads for color extraction. | Do not substitute *input asset*, *source media*, *uploaded visual*, or *incoming image*. | +| palette | The ordered set of colors in the current ColorCraft workspace. | Do not substitute *color set*, *collection*, *theme*, or *scheme*. | +| palette color | One color in the current palette. | Use *color* when the context is unambiguous. | +| extracted color | A palette color returned by color extraction. | Do not use for a manually added or suggested color. | +| suggested color | A color proposed in Suggestions. | Do not call it an extracted color. | +| base color | The palette color selected as the start of a suggestion approach. | Preserve this term in Suggestions procedures. | +| swatch | The visual sample that represents one palette color. | Do not use as a synonym for the color value when the distinction matters. | +| color extraction | The process that derives representative colors from a source image. | Do not substitute *generation*, *scanning*, *image analysis*, or *palette creation*. | +| processing sample | The deterministic pixel sample used by color extraction. | Do not imply that the sample contains every source-image pixel. | +| population | The proportion of the processing sample assigned to a cluster. | Do not describe this value as complete-image population. | +| sampled pixel count | The number of processing-sample pixels assigned to a cluster. | Preserve the API field `pixelCount`. | +| representative color | The sampled RGB medoid nearest a LAB cluster center. | Do not call it an average, converted LAB median, or exact dominant color of the complete source image. | +| harmony relationship | A detected geometric hue relationship. | Do not use *harmony* as an automatic judgment of visual quality. | +| relationship fit | A value from 0 through 100 that summarizes relationship confidence and meaningful-hue coverage. | Never substitute *harmony score*, *palette score*, *quality score*, *beauty score*, or *aesthetic score*. | +| relationship confidence | The confidence value for one detected harmony relationship. | Keep separate from relationship fit. | +| angular deviation | The measured difference between detected and expected hue geometry. | State the unit when it is not already clear. | +| meaningful hue | A hue with enough saturation to provide geometric evidence under the current algorithm. | The current minimum saturation is documented in `color-analysis.md`. | +| contrast ratio | The relative-luminance contrast between two assigned colors. | A ratio is a measurement, not a complete accessibility result. | +| contrast pair | Two colors evaluated together for contrast. | State the roles when the pair is role-based. | +| color role | A semantic interface role assigned to a palette color. | Current labels are Page background, Surface, Primary text, Secondary text, Primary action, Action text, Border, and Focus indicator. | +| role assignment | The association between a palette color and a color role. | Do not use for an unassigned palette color. | +| contrast-role check | A contrast test between two meaningful assigned color roles. | Do not use as an exact synonym for the all-pairs contrast matrix. | +| all-pairs contrast matrix | The advanced matrix that compares every palette color pair. | Keep separate from contrast-role checks. | +| suggestion approach | One method that proposes colors from a base color. | Current API approaches include complementary, triadic, analogous, split-complementary, tetradic, rectangular, monochromatic, double-complementary, and shades and tints. | +| Review | The application view that contains palette analysis. | Preserve capitalization when referring to the UI destination. | +| Overview | The Review tab that summarizes the current analysis. | Preserve capitalization. | +| Harmony | The Review tab that presents detected geometric relationships. | Preserve capitalization. | +| Contrast | The Review tab that presents role assignments and contrast results. | Preserve capitalization. | +| Suggestions | The Review tab that proposes optional colors. | Preserve capitalization. | +| Export | The application view that formats the current palette for external use. | Preserve capitalization. | +| export format | One supported representation of the current palette. | Current labels are CSS custom properties, JSON, Tailwind theme colors, and SVG swatch sheet. | +| exported file | A file that the browser creates from the current palette. | Do not imply that ColorCraft stores the file. | +| copy | Place generated export text on the system clipboard. | Do not use as a synonym for export or download. | +| download | Save generated export data as a local file through the browser. | Do not use as a synonym for copy. | +| current session | The active in-memory application state in the current browser tab. | State separately whether data is saved in IndexedDB. | +| unsaved palette | A current palette that has no saved palette record. | Unsaved changes are session-only. | +| saved palette | A versioned palette record in the browser's IndexedDB database. | Do not imply an account or cloud copy. | +| Palette Library | The Library view that lists saved palettes in the current browser origin. | Use **Library** for the navigation label. | +| local | The application runs on the current computer or configured local network. | Do not use as an automatic synonym for private, secure, offline, anonymous, or persistent. | +| source-image preview | The in-memory browser preview of the current source image. | ColorCraft does not store source-image bytes in the Palette Library. | + +## Required domain distinctions + +### Palette and color scheme + +A palette is the ordered set of current colors. A color scheme can describe a +theoretical arrangement. Do not use both terms only for variation. + +### Extraction and analysis + +Color extraction derives representative colors from a source image. Analysis +evaluates a palette. A manual palette can be analyzed without extraction. + +### Relationship and suggestion + +A harmony relationship describes measured palette geometry. A suggestion +proposes an optional palette change. A detected relationship does not instruct +the user to change the palette. + +### Relationship fit and contrast + +Relationship fit describes hue geometry. Contrast describes relative +luminance. A strong relationship fit does not imply sufficient contrast. +Sufficient contrast does not imply a strong harmony relationship. + +### Contrast result and accessibility + +Contrast evaluation measures one accessibility requirement. A passing contrast +result does not prove complete WCAG conformance or complete interface +accessibility. + +Write **The assigned text and background colors meet WCAG AA contrast**. Do not +write **The palette is accessible** or **The design is WCAG compliant**. + +### Extracted population and complete-image population + +Population and `pixelCount` describe the deterministic processing sample after +resize and transparency handling. These values do not count every decoded +source-image pixel. + +### Export and persistence + +Export creates text or a file outside ColorCraft. Export does not create or +update a saved palette record. **Save palette** and **Save changes** write the +current palette to IndexedDB. + +### Copy and download + +Copy sends generated text to the clipboard. Download creates a file through the +browser. + +### Reset and delete + +**New palette** clears current session work after any required confirmation. +**Delete palette** removes a saved palette record from IndexedDB. Do not call +session reset deletion. + +## Maintenance + +Add a terminology entry when a stable product concept has no preferred term. +Verify new entries against application code, API contracts, and tests. Update +the canonical document instead of copying large explanations into the README.