Fix (linux, Flatpak): app panics on launch - bundle libayatana-appindicator, update GNOME runtime to 51 (#551)

* Fix (linux, Flatpak): app panics on launch (Failed to load ayatana-appindicator3) - Bundle libayatana-appindicator via Flathub shared-modules - Update GNOME runtime from EOL 48 to 51

* Dev: lint - Fix existing ESLint and Stylelint errors from main - Ignore Flatpak shared-modules and build dirs in ESLint
This commit is contained in:
Aleksey Hoffman authored and GitHub committed 2026-10-17 17:49:33 +03:00
1 parent 61cdbb61f2
commit 3df1570d17
18 files changed
+121 -36

No files matched your search

+1 -1
View File
@@ -287,7 +287,7 @@ jobs:
run: |
sudo apt-get install -y flatpak flatpak-builder
flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install -y --noninteractive --user flathub org.gnome.Platform//48 org.gnome.Sdk//48
flatpak install -y --noninteractive --user flathub org.gnome.Platform//51 org.gnome.Sdk//51
- name: Build Flatpak bundle
if: matrix.platform == 'linux'
+1
View File
@@ -33,3 +33,4 @@ flatpak/.deb-input/
flatpak/.build/
flatpak/.repo/
flatpak/output/
.flatpak-builder/
+3
View File
@@ -0,0 +1,3 @@
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git
+4 -1
View File
@@ -7,7 +7,10 @@ import checkFile from 'eslint-plugin-check-file';
import pluginVueI18n from '@intlify/eslint-plugin-vue-i18n';
export default defineConfigWithVueTs(
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/e2e-webdriver/**', 'src-tauri/**']),
globalIgnores([
'**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/e2e-webdriver/**', 'src-tauri/**',
'flatpak/shared-modules/', 'flatpak/.build/', 'flatpak/.repo/', 'flatpak/.deb-input/', 'flatpak/output/', '.flatpak-builder/',
]),
pluginVue.configs['flat/strongly-recommended'],
vueTsConfigs.recommended,
pluginStylistic.configs.customize({
+11 -2
View File
@@ -10,7 +10,8 @@ repo_directory="$flatpak_directory/.repo"
output_directory="$flatpak_directory/output"
manifest_path="$flatpak_directory/com.sigma-file-manager.app.yml"
app_id="com.sigmafilemanager.app"
runtime_ref="org.gnome.Platform/x86_64/48"
runtime_version="51"
runtime_ref="org.gnome.Platform/x86_64/${runtime_version}"
version="$(node -p "require('$root_directory/package.json').version")"
bundle_path="$output_directory/Sigma-File-Manager-${version}-linux.flatpak"
@@ -62,9 +63,17 @@ ensure_flatpak_runtime() {
fi
flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install -y --noninteractive --user flathub org.gnome.Platform//48 org.gnome.Sdk//48
flatpak install -y --noninteractive --user flathub "org.gnome.Platform//${runtime_version}" "org.gnome.Sdk//${runtime_version}"
}
ensure_shared_modules() {
if [ -f "$flatpak_directory/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json" ]; then
return
fi
git -C "$root_directory" submodule update --init flatpak/shared-modules
}
ensure_shared_modules
deb_package="$(ensure_deb_package)"
mkdir -p "$deb_input_directory" "$output_directory"
cp "$deb_package" "$deb_input_directory/sigma-file-manager.deb"
+5 -1
View File
@@ -1,6 +1,6 @@
id: com.sigmafilemanager.app
runtime: org.gnome.Platform
runtime-version: '48'
runtime-version: '51'
sdk: org.gnome.Sdk
command: sigma-file-manager
appstream-compose: false
@@ -22,6 +22,10 @@ finish-args:
- --filesystem=xdg-config/autostart:create
modules:
# Tauri's tray icon (libappindicator-sys) dlopens libayatana-appindicator3 at startup,
# which the GNOME runtime doesn't ship.
- shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json
- name: sigma-file-manager
buildsystem: simple
sources:
@@ -26,6 +26,7 @@ const props = withDefaults(defineProps<{
align?: PopoverContentProps['align'];
side?: PopoverContentProps['side'];
}>(), {
tagSummary: undefined,
allowCreate: true,
fullWidth: true,
align: 'end',
@@ -33,7 +34,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
open: [];
'open': [];
'open-change': [open: boolean];
'toggle-tag': [tagId: string];
'create-tag': [name: string];
@@ -100,6 +101,7 @@ watch(() => props.isSelectorMounted, (mounted) => {
if (mounted) {
showEditor.value = true;
if (prefersReducedMotion()) {
isExpanded.value = true;
return;
@@ -165,11 +167,11 @@ onUnmounted(() => {
:title="resolvedTagSummary"
@click="emit('open')"
>
<TagIcon class="tag-selector__trigger-icon-plus" />
<TagOverflowList
v-if="overflowTags.length > 0"
:tags="overflowTags"
/>
<TagIcon class="tag-selector__trigger-icon-plus" />
<TagOverflowList
v-if="overflowTags.length > 0"
:tags="overflowTags"
/>
</button>
</div>
</template>
@@ -4,7 +4,9 @@ Copyright © 2021 - present Aleksey Hoffman. All rights reserved.
-->
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import {
computed, nextTick, onBeforeUnmount, ref, watch,
} from 'vue';
import { convertFileSrc } from '@tauri-apps/api/core';
import { useI18n } from 'vue-i18n';
import { FileIcon, ImageIcon, SearchIcon, XIcon } from '@lucide/vue';
@@ -315,7 +315,10 @@ describe('clipboard store', () => {
});
it('saves a replacement clipboard image after a pending save finishes', async () => {
const firstSave = createDeferred<{ path: string; sizeBytes: number }>();
const firstSave = createDeferred<{
path: string;
sizeBytes: number;
}>();
let saveCount = 0;
invokeMock.mockImplementation(async (commandName: string) => {
if (commandName === 'save_system_clipboard_image_to_temp') {
@@ -284,8 +284,14 @@ describe('user stats tagged item removal', () => {
path: item.path,
tagIds: item.tagIds,
}))).toEqual([
{ path: '/both', tagIds: ['tag-personal'] },
{ path: '/personal-1', tagIds: ['tag-personal'] },
{
path: '/both',
tagIds: ['tag-personal'],
},
{
path: '/personal-1',
tagIds: ['tag-personal'],
},
]);
expect(lazyStoreSetMock).toHaveBeenCalledWith('taggedItems', store.taggedItems);
});
+2 -2
View File
@@ -16,10 +16,10 @@
}
.animate-dashboard-content {
opacity: 0;
animation-fill-mode: both;
animation-name: animate-dashboard-content;
animation-timing-function: ease-out;
animation-fill-mode: both;
opacity: 0;
}
@keyframes animate-dashboard-content {
@@ -23,12 +23,21 @@ describe('formatDashboardFileSize', () => {
describe('formatDashboardDirectorySize', () => {
it('formats a completed directory size', () => {
expect(formatDashboardDirectorySize({ status: 'Complete', size: 1048576 })).toBe('1.0 MB');
expect(formatDashboardDirectorySize({
status: 'Complete',
size: 1048576,
})).toBe('1.0 MB');
});
it('uses a dash while a directory size is still unknown', () => {
expect(formatDashboardDirectorySize(undefined)).toBe(UNKNOWN_DASHBOARD_ITEM_SIZE);
expect(formatDashboardDirectorySize({ status: 'Loading', size: 0 })).toBe(UNKNOWN_DASHBOARD_ITEM_SIZE);
expect(formatDashboardDirectorySize({ status: 'Error', size: 0 })).toBe(UNKNOWN_DASHBOARD_ITEM_SIZE);
expect(formatDashboardDirectorySize({
status: 'Loading',
size: 0,
})).toBe(UNKNOWN_DASHBOARD_ITEM_SIZE);
expect(formatDashboardDirectorySize({
status: 'Error',
size: 0,
})).toBe(UNKNOWN_DASHBOARD_ITEM_SIZE);
});
});
+4 -1
View File
@@ -31,7 +31,10 @@ describe('sortTaggedItems', () => {
createItem('/docs/alpha', 3),
createItem('/other/beta', 2),
];
const getName = (item: TaggedItem) => item.path.split('/').at(-1) ?? item.path;
function getName(item: TaggedItem) {
return item.path.split('/').at(-1) ?? item.path;
}
expect(sortTaggedItems(items, 'name', getName).map(getName)).toEqual([
'alpha',
+28 -7
View File
@@ -43,8 +43,14 @@ describe('getTaggedSections', () => {
tagId: section.tag.id,
paths: section.items.map(item => item.path),
}))).toEqual([
{ tagId: 'work', paths: ['/work-1', '/work-2'] },
{ tagId: 'personal', paths: ['/personal-1', '/work-2'] },
{
tagId: 'work',
paths: ['/work-1', '/work-2'],
},
{
tagId: 'personal',
paths: ['/personal-1', '/work-2'],
},
]);
});
@@ -68,8 +74,14 @@ describe('getTaggedSections', () => {
tagId: section.tag.id,
count: section.items.length,
}))).toEqual([
{ tagId: 'work', count: 1 },
{ tagId: 'archive', count: 0 },
{
tagId: 'work',
count: 1,
},
{
tagId: 'archive',
count: 0,
},
]);
});
});
@@ -100,9 +112,18 @@ describe('getDashboardTagSections', () => {
tagId: section.tag.id,
count: section.items.length,
}))).toEqual([
{ tagId: 'work', count: 1 },
{ tagId: 'archive', count: 0 },
{ tagId: ORPHANED_TAG_ID, count: 1 },
{
tagId: 'work',
count: 1,
},
{
tagId: 'archive',
count: 0,
},
{
tagId: ORPHANED_TAG_ID,
count: 1,
},
]);
});
});
@@ -163,21 +163,30 @@ describe('virtual-location-action-target', () => {
expect(resolveDuplicateCurrentTabShortcutTarget(
'C:/Users',
getVirtualLocationActionContext([], 'C:/Users'),
)).toEqual({ kind: 'clone-path', path: 'C:/Users' });
)).toEqual({
kind: 'clone-path',
path: 'C:/Users',
});
});
it('clones the locations virtual path when nothing is selected', () => {
expect(resolveDuplicateCurrentTabShortcutTarget(
LOCATIONS_VIRTUAL_PATH,
getVirtualLocationActionContext([], LOCATIONS_VIRTUAL_PATH),
)).toEqual({ kind: 'clone-path', path: LOCATIONS_VIRTUAL_PATH });
)).toEqual({
kind: 'clone-path',
path: LOCATIONS_VIRTUAL_PATH,
});
});
it('clones the wsl host virtual path when nothing is selected', () => {
expect(resolveDuplicateCurrentTabShortcutTarget(
WSL_HOST_VIRTUAL_PATH,
getVirtualLocationActionContext([], WSL_HOST_VIRTUAL_PATH),
)).toEqual({ kind: 'clone-path', path: WSL_HOST_VIRTUAL_PATH });
)).toEqual({
kind: 'clone-path',
path: WSL_HOST_VIRTUAL_PATH,
});
});
it('opens the selected location when browsing virtual locations', () => {
+4 -1
View File
@@ -58,7 +58,10 @@ export const DASHBOARD_CARD_ENTRANCE: StaggerAnimationOptions = {
export function getDashboardContentEntranceBinding(): {
class: string;
style: { animationDelay: string; animationDuration: string };
style: {
animationDelay: string;
animationDuration: string;
};
} {
return {
class: DASHBOARD_CONTENT_ENTRANCE_CLASS,
+10 -4
View File
@@ -100,9 +100,12 @@ export function getVirtualLocationActionContext(
};
}
export type DuplicateCurrentTabShortcutTarget =
| { kind: 'open-selection' }
| { kind: 'clone-path'; path: string };
export type DuplicateCurrentTabShortcutTarget
= | { kind: 'open-selection' }
| {
kind: 'clone-path';
path: string;
};
export function resolveDuplicateCurrentTabShortcutTarget(
currentDirectoryPath: string | null | undefined,
@@ -116,5 +119,8 @@ export function resolveDuplicateCurrentTabShortcutTarget(
return null;
}
return { kind: 'clone-path', path: currentDirectoryPath };
return {
kind: 'clone-path',
path: currentDirectoryPath,
};
}