From 4e1926588bbffe00fb7e0db61f8411a3518f77dc Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Wed, 16 Sep 2026 12:09:58 +0200 Subject: [PATCH] Show the licence, attribution and source offer in the interface Two obligations the licence creates had nowhere to land. The attribution term requires the notice to stay visible in the Appropriate Legal Notices a work displays, and section 13 owes the Corresponding Source to anyone interacting with the program over a network - neither is discharged by a file sitting in a repository the user of a deployment never sees. So the About dialog is not decoration: it is the only place either obligation is met, which is why the component says so in a comment. A deployment that drops it stops complying with the licence it ships under, and the attribution term would be asking of others something this interface does not do itself. The login page sits outside this layout, so an unauthenticated visitor still sees none of it. Co-Authored-By: Claude Opus 5 (1M context) --- src/app/layouts/app-layout/app-layout.html | 14 ++ src/app/layouts/app-layout/app-layout.ts | 16 ++- src/app/shared/about-dialog/about-dialog.css | 133 ++++++++++++++++++ src/app/shared/about-dialog/about-dialog.html | 66 +++++++++ src/app/shared/about-dialog/about-dialog.ts | 47 +++++++ 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 src/app/shared/about-dialog/about-dialog.css create mode 100644 src/app/shared/about-dialog/about-dialog.html create mode 100644 src/app/shared/about-dialog/about-dialog.ts diff --git a/src/app/layouts/app-layout/app-layout.html b/src/app/layouts/app-layout/app-layout.html index e8e82c7..635ffaa 100644 --- a/src/app/layouts/app-layout/app-layout.html +++ b/src/app/layouts/app-layout/app-layout.html @@ -1,3 +1,9 @@ + +
@@ -31,6 +37,10 @@ Admin users } + @if (changePasswordSuccess()) { @@ -40,6 +50,10 @@
} + @if (aboutOpen()) { + + } + @if (changePasswordOpen() && loggedUser()?.username; as username) { - ISTI-CNR +// SPDX-License-Identifier: AGPL-3.0-or-later +// Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM. + import { afterNextRender, ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -9,12 +13,13 @@ import { ChangePasswordRequest } from '@models/user'; import { Authorization } from '@services/authorization/authorization'; import { BlocksService } from '@services/blocks/blocks'; import { ContainersService } from '@services/containers/containers'; +import { AboutDialogComponent } from '@shared/about-dialog/about-dialog'; import { ChangePasswordDialogComponent } from '@shared/change-password-dialog/change-password-dialog'; import { scheduleSignalClear } from '@utilities/temporary-signal'; @Component({ selector: 'app-app-layout', - imports: [RouterOutlet, RouterLink, RouterLinkActive, ChangePasswordDialogComponent, MatButtonModule, MatIconModule, MatMenuModule, MatTabsModule, MatToolbarModule], + imports: [RouterOutlet, RouterLink, RouterLinkActive, AboutDialogComponent, ChangePasswordDialogComponent, MatButtonModule, MatIconModule, MatMenuModule, MatTabsModule, MatToolbarModule], templateUrl: './app-layout.html', styleUrl: './app-layout.css', changeDetection: ChangeDetectionStrategy.OnPush @@ -28,6 +33,7 @@ export class AppLayout { private containersService = inject(ContainersService); loggedUser = this.authService.loggedInUser; + aboutOpen = signal(false); changePasswordOpen = signal(false); changePasswordSaving = signal(false); changePasswordError = signal(null); @@ -58,6 +64,14 @@ logout() { }); } + openAboutDialog() { + this.aboutOpen.set(true); + } + + closeAboutDialog() { + this.aboutOpen.set(false); + } + openChangePasswordDialog() { if (!this.loggedUser()?.username?.trim()) return; this.changePasswordError.set(null); diff --git a/src/app/shared/about-dialog/about-dialog.css b/src/app/shared/about-dialog/about-dialog.css new file mode 100644 index 0000000..571e628 --- /dev/null +++ b/src/app/shared/about-dialog/about-dialog.css @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Lucio Lelii - ISTI-CNR + * SPDX-License-Identifier: AGPL-3.0-or-later + * Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM. + */ + +/* Matches the chrome of the password dialogs; kept separate because this one is not a form. */ +.about-dialog-backdrop { + position: fixed; + inset: 0; + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: rgba(15, 23, 42, 0.5); +} + +.about-dialog-modal { + width: min(560px, calc(100vw - 32px)); + max-height: min(80vh, 720px); + overflow: auto; + border: 1px solid #e2e8f0; + border-radius: 20px; + background: #ffffff; + box-shadow: 0 24px 64px rgba(15, 23, 42, 0.28); + padding: 24px; +} + +.about-dialog-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; +} + +.about-dialog-title { + margin: 0; + font-size: 20px; + font-weight: 600; + color: #0f172a; +} + +.about-dialog-subtitle { + margin: 4px 0 0; + font-size: 13px; + color: #64748b; +} + +.about-dialog-body { + margin-top: 20px; + display: flex; + flex-direction: column; + gap: 18px; + font-size: 13.5px; + line-height: 1.6; + color: #334155; +} + +.about-dialog-body p { + margin: 0; +} + +.about-dialog-section-title { + margin: 0 0 6px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: #1d4ed8; +} + +.about-dialog-copyright { + font-weight: 600; + color: #0f172a; +} + +.about-dialog-affiliation { + font-size: 12.5px; + color: #64748b; +} + +.about-dialog-note { + font-size: 12.5px; + color: #64748b; + margin-bottom: 6px; +} + +.about-dialog-attribution { + margin: 0; + padding: 10px 14px; + border-left: 3px solid #2563eb; + border-radius: 0 8px 8px 0; + background: #f1f5f9; + font-style: italic; + color: #0f172a; +} + +.about-dialog-links { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 6px; +} + +.about-dialog-links li { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + +.about-dialog-link-icon { + font-size: 18px; + width: 18px; + height: 18px; + color: #64748b; +} + +.about-dialog-body a { + color: #1d4ed8; + word-break: break-all; +} + +.about-dialog-body code { + padding: 1px 5px; + border: 1px solid #e2e8f0; + border-radius: 4px; + background: #f1f5f9; + font-size: 12px; +} diff --git a/src/app/shared/about-dialog/about-dialog.html b/src/app/shared/about-dialog/about-dialog.html new file mode 100644 index 0000000..d49d891 --- /dev/null +++ b/src/app/shared/about-dialog/about-dialog.html @@ -0,0 +1,66 @@ + + +
+
+
+
+

About HumAIn Flow

+

Licence, attribution and source code

+
+ +
+ +
+
+ +

{{ affiliation }}

+
+ +
+

Licence

+

+ HumAIn Flow is free software, released under the + {{ licenseName }}, + with an additional attribution term under section 7(b) of that licence. +

+
+ +
+

Attribution

+

+ This notice must be preserved in any work based on HumAIn Flow. +

+
{{ attribution }}
+
+ +
+

Source code

+

+ You are entitled to the complete source of the version you are using, including any + modification made to it. +

+ +
+ +
+

Citation

+

+ If you use HumAIn Flow in academic work, please cite it. The citation metadata is in the + CITATION.cff file of each repository. +

+
+
+
+
diff --git a/src/app/shared/about-dialog/about-dialog.ts b/src/app/shared/about-dialog/about-dialog.ts new file mode 100644 index 0000000..60796cb --- /dev/null +++ b/src/app/shared/about-dialog/about-dialog.ts @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2025-2026 Lucio Lelii - ISTI-CNR +// SPDX-License-Identifier: AGPL-3.0-or-later +// Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM. + +import { ChangeDetectionStrategy, Component, output } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; + +/** + * The application's Appropriate Legal Notices, in the sense section 0 of the GNU AGPL gives that + * term. + * + * This is not decoration. Two obligations are discharged here and nowhere else in the interface: + * the attribution that LICENSE-ADDENDUM requires every derivative to keep visible to its users, + * and the offer of the Corresponding Source that section 13 owes to anyone interacting with the + * program over a network. Changing the wording is fine; removing either one puts the deployment + * out of compliance with the licence it ships under. + */ +@Component({ + selector: 'app-about-dialog', + imports: [MatButtonModule, MatIconModule], + templateUrl: './about-dialog.html', + styleUrl: './about-dialog.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AboutDialogComponent { + readonly closed = output(); + + readonly copyright = 'Copyright © 2025-2026 Lucio Lelii'; + readonly affiliation = + 'Institute of Information Science and Technologies "A. Faedo" (ISTI-CNR), Pisa, Italy'; + + readonly attribution = 'Based on HumAIn Flow, originally developed by Lucio Lelii (ISTI-CNR)'; + + readonly licenseName = 'GNU Affero General Public License v3.0 or later'; + readonly licenseUrl = 'https://www.gnu.org/licenses/agpl-3.0.html'; + + readonly sources = [ + { label: 'Web interface', url: 'https://github.com/luciolelii/humanaiFlow-angular' }, + { label: 'Backend service', url: 'https://github.com/luciolelii/humainflow-service' } + ]; + + close(event?: Event) { + event?.stopPropagation(); + this.closed.emit(); + } +}