diff --git a/.github/workflows/pages.yaml b/.github/workflows/pages.yaml
index 7c9191d6..1f6e038b 100644
--- a/.github/workflows/pages.yaml
+++ b/.github/workflows/pages.yaml
@@ -54,7 +54,7 @@ jobs:
- run: rm package.json pnpm-lock.yaml
- name: Publish to Cloudflare Pages
- if: "github.repository_owner == 'NuschtOS'"
+ if: github.event_name == 'push'
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
diff --git a/nix/frontend.nix b/nix/frontend.nix
index c7bf7a87..f4c04a21 100644
--- a/nix/frontend.nix
+++ b/nix/frontend.nix
@@ -24,7 +24,7 @@ stdenv.mkDerivation (finalAttrs: {
strictDeps = true;
postPatch = ''
- substituteInPlace src/index.html \
+ substituteInPlace src/index.html public/opensearch-options.xml public/opensearch-packages.xml \
--replace-fail '##TITLE##' ${lib.escapeShellArg config.title}
# remove development files
diff --git a/package.json b/package.json
index dd3c5a1d..fce4cf5f 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"@angular/common": "^22.0.0",
"@angular/compiler": "^22.0.0",
"@angular/core": "^22.0.0",
+ "@angular/cdk": "^22.0.0",
"@angular/forms": "^22.0.0",
"@angular/platform-browser": "^22.0.0",
"@angular/router": "^22.0.0",
diff --git a/public/opensearch-options.xml b/public/opensearch-options.xml
new file mode 100644
index 00000000..6e1b3335
--- /dev/null
+++ b/public/opensearch-options.xml
@@ -0,0 +1,8 @@
+
+
+ ##TITLE## - Options
+ ##TITLE## options search
+ UTF-8
+ favicon.ico
+
+
diff --git a/public/opensearch-packages.xml b/public/opensearch-packages.xml
new file mode 100644
index 00000000..c6b8dbea
--- /dev/null
+++ b/public/opensearch-packages.xml
@@ -0,0 +1,8 @@
+
+
+ ##TITLE## - Packages
+ ##TITLE## packages search
+ UTF-8
+ favicon.ico
+
+
diff --git a/src/app/core/opensearch-link.ts b/src/app/core/opensearch-link.ts
new file mode 100644
index 00000000..815571b5
--- /dev/null
+++ b/src/app/core/opensearch-link.ts
@@ -0,0 +1,18 @@
+import { CONFIG } from './config.domain';
+
+export type OpenSearchType = 'options' | 'packages';
+
+export function setOpenSearchLink(document: Document, type: OpenSearchType): void {
+ const existing = document.head.querySelector('link[data-nuschtos-opensearch]');
+ const link = existing ?? document.head.appendChild(document.createElement('link'));
+
+ link.rel = 'search';
+ link.type = 'application/opensearchdescription+xml';
+ link.dataset['nuschtosOpensearch'] = 'true';
+ link.title = `${CONFIG.title} ${type === 'options' ? 'Options' : 'Packages'} Search`;
+ link.href = `${CONFIG.baseHref}opensearch-${type}.xml`;
+}
+
+export function clearOpenSearchLink(document: Document): void {
+ document.head.querySelector('link[data-nuschtos-opensearch]')?.remove();
+}
diff --git a/src/app/pages/options/options-page.component.ts b/src/app/pages/options/options-page.component.ts
index d7931074..0c821ed8 100644
--- a/src/app/pages/options/options-page.component.ts
+++ b/src/app/pages/options/options-page.component.ts
@@ -1,11 +1,12 @@
import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnDestroy } from '@angular/core';
-import { CONFIG } from '../../core/config.domain';
+import { DOCUMENT } from '@angular/common';
import { OptionComponent } from '../../core/components/option/option.component';
import { OptionsService } from '../../core/data/options.service';
import { OptionsSearchComponent } from '../../core/components/search/search.component';
import { RouterLink } from '@angular/router';
-import { AsyncPipe, DecimalPipe, formatNumber } from '@angular/common';
+import { AsyncPipe, formatNumber } from '@angular/common';
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
+import { clearOpenSearchLink, setOpenSearchLink } from '../../core/opensearch-link';
@Component({
selector: 'app-options',
@@ -27,8 +28,10 @@ export class OptionsPageComponent implements OnDestroy {
constructor(
protected readonly searchService: OptionsService,
+ @Inject(DOCUMENT) private readonly document: Document,
@Inject(LOCALE_ID) private readonly locale: string
) {
+ setOpenSearchLink(this.document, 'options');
this.searchService.getIndexSize()
.pipe(takeUntil(this.destroy$))
.subscribe(size => {
@@ -36,6 +39,7 @@ export class OptionsPageComponent implements OnDestroy {
});
}
ngOnDestroy(): void {
+ clearOpenSearchLink(this.document);
this.destroy$.next();
this.destroy$.complete();
}
diff --git a/src/app/pages/packages/packages-page.component.ts b/src/app/pages/packages/packages-page.component.ts
index 92a7008e..b00cba3c 100644
--- a/src/app/pages/packages/packages-page.component.ts
+++ b/src/app/pages/packages/packages-page.component.ts
@@ -1,9 +1,11 @@
import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnDestroy } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
import { PackagesSearchComponent } from '../../core/components/search/search.component';
import { PackagesService } from '../../core/data/packages.service';
import { PackageComponent } from "../../core/components/package/package.component";
import { AsyncPipe, formatNumber } from '@angular/common';
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
+import { clearOpenSearchLink, setOpenSearchLink } from '../../core/opensearch-link';
@Component({
selector: 'app-packages-page.component',
@@ -23,8 +25,10 @@ export class PackagesPageComponent implements OnDestroy {
constructor(
protected readonly searchService: PackagesService,
+ @Inject(DOCUMENT) private readonly document: Document,
@Inject(LOCALE_ID) private readonly locale: string
) {
+ setOpenSearchLink(this.document, 'packages');
this.searchService.getIndexSize()
.pipe(takeUntil(this.destroy$))
.subscribe(size => {
@@ -32,6 +36,7 @@ export class PackagesPageComponent implements OnDestroy {
});
}
ngOnDestroy(): void {
+ clearOpenSearchLink(this.document);
this.destroy$.next();
this.destroy$.complete();
}