Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/licenses.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Update Third-Party Licenses

on:
push:
branches: [main]
paths:
- 'package-lock.json'
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
update-licenses:
name: Update Licenses
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- run: npm ci

- name: Generate third-party license file
run: npx generate-license-file --input package.json --output THIRD_PARTY_LICENSES.txt --overwrite

- name: Check for changes
id: check
run: |
if git diff --quiet THIRD_PARTY_LICENSES.txt; then
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "changed=true" >> $GITHUB_OUTPUT
fi

- name: Create Pull Request
if: steps.check.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v7
with:
commit-message: 'chore: update third-party licenses'
title: 'chore: update third-party licenses'
body: |
This PR updates the THIRD_PARTY_LICENSES.txt file with the latest
license information from npm dependencies.

Auto-generated by the licenses workflow.
branch: chore/update-licenses
delete-branch: true
labels: dependencies
4,014 changes: 4,014 additions & 0 deletions THIRD_PARTY_LICENSES.txt

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
"$lib/*": ["./src/renderer/src/lib/*"],
"$paraglide/*": ["./src/renderer/src/paraglide/*"]
},
"ignoreDependencies": ["tailwindcss", "daisyui"]
"ignoreDependencies": ["tailwindcss", "daisyui"],
"ignoreBinaries": ["generate-license-file"]
}
5 changes: 5 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
"settings_licenseModal_agree": "By installing this model, you agree to the terms of the {license} license.",
"settings_licenseModal_acceptInstall": "Accept & Install",

"licenses_title": "Third Party Licenses",
"licenses_notFound": "License information is not available.",

"common_button_close": "Close",

"sidebar_analysis": "Analysis",
"sidebar_detections": "Detections",
"sidebar_map": "Map",
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
"files": [
"out/**/*"
],
"extraResources": [
{
"from": "THIRD_PARTY_LICENSES.txt",
"to": "THIRD_PARTY_LICENSES.txt"
}
],
"extraMetadata": {
"main": "out/main/index.js"
},
Expand Down
5 changes: 5 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@
click: () => mainWindow?.webContents.send('menu:setup-wizard'),
},
{ type: 'separator' },
{
label: 'Third Party Licenses',
click: () => mainWindow?.webContents.send('menu:show-licenses'),
},
{ type: 'separator' },
{
label: 'About Birda GUI',
click: () => {
Expand Down Expand Up @@ -205,7 +210,7 @@
// Read saved language preference
let language = 'en';
try {
const settingsRaw = await fs.promises.readFile(

Check warning on line 213 in src/main/index.ts

View workflow job for this annotation

GitHub Actions / Lint, Type Check & Build

Found readFile from package "fs" with non literal argument at index 0
path.join(app.getPath('userData'), 'birda-gui-settings.json'),
'utf-8',
);
Expand Down
2 changes: 2 additions & 0 deletions src/main/ipc/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { registerAnalysisHandlers } from './analysis';
import { registerCatalogHandlers } from './catalog';
import { registerFileHandlers } from './files';
import { registerLabelHandlers } from './labels';
import { registerLicenseHandlers } from './licenses';
import { registerModelHandlers } from './models';
import { registerSettingsHandlers } from './settings';

Expand All @@ -10,6 +11,7 @@ export async function registerHandlers(): Promise<void> {
registerCatalogHandlers();
registerFileHandlers();
registerLabelHandlers();
registerLicenseHandlers();
registerModelHandlers();
await registerSettingsHandlers();
}
17 changes: 17 additions & 0 deletions src/main/ipc/licenses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ipcMain, app } from 'electron';
import fs from 'fs';
import path from 'path';

export function registerLicenseHandlers(): void {
ipcMain.handle('app:get-licenses', async () => {
const licensePath = app.isPackaged
? path.join(process.resourcesPath, 'THIRD_PARTY_LICENSES.txt')
: path.join(app.getAppPath(), 'THIRD_PARTY_LICENSES.txt');

try {
return await fs.promises.readFile(licensePath, 'utf-8');
} catch {
return null;
}
});
}
2 changes: 2 additions & 0 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const ALLOWED_INVOKE_CHANNELS = new Set([
'fs:read-coordinates',
'fs:scan-source',
'fs:open-in-explorer',
'app:get-licenses',
]);

const ALLOWED_RECEIVE_CHANNELS = new Set([
Expand All @@ -49,6 +50,7 @@ const ALLOWED_RECEIVE_CHANNELS = new Set([
'menu:focus-search',
'menu:toggle-log',
'menu:setup-wizard',
'menu:show-licenses',
]);

contextBridge.exposeInMainWorld('birda', {
Expand Down
11 changes: 11 additions & 0 deletions src/renderer/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ProgressPanel from '$lib/components/ProgressPanel.svelte';
import LogPanel from '$lib/components/LogPanel.svelte';
import SetupWizard from '$lib/components/SetupWizard.svelte';
import LicenseViewer from '$lib/components/LicenseViewer.svelte';
import AnalysisPage from './pages/AnalysisPage.svelte';
import DetectionsPage from './pages/DetectionsPage.svelte';
import MapPage from './pages/MapPage.svelte';
Expand All @@ -27,12 +28,15 @@
offLog,
onSetupWizard,
offSetupWizard,
onShowLicenses,
offShowLicenses,
} from '$lib/utils/ipc';
import { setupMenuListeners } from '$lib/utils/shortcuts';
import { onMount, onDestroy } from 'svelte';

let cleanupMenu: (() => void) | null = null;
let showWizard = $state<boolean | null>(null); // null = loading, true/false = resolved
let showLicenses = $state(false);

async function handleWizardComplete() {
try {
Expand Down Expand Up @@ -142,6 +146,10 @@
showWizard = true;
});

onShowLicenses(() => {
showLicenses = true;
});

cleanupMenu = setupMenuListeners({
onOpenFile: (path: string) => {
appState.sourcePath = path;
Expand Down Expand Up @@ -173,6 +181,7 @@
offAnalysisProgress();
offLog();
offSetupWizard();
offShowLicenses();
cleanupMenu?.();
});
</script>
Expand Down Expand Up @@ -207,3 +216,5 @@
</div>
</main>
{/if}

<LicenseViewer bind:open={showLicenses} />
68 changes: 68 additions & 0 deletions src/renderer/src/lib/components/LicenseViewer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts">
import { X, Loader } from '@lucide/svelte';
import { getLicenses } from '$lib/utils/ipc';
import * as m from '$paraglide/messages';

let {
open = $bindable(), // eslint-disable-line @typescript-eslint/no-useless-default-assignment -- $bindable() required for Svelte bind:
}: { open: boolean } = $props();

let licenseText = $state<string | null>(null);
let loading = $state(false);
let error = $state<string | null>(null);

$effect(() => {
if (open && licenseText === null) {
loading = true;
error = null;
getLicenses()
.then((text) => {
licenseText = text;
})
.catch((e: unknown) => {
error = e instanceof Error ? e.message : String(e);
})
.finally(() => {
loading = false;
});
}
});

function close() {
open = false;
}
</script>

{#if open}
<dialog class="modal modal-open">
<div class="modal-box flex h-[80vh] max-w-4xl flex-col">
<div class="flex items-center justify-between">
<h2 class="text-lg font-semibold">{m.licenses_title()}</h2>
<button onclick={close} class="btn btn-ghost btn-sm btn-square">
<X size={20} />
</button>
</div>

<div class="mt-4 flex-1 overflow-auto">
{#if loading}
<div class="flex items-center justify-center py-12">
<Loader size={24} class="animate-spin" />
</div>
{:else if error}
<p class="text-error text-sm">{error}</p>
{:else if licenseText}
<pre class="text-base-content/70 font-mono text-xs leading-relaxed whitespace-pre-wrap">{licenseText}</pre>
{:else}
<p class="text-base-content/50 text-sm">{m.licenses_notFound()}</p>
{/if}
</div>

<div class="modal-action">
<button onclick={close} class="btn">{m.common_button_close()}</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button onclick={close}>close</button>
</form>
</dialog>
{/if}
13 changes: 13 additions & 0 deletions src/renderer/src/lib/utils/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,19 @@ export function exportRegionAsWav(wavBytes: Uint8Array, defaultName?: string): P
return window.birda.invoke('clip:export-region', base64, defaultName) as Promise<string | null>;
}

// Licenses
export function getLicenses(): Promise<string | null> {
return window.birda.invoke('app:get-licenses') as Promise<string | null>;
}

export function onShowLicenses(callback: () => void): void {
window.birda.on('menu:show-licenses', callback as (...args: unknown[]) => void);
}

export function offShowLicenses(): void {
window.birda.removeAllListeners('menu:show-licenses');
}

// Spectrogram cache
export function saveSpectrogram(clipPath: string, freqMax: number, height: number, dataUrl: string): Promise<string> {
return window.birda.invoke('clip:save-spectrogram', clipPath, freqMax, height, dataUrl) as Promise<string>;
Expand Down
Loading