Skip to content
focale-editorPublic

About

The website of Focale, an advanced, local-first raster image editor for Linux, Windows and macOS.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Focale Website

The landing page for Focale, an advanced, local-first raster image editor for Linux, Windows and macOS.

The site is a statically generated Nuxt application, published to GitHub Pages at https://focale-editor.app. It presents the editor and its downloads, and provides user guides and technical format references.

Feedback and help

Focale Community centralizes bug reports, feature requests, improvements and questions about the editor, website, documentation and downloads. Include the page URL for website reports.

/issues redirects to the community's issue-template chooser. Its standalone public/issues/index.html uses a meta refresh and a fallback link, so the alias works on GitHub Pages without JavaScript or a Nuxt hydration step.

Stack

Nuxt 4 · OpenVue · @nuxtjs/i18n · SCSS · TypeScript, built with pnpm.

Six locales ship today — English, French, Spanish, Italian, Portuguese and German — with English served unprefixed and the rest under /fr, /es, /it, /pt, /de.

The static page hydrates in its URL's language. On arrival at /, app/plugins/browser-locale.client.ts redirects through onNuxtReady, after hydration (including async pages), to an explicit saved language choice, or the supported browser language (English fallback). The redirect preserves query parameters and anchors, replaces the history entry and leaves explicit localized or documentation links alone. Only selections in LocaleSwitcher write the one-year focale_locale cookie, as manual:<code>. Automatic detection and route changes do not save a preference. Older bare-code cookies have an ambiguous origin; ignore and clear them on arrival at /, then detect the browser language again. useLocalePreference shares this contract between the selector and redirect plugin. Keep module-level browser detection disabled so it cannot change translations before hydration.

Development

pnpm install
pnpm dev             # http://localhost:3000

Quality gates, all expected to pass before a push:

pnpm install --frozen-lockfile
pnpm lint            # ESLint, with @nuxt/eslint's stylistic rules
pnpm typecheck       # vue-tsc against the generated Nuxt types
pnpm generate        # the static build, exactly as CI runs it
node --experimental-strip-types --test test/browser_locale_redirect.test.ts test/download_catalog.test.ts test/changelog.test.ts
pnpm test:hydration  # run after generate, against the actual static HTML and JS

After dependency changes, verify the full frozen installation in a clean checkout. A lockfile-only check can miss missing transitive dependency snapshots.

pnpm generate writes .output/public, which can be served with any static server (pnpm dlx serve .output/public).

The hydration test executes the generated client bundle in jsdom with HTML scripting enabled. It covers the six locales, browser-language redirection, saved preferences, explicit routes, query parameters and anchors, and the download loading, empty/error and available states, including the fallback without JavaScript. This is a DOM regression check, not a browser layout test. Keep NoScriptDownloads' contents as escaped, opaque HTML: with scripting enabled the HTML parser treats <noscript> contents as text, so Vue must not try to hydrate nested components there.

Configuration

The public download catalog has a default URL. Copy .env.example to .env only when an override is needed; no signup form or secret is required.

Variable Purpose
NUXT_PUBLIC_DOWNLOAD_CATALOG_URL Optional URL of the public catalog; defaults to https://get.focale-editor.app/downloads.json.

The site is generated statically. useDownloads fetches the catalog in the browser so releases can appear without rebuilding the website.

Deployment

.github/workflows/deploy.yml runs on every push to main: it tests language redirection, generates the site, checks hydration and uploads it to GitHub Pages. Set Settings → Pages → Source to GitHub Actions in the repository settings.

The custom domain lives in public/CNAME, so it survives every deployment.

Layout

app/
├── assets/
│   └── styles/   SCSS tokens, mixins and the global stylesheet
├── components/   shared layout/, docs/, ui/ and local home/ sections
├── composables/  downloads, navigation, SEO and content sources
├── layouts/      the shell every page renders into
├── pages/        one route per file
└── theme/        the OpenVue preset, aligned with the editor's palette
i18n/locales/     one JSON file per language
public/           favicon.ico, apple-touch-icon, og-image, CNAME

Adding a page means adding app/pages/<name>.vue, its keys to the six locale files, and — if it belongs in the navigation — an entry in app/composables/useSiteNavigation.ts, which both the header and the footer read.

The sections in app/components/home/ belong to the homepage. They are excluded from Nuxt's component auto-imports and imported explicitly by app/pages/index.vue. Shared layout, documentation and UI components retain flat auto-imported names.

The header has three page links: Home, Documentation and Contribute (/support). Homepage section anchors remain in the footer. SupportSection introduces ways to help the project after the downloads and links to the localized support page; its copy lives in support.home in each locale file. The hero and header link to #downloads, which remains available while the catalog loads or is unavailable. The website no longer contains an alpha signup form.

Using a new OpenVue component means naming it in primevue.components.include in nuxt.config.ts. Auto-import is deliberately off: it registers all eighty-odd components and inlines every one's theme CSS into every page, which tripled the generated HTML. An undeclared component simply will not resolve.

app/assets/styles/_shared.scss is injected into every component's style block, so tokens and mixins are available without importing them.

Release notes

/changelog and its localized routes show the checked-in public/changelog.json export. The private editor's Publish Focale workflow refreshes it from the published source tag after desktop publication; its normal push then rebuilds this site. No new credentials or runtime GitHub request are needed here.

An empty catalogue displays a translated fallback linking to the downloads. Dates and page navigation are localized in all six languages; reviewed notes retain their source language. Each version has a stable #v1.2.3 anchor. The footer links to the localized history.

Schema version 1 contains releases in newest-first order, each with version, ISO date and changes (plain-text kind and description). Invalid exports fail static generation. Notes are rendered with Vue text interpolation, without HTML or Markdown execution. The editor exporter understands its configured dart run release template, excludes Unreleased sections and removes private links. Coordinate format changes with tool/website_sync/changelog.py in Focale. Do not invent release entries here; the source changelog is authoritative.

Editor branding and screenshots

The private Focale application's Publish Focale workflow produces the logo, favicons, social image, reviewed notices and downloadable format references. It commits only selected public resources to this repository; the resulting push runs the Pages deployment. Building or developing this website requires neither private source access nor an editor checkout.

Maintainers with access to Focale can run Publish Focale → website on a chosen source ref to refresh resources without a desktop release. Enable screenshots only when regenerating the gallery. Release publication updates other resources automatically from its source tag. The workflow and its FOCALE_WEBSITE_TOKEN are configured in Focale, not here; no local directory conventions are required.

The gallery uses four scenes, both themes and French/English captures, encoded as 1600/3200-pixel WebP images plus 320-pixel thumbnails. French routes use French captures; other locales use English. Changes to scene IDs or image dimensions require coordinating this public contract with the editor. Palette tokens and the OpenVue preset are maintained in app/assets/styles and app/theme/openvue-preset.ts. The hero uses a translated title and accent phrase; the English social image keeps “Retouch, compose and create” followed by the blue “with precision.”. Its composition is generated by Focale's tool/website_sync/branding.py.

Desktop downloads

useDownloads loads https://get.focale-editor.app/downloads.json in the browser. The Focale distribution workflow publishes this catalog only after the macOS Apple Silicon, macOS Intel, Windows x64 and Linux x64 artifacts are available. DownloadsSection presents their GitHub Release attachments on this main site; the header and hero always link to the download section. While the catalog loads, the section displays a loading status. An empty, invalid or unreachable catalog displays a warning with a direct link to GitHub releases. No website rebuild is needed for subsequent desktop versions. NUXT_PUBLIC_DOWNLOAD_CATALOG_URL can override the endpoint at build time. The GetFocale repository hosts metadata only.

Download validation accepts focale-<version>-<target>.<extension> and the previous Focale-<version>+<build>-<target> names. Windows installers retain -setup.exe in both conventions; ZIP and MSIX packages omit that suffix. Keep both forms so older releases remain downloadable. The target, version, extension and public release path must match the catalog entry.

Catalog checks: node --experimental-strip-types --test test/download_catalog.test.ts. Validation: pnpm lint, pnpm typecheck, pnpm generate (inspect .output/public).

Documentation and third-party notices

app/pages/docs/ provides getting started, editing, shortcuts, troubleshooting, platforms, import/export formats, native projects and native preset libraries, alongside the existing licenses and corresponding-source pages. Each portable library has its own page: .fbrush, .fpattern, .fshape, .fstyle, .fswatch, .fgradient, .fcurve and .faction, with usage, schema, examples, validation limits and interchange directions. All pages ship in the six site languages.

DocumentationArticle renders the structured docs.articles content in the locale JSON files; DocumentationLayout also serves the notices pages. app/utils/documentation.ts registers stable routes, groups, code samples and downloadable references. Add a route under app/pages/docs/, its metadata there and the article in all six locales. Code samples stay outside translated strings so JSON braces are not parsed as translation placeholders. useDocumentation supplies both the index and navigation. documentationExamples.ts holds the dedicated native-library JSON examples. Navigation back to homepage anchors works from documentation routes too.

About

The website of Focale, an advanced, local-first raster image editor for Linux, Windows and macOS.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Sponsor this project

Contributors

Languages