57 Commits

Author SHA1 Message Date
Dylan M. Taylor ec205366eb Release 0.13.0 2026-11-01 12:28:43 -05:00
Dylan M. Taylor 12c3a9b203 Release 0.12.0 2026-10-31 23:36:34 -04:00
Brandon Thomas 61f54f2739 Release 0.11.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 01:53:58 -04:00
Brandon Thomas a5ef79f379 Release 0.11.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:26:28 -04:00
Dylan M. Taylor 8b9dc209dc fix(update): pin merged Flatpak self-update support 2026-10-30 22:58:06 -04:00
Dylan M. Taylor 93de4a9008 fix(update): use system Flatpak bundle self-update 2026-10-30 22:50:36 -04:00
Jeremy b1d557a173 build(deps): craft-update with the version-choice file in a host folder
craft-update 9817c81 moves versions.json to ArtCraft/versions under the host's
per-user data folder, resolved from inside the Flatpak too, so a Flatpak
launcher writes where host apps and granted Flatpak apps read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 21:56:42 +00:00
Jeremy b43955db51 build(deps): craft-update with the shared version-choice file
Pins craft-update to storytold/craft-libs#449 (0ada678) for craft_update::pins.
Bump to the merged commit once that PR lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:00:54 +00:00
Dylan M. Taylor 3fad1d643f deps: bump tar, zip, roxmltree, rustls-webpki and symphonia to their latest versions (#1036)
- `rustls-webpki`: 0.103.15 → 0.103.16
- `zip`: 8.6.0, 9.1.0 → 9.1.3

Requirement `zip` 8 → 9. The portable-ZIP check now treats an entry name that doesn't decode as unsafe, like one with `\\` or `:`. That code is Windows-only and wasn't compiled here (Linux); Windows CI is the check.
2026-10-29 22:21:27 -04:00
skylar taylor-barrick c1d0d881a3 Release 0.10.0 2026-10-29 13:26:57 -06:00
Brandon Thomas 9613c285dd Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:25:41 -04:00
aiongg 6d5e96ffe8 refactor(ui): use craft-ui's ComboBox for every dropdown (#939)
* ui: use craft_ui::ComboBox for the settings and sort dropdowns

* chore(deps): craft-libs 60d60d48 (dropdown fixes)

* chore(deps): craft-libs 6af63481 (dropdown fixes)

* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)

* chore(deps): craft-libs 92d392cf (#387 merged)
2026-10-27 10:26:20 -04:00
skylar c77c63dce4 chore(deps): bump serde_json, winresource, plist, ureq, tray-icon 2026-10-25 16:36:25 -06:00
Dylan M. Taylor a9ac66106e build: pin craft-update to craft-libs main (495e597) (#896)
Picks up the update prompt fix that draws its close mark and version pill
without glyphs a font may lack, so the prompt no longer takes the app down
when a bundled face has no ✕ or →.

Part of tracking issue storytold/craft-libs#319

🤖 Generated with Codebuff

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-25 17:37:28 -04:00
Dylan M. Taylor e2b1b73a08 refactor: delegate self-update functionality to craft-update (#871)
* refactor: delegate self_update to craft-update

* ci: point craft-update patch to dylanmtaylor/craft-libs branch

* nix: set allowBuiltinFetchGit for git dependencies

* chore(deps): update craft-update commit

* build: pin craft-update to storytold/craft-libs fe61243 and drop the temporary patch

* fix(update): share craft_update::Stop so pause and cancel reach the self-update

* build: pin craft-update to the dirs-free, zip 9 build

Satisfies cargo-deny: no MPL-2.0 option-ext, no zip 8 advisories, and a versioned git dependency instead of a wildcard.

* build: pin craft-update to craft-libs main (b9ba47f)
2026-10-25 13:40:46 -04:00
skylar taylor-barrick 4ab652afd8 Migrate Craft Launcher to egui 0.37 (#873)
* Migrate Launcher to egui 0.37 and preserve current-main behavior

* egui037: record final published pins and local qualification

* fix(ui): gate CJK language drawing until fonts are installed

* Document migrated launcher UI contracts and regression invariants
2026-10-25 10:49:00 -04:00
Brandon Thomas 932e718bbc Release 0.8.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:52:58 -04:00
Brandon Thomas 75274994e7 Release 0.8.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:44:58 -04:00
smittie2000 16285cd62e xtask local: order pre-releases by semver (rc.10 after rc.9) (#820)
compare_versions compared pre-release tags as plain strings, so
1.0.0-rc.10 sorted before 1.0.0-rc.9. `cargo xtask local` uses it to pick
the newest mirrored build for `status` and to choose what `prune` keeps,
so a tenth release candidate would have been treated as older than the
ninth and pruned first.

Parse with the workspace's semver crate and compare by precedence, which
orders numeric pre-release parts as numbers.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:43:05 -04:00
Jake Macek 79360af675 macOS: quit through terminate: so quitting doesn't crash (#333) (#335)
Every real quit sent ViewportCommand::Close, and eframe dropped the
window inside AppKit's run loop. With the window focused, AppKit threw
in -[_NSTouchBarFinderObservation invalidate]. craft-quit's
on_close_request cancels that close and quits through
-[NSApplication terminate:] instead. It runs last in logic, so a close
put away in the tray (CancelClose) is left alone.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:42:35 -04:00
Dylan M. Taylor c190a590bc Merge pull request #750 from dylanmtaylor/security/ipc-local-service-hardening
security: authenticate single-instance replace and bound the local mirror server
2026-10-22 19:41:10 -04:00
Dylan M. Taylor 7cf50d7a10 security: address CodeRabbit review on IPC token CSPRNG, retry, and mirror timeouts 2026-10-22 19:28:40 -04:00
Brandon Thomas 5f3b7aaf4b Release 0.7.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:37:28 -04:00
skylartaylor 20715c1eed Merge current main and fix the CI formatting failures 2026-10-22 19:59:21 +02:00
Dylan M. Taylor 693ad64275 Merge pull request #726 from dylanmtaylor/time/chrono-1785
Use jiff instead of hand-rolled UTC conversion
2026-10-22 12:43:55 -04:00
dotCooCoo bf16e21f76 fix(windows): self-update runs no PowerShell and installs only files signed like the launcher
On Windows the launcher finished its own update with a detached
powershell.exe started with -ExecutionPolicy Bypass -WindowStyle Hidden.
The script waited for the launcher to exit, ran the new MSI or unpacked
the portable zip with Expand-Archive, deleted the download and started the
new exe. Antivirus heuristics score that sequence, and the command line
sits in the signed exe as a string.

The update helper is now a copy of the launcher itself, put in
<cache>\launcher-update-helper and started with --finish-update. It waits
for the old process with OpenProcess and WaitForSingleObject (winsafe),
runs msiexec /i /passive /norestart and checks its exit code, or unpacks
the portable zip with the checks portable app installs already use, and
then starts the launcher again. main() hands --finish-update to the helper
before logging, the single-instance claim and the window.

The helper finishes only the launcher's own update:
- the exe is this launcher's MSI or portable installation, and the
  helper, the download and the staging folder are in that installation's
  cache;
- the MSI, every program in the portable zip and the exe it starts carry
  a valid Authenticode signature from the helper's own publisher
  (verifysign, through WinTrust).
The launcher checks its own signature, the installed exe and the new MSI
before it quits, so an unsigned build reports that it doesn't install
updates instead of closing. A failed update clears the self-update marker,
so the next check offers it again, and the next start logs the error.
2026-10-21 12:07:47 -07:00
dotCooCoo e41c282432 fix(windows): read a portable app's version without starting PowerShell
After unpacking a portable app, the launcher checks the exe's
ProductVersion against the release. It started a hidden powershell.exe
-NoLogo -NoProfile -NonInteractive -Command to read
FileVersionInfo.ProductVersion, which costs a PowerShell start on every
portable install and is one of the command lines antivirus heuristics
score.

executable_version now reads the ProductVersion string from the file's
VERSIONINFO with GetFileVersionInfo and VerQueryValue (winsafe), in the
first language the file lists, and no process is started.
2026-10-21 12:02:33 -07:00
Dylan M. Taylor caea05eff8 Use jiff for UTC/calendar conversion instead of hand-rolled copies
This repo already uses jiff (ui-egui, for the system time zone), so the
hand-rolled Howard Hinnant civil/days-from-civil copies move to jiff rather than
pulling in a second time crate. jiff is lifted to a workspace dependency:
- crates/core/src/time.rs: parse_rfc3339's days_from_civil and civil_from_unix
- xtask/src/local.rs: rfc3339 (feed timestamps)
ui-egui now takes jiff from the workspace. core keeps only civil-date math (no
time zone), so the wasm build is unaffected. Output and parsing semantics are
unchanged and pinned by the existing tests (RFC 3339 parsing, leap days, day
rollover, the feed's YYYY-MM-DDTHH:MM:SSZ). AGENTS.md now says time goes through
jiff.

Follow-up to storytold/cadcraft#1785
2026-10-21 07:59:58 -04:00
Brandon Thomas 5479f0a618 Release 0.6.1: saved filters no longer hide every app (#678)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:56:04 -04:00
Brandon Thomas 8b107ffff2 Release 0.6.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:17:37 -04:00
skylar taylor-barrick b39b80580c Merge main into the redesign
Main's Arch package option (#565) went into the Linux install-format list in
dialogs.rs, which this branch moved to settings_dialog.rs; it is added there.
2026-10-17 23:55:57 -06:00
Brandon Thomas ed6b32ebf3 Release 0.5.1: fix the macOS build (self-update passes install_dmg's replace flag)
install_dmg gained a replace parameter in #461; the macOS-only self-update call and its test
weren't updated, so the 0.5.0 release's macOS job failed to compile. Self-update writes over the
launcher's own bundle, which copy_app accepts without replace, so pass false.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:43:28 -04:00
skylar taylor-barrick 95f693ecfb Show dates on this computer's calendar, one way everywhere
Cards wrote "Last updated" as a fixed 07/10/2026 while the table, app page and
release notes said "Oct 7, 2026", and every date was the UTC day, so a release
published late on the 7th UTC read as the 7th in Tokyo. local_time::civil adds
the system zone's offset at that instant (jiff on the desktop, which re-reads the
zone every few minutes; the browser's Date on the web) and falls back to UTC
when the zone can't be read. date_text is the one formatter.
2026-10-17 20:36:34 -06:00
Brandon Thomas 494f43d33d Release 0.5.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:43:30 -04:00
Mex 0f52d3f305 Resolve MSI package paths before installing or self-updating (#356) 2026-10-17 14:18:17 -06:00
ash 307efcf45a feat(install): choose where Craft apps are installed (#461)
* feat(install): choose where Craft apps are installed

Settings gets a default install folder, and a fresh install asks for the
app's folder (prefilled with the default). Updates stay beside the copy
they replace. Works for MSI (INSTALLFOLDER), DMG bundles, and AppImages;
on Linux a custom folder means AppImage. Flatpak and the web build keep
the system location and skip the prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(windows): integrate portable installs with custom locations

* fix(install): ask before writing over something already in the install folder

Staging used a predictable `.<name>.partial` path beside the destination, so a
file the user kept under that name could be overwritten (AppImage) or deleted
(macOS). Stage in a fresh private folder instead.

A new install whose destination already exists now stops before downloading
and asks "Replace what's there?". Replace installs over it: the AppImage or
.app is swapped in, a portable install writes over same-named files only, and
the MSI installs into the existing folder. Cancel leaves everything as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(install): a planted .partial symlink can't redirect AppImage staging

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Skyfuller1303 <skyfullerdesign@gmail.com>
2026-10-15 20:46:32 -06:00
Brandon Thomas cbae640688 Add FigCraft; release 0.4.1 (#313)
* Add FigCraft to the launcher

FigCraft (storytold/figcraft): interface design and prototyping, the
thirteenth Craft App and the sixth in the New group.

- Catalog row (brand colour #02da96 from FigCraft's icon README), details
  page (no pictures yet), Linux desktop categories, icon tables, tagline
  in every catalog, xtask local mirror list.
- Icon copied unmodified from FigCraft's repo; ATTRIBUTION.md row.
- The Windows installer art fits five rows of icons.
- Test that a companion figcraft-server tarball is never picked as the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Release 0.4.1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:28:29 -04:00
Brandon Thomas d6fdab6596 Release 0.4.0 (#291)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:29:23 -04:00
Alexis cb82e851cd Add a details drawer for each Craft App (#60)
* Add a hand-written details page for each Craft App

core::details holds, for every app in the suite, a headline, a few
paragraphs, a list of features, where it runs and the list of its
pictures. The copy is the launcher's own: written from each app's page on
getartcraft.com and, for the five newer apps, from its README. It is
compiled in, so it works offline and in the web build, and it leaves out
figures that go stale (effect counts, timings).

The pictures are not in this repository. A page names them (a key and a
caption each) and AppDetails::screenshot_url turns a key into the address
of a 1200 px JPEG in storytold/artcraft-launcher-assets, pinned to a tag
(SCREENSHOTS_REV) so a saved copy never goes stale. It only answers for
keys the page lists, so no address is ever built from anyone else's text.
MAX_SCREENSHOT_BYTES caps what is accepted, and looks_like_jpeg refuses a
login page or error page that arrives as a 200.

PdfCraft lists none yet: its screenshots still show its old name
(PrintCraft). ATTRIBUTION.md points to the pictures repository, which
carries each picture's own provenance.

Tests check that every app has exactly one page, that each is filled in,
and that addresses come only from a page's own pictures.

* Open an app's details in a drawer from its card

Clicking a card (or an app's name in the table) slides a drawer in from
the right over the dimmed grid. It shows the app's headline, version and
main button, links to its website, GitHub and releases, a gallery of
screenshots, its features, the facts and notes of its latest release, and
the same trademark notice as the grid. Esc, the close button or a click
on the grid closes it. The page's text is the hand-written copy in
core::details; the version, date, download size and notes come from the
release the launcher already fetches.

The pictures are fetched when a page is opened, not bundled. The UI sends
Command::FetchScreenshots and gets one Event::Screenshot per picture, the
lead one first. The desktop backend downloads them through the launcher's
Http (its User-Agent, a 2 MB cap, redirects followed) and keeps them under
<cache>/screenshots/<revision>/<app>/<key>.jpg, so later opens, and any
time offline, read from disk. Only a JPEG is kept: a login page or error
page that arrives as a 200 is refused. A missing picture doesn't hold the
others back, but once the network has failed the rest fail at once instead
of each waiting for its own timeout. The web build does the same with
fetch and leaves the caching to the browser.

Until a picture is here, a frame of the same size says "Loading pictures…"
or, when it failed, that it couldn't be loaded and that reopening the page
tries again; nothing moves when the picture arrives. The lead picture
opens full-window, the arrows over the pictures and the arrow keys flip
through them, wrapping around, and a scroll bar stays visible (egui's
floating one only shows under the pointer).

The drawer's labels are tl! strings, with 13 rows in each of ru, zh-CN,
de, fr and pt-BR; details.rs joins the sources the catalog coverage test
reads. Every size taken from the window is kept non-negative: the new
tests resize it from 40 x 30 to 4000 x 2400 with the drawer open.

actions() becomes actions_in(), which can leave the links out of the
... menu where the page already shows them as buttons. The image crate's
jpeg feature is enabled to decode the pictures (zune-jpeg and zune-core
join Cargo.lock). The dim and the enlarged picture's backdrop are colours
in theme.rs.

* Bring the details drawer up to date with main

- Website is always a button now (#198); drop the "Like {name}" line and the trademark notice,
  which main removed with the product comparisons (93caf01).
- Bring back widgets::eyebrow for the drawer's section headings (#187 dropped its last caller).
- The drawer's ⋯ menu leaves out only the links, so Hide/Unhide (#185) stays.
- No slide when the OS asks for reduced motion (#201).
- Translate the screen readers' "{name} details" label, and add the drawer's strings to the
  Spanish, Czech, Polish, Hungarian and Japanese catalogs.
- AGENTS.md: name the pictures' download origin.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 05:00:22 -04:00
Jeremy Yao 1c03e2d2ea Accessibility/UI: follow the OS's animation setting (#201)
* Add a Polish interface

- pl.tsv: the full catalog (287 rows), written clean-room from the English
  labels; plurals use the same one|few|many rule as Russian.
- Registered as the seventh language: "Polski" in the picker, locales pl,
  pl-PL and pl_PL.UTF-8, complete_menus = true so the completeness tests
  cover it.
- tests/polish.rs: the Polish UI test, modeled on the French one.
- README and AGENTS updated for the new language.

* Add Hungarian localization and fix responsive settings layout

* i18n: add Czech (cs) interface language

Czech catalog with every key of the most complete catalog (287 rows), the
cs language entry with locale detection (cs-CZ, cs_CZ.UTF-8, ?lang=cs),
a Czech plural rule (1 / 2-4 / other) with a unit test, and the README
language list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n: add Japanese (ja) interface language

ja.tsv translates the whole interface, the tray menu and tooltip, each app's
tagline and the backend's install/update messages into Japanese, and
Settings > Language offers 日本語. Japanese locales (ja, ja-JP, ja_JP.UTF-8…)
select it; Japanese has one plural form.

Japanese is a CJK language, so cjk_font.rs now looks for a Japanese system
font (Hiragino Sans on macOS; Yu Gothic, Meiryo or MS Gothic on Windows;
Noto Sans CJK JP, Noto Sans JP, IPAGothic, Takao or VL Gothic on Linux and
FreeBSD), falling back to a Chinese one, which draws kana and most kanji,
where the system has none. The font is loaded per language, so switching
between Japanese and Chinese reloads the right face.

A fallback face is nudged onto the body font's baseline: egui puts a fallback
glyph's baseline at that face's own ascent and only centres the two line
boxes, which left a face whose metrics differ from Inter's riding high. On
Windows, Yu Gothic's line gap is half an em, so its kana and kanji sat about
0.29 em above the Latin text beside them (Microsoft YaHei, used for Chinese,
happens to match). The nudge, applied through a FontTweak, is the difference
in ascent and line height, and helps the Chinese faces too.

The README and AGENTS list the new language. Tests: the complete-catalog
checks cover ja.tsv (placeholders, plural forms, every tl! literal, taglines,
package formats), plus a locale/plural test, a Japanese glyph-coverage test,
a baseline test and tests/japanese.rs (grid, table, dates, a backend message).

* i18n: make the CJK font choice language-neutral

The CJK comments, the missing-font note and the plural-rule docs named the
languages, so each new one meant editing them all; they now say "CJK". Each
language's writing system (its `script`) now lives in the registry, and a
language is drawn only with a face for its own script — never another's,
which would leave characters as tofu — so a language the system has no font
for stays in English, like any other. Adding Korean or Traditional Chinese is
a registry row and a candidate list, not a special case.

* Hide Craft apps the user is not interested in.

* Read the Linux desktop appearance portal so system theme matching works on Wayland.

* UI: follow the OS's animation setting

With Windows' "Animation effects" switched off, the launcher still glided each
notch of the mouse wheel over several frames and faded its dropdowns in. egui
does both by default, and nothing asked the OS.

The shell now says what the OS wants (Host::reduce_motion) and the UI asks it
about once a second while it draws, so a change shows without a restart. With
fewer animations asked for:

- a mouse wheel scrolls at once. egui has no switch for its wheel smoothing but
  applies a trackpad's small steps as they come, so each wheel event is
  rewritten as a run of those in the shell's raw_input_hook;
- dropdowns, tooltips and dialogs appear without a fade, and scrolling to a
  place jumps there (style.animation_time, style.scroll_animation);
- the rule along the top of a hovered card appears instead of wiping in.

Progress bars and spinners keep moving: they say that work is going on.

Where the setting comes from:

- Windows: SPI_GETCLIENTAREAANIMATION, read as bit 33 of UserPreferencesMask in
  the registry, as SystemParametersInfoW needs unsafe;
- macOS: NSWorkspace's accessibilityDisplayShouldReduceMotion;
- Linux and the BSDs: the XDG settings portal over D-Bus (its reduced-motion,
  GNOME's enable-animations, KDE's AnimationDurationFactor), followed on a
  thread so the UI never waits for D-Bus;
- web: prefers-reduced-motion.

Animations stay on wherever the setting can't be read. winreg and zbus become
direct dependencies of the desktop app; both were already in Cargo.lock.

The Windows path was run on a machine with the setting off. The macOS and Linux
paths were type-checked for those targets but never run, and the web one only
compiled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* UI tests: hovering a card follows the OS's animation setting

With animations off, moving the pointer onto a card leaves the window still
within 3 frames: its rule and its icon's rise show at once. With them on, the
0.3 s wipe takes 19 frames at 60 frames a second. Each test fails when the
card's timing is made to ignore the setting.

The test window no longer has a download in progress, so it is still unless
something animates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n(pl): use Polish plural rule, so 21 is "21 aplikacji"

plural_east_slavic treats 21, 31 and 101 as "one", which is right for
Russian but not Polish, where only 1 itself takes the singular.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* system theme: back off to every 5 minutes when there's no appearance portal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Merge #189 and main into #201: one raw_input_hook, and test anchors on taglines

Main no longer shows the "Like …" product lines, so the tests find cards by their taglines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n(cs): translate "Installed · latest {latest}" (added by #195)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n(pl): translate "Installed · latest {latest}" (added by #195)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n(hu): translate "Installed · latest {latest}" (added by #195)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n(ja): translate "Installed · latest {latest}" (added by #195)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n: hide/unhide strings for the Spanish, Czech, Polish, Hungarian and Japanese catalogs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* hungarian test: count only the settings body's links, not the cards' Website links behind the modal

#198 gave every card a Website link, and Hungarian labels both Webhely.

* hungarian test: card links are 14px text, settings icons 24px; filter on that

---------

Co-authored-by: DzikStar <82441193+DzikStar@users.noreply.github.com>
Co-authored-by: ferivoq <ferikegameplay@gmail.com>
Co-authored-by: Losminek <301412321+Losminek@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: leonaci <leonacism@gmail.com>
Co-authored-by: satchmo91 <eherrmann1991@gmail.com>
Co-authored-by: Vikram Bhaskaran <vikram.bhaskaran@outlook.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 02:49:59 -04:00
Vikram Bhaskaran cf573b2d35 Fix system theme matching on Linux Wayland (#189)
* Read the Linux desktop appearance portal so system theme matching works on Wayland.

* system theme: back off to every 5 minutes when there's no appearance portal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:49:47 -04:00
Brandon Thomas 6e7a122866 Release 0.3.2: sign Windows builds again (#174)
Since the release workflow moved to craft-common-actions (#129), the caller
passed only the Apple and WINDOWS_CERTIFICATE secrets, not the AZURE_* ones that
Trusted Signing uses, so the 0.3.1 MSIs and portable exes came out unsigned.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:03:52 -04:00
Brandon Thomas b66486df82 Release 0.3.1: Linux riscv64 packages again under the shared release workflow (#170)
craft-common-actions calls `package.sh --formats deb,rpm,tar`, but package.sh
split formats on spaces only, so the riscv64 job built the binary, matched no
format, wrote nothing to dist/release and failed the 0.3.0 release at upload.
package.sh now accepts commas or spaces and rejects unknown format names.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:28:01 -04:00
Brandon Thomas 41369d3416 Release 0.3.0; rustfmt platform.rs and install.rs (#160)
Bumps the workspace version to 0.3.0 for the new features since 0.2.1, and
applies cargo fmt to the two files from #149 that failed CI's format check.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:49:47 -04:00
BFlat c1a9b36f82 macOS: open disk images reliably, and say why when they won't open; 0.2.1 (#135)
* macOS: open disk images reliably, and say why when they won't open

Installing or updating an app on macOS could fail with only "couldn't open
the disk image". hdiutil's own reason was thrown away, and every install of
an app mounted its image at the same folder, which an attach that was
interrupted or timed out could still be holding.

- Each install mounts its image at a new, empty folder.
- The attach skips hdiutil's checksum pass (-noverify): the download's
  SHA-256 is already checked against the release, and on a large image that
  pass alone could run past the time limit, now 5 minutes.
- When the image still won't open, or the app won't copy, the message says
  why in hdiutil's or ditto's words ("couldn't open the disk image:
  Resource temporarily unavailable").
- hdiutil, ditto and codesign are run by full path: a launcher started from
  Finder or at login gets a minimal PATH.

process::run is capture's sibling that returns the helper's error message;
capture is now built on it. The launcher's own macOS self-update installs
through the same code, so it gets all of this too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Release 0.2.1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:51:54 +07:00
Brandon Thomas bd1182e75c Release 0.2.0
A minor release, not 0.1.4 (the previous commit's number, never built or
tagged): it adds features. Since 0.1.3, from community contributions: an
interface in Russian and Simplified Chinese (Chinese drawn with the
system's own font); several apps can install at once with one password
prompt on Linux; a verified download is kept for a retry when its install
step fails; the window closes to the tray on Wayland; HTTPS certificates
are verified by the OS; Direct3D 12 by default on Windows (fixes a startup
crash on hybrid AMD laptops); app names centred on their icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:21:43 -04:00
Brandon Thomas a0b301b8df Release 0.1.4
Since 0.1.3, from community contributions: an interface in Russian and
Simplified Chinese (Chinese drawn with the system's own font); several
apps can install at once with one password prompt on Linux; a verified
download is kept for a retry when its install step fails; the window
closes to the tray on Wayland; HTTPS certificates are verified by the OS;
Direct3D 12 by default on Windows (fixes a startup crash on hybrid AMD
laptops); app names centred on their icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:21:09 -04:00
Flowershangfromthebranches bf452db810 Add a Simplified Chinese interface, drawn with the system's Chinese font
zh-CN.tsv translates the whole interface, the tray menu and tooltip, each
app's tagline and the backend's install/update messages into Simplified
Chinese, and Settings > Language offers 简体中文. Simplified Chinese
locales (zh-CN, zh-SG, zh-Hans, zh_CN.UTF-8, a bare zh) select it; an
explicit Traditional script (zh-Hant-CN) or a Traditional region (zh-TW,
zh-HK, zh-MO) doesn't. Chinese has one plural form. The tray's menu and
tooltip now follow the interface language (Russian rows added too).

The bundled faces have no Chinese glyphs, so on the desktop the
launcher borrows the system's own Chinese font (cjk_font.rs): PingFang
(in /System/Library/Fonts on older macOS, a downloaded font asset on
newer ones), Hiragino Sans GB or Heiti on macOS; Microsoft YaHei or
SimSun on Windows; Noto Sans CJK (static or variable) or WenQuanYi on
Linux and FreeBSD. The face is picked by its family name (PingFang SC,
Noto Sans CJK SC…), regular weight, rather than a hard-coded index in
the collection. Files over 100 MB are skipped and nothing panics. The
font (PingFang is 78 MB) is read only when Chinese is the language in
use, chosen or through Auto, and then kept for the session. Until then
the picker says "Simplified Chinese", and "Simplified Chinese (简体中文)"
once the font is loaded. With no Chinese font (and on the web), the
interface stays in English and Settings explains why, instead of boxes
for every character.

Rebased onto main from the original PR (#34). Changed from the original:
- No bundled font: NotoSansSC-Regular.ttf (10.6 MB), its OFL and README,
  their ATTRIBUTION and NOTICE rows and the zh-CN screenshots are left
  out; the system font replaces them, as above.
- The i18n framework fixes in that PR (trim_end in the catalog tests,
  the n == 1 plural fallback, 1-3 @plural forms, the registry-driven
  language label) landed with the Russian catalog; this adds the
  script-aware zh tag matching on top.
- zh-CN.tsv: "Verifying checksum…" mentions the checksum (正在验证校验和…),
  update.check_auto keeps "automatically" (自动检查更新), "Launcher" stays
  in English like the rest of the product name, rows were added for
  every string main gained since, and rows for messages the launcher
  never sends were dropped.
- tests/chinese.rs works with or without a system Chinese font: with
  one it switches to 简体中文 and checks the grid, table, web and saved
  setting; without one it checks the English fallback and the note. The
  glyph-coverage test now covers the system font, when there is one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:11:51 -04:00
Maksim cf5604dc65 Add UI localisation (i18n) with a Russian catalog
Every user-visible string in the interface is now looked up from a
per-language catalog at render time instead of being compiled in as
English. English stays the source of truth and the lookup key; a missing
row falls back to English rather than failing.

- crates/ui-egui/src/i18n: `t`/`tl!` for literals, `tr_ctx` (one English
  word needing different translations), `tr_id` (command-id keyed),
  `trn`/`tn` (plurals), `fmt` ({name} placeholders filled after the
  lookup) and `message`/`msg` (backend status and error text, translated
  where it is shown). Split into lang.rs (the registry and locale tags),
  detect.rs (the system language), translate.rs (lookups) and catalog.rs
  (the TSV parser); mod.rs only declares them.
- ru.tsv: the whole interface in Russian, with East Slavic plural forms,
  month abbreviations, a day-month-year date template and each app's
  tagline. Product and app names (ArtCraft Launcher, Craft App Suite,
  PhotoCraft…) stay in English.
- The language follows the system by default: ARTCRAFT_LAUNCHER_LOCALE,
  then the OS's preferred UI languages through the sys-locale crate
  (desktop only; LANGUAGE/LC_*/LANG on Linux and the BSDs, the preferred
  languages on macOS, the user's UI languages on Windows), and `?lang=`
  then `navigator.languages` on the web. Settings > Language overrides it.
- core stays pure and wasm-safe: `time::ago_kind` and `month_short_en`
  let the UI render relative times and dates in the current language.

Ported onto main from the original PR (#28): every string main added
since (feed notices and alerts, the "using ArtCraft API and GitHub"
footer, window buttons, waiting and retry states, the install-cancelled
toasts, filter tooltips, package formats, the reveal label) goes through
the catalog, with Russian rows. Also changed from the original:
- `@msg` templates now match real messages: `Couldn't open {name}: {e}`
  translates "Couldn't open PhotoCraft: …", the most specific template
  wins. Rows for messages the launcher never sends (clipboard, recovery
  data) are gone; rows for its install/update/remove toasts are added.
- The locale probes no longer spawn /usr/bin/defaults or reg.exe; the
  small, dependency-free sys-locale crate asks the OS instead.
- Folded in from #34's framework fixes: catalog tests split rows with
  trim_end (trim dropped the leading TAB, so plain rows were silently
  skipped), the English plural fallback goes by n == 1, @plural rows take
  1 to 3 forms, and Settings::language_label comes from the registry.
- Tests are registry-driven: placeholders, plural form counts and, for
  every catalog marked complete, coverage of every tl!/t literal, every
  tn plural key, each tagline and the package formats. A Russian UI test
  renders the grid and table. Catalog warnings go to the log, not stderr.
- ru.tsv: "Launcher" and "Craft App Suite" stay in English; "лаунчер" is
  spelled one way; "Verifying…" (Проверка целостности…) and "Discard
  download" (Удалить скачанное) no longer read like "Checking…" and
  "Cancel download"; the table's Installed/Latest headers are short
  enough to fit; the reveal rows match the backend's "Show in …" labels.
- The toolbar drops its "Sort" caption when longer labels leave no room
  for it, rather than drawing over the filters.
- Unused settings helpers and LANGUAGE_CODES were removed, and core's doc
  comments now describe what the UI actually translates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:06:46 -04:00
RealtaNua 7a193042fd Stay in the tray on Wayland when the window closes
winit cannot hide a Wayland window, so closing to the tray cancelled the
close and then did nothing. Where the window cannot hide, the close now
goes through and the launcher keeps running with only the tray; a tray
click or a second launch opens a new window in the same process. Other
platforms keep the single hidden window. The header's own close button
(no decorations) takes the same path as the window manager's.

With no window, settings that change (the last check, updates announced)
are saved to eframe's app.ron as they change, so quitting from the tray
no longer loses them. A --hidden start at login on Wayland now starts in
the tray without mapping a window first, so none flashes up.

Rebased onto main from the original PR: the Launcher/Shell split now
carries main's custom title bar, has_desktops, macOS "show on the current
desktop", wgpu options and theme-token clear colour. The settings saving
with no window and the windowless start at login were added here.

Fixes #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:32:53 -04:00