* Add a French interface
fr.tsv translates the whole interface, the tray menu and tooltip, each app's
tagline and the backend's install/update messages, and Settings > Language
offers Français. fr, fr-FR, fr_CA.UTF-8 and the like select it. French counts
0 and 1 as singular, so it gets its own plural rule. tests/french.rs checks the
grid, the table, dates and a backend message.
* Rebase onto German: French last in the registry, new update strings, lowercase months
French writes month abbreviations in lower case (12 août 2026). Tests use a
never-supported tag (xx-XX) next to fr-FR.
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>
* Add a German interface
de.tsv translates the whole interface, the tray menu and tooltip, each
app's tagline, the package formats and the backend's install/update
messages into German, and Settings > Language offers Deutsch. German
locales (de, de-DE, de-AT, de-CH, de_DE.UTF-8…) select it through Auto.
German has two plural forms (one|other), like English. Dates read
"3. Okt. 2026", relative times "vor 5 Min." / "vor 2 Tagen", and the
user is addressed with the informal "du". Product and app names stay in
English.
The locale test that used de-DE as an unsupported tag now uses fr-FR,
and new tests cover German tag matching, plurals, the date format and a
backend message.
* Test the German UI and shorten labels the table clipped
tests/german.rs picks Deutsch in Settings, checks the setting is saved,
and checks the grid, the table and the web build in German, like the
Chinese UI test. The table clipped three German labels, now shorter:
the "Installed" and "Released" columns read "Lokal" and "Datum", and
the "Available to install" status reads "Installierbar".
* Rebase onto main: translate the new update strings; use a never-supported tag in tests
xx-XX instead of fr-FR, so the test keeps meaning 'unsupported' as more
languages arrive.
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>
* Find AppImages outside ~/Applications; add a menu entry for installed ones
Detection only looked in ~/Applications and only for the exact release
file name, so an AppImage the user kept in Gear Lever's ~/AppImages or in
~/.local/bin, or one renamed to photocraft.appimage (Gear Lever's rename,
lower-case extension), showed as not installed. Detection now also looks
in ~/AppImages and ~/.local/bin, accepts either case of the extension, and
matches only the release name or the bare app name (version unknown for
the latter). Updates still install into ~/Applications and only remove
old copies there; AppImages the user keeps elsewhere are never deleted.
Installing an AppImage now writes ai.storyteller.<app>.desktop under
$XDG_DATA_HOME/applications (the host's ~/.local/share when the launcher
runs as a Flatpak) and copies the app's bundled 128 px icon into
icons/hicolor/128x128/apps. Exec is quoted per the Desktop Entry spec;
Categories and StartupWMClass match each app's own .desktop file. The
entry carries X-ArtCraft-Launcher=true and the record lists the files
written, so the launcher only replaces or removes what it wrote (plus an
entry the app wrote for itself that runs the copy being replaced or
removed). The .desktop text is a pure function with unit tests.
Fixes#14Fixes#37
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove only AppImages the launcher installed; rebase onto main
AppImages detected in ~/AppImages or ~/.local/bin belong to the user (or Gear
Lever): Remove now deletes only those in ~/Applications or recorded by the
launcher, and explains otherwise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The launcher offered a .deb whenever apt-get and dpkg were on PATH, and
an .rpm only when dpkg was absent. Fedora can have apt and dpkg
installed (mmdebstrap and friends pull them in), so there it downloaded
the .deb and ran apt-get on it, with .rpm ruled out entirely (#21).
The distro family now comes from ID and ID_LIKE in /etc/os-release:
Debian and Ubuntu get .deb, Fedora, RHEL, CentOS, SUSE and openSUSE get
.rpm, each only with pkexec and the matching tools. Arch and unknown
distros get no system package. After the distro's package, AppImage now
ranks ahead of Flatpak, whose bundles need a Flathub runtime. Installing
a .deb without apt-get fails with a clear message, and removing one falls
back to dpkg -r.
The parsing and the choice are pure functions in core's new distro
module, tested for Fedora, Rocky, openSUSE, Ubuntu, Mint, Arch and an
unknown distro.
Fixes#21
Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: install only apps signed by the Craft team, at the release's version
A Craft App, or the launcher's own update, now replaces the installed copy
only when the staged bundle carries a valid Developer ID signature from team
DJ6XS33FX8 (codesign with a requirement pinning the team, which works
offline) and reports the release's version. A bundle that reports only the
numbers matches a pre-release with the same numbers. On failure the staged
copy is removed and the installed app is left as it was.
* Rebase onto main: keep both sides' tests in install.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: s17sh70x2jfl031ag <287216888+s17sh70x2jfl031ag@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ureq only reads HTTPS_PROXY, ALL_PROXY and friends, so a proxy set in
the OS was ignored and GitHub stayed out of reach where it needs one
(#30). Http::new now picks the proxy once and gives it to every agent:
the environment variables first, as before, else the OS setting.
On Windows that is the manual proxy in HKCU Internet Settings, in
either the single host:port form or the per-protocol
http=...;https=... form (the https entry wins), with ProxyOverride as
the bypass list. On macOS it is the HTTPS (else HTTP) proxy from
scutil --proxy with its exceptions list. Linux and the BSDs keep
using the environment. Loopback always goes direct. The chosen proxy
is logged at info level without credentials.
ureq's own win-system-proxy feature treats the whole ProxyServer
value as one address, which breaks the per-protocol form, so the
parsing lives here in pure, tested functions. PAC scripts and WPAD
are not supported.
Fixes#30
Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Issue #44: the footer kept saying "GitHub's hourly limit was reached"
when it hadn't been, or long after it stopped mattering.
A 403 or 429 from the API counted as rate limited until
x-ratelimit-reset. GitHub sends that header with every answer, so a
secondary limit (quota left, Retry-After: 60) became up to an hour of
"hourly limit reached". The reset now only counts when
x-ratelimit-remaining is 0; otherwise Retry-After decides, or a minute
on a bare 429, as GitHub documents.
The notice also stayed up after the ArtCraft feed had answered every
app. A check now reports the limit only when some app had to come from
the release pages or the saved answers.
Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* linux: prefer Flatpak if available and support Flatpak sandbox execution
* linux: match default package format to launcher format (Flatpak for Flatpak, AppImage for AppImage)
* ui: default package preference selection to launcher format (Flatpak for Flatpak, AppImage for AppImage)
* Automatically download and install updates when available
* Fix clippy (unnecessary_map_or, collapsible_if) and rustfmt
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>
* Add Linux riscv64 release build and switch runners to Ubuntu 26.04
- release.yml: new linux-riscv64 job cross-compiled on ubuntu-26.04 against
ports.ubuntu.com riscv64 libs, tar.gz only, artcraft-launcher --version
smoke-tested under QEMU. Updated flatpak runner matrix to ubuntu-26.04.
Draft-release job now requires linux-riscv64 and checks the tarball.
- packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE / EMULATOR support.
- platform.rs: add Arch::Riscv64 variant, map linux-riscv64 token, and prioritize
tarball format for riscv64 on Linux.
- web.rs: detect riscv64 architecture in visitor user agent.
- README and AGENTS.md: document the riscv64 tarball.
* linux: build deb and rpm packages for riscv64
* ci: configure actionlint to recognize ubuntu-26.04 runner labels
* Keep riscv64 and Flatpak release jobs on Ubuntu 24.04
24.04 is what PhotoCraft's linux-riscv64 job uses, so the riscv64 binaries
need glibc 2.39 (as the README says) rather than 26.04's newer glibc, and
the release pipeline keeps runner images it has already proven on. Drop the
actionlint label config that 26.04 needed, revert the default_kinds and
download_kinds matches to match on the OS alone, simplify the riscv UA check,
and rustfmt.
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>
When dpkg/rpm don't list the app, the launcher can't know which package
manager installed it, so label it a system binary (open it, leave update
and removal to whatever installed it) instead of Deb/Rpm with a guessed
package name. Require the binary itself; fix clippy and rustfmt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Include InstallSource::Rpm in ours package filter
- Add bundle_id to candidate names for Deb and Rpm packages
- Strip architecture suffix from package name in parse_dpkg
- Clear AppImage environment variables in process::command to prevent polluting system tools
- Fall back to detecting system desktop entries and binaries with CLI version extraction
- Add unit tests for candidate names, RPM filtering, and binary CLI version
Fixes#97
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>
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>
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>
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>
Each install ran its own pkexec call at the same time; all but the first
lost the dpkg lock and failed with error 100. Installs that reach the
system installer now wait their turn ("Waiting for another app…", with
Cancel), and Linux packages waiting together go into one privileged call,
so they share one password prompt. apt waits up to 60 s for another
package tool's lock. On Windows msiexec runs one at a time (error 1618).
The launcher's own update waits for other installs before it restarts.
Ported onto main and onto "Keep the verified download when the install
step fails", with these changes from the original PR:
- A failed job counts as installed when the new version is found
afterwards only if it failed in a Linux call shared with other apps;
an MSI, AppImage or single-package error, a pause, and either
cancellation (including INSTALL_CANCELLED) stand.
- The launcher's update waits for other installs at most 10 minutes and
can be cancelled from its banner ("Cancel update"); giving up names the
apps still installing and says to answer or close the password prompt.
- Cancellation toasts say what was called off: "Download cancelled",
"Install cancelled" (download kept), "Uninstall cancelled" or
"Update cancelled", instead of a bare "Cancelled".
- The waiting card uses the restyled layout and the app's own colour,
with no hard-coded sizes.
Fixes#6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A download that passed its SHA-256 check was deleted whether or not the
install succeeded, so every retry downloaded it again (usually after the
password prompt was dismissed). It now stays until the install succeeds,
is hashed again before it is reused, and shows as "Retry install" (or
"Retry update to <version>") with "Download again" in the menu. A
dismissed system prompt now says "Install cancelled" rather than
"Download cancelled". A download checked by size only is still deleted.
Ported onto main from the original PR: the check thread, rewritten for
ArtCraft's release feed, reports kept downloads after it marks the apps
loaded; the Retry button uses the restyled widgets::button; the backend
tests build on main's Shared and ask no real service; the grid test
scrolls the card into view. Finished files kept after a failed install
are now cleaned up after 7 days instead of 30 (paused downloads keep 30).
Fixes#5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The grid card's text started at the top of its row and sat 11 pt below the
icon's centre. The text is now measured (centered_lines) and the icon's slot
is as tall as the text, so the two share a centre at any scale. The table
already centred its rows on main. The icon now reports itself to
accessibility, which the alignment test uses to find it.
Ported onto the restyled card from the original PR.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
harness() called .wgpu(), which builds a wgpu device for every test up
front. Nine tests then created, used and destroyed a Vulkan device at the
same time on parallel test threads, and Vulkan loaders before 1.4.350
crash on that (KhronosGroup/Vulkan-Loader#1863): about one run in ten
ended in SIGSEGV here.
Without .wgpu() egui_kittest's default renderer builds the same wgpu
renderer on the first render, which only happens when
ARTCRAFT_LAUNCHER_SNAPSHOTS is set. Ordinary runs no longer need a
graphics adapter at all.
Fixes#12
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On Windows the launcher now asks wgpu for DX12 with GL as the fallback instead
of wgpu's default list, which starts with Vulkan. WGPU_BACKEND still overrides
it; other platforms keep wgpu's defaults.
Without this the launcher died on startup with STATUS_ACCESS_VIOLATION on an
ASUS G513QY (Ryzen 9 5900HX integrated Radeon + Radeon RX 6800M). The crash is
inside AMD's Vulkan driver (amdvlk64.dll): the integrated GPU runs the OEM
driver from 2021 while the 6800M runs Adrenalin from 2026, both register a
Vulkan ICD, the loader reports the 6800M twice, and wgpu's pick dies right
after the surface is configured. AMD's installer never updates the OEM iGPU
driver, so this state is common on hybrid laptops. DX12 and GL both work.
Also reorders one import in self_update.rs that stable rustfmt flagged, so
`cargo xtask ci` passes again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On some Windows PCs every app showed "Not available here" / "Offline" and
the tooltip said "invalid peer certificate: UnknownIssuer". We read the
Windows root store once with rustls-native-certs, but Windows only adds
many roots when something asks it to verify a chain. Chrome and Edge
verify on their own, so a PC that only uses them can lack the root
GitHub's certificate needs. Antivirus HTTPS scanning hits the same path.
Use ureq's platform-verifier feature (rustls-platform-verifier) so the OS
checks each chain itself and fetches missing roots. Linux still reads the
system store. No bundled root list comes in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since 0.1.2: release information comes from ArtCraft's API, with GitHub
as the fallback; calls to the API are rate-limited and back off hard on
HTTP 429; the footer names the services in use; ArtCraft announcements
and alerts are shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every launcher polls the same server, so a FeedThrottle now gates each request:
- at most one a minute once the first load is over;
- up to ten a minute during the first load, front-loaded, with the gaps doubling after each
failure (the first load ends at the first answer or after ten requests);
- failures back off 2, 4, 8 … minutes (at most an hour), with jitter;
- HTTP 429 waits at least 15 minutes, doubling per 429 in a row up to six hours, never sooner
than Retry-After, plus up to 50% jitter so a herd of clients doesn't return in step. The
backoff is saved to cache/feed-throttle.json, so relaunching doesn't skip it.
A check that would have to wait more than ten seconds goes without the feed this time, and the
saved answer stands in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Footer: "Checked just now · using ArtCraft API", "· using GitHub" or
"· using ArtCraft API and GitHub", from where each release on screen came
from (Latest events now carry their origin), so it follows what's shown.
Retries and fallback:
- The very first poll (nothing ever loaded or saved) tries the ArtCraft
API once, then GitHub at once, so the window fills
- Every other poll tries the API up to three times (1 s, then 3 s apart)
- Until every app has loaded once, a failing API lets GitHub be asked
fairly freely (every 5 minutes per app)
- After that, GitHub is asked only for apps the API doesn't list or
hasn't refreshed in a day, or once the API has seriously failed (three
checks in a row and an hour without an answer); then hourly per app.
Until then the API's last answer keeps being shown
Tests: the fallback policy for each stage, and the footer wording.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launcher now asks storyteller-web
(GET https://api.storyteller.ai/v1/craft_apps/release_info), which polls
GitHub for every Craft App on a schedule, instead of polling GitHub itself.
- At launch and every five minutes: one request (10 s timeout) for every
app's latest release, plus renames and notices
- Per app, GitHub is asked only when the feed can't vouch for it: the app
isn't listed, its entry is more than 24 hours old, or the feed didn't
answer. Even then, at most once an hour per app (Refresh asks at once).
With pre-releases on, GitHub decides (the feed serves stable releases)
- Read leniently (crates/core/src/feed.rs): every field optional, unknown
fields ignored, wrong types read as missing, one bad entry never spoils
the rest. Download links must still point at the app's own GitHub
releases; a moved repo counts only when the feed's repo_renames says so
- The last good answer is saved for an instant first paint and stands in
(while still fresh) when the feed can't be reached
- Announcements show inline until dismissed; alerts pop up until the user
presses OK (with "Open link" for web links); app renames show as
announcements; dismissals persist
- "Check for updates": Automatically (every five minutes) or Only when I
ask; older saved choices load as Automatically
- ARTCRAFT_LAUNCHER_FEED_URL points at a staging server or a local copy
Tests: the feed parser against hostile input, renames and notices; a
contract test against a response produced by storyteller-web's own code;
the feed-first rule with a counting fake GitHub; the notices UI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setup wizard gains a folder page between Welcome and Ready: pick where
ArtCraft Launcher goes (default %LOCALAPPDATA%\Programs\ArtCraft Launcher;
still per user, so it notes to pick a folder you can write to), and a
"Create a desktop shortcut" box. The page is the stock WixUI_InstallDir one
plus the box, with the same path checks and Change... browser.
Both choices are recorded under HKCU and reused by the next version, so an
update (self-update runs the MSI with /passive) installs into the same folder
and keeps the shortcut choice. The box starts ticked on a first install; an
update from 0.1.0 or 0.1.1, which never asked, adds no desktop icon. A value
given on the command line (INSTALLDESKTOPSHORTCUT=0/1) wins. The desktop
shortcut is a normal MSI component, removed on uninstall.
self_update.rs now recognises an MSI install by the folder the MSI records
(InstallDir), not only the default one, so a launcher installed elsewhere
still updates itself.
The Windows ARM64 workflow now installs twice on real hardware: with the
defaults (default folder, Start Menu and desktop shortcuts) and into a chosen
folder without the desktop shortcut, checking each uninstall removes it all.
Version 0.1.2.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The launcher now looks like the apps page on getartcraft.com: the site's
colour tokens, square corners, 1 px hairlines instead of shadows, app cards
in a shared-hairline grid with numbered tabs and "Photo*Craft*" wordmarks,
mono upper-case labels and buttons, and square badges for status.
Fonts are the site's own, embedded in the binary: Archivo (620, 118 % width)
for headings and app names, Inter for body text and Geist Mono for labels,
all variable fonts from google/fonts (OFL-1.1) set to the site's weights.
The window has a custom title bar on Windows and Linux (drag to move,
double-click to maximize, minimize/maximize/close, resize from the edges)
and a transparent one on macOS with the traffic lights in the header.
The MSI used to flash a progress bar and close. Double-clicked, it now opens
a WixUI wizard (Welcome, Ready, progress, Finish with "Launch ArtCraft
Launcher"), with art drawn by package.ps1 from the ArtCraft mark and the
Craft apps' icons. Self-update still runs it with /passive. CI installs the
WiX UI and Util extensions.
The app icon is now the blue "A" mark, getartcraft.com's favicon.
packaging/icons.sh runs on any OS: `cargo xtask icns` packs the .icns.
Also: two clippy fixes in crates/native that newer clippy rejects, and
refreshed README screenshots.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- LinkedIn icon linking to https://www.linkedin.com/company/artcraft-ai,
in the footer and Settings › Community
- Social icons now read X, Instagram, LinkedIn, YouTube, TikTok
- The icon is from Font Awesome Free 6.7.2 (CC BY 4.0), since Simple
Icons no longer carries LinkedIn's logo; white fill and a square view
box, artwork unchanged. Credited in assets/social/README.md,
ATTRIBUTION.md and NOTICE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening the launcher from the menu bar used to switch to the desktop
(Space) its window was last on, which is jarring when you're working on
another one. A new setting, "Show the window on the current desktop"
(on by default), sets AppKit's "move to active space" behavior on the
window whenever it's shown, so it comes to the desktop you're on instead.
Turning it off restores macOS's default of switching to the window.
macOS only: the code and its dependencies compile only for macOS, and
the setting appears only where the host has desktops. objc2,
objc2-foundation and objc2-app-kit are already in Cargo.lock through
winit and tray-icon; they're pinned to those versions, and only safe
AppKit calls are used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-update fixes since 0.1.0 (f4f1d77): the restart helper no longer
passes a pretend version on, a just-installed release isn't offered
again, and macOS DMG installs pass strict signature checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes found trying "Update and restart" on a real install:
- The restart helper no longer passes ARTCRAFT_LAUNCHER_PRETEND_VERSION
on to the new launcher. It inherited the old launcher's environment,
so the restarted copy still believed it was old and offered the same
update again, forever. A pretend version now lasts one run.
- Loop guard: before restarting, the launcher records the release it
updated to (self-update.txt in its data folder) and, for a day, won't
offer that release again. Protects against a release whose binary
reports a different version from its tag.
- macOS installs from a DMG (the launcher's own update and every Craft
App) copy with `ditto --norsrc --noextattr --noacl`. The disk image
adds Finder metadata to its files; copying it made
`codesign --verify --strict` reject the bundle as carrying detritus,
although Gatekeeper still accepted it. Signatures live in the bundle's
files, not in extended attributes.
Tests: the helper command removes the pretend variable; a just-installed
release isn't offered again until the guard lapses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a newer launcher is released, the banner offers "Update and
restart": the new build is downloaded and verified like any Craft App,
put in place the way this copy was installed, and started by a small
helper once this process has exited (it holds the single-instance lock,
and Windows locks a running exe).
- macOS .app: new bundle copied from the DMG over this one, reopened
- Windows MSI (per-user): helper runs the new MSI after exit
- Windows portable: helper unzips over the folder after exit; settings stay
- Linux AppImage: new AppImage renamed over this one
- Linux .deb / .rpm: system package manager, then restart
- Flatpak, FreeBSD and dev builds keep the download link
ARTCRAFT_LAUNCHER_PRETEND_VERSION makes an installed copy act old, to try
the update against the current release. Tested: the helper waits for the
old process to exit; a real DMG replaces a bundle in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebased onto main and combined with 8143a26: keeps the -bin/-git AUR detection and pre-release-aware version parsing from main, adds this change's pacman removal (now using the detected package name), its X.Y.Z-anywhere fallback and its dialog wording.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ideas from the ArtCraft Suite Updater:
- Arch / AUR: detect each app through pacman under all three AUR names
(<app>, <app>-bin, <app>-git; PdfCraft also as printcraft*). Updates
and removals of AUR installs show the paru / yay / pacman command
instead of installing a second copy
- Read package-manager versions the package-manager way: drop the
revision (0.5.0-1), epoch (1:) and VCS suffix (0.3.0.r47.g…), and map
Debian's ~. Fixes .deb installs of the latest release showing an
update forever
- Flatpak installs that list no version are read with `flatpak info`
- Look again at what's installed every 5 minutes, and when the window
regains focus (at most every 30 s), so apps installed or removed
outside the launcher show up; one detection pass at a time
- Package-list parsing lives in crates/native/src/packages.rs, tested on
every platform
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: the grid view (dark theme) is the main screenshot, with a
smaller table view (light theme) below it
- CI tests on Linux and Windows only: per craftrules, the org's macOS
runners are kept for release builds, and release.yml builds and checks
macOS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docs/screenshots/artcraft-launcher.png: the table view on macOS with
live release data, shown below the README's navigation links
- Credit the screenshot in ATTRIBUTION.md
- Trim the closing line of the Thanks section
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The app icon (Dock, taskbar, Start menu, Linux menus, installers) is
now the black ArtCraft "A" mark, rendered unmodified from
docs/brand/artcraft-mark-black.svg by packaging/icons.sh; the old
tile icon is gone
- The app icon is an ArtCraft trademark, not MIT/Apache: say so in
assets/app-icon/, ATTRIBUTION, NOTICE, README and AGENTS.md
- Settings shows the blue mark in dark theme so it stays visible
- README: a Thanks section crediting the eleven community launchers
whose ideas shaped this one
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Small monochrome service icons (Simple Icons 16.34.0, CC0) linking to
ArtCraft's accounts, after Website · GitHub · Discord in the footer and
in Settings › Community; quieter than Discord, brighter on hover
- Settings: the community description gets its own line, so the
Website and GitHub buttons no longer overlap it
- Credit the icons in assets/social/README.md, ATTRIBUTION.md and NOTICE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Header: the ArtCraft wordmark (white on dark, ink on light), "Launcher ·
Craft App Suite" and the tagline "Open Source Tools for Creatives,
Makers, and Builders"
- Tray / menu-bar icon is now the ArtCraft "A" mark, rendered unmodified
from docs/brand; the old self-drawn tray icons are gone
- Repo docs (NOTICE, ATTRIBUTION, README, asset licenses, AGENTS.md)
call out that the ArtCraft name, logo and mark are not MIT/Apache
- Table: "Inspired by" column; every column sorts both ways, empty cells
last; the grid's sort menu has the same options
- Adobe / Microsoft / Autodesk / Avid trademark notice under both views;
inspirations shown with ®
- Links: Discord button in the header; Website · GitHub · Discord in the
footer, tray menu and Settings; each card links to its app's website
(classic apps) and GitHub repo, and the … menu has both
- Fix links never opening: enable eframe's "links" feature
- Clear stale partial downloads at startup
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every download failed at its last read: ureq's body limit errors once
the limit is reached, and it was set to the exact file size. Sizes and
checksums are now checked by the launcher, with headroom on the limit.
- Pause keeps the partial file; Resume continues from the same byte
(HTTP range requests). Cancel discards it.
- Dropped or stalled connections retry with backoff from where they
stopped, shown as "Reconnecting…"; if the connection stays down the
download pauses itself instead of failing
- Partial downloads survive quitting and show as paused next launch
- Ask for identity encoding so bytes arrive exactly as published
- SHA-256 from GitHub's digest or SHA256SUMS.txt; a mismatch refetches
once from scratch. Releases with no checksum install with a size
check and say so; a checksum list that can't be fetched is an error
- Tests against a scripted local server for each failure mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A native Rust/egui launcher for the 12 Craft Apps (7 classic + 5 new).
- Grid (icon cards) or sortable table view, filters and search
- Status per app: available, installed and up to date, or update
available with version and release date
- Installs where each OS keeps apps, from the official release packages:
/Applications (DMG), Program Files (MSI), deb/rpm via the system
package manager (Flatpak/AppImage fallback), /usr/local on FreeBSD
- Detects apps installed by hand; updates keep the installed format
- Every download verified against its published SHA-256; running apps
are never replaced
- Tray/menu-bar mode, start at login, one notification per new version
- GitHub API with ETag cache, falling back to release pages when
rate-limited; release source is pluggable for a future catalog API
- Web build lists latest releases with per-platform downloads
- Packaging and release workflows for macOS, Windows (x64/x86/arm64),
Linux (x86_64/aarch64), FreeBSD and web
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>