The Flatpak check took any entry with this app's X-Flatpak and a flatpak run
Exec as on, also one a startup manager had hidden (Hidden=true, or GNOME's
X-GNOME-Autostart-enabled=false) or whose Exec started a different app.
It now reads the [Desktop Entry] keys as desktop-entry parsers do and
counts the entry only when it isn't hidden and its Exec is exactly the line
this launcher writes for its own app id.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After switching from the host build to the Flatpak, the old host entry has
the same file name, so the setting showed on while its Exec still named the
host program. In the Flatpak only the Flatpak form (X-Flatpak=<id> and an
Exec of flatpak run) counts as on, and turning it on rewrites an old entry.
Both manifests grant xdg-config/autostart:create. Flatpak resolves that
on the host and binds the folder at its own path (common/flatpak-context.c,
common/flatpak-exports.c), so the entry can be written there also when
$XDG_CONFIG_HOME is outside home, and the folder is made on a fresh account.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inside the Flatpak the autostart entry went into the sandbox's own config
folder (~/.var/app/<id>/config/autostart), which the host session never
reads, and its Exec line named the in-sandbox /app/bin path. When
/.flatpak-info exists the entry now goes into the host's autostart folder
($HOST_XDG_CONFIG_HOME, else ~/.config), is named after the Flatpak app id,
and starts the app with `flatpak run --command=artcraft-launcher <id>
--hidden` plus X-Flatpak, as the XDG Background portal writes it. Outside a
Flatpak the path and the entry are unchanged. Both manifests note that the
entry relies on --filesystem=home.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The dialog's Install is disabled with a reason for a refused version.
- The out-of-reach tooltip in card and table; no note for a repository
copy; the note for a release without a download here.
- Clicking Reinstall installs; clicking a greyed-out Install version X
sends nothing.
- German rows: a single main button must sit on the menu's line, a
stacked pair must be centred on it and stay out of the next row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The version dialog's Install used has_download, so for a copy the
backend can't replace it stayed enabled and the install was refused
(CodeRabbit). version_installs and version_refusal are now shared by
the card's Install version X and the dialog: both are disabled and
say why on hover.
- A stacked Open/Update sat at the top of its table row, 7 px above the
menu; it is now laid out at its own size and centred like the rest.
- Docs of AppEntry::installable and AppStatus::Installed say what the
answer means now: an install would go ahead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- When a copy appears between a publisher looking and taking reporting,
the answer uses the view its detection asked for (last_system_kinds)
instead of probing there. A copy only enters installed through
publish_detected, which asks first.
- Event::Installable's doc covers both emissions: in the batch with the
Latest or Detected it depends on, and alone when only the plan changes.
- Tests: no probe under the lock (expired view kept), a plan report's
answer follows the current settings, a concurrent poll never splits a
pair.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A disabled 'Install version X' for a version that can't replace this
copy says so on hover (Jeremy8776).
- The 'latest X' note for a release that can't install here only shows
while the app follows the latest release: right after a version is
chosen, the old track's release is refused for that alone (Jeremy8776).
- Tests: the note in the table, the kept-version case, a repository
Flatpak keeping Reinstall.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
run_install updates a repository-managed Flatpak with flatpak update (on
Linux, on the stable track) before any download is planned, but the
answer treated it as managed elsewhere and said no, so the new Reinstall
gate hid the launcher's only route to that update (Jeremy8776).
repository_update now holds that rule, and both run_install and the
answer ask it. The system_kinds doc says when it runs under a lock.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stages the race deterministically: a report in progress holds the
planning lock, a worker started before the settings change waits behind
it, the preferred format changes, and the worker's report is checked to
be the new one. Capturing settings at the worker's start would fail it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Detection, an install's end and a check's end reported kept downloads
with settings captured when their thread started, so one could land
after the report of a newer settings change and show a stale Resume or
Retry. report_partial now reads the settings itself under a planning
lock, so whichever report comes last was worked out last.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Configure recomputed every app's planned download on the UI thread, and
inside the Flatpak sandbox each asks the host (up to 3 s). That now runs
on a thread, one at a time, each with the settings as they are when it
starts, so the newest settings have the last word. Installs and planned
downloads still ask the system afresh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An installed copy behind a release the launcher can't install over it
shows 'latest X' beside its version, with a tooltip saying why, the
way a version kept on purpose does (Jeremy8776).
- The single update button keeps its full accessible name when its label
is shortened (Jeremy8776).
- Reinstall and 'Install version X' only show or enable where an install
would go ahead, so they can't end in 'has no download' (Jeremy8776).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The answer reads the backend options when it is worked out, so a job
that started before a settings change no longer answers with the old
version track (Jeremy8776).
- What the system can install is asked before the reporting lock is
taken and kept for a minute: in the Flatpak sandbox asking runs
flatpak-spawn on the host (up to 3 s). Installs still ask afresh
(Jeremy8776, CodeRabbit).
- No answer, and nothing asked, for an app that isn't installed: the UI
only uses it for an installed copy. Startup's cached replay, before
detection, no longer asks at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install_plan takes how a copy managed elsewhere is explained. Installs
keep the full message (with pacman -Si naming the repository); the
installability answer, which runs on every check under the reporting
lock, only needs yes or no.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A copy installed outside the launcher, such as a FreeBSD system binary
in /usr/local/bin, was reported installable when a newer release had a
matching tarball, because only the download choice was asked. The install
then refused it as installed outside the launcher. install_plan now holds
every check an install makes before downloading (a copy managed
elsewhere, the platform, the release and its version track, the package),
and both run_install and Event::Installable use it.
Refs review by FredApps on #1252.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A poll could fall between Installable and the Latest or Detected after it,
and an answer worked out from older state could land after a newer one.
Each change to an app's latest release or installed copy is now recorded,
answered and queued under one lock, and the pair is queued together.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Long languages (German, French, Czech, Polish, Hungarian, Spanish, Russian,
Portuguese) don't fit OPEN | UPDATE in the table's action slot. Their rows
now grow to hold Open, naming its version, above Update, instead of
dropping Open into the menu. Cards and the details page keep a fixed
height and stay side by side, or show the update alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UI guessed from any download this platform takes, but an install keeps
the way a copy was installed (an AppImage with an AppImage, an MSI with an
MSI), so an AppImage copy with a .deb-only release still showed Update,
was announced and started automatically, and then failed. The backend now
sends Event::Installable, from the same plan an install makes, before each
Latest and Detected it depends on and whenever the plan changes. The UI
uses it for installed copies and keeps its own check where there is no
answer (the web build).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While an update waits (Update to X, Retry update), the main button is two
halves: an outlined Open that launches the installed copy, naming its
version where it fits, and the solid Update. Labels are measured and step
down to fit; the left keeps its version longest. Where even OPEN | UPDATE
doesn't fit, the single update button shows as before and Open stays in
the menu. Each half's accessible name is the full meaning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An installed app whose newer release has no installable download here
showed Update available, counted in Updates, notified and was picked by
auto-update, and installing failed with 'has no download'. It now reports
Installed, like a repository-managed copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choose version, and the launcher's own version list in Settings, showed
only the tag. Each row now adds, at its right, the day the release came
out and the size of the package this machine would download, in the
launcher's usual date and size formats. Either is left out when unknown;
the row keeps the tag as its name and carries them as its description.
On the selected row they take the row's text colour, not the muted grey.
The size follows the backend's choice of package. An app's installed
copy keeps its format (an MSI stays an MSI, the launcher's Windows copy
is its portable ZIP, an AppImage stays an AppImage), else the preferred
formats among those it installs here; FreeBSD always takes the tarball.
Where that depends on package managers the UI can't see (other Linux
installs), the row gives the date alone. The launcher's own rows size
what its self-update downloads, the format the running copy was
installed as, which the backend now reports once it knows; until then
the manual download, except on Linux.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each installed app's menu gains "Launch options…": flags typed there are
saved in Settings.launch_flags (app.ron, keyed by catalog id) and added to
the app's command line on Open. core::args::split turns the text into
arguments (whitespace, "…"/'…' quoting, backslashes kept for Windows
paths) and refuses an unclosed quote, both in the dialog and at launch.
Flatpak, launcher-unpacked, plain binary and macOS .app installs all pass
the flags through; strings are translated in every complete catalog.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add the Georgian rows for the strings main added (move, pin, reorder) and
the three Installed fields in the Georgian test, matching the Traditional
Chinese fix.
Add the variable OTC filenames NotoSansCJK-VF.otf.ttc and NotoSansCJK-VF.ttf.ttc, and the subset variable NotoSansTC-VF.otf, so an installation carrying only one of them still draws the Traditional Chinese interface.
Fill the new Installed fields in the TC test and add zh-TW rows for the
pinning and retry strings that main added, so the catalog completeness
test passes again.
- The action row's width lost a gap for every app, pinned or not (`+ gap`), so
each row's menu ran 8 px past its column. The main button keeps its old slot;
a pinned app's grip now leads the row and the table widens its actions column
by one square while a pinned app is shown, so no button is squeezed (#956) and
every row stays in line. A test checks a pinned row against an unpinned one.
- The grip only shows in the grid and the table, where dropping works; on the
details page it was a control that did nothing.
- A released drag ends in one place, after the list draws, instead of in the
grid, the table, the empty list and each drop target.
- Move up and Move down use the drag's `reorder_pinned_to`; `reorder_pinned`
and its test go, and `pinned_neighbor` returns the slot.
- Drop the settings test that only re-ran its own steps (the UI test covers
hiding a pinned app), the tool-generated design note, and use "Lösen" for
Unpin in German.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
openSUSE's google-noto-sans-tc-fonts installs NotoSansCJKtc-VF.otf directly in /usr/share/fonts/truetype; search it so the Traditional Chinese interface does not fall back to English there.